|
|
@@ -7,16 +7,78 @@ import 'package:quill_delta/quill_delta.dart';
|
|
7
|
7
|
/// used for redo or undo function
|
|
8
|
8
|
///
|
|
9
|
9
|
class NotusSearch {
|
|
10
|
|
- const NotusSearch(this.delta);
|
|
11
|
|
- final Delta delta;
|
|
|
10
|
+
|
|
|
11
|
+ Delta undoDelta;
|
|
|
12
|
+ List<RegExpMatch> _result = [];
|
|
|
13
|
+ SearchResultEntity _entity = const SearchResultEntity();
|
|
12
|
14
|
|
|
13
|
|
- void search(NotusDocument doc, String text) {
|
|
14
|
|
- doc.compose(delta, ChangeSource.remote, search: true);
|
|
|
15
|
+ SearchResultEntity search(NotusDocument doc, String text) {
|
|
|
16
|
+ // 恢复
|
|
|
17
|
+ for (var i = 0; i < _result.length; i++) {
|
|
|
18
|
+ RegExpMatch item = _result[i];
|
|
|
19
|
+ doc.format(item.start, item.end - item.start, NotusAttribute.highlight.unset, search: true);
|
|
|
20
|
+ }
|
|
15
|
21
|
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);
|
|
|
22
|
+ int offset = 0;
|
|
|
23
|
+ _result = RegExp(r''+ text + '', caseSensitive: false, multiLine: true).allMatches(doc.toPlainText()).toList();
|
|
|
24
|
+ for (var i = 0; i < _result.length; i++) {
|
|
|
25
|
+ RegExpMatch item = _result[i];
|
|
|
26
|
+ if (i == 0) {
|
|
|
27
|
+ offset = item.end;
|
|
|
28
|
+ }
|
|
|
29
|
+ doc.format(item.start, item.end - item.start, NotusAttribute.highlight.fromDouble(i == 0 ? 0.8 : 0.5), search: true);
|
|
19
|
30
|
}
|
|
|
31
|
+ _entity = _entity.copyWith(current: _result.isEmpty ? 0 : 1, total: _result.length, offset: offset);
|
|
|
32
|
+ } else {
|
|
|
33
|
+ _entity = SearchResultEntity();
|
|
|
34
|
+ }
|
|
|
35
|
+ return _entity;
|
|
|
36
|
+ }
|
|
|
37
|
+
|
|
|
38
|
+ SearchResultEntity next(NotusDocument doc) {
|
|
|
39
|
+ if (_entity.hasNext) {
|
|
|
40
|
+ RegExpMatch current = _result[_entity.current - 1]; // 0 => 1
|
|
|
41
|
+ doc.format(current.start, current.end - current.start, NotusAttribute.highlight.fromDouble(0.5), search: true);
|
|
|
42
|
+
|
|
|
43
|
+ RegExpMatch item = _result[_entity.current];
|
|
|
44
|
+ doc.format(item.start, item.end - item.start, NotusAttribute.highlight.fromDouble(0.8), search: true);
|
|
|
45
|
+ int next = _entity.current + 1;
|
|
|
46
|
+ _entity = _entity.copyWith(current: next, offset: item.end); // 1 => 2
|
|
20
|
47
|
}
|
|
|
48
|
+ return _entity;
|
|
|
49
|
+ }
|
|
|
50
|
+ SearchResultEntity prev(NotusDocument doc) {
|
|
|
51
|
+ if (_entity.hasPrev) {
|
|
|
52
|
+ RegExpMatch current = _result[_entity.current - 1];
|
|
|
53
|
+ doc.format(current.start, current.end - current.start, NotusAttribute.highlight.fromDouble(0.5), search: true);
|
|
|
54
|
+
|
|
|
55
|
+ RegExpMatch item = _result[_entity.current - 2];
|
|
|
56
|
+ doc.format(item.start, item.end - item.start, NotusAttribute.highlight.fromDouble(0.8), search: true);
|
|
|
57
|
+ int prev = _entity.current - 1;
|
|
|
58
|
+ _entity = _entity.copyWith(current: prev == 0 ? 1 : prev, offset: item.end); // 1 => 2
|
|
|
59
|
+ }
|
|
|
60
|
+ return _entity;
|
|
|
61
|
+ }
|
|
|
62
|
+}
|
|
|
63
|
+
|
|
|
64
|
+class SearchResultEntity {
|
|
|
65
|
+ final int total;
|
|
|
66
|
+ final int current;
|
|
|
67
|
+ final bool hasPrev;
|
|
|
68
|
+ final bool hasNext;
|
|
|
69
|
+ final int offset;
|
|
|
70
|
+
|
|
|
71
|
+ const SearchResultEntity({this.total = 0, this.current = 0, this.offset = 0}) : hasPrev = current > 1, hasNext = current < total;
|
|
|
72
|
+
|
|
|
73
|
+ SearchResultEntity copyWith({
|
|
|
74
|
+ int total,
|
|
|
75
|
+ int current,
|
|
|
76
|
+ int offset,
|
|
|
77
|
+ }) {
|
|
|
78
|
+ return SearchResultEntity(
|
|
|
79
|
+ total: total ?? this.total,
|
|
|
80
|
+ current: current ?? this.current,
|
|
|
81
|
+ offset: offset ?? this.offset,
|
|
|
82
|
+ );
|
|
21
|
83
|
}
|
|
22
|
84
|
}
|