浏览代码

add search

lucky1213 4 年前
父节点
当前提交
bac6bdf30e

+ 20
- 4
packages/notus/lib/src/document.dart 查看文件

@@ -12,6 +12,7 @@ import 'document/line.dart';
12 12
 import 'document/node.dart';
13 13
 import 'heuristics.dart';
14 14
 import 'history.dart';
15
+import 'search.dart';
15 16
 
16 17
 /// Source of a [NotusChange].
17 18
 enum ChangeSource {
@@ -44,6 +45,7 @@ class NotusDocument {
44 45
         // _delta = Delta()..insert('\n') {
45 46
       _history = NotusHistory(),
46 47
         _delta = Delta()..insert('\n') {
48
+      _searchController = NotusSearch(_delta);
47 49
     _loadDocument(_delta);
48 50
   }
49 51
 
@@ -51,6 +53,7 @@ class NotusDocument {
51 53
       : _heuristics = NotusHeuristics.fallback,
52 54
       _history = NotusHistory(),
53 55
         _delta = Delta.fromJson(data) {
56
+          _searchController = NotusSearch(_delta);
54 57
     _loadDocument(_delta);
55 58
   }
56 59
 
@@ -58,6 +61,7 @@ class NotusDocument {
58 61
       : assert(delta != null),
59 62
         _heuristics = NotusHeuristics.fallback,
60 63
         _history = NotusHistory(),
64
+        _searchController = NotusSearch(delta),
61 65
         _delta = delta {
62 66
     _loadDocument(_delta);
63 67
   }
@@ -73,6 +77,13 @@ class NotusDocument {
73 77
 
74 78
   NotusHistory get history => _history;
75 79
 
80
+  NotusSearch _searchController;
81
+
82
+  set searchController(NotusSearch value) {
83
+    _searchController = value;
84
+  }
85
+  NotusSearch get searchController => _searchController;
86
+
76 87
   /// The root node of this document tree.
77 88
   RootNode get root => _root;
78 89
   final RootNode _root = RootNode();
@@ -172,7 +183,7 @@ class NotusDocument {
172 183
   /// Returns an instance of [Delta] actually composed into this document.
173 184
   /// The returned [Delta] may be empty in which case this document remains
174 185
   /// unchanged and no [NotusChange] is published to [changes] stream.
175
-  Delta format(int index, int length, NotusAttribute attribute) {
186
+  Delta  format(int index, int length, NotusAttribute attribute, {bool search = false}) {
176 187
     assert(index >= 0 && length >= 0 && attribute != null);
177 188
 
178 189
     var change = Delta();
@@ -188,7 +199,7 @@ class NotusDocument {
188 199
     final formatChange =
189 200
         _heuristics.applyFormatRules(this, index, length, attribute);
190 201
     if (formatChange.isNotEmpty) {
191
-      compose(formatChange, ChangeSource.local);
202
+      compose(formatChange, ChangeSource.local, search: search);
192 203
       change = change.compose(formatChange);
193 204
     }
194 205
 
@@ -230,7 +241,7 @@ class NotusDocument {
230 241
   /// of this document.
231 242
   ///
232 243
   /// In case the [change] is invalid, behavior of this method is unspecified.
233
-  void compose(Delta change, ChangeSource source, {bool history}) {
244
+  void compose(Delta change, ChangeSource source, {bool history, bool search}) {
234 245
     _checkMutable();
235 246
     change.trim();
236 247
     assert(change.isNotEmpty);
@@ -258,7 +269,8 @@ class NotusDocument {
258 269
     // _controller.add(NotusChange(before, change, source));
259 270
     final notusChange = NotusChange(before, change, source);
260 271
     _controller.add(notusChange);
261
-    if (history != true) _history?.handleDocChange(notusChange);
272
+    if (history != true && search != true) _history?.handleDocChange(notusChange);
273
+    if (search != true) _searchController = NotusSearch(_delta);
262 274
   }
263 275
 
264 276
   //
@@ -311,6 +323,10 @@ class NotusDocument {
311 323
     }
312 324
   }
313 325
 
326
+  void search(String text) {
327
+    _searchController.search(this, text);
328
+  }
329
+
314 330
   Delta undo() {
315 331
     return _history?.undo(this);
316 332
   }

+ 22
- 0
packages/notus/lib/src/search.dart 查看文件

@@ -0,0 +1,22 @@
1
+import 'package:notus/notus.dart';
2
+import 'package:notus/src/document.dart';
3
+import 'package:quill_delta/quill_delta.dart';
4
+
5
+///
6
+/// record users operation or api change(Collaborative editing)
7
+/// used for redo or undo function
8
+///
9
+class NotusSearch {
10
+  const NotusSearch(this.delta);
11
+  final Delta delta;
12
+
13
+  void search(NotusDocument doc, String text) {
14
+    doc.compose(delta, ChangeSource.remote, search: true);
15
+    if (text.isNotEmpty) {
16
+      var result = RegExp(r''+ text + '', caseSensitive: false, multiLine: true).allMatches(NotusDocument.fromDelta(delta).toPlainText()).toList();
17
+      for (var item in result) {
18
+        doc.format(item.start, item.end - item.start, NotusAttribute.highlight, search: true);
19
+      }
20
+    }
21
+  }
22
+}

+ 6
- 12
packages/zefyr/example/lib/src/full_page.dart 查看文件

@@ -33,7 +33,7 @@ class FullPageEditorScreen extends StatefulWidget {
33 33
 
34 34
 Delta getDelta() {
35 35
   final doc =
36
-    r'[{"insert":"Zefyr", "attributes": {"color": "#595959", "highlight": true}},{"insert":"\n","attributes":{"heading":1}},{"insert":"Soft and gentle rich text editing for Flutter applications.","attributes":{"i":true}},{"insert":"\n"},{"insert":"​","attributes":{"embed":{"type":"image","source":"asset://images/breeze.jpg"}}},{"insert":"\n"},{"insert":"Photo by Hiroyuki Takeda.","attributes":{"i":true}},{"insert":"\nZefyr is currently in "},{"insert":"early preview","attributes":{"b":true}},{"insert":". If you have a feature request or found a bug, please file it at the "},{"insert":"issue tracker","attributes":{"a":"https://github.com/memspace/zefyr/issues"}},{"insert":'
36
+    r'[{"insert":"Zefyr", "attributes": {"color": "#595959"}},{"insert":"\n","attributes":{"heading":1}},{"insert":"Soft and gentle rich text editing for Flutter applications.","attributes":{"i":true}},{"insert":"\n"},{"insert":"​","attributes":{"embed":{"type":"image","source":"asset://images/breeze.jpg"}}},{"insert":"\n"},{"insert":"Photo by Hiroyuki Takeda.","attributes":{"i":true}},{"insert":"\nZefyr is currently in "},{"insert":"early preview","attributes":{"b":true}},{"insert":". If you have a feature request or found a bug, please file it at the "},{"insert":"issue tracker","attributes":{"a":"https://github.com/memspace/zefyr/issues"}},{"insert":'
37 37
     r'".\nDocumentation"},{"insert":"\n","attributes":{"heading":3}},{"insert":"Quick Start","attributes":{"a":"https://github.com/memspace/zefyr/blob/master/doc/quick_start.md"}},{"insert":"\n","attributes":{"block":"ul"}},{"insert":"Data Format and Document Model","attributes":{"a":"https://github.com/memspace/zefyr/blob/master/doc/data_and_document.md"}},{"insert":"\n","attributes":{"block":"ul"}},{"insert":"Style Attributes","attributes":{"a":"https://github.com/memspace/zefyr/blob/master/doc/attr'
38 38
     r'ibutes.md"}},{"insert":"\n","attributes":{"block":"ul"}},{"insert":"Heuristic Rules","attributes":{"a":"https://github.com/memspace/zefyr/blob/master/doc/heuristics.md"}},{"insert":"\n","attributes":{"block":"ul"}},{"insert":"FAQ","attributes":{"a":"https://github.com/memspace/zefyr/blob/master/doc/faq.md"}},{"insert":"\n","attributes":{"block":"ul"}},{"insert":"Clean and modern look"},{"insert":"\n","attributes":{"heading":2}},{"insert":"Zefyr’s rich text editor is built with simplicity and fle'
39 39
     r'xibility in mind. It provides clean interface for distraction-free editing. Think Medium.com-like experience.\nMarkdown inspired semantics"},{"insert":"\n","attributes":{"heading":2}},{"insert":"Ever needed to have a heading line inside of a quote block, like this:\nI’m a Markdown heading"},{"insert":"\n","attributes":{"block":"quote","heading":3}},{"insert":"And I’m a regular paragraph"},{"insert":"\n","attributes":{"block":"quote"}},{"insert":"Code blocks"},{"insert":"\n","attributes":{"headin'
@@ -88,17 +88,11 @@ class _FullPageEditorScreenState extends State<FullPageEditorScreen> {
88 88
         children: <Widget>[
89 89
           Container(
90 90
             height: 50,
91
-            child: RichText(
92
-              text: TextSpan(
93
-                text: '123',
94
-                style: TextStyle(
95
-                  color: Colors.black
96
-                ),
97
-                recognizer: TapGestureRecognizer()..onTap = () {
98
-                  print('object');
99
-                }
100
-              ),
101
-            ),
91
+            child: TextField(
92
+              onChanged: (text) {
93
+                _controller.search(text);
94
+              },
95
+            )
102 96
           ),
103 97
           Expanded(
104 98
                       child: ZefyrScaffold(

+ 20
- 4
packages/zefyr/lib/src/widgets/controller.dart 查看文件

@@ -162,8 +162,8 @@ class ZefyrController extends ChangeNotifier {
162 162
     notifyListeners();
163 163
   }
164 164
 
165
-  void formatText(int index, int length, NotusAttribute attribute) {
166
-    final change = document.format(index, length, attribute);
165
+  void formatText(int index, int length, NotusAttribute attribute, {bool search = false}) {
166
+    final change = document.format(index, length, attribute, search: search);
167 167
     _lastChangeSource = ChangeSource.local;
168 168
 
169 169
     if (length == 0 &&
@@ -209,12 +209,28 @@ class ZefyrController extends ChangeNotifier {
209 209
 
210 210
   void undo() {
211 211
     Delta change = document.undo();
212
-    updateSelection(TextSelection.collapsed(offset: change.transformPosition(_selection.extentOffset, force: false)));
212
+    if (change != null) {
213
+      var offset = change.transformPosition(_selection.extentOffset, force: true);
214
+      updateSelection(TextSelection.collapsed(offset: offset), source: ChangeSource.local);
215
+    }
213 216
   }
214 217
 
215 218
   void redo() {
216 219
     Delta change = document.redo();
217
-    updateSelection(TextSelection.collapsed(offset: change.transformPosition(_selection.extentOffset, force: true)));
220
+    if (change != null) {
221
+      var offset = change.transformPosition(_selection.extentOffset, force: true);
222
+      updateSelection(TextSelection.collapsed(offset: offset), source: ChangeSource.local);
223
+    }
224
+  }
225
+
226
+  void search(String text) {
227
+    document.search(text);
228
+    _lastChangeSource = ChangeSource.remote;
229
+    notifyListeners();
230
+    // var result = RegExp(r''+ text + '', caseSensitive: false, multiLine: true).allMatches(plainTextEditingValue.text).toList();
231
+    // for (var item in result) {
232
+    //   formatText(item.start, item.end - item.start, NotusAttribute.highlight, ignoreChange: true);
233
+    // }
218 234
   }
219 235
   
220 236
   /// Returns style of specified text range.