Browse Source

add search

lucky1213 4 years ago
parent
commit
bac6bdf30e

+ 20
- 4
packages/notus/lib/src/document.dart View File

12
 import 'document/node.dart';
12
 import 'document/node.dart';
13
 import 'heuristics.dart';
13
 import 'heuristics.dart';
14
 import 'history.dart';
14
 import 'history.dart';
15
+import 'search.dart';
15
 
16
 
16
 /// Source of a [NotusChange].
17
 /// Source of a [NotusChange].
17
 enum ChangeSource {
18
 enum ChangeSource {
44
         // _delta = Delta()..insert('\n') {
45
         // _delta = Delta()..insert('\n') {
45
       _history = NotusHistory(),
46
       _history = NotusHistory(),
46
         _delta = Delta()..insert('\n') {
47
         _delta = Delta()..insert('\n') {
48
+      _searchController = NotusSearch(_delta);
47
     _loadDocument(_delta);
49
     _loadDocument(_delta);
48
   }
50
   }
49
 
51
 
51
       : _heuristics = NotusHeuristics.fallback,
53
       : _heuristics = NotusHeuristics.fallback,
52
       _history = NotusHistory(),
54
       _history = NotusHistory(),
53
         _delta = Delta.fromJson(data) {
55
         _delta = Delta.fromJson(data) {
56
+          _searchController = NotusSearch(_delta);
54
     _loadDocument(_delta);
57
     _loadDocument(_delta);
55
   }
58
   }
56
 
59
 
58
       : assert(delta != null),
61
       : assert(delta != null),
59
         _heuristics = NotusHeuristics.fallback,
62
         _heuristics = NotusHeuristics.fallback,
60
         _history = NotusHistory(),
63
         _history = NotusHistory(),
64
+        _searchController = NotusSearch(delta),
61
         _delta = delta {
65
         _delta = delta {
62
     _loadDocument(_delta);
66
     _loadDocument(_delta);
63
   }
67
   }
73
 
77
 
74
   NotusHistory get history => _history;
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
   /// The root node of this document tree.
87
   /// The root node of this document tree.
77
   RootNode get root => _root;
88
   RootNode get root => _root;
78
   final RootNode _root = RootNode();
89
   final RootNode _root = RootNode();
172
   /// Returns an instance of [Delta] actually composed into this document.
183
   /// Returns an instance of [Delta] actually composed into this document.
173
   /// The returned [Delta] may be empty in which case this document remains
184
   /// The returned [Delta] may be empty in which case this document remains
174
   /// unchanged and no [NotusChange] is published to [changes] stream.
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
     assert(index >= 0 && length >= 0 && attribute != null);
187
     assert(index >= 0 && length >= 0 && attribute != null);
177
 
188
 
178
     var change = Delta();
189
     var change = Delta();
188
     final formatChange =
199
     final formatChange =
189
         _heuristics.applyFormatRules(this, index, length, attribute);
200
         _heuristics.applyFormatRules(this, index, length, attribute);
190
     if (formatChange.isNotEmpty) {
201
     if (formatChange.isNotEmpty) {
191
-      compose(formatChange, ChangeSource.local);
202
+      compose(formatChange, ChangeSource.local, search: search);
192
       change = change.compose(formatChange);
203
       change = change.compose(formatChange);
193
     }
204
     }
194
 
205
 
230
   /// of this document.
241
   /// of this document.
231
   ///
242
   ///
232
   /// In case the [change] is invalid, behavior of this method is unspecified.
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
     _checkMutable();
245
     _checkMutable();
235
     change.trim();
246
     change.trim();
236
     assert(change.isNotEmpty);
247
     assert(change.isNotEmpty);
258
     // _controller.add(NotusChange(before, change, source));
269
     // _controller.add(NotusChange(before, change, source));
259
     final notusChange = NotusChange(before, change, source);
270
     final notusChange = NotusChange(before, change, source);
260
     _controller.add(notusChange);
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
     }
323
     }
312
   }
324
   }
313
 
325
 
326
+  void search(String text) {
327
+    _searchController.search(this, text);
328
+  }
329
+
314
   Delta undo() {
330
   Delta undo() {
315
     return _history?.undo(this);
331
     return _history?.undo(this);
316
   }
332
   }

+ 22
- 0
packages/notus/lib/src/search.dart View File

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 View File

33
 
33
 
34
 Delta getDelta() {
34
 Delta getDelta() {
35
   final doc =
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
     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'
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
     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'
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
     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'
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
         children: <Widget>[
88
         children: <Widget>[
89
           Container(
89
           Container(
90
             height: 50,
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
           Expanded(
97
           Expanded(
104
                       child: ZefyrScaffold(
98
                       child: ZefyrScaffold(

+ 20
- 4
packages/zefyr/lib/src/widgets/controller.dart View File

162
     notifyListeners();
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
     _lastChangeSource = ChangeSource.local;
167
     _lastChangeSource = ChangeSource.local;
168
 
168
 
169
     if (length == 0 &&
169
     if (length == 0 &&
209
 
209
 
210
   void undo() {
210
   void undo() {
211
     Delta change = document.undo();
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
   void redo() {
218
   void redo() {
216
     Delta change = document.redo();
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
   /// Returns style of specified text range.
236
   /// Returns style of specified text range.