From dc1a58cd6887cf013539d63268e051e1972e5af0 Mon Sep 17 00:00:00 2001
From: CatHood0 <santiagowmar@gmail.com>
Date: Sun, 18 Aug 2024 03:27:34 -0400
Subject: [PATCH] Feat: now spell-checking works in real-time

---
 example/lib/screens/quill/quill_screen.dart   |  2 ++
 lib/flutter_quill.dart                        |  1 +
 .../default_spellchecker_service.dart         | 14 ++++++++---
 .../simple_spellchecker_impl.dart             | 25 ++++++++++++++++---
 .../spellchecker/spellchecker_service.dart    |  7 +++++-
 lib/src/editor/widgets/text/text_line.dart    | 15 +++++++++--
 pubspec.yaml                                  |  2 +-
 7 files changed, 55 insertions(+), 11 deletions(-)

diff --git a/example/lib/screens/quill/quill_screen.dart b/example/lib/screens/quill/quill_screen.dart
index b47a8c3d..5366a543 100644
--- a/example/lib/screens/quill/quill_screen.dart
+++ b/example/lib/screens/quill/quill_screen.dart
@@ -43,6 +43,8 @@ class _QuillScreenState extends State<QuillScreen> {
   void initState() {
     super.initState();
     _controller.document = widget.args.document;
+    SpellcheckerServiceProvider.setInstance(
+        SimpleSpellCheckerImpl(language: 'en'));
   }
 
   @override
diff --git a/lib/flutter_quill.dart b/lib/flutter_quill.dart
index ded85364..349ca110 100644
--- a/lib/flutter_quill.dart
+++ b/lib/flutter_quill.dart
@@ -24,6 +24,7 @@ export 'src/editor/raw_editor/config/raw_editor_configurations.dart';
 export 'src/editor/raw_editor/quill_single_child_scroll_view.dart';
 export 'src/editor/raw_editor/raw_editor.dart';
 export 'src/editor/raw_editor/raw_editor_state.dart';
+export 'src/editor/spellchecker/simple_spellchecker_impl.dart';
 export 'src/editor/spellchecker/spellchecker_service.dart';
 export 'src/editor/spellchecker/spellchecker_service_provider.dart';
 export 'src/editor/style_widgets/style_widgets.dart';
diff --git a/lib/src/editor/spellchecker/default_spellchecker_service.dart b/lib/src/editor/spellchecker/default_spellchecker_service.dart
index 67249672..0d6c3a77 100644
--- a/lib/src/editor/spellchecker/default_spellchecker_service.dart
+++ b/lib/src/editor/spellchecker/default_spellchecker_service.dart
@@ -1,11 +1,17 @@
-import 'package:flutter/material.dart';
-import 'spellchecker_service.dart';
+import 'package:flutter/gestures.dart' show LongPressGestureRecognizer;
+import 'package:flutter/material.dart' show TextSpan;
+import 'spellchecker_service.dart' show SpellcheckerService;
 
-class DefaultSpellcheckerService extends SpellcheckerService{
+class DefaultSpellcheckerService extends SpellcheckerService {
   DefaultSpellcheckerService() : super(language: 'en');
 
   @override
-  List<TextSpan>? fetchSpellchecker(String text) {
+  void dispose() {}
+
+  @override
+  List<TextSpan>? fetchSpellchecker(String text,
+      {LongPressGestureRecognizer Function(String p1)?
+          customLongPressRecognizerOnWrongSpan}) {
     return null;
   }
 }
diff --git a/lib/src/editor/spellchecker/simple_spellchecker_impl.dart b/lib/src/editor/spellchecker/simple_spellchecker_impl.dart
index ae806a2b..b68f9135 100644
--- a/lib/src/editor/spellchecker/simple_spellchecker_impl.dart
+++ b/lib/src/editor/spellchecker/simple_spellchecker_impl.dart
@@ -1,13 +1,32 @@
+import 'package:flutter/gestures.dart';
 import 'package:flutter/material.dart';
+import 'package:simple_spell_checker/simple_spell_checker.dart';
 
 import 'spellchecker_service.dart';
 
 class SimpleSpellCheckerImpl extends SpellcheckerService {
-  SimpleSpellCheckerImpl({required super.language});
+  SimpleSpellCheckerImpl({required super.language})
+      : checker = SimpleSpellChecker(
+          language: language,
+          safeDictionaryLoad: true,
+        );
+  final SimpleSpellChecker checker;
 
   @override
-  List<TextSpan>? fetchSpellchecker(String text) {
-    return null;
+  List<TextSpan>? fetchSpellchecker(
+    String text, {
+    LongPressGestureRecognizer Function(String word)?
+        customLongPressRecognizerOnWrongSpan,
+  }) {
+    return checker.check(
+      text,
+      customLongPressRecognizerOnWrongSpan:
+          customLongPressRecognizerOnWrongSpan,
+    );
   }
 
+  @override
+  void dispose() {
+    checker.dispose(closeDirectionary: true);
+  }
 }
diff --git a/lib/src/editor/spellchecker/spellchecker_service.dart b/lib/src/editor/spellchecker/spellchecker_service.dart
index b69ae0bd..22748eb3 100644
--- a/lib/src/editor/spellchecker/spellchecker_service.dart
+++ b/lib/src/editor/spellchecker/spellchecker_service.dart
@@ -1,8 +1,13 @@
+import 'package:flutter/gestures.dart';
 import 'package:flutter/material.dart';
 
 abstract class SpellcheckerService {
   SpellcheckerService({required this.language});
 
   final String language;
-  List<TextSpan>? fetchSpellchecker(String text);
+
+  void dispose();
+  List<TextSpan>? fetchSpellchecker(String text,
+      {LongPressGestureRecognizer Function(String)?
+          customLongPressRecognizerOnWrongSpan});
 }
diff --git a/lib/src/editor/widgets/text/text_line.dart b/lib/src/editor/widgets/text/text_line.dart
index 574c4126..bf3a0ab8 100644
--- a/lib/src/editor/widgets/text/text_line.dart
+++ b/lib/src/editor/widgets/text/text_line.dart
@@ -391,10 +391,8 @@ class _TextLineState extends State<TextLine> {
     final nodeStyle = textNode.style;
     final isLink = nodeStyle.containsKey(Attribute.link.key) &&
         nodeStyle.attributes[Attribute.link.key]!.value != null;
-
     final style =
         _getInlineTextStyle(nodeStyle, defaultStyles, lineStyle, isLink);
-
     if (widget.controller.configurations.requireScriptFontFeatures == false &&
         textNode.value.isNotEmpty) {
       if (nodeStyle.containsKey(Attribute.script.key)) {
@@ -406,6 +404,19 @@ class _TextLineState extends State<TextLine> {
       }
     }
 
+    // verify is node is not link because we never need check a link word
+    if (!isLink) {
+      final service = SpellcheckerServiceProvider.instance;
+      final spellcheckedSpans = service.fetchSpellchecker(textNode.value);
+      if (spellcheckedSpans != null && spellcheckedSpans.isNotEmpty) {
+        return TextSpan(
+          children: spellcheckedSpans,
+          style: style,
+          mouseCursor: null,
+        );
+      }
+    }
+
     final recognizer = _getRecognizer(node, isLink);
     return TextSpan(
       text: textNode.value,
diff --git a/pubspec.yaml b/pubspec.yaml
index 3656e1fc..b2d545c4 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -45,7 +45,7 @@ dependencies:
   # Dart Packages
   intl: ^0.19.0
   dart_quill_delta: ^10.0.0
-  simple_spell_checker: ^1.0.1
+  simple_spell_checker: ^1.0.3
   collection: ^1.17.0
   quiver: ^3.2.1
   equatable: ^2.0.5