Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: Spellchecker for Flutter Quill #2118

Merged
merged 8 commits into from
Aug 18, 2024

Conversation

CatHood0
Copy link
Collaborator

@CatHood0 CatHood0 commented Aug 17, 2024

Description

For many versions I have been wondering how we could add spell-checking to the project or when it would even be implemented. However, now here is a feature that surely more than one of the thousands who use the package have wanted for their project, but that, unfortunately, had not been easy to implement.

Thanks to the simple_spell_checker package, getting which words are not spelled correctly from those that are was never so easy and fast.

Now, in order to activate this Feature we would have to take into account the existence of the SpellcheckerService class which allows us to create any custom implementation for our editor to directly use without any cover-ups.

abstract class SpellCheckerService<T> {
  SpellCheckerService({required this.language});

  final String language;

  /// dispose all the resources used for SpellcheckerService
  ///
  /// if [onlyPartial] is true just dispose a part of the SpellcheckerService
  /// (this comes from the implementation)
  ///
  /// if [onlyPartial] is false dispose all resources
  void dispose({bool onlyPartial = false});

  /// set a new language state used for SpellcheckerService
  void setNewLanguageState({required String language});

  /// update language a custom language into the SpellcheckerService
  void updateCustomLanguageIfExist({required T languageIdentifier});

  /// set a new custom language for SpellcheckerService
  void addCustomLanguage({required T languageIdentifier});

  /// Facilitates a spell check request.
  ///
  /// Returns a [List<TextSpan>] with all misspelled words divide from the right words.
  List<TextSpan>? checkSpelling(String text,
      {LongPressGestureRecognizer Function(String)?
          customLongPressRecognizerOnWrongSpan});
}

By default DefaultSpellcheckerService is the one that the editor uses, which as we can see, has no function because it directly returns a null value.

class DefaultSpellCheckerService extends SpellCheckerService<Object?> {
  DefaultSpellCheckerService() : super(language: 'en');

  @override
  void dispose({bool onlyPartial = false}) {}

  @override
  List<TextSpan>? checkSpelling(
    String text, {
    LongPressGestureRecognizer Function(String p1)? customLongPressRecognizerOnWrongSpan,
  }) {
    return null;
  }

  @override
  void addCustomLanguage({languageIdentifier}) {}

  @override
  void setNewLanguageState({required String language}) {}

  @override
  void updateCustomLanguageIfExist({languageIdentifier}) {}
}

And now, if we want to activate this new functionality that we added, we would have to use SimpleSpellCheckerService which contains the implementation that we need to be able to activate spell-checking as if we were in Word or LibreOffice (obviously I'm exaggerating)

class SimpleSpellCheckerService extends SpellCheckerService<LanguageIdentifier> {
  SimpleSpellCheckerService({required super.language})
      : checker = SimpleSpellChecker(
          language: language,
          safeDictionaryLoad: true,
        );

  /// [SimpleSpellChecker] comes from the package [simple_spell_checker]
  /// that give us all necessary methods for get our spans with highlighting
  /// where needed
  final SimpleSpellChecker checker;

  @override
  List<TextSpan>? checkSpelling(
    String text, {
    LongPressGestureRecognizer Function(String word)? customLongPressRecognizerOnWrongSpan,
  }) {
    return checker.check(
      text,
      customLongPressRecognizerOnWrongSpan: customLongPressRecognizerOnWrongSpan,
    );
  }

  @override
  void dispose({bool onlyPartial = false}) {
    if (onlyPartial) {
      checker.disposeControllers();
      return;
    }
    checker.dispose();
  }

  @override
  void addCustomLanguage({required languageIdentifier}) {
    checker
      ..registerLanguage(languageIdentifier.language)
      ..addCustomLanguage(languageIdentifier);
  }

  @override
  void setNewLanguageState({required String language}) {
    checker.setNewLanguageToState(language);
  }

  @override
  void updateCustomLanguageIfExist({required languageIdentifier}) {
    checker.updateCustomLanguageIfExist(languageIdentifier);
  }
}

In order to make our instance valid, we can use:

SpellCheckerServiceProvider.setNewCheckerService(SimpleSpellCheckerService(language: '<your_language>'));
//or
FlutterQuillExtensions.useSpellCheckerService('<your_language>');

And voilá, we will have at our disposal a powerful tool that will be useful for those who write novels, notes, or any type of document that requires correct spelling and grammar.

Note

It should be noted that the dispose() method of SpellcheckerService is not just for show. This method, at least for SimpleSpellCheckerService, is responsible for closing the spell-checker itself once we no longer use it. This is important to note because SimpleSpellChecker is used within our custom service, and it contains several StreamControllers that need to be closed to avoid memory issues.

SpellcheckerServiceProvider.dispose(); // call when spell-checker will no longer used

A little preview of this Feat:

there's some issue with some words, but this is an error from simple_spell_checker since i delete case sensitive (i'll fix it later)

clideo_editor_c7df3d3218b0457db07845b1d3efdefe.mp4
  • New feature: Adds new functionality without breaking existing features.
  • 🛠️ Bug fix: Resolves an issue without altering current behavior.
  • 🧹 Code refactor: Code restructuring that does not affect behavior.
  • Breaking change: Alters existing functionality and requires updates.
  • 🧪 Tests: Adds new tests or modifies existing tests.
  • 📝 Documentation: Updates or additions to documentation.
  • 🗑️ Chore: Routine tasks, or maintenance.
  • Build configuration change: Changes to build or deploy processes.

@CatHood0 CatHood0 added the enhancement New feature or request label Aug 17, 2024
@CatHood0 CatHood0 self-assigned this Aug 17, 2024
@CatHood0 CatHood0 marked this pull request as draft August 17, 2024 12:15
@CatHood0 CatHood0 changed the title Feat: added spellchecker service Feat: Spellchecker for Flutter Quill Aug 17, 2024
@CatHood0 CatHood0 requested a review from EchoEllet August 18, 2024 07:28
@CatHood0 CatHood0 marked this pull request as ready for review August 18, 2024 12:51
@CatHood0
Copy link
Collaborator Author

CatHood0 commented Aug 18, 2024

Note: i'll move SimpleSpellCheckerServiceImpl to flutter_quill_extensions but by now i need first add this to the new version of the repo and after i'll can be able to add it in the extensions

@CatHood0 CatHood0 requested a review from singerdmx August 18, 2024 12:56
Copy link
Owner

@singerdmx singerdmx left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice

@singerdmx singerdmx merged commit 0d12456 into singerdmx:master Aug 18, 2024
2 checks passed
@EchoEllet
Copy link
Collaborator

EchoEllet commented Aug 18, 2024

first add this to the new version of the repo and after i'll can be able to add it in the extensions

You can use the local development mode, which will use packages from your local machine. The CI will force push the publishing, and then we only need to increase the minimum version of flutter_quill.

You will find a dart script in the scripts directory.

dart ./scripts/enable_local_dev.dart

@FelixMittermeier
Copy link
Collaborator

@CatHood0 Thanks for the useful contribution! I wanted to test it in my app but was not able to make it work. Is there any setup required to make it work and highlight the misspelled words? Thanks

@CatHood0
Copy link
Collaborator Author

CatHood0 commented Aug 18, 2024

@FelixMittermeier Can you show the code that are you using? Remenber always set the instance of SimpleSpellCheckerServiceImpl using SpellcheckerServiceProvider to activate correctly the feature. By now the languages supported by default are: pt, fr, es, en, it, no, sv, and de. By now you need to know of the implementation of the feature is experimental yet since i cannot make a perfect spell checker.

there's some issue with some words, but this is an error from simple_spell_checker since i delete case sensitive

I accidentally do this, and several words now are not showed as incorrect now. But it should still work you write random text

@FelixMittermeier
Copy link
Collaborator

@CatHood0 Thanks for your answer. I missed the part to call:

SpellcheckerServiceProvider.setInstance(SimpleSpellCheckerImpl(language: 'de'));

first. By the way, in your first message you wrote: SimpleSpellCheckerServiceImpl but this is a different name which does not exist. Maybe you can modify it there as well to avoid confusion.

Also I noticed it's unfortunately not ready for production yet. For example if the text contains special characters ("Umlaute" in German) like ä, ö, ü etc. then the spell checker somehow completely removes them. My string input was for example:

damit es ausgewählt ist
then the ä was just removed from the string by the spell checker package:

IMG_217BBC0A5F6D-1

@CatHood0
Copy link
Collaborator Author

CatHood0 commented Aug 18, 2024

@FelixMittermeier this is an issue from the Spell checker package. I'll fix it. Thanks for show the characters that are being ignored!

@CatHood0
Copy link
Collaborator Author

CatHood0 commented Aug 18, 2024

@FelixMittermeier please, take a look newly about the PR since i change some parts to be make more sense with the next version that moves this spell checker to extension and solve your issue.

@CatHood0 CatHood0 deleted the spell_checker_feature branch August 19, 2024 00:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants