@@ -17,51 +17,76 @@ export const decorateFindReplace: Decorate<FindReplaceConfig> = ({
17
17
}
18
18
19
19
const texts = node . children . map ( ( it ) => it . text ) ;
20
+ const str = texts . join ( '' ) . toLowerCase ( ) ;
21
+ const searchLower = search . toLowerCase ( ) ;
22
+
23
+ let start = 0 ;
24
+ const matches : number [ ] = [ ] ;
25
+ while ( ( start = str . indexOf ( searchLower , start ) ) !== - 1 ) {
26
+ matches . push ( start ) ;
27
+ start += searchLower . length ;
28
+ }
20
29
21
- // Try to find a match
22
- const matchStart = texts . join ( '' ) . toLowerCase ( ) . indexOf ( search . toLowerCase ( ) ) ;
23
- if ( matchStart === - 1 ) {
30
+ if ( ! matches . length ) {
24
31
return [ ] ;
25
32
}
26
33
27
- const matchEnd = matchStart + search . length ;
28
- let cumulativePosition = 0 ;
29
34
const ranges : SearchRange [ ] = [ ] ;
35
+ let cumulativePosition = 0 ;
36
+ let matchIndex = 0 ; // Index in the matches array
30
37
31
- for ( const [ i , text ] of texts . entries ( ) ) {
38
+ for ( const [ textIndex , text ] of texts . entries ( ) ) {
32
39
const textStart = cumulativePosition ;
33
- const textEnd = cumulativePosition + text . length ;
34
-
35
- // Corresponding offsets within the text string
36
- const overlapStart = Math . max ( matchStart , textStart ) ;
37
- const overlapEnd = Math . min ( matchEnd , textEnd ) ;
38
-
39
- if ( overlapStart < overlapEnd ) {
40
- // Overlapping region exists
41
- const anchorOffset = overlapStart - textStart ;
42
- const focusOffset = overlapEnd - textStart ;
43
-
44
- // Corresponding offsets within the search string
45
- const searchOverlapStart = overlapStart - matchStart ;
46
- const searchOverlapEnd = overlapEnd - matchStart ;
47
-
48
- const textNodePath = [ ...path , i ] ;
49
-
50
- ranges . push ( {
51
- anchor : {
52
- path : textNodePath ,
53
- offset : anchorOffset ,
54
- } ,
55
- focus : {
56
- path : textNodePath ,
57
- offset : focusOffset ,
58
- } ,
59
- search : search . substring ( searchOverlapStart , searchOverlapEnd ) ,
60
- [ type ] : true ,
61
- } ) ;
40
+ const textEnd = textStart + text . length ;
41
+
42
+ // Process matches that overlap with the current text node
43
+ while ( matchIndex < matches . length && matches [ matchIndex ] < textEnd ) {
44
+ const matchStart = matches [ matchIndex ] ;
45
+ const matchEnd = matchStart + search . length ;
46
+
47
+ // If the match ends before the start of the current text, move to the next match
48
+ if ( matchEnd <= textStart ) {
49
+ matchIndex ++ ;
50
+ continue ;
51
+ }
52
+
53
+ // Calculate overlap between the text and the current match
54
+ const overlapStart = Math . max ( matchStart , textStart ) ;
55
+ const overlapEnd = Math . min ( matchEnd , textEnd ) ;
56
+
57
+ if ( overlapStart < overlapEnd ) {
58
+ const anchorOffset = overlapStart - textStart ;
59
+ const focusOffset = overlapEnd - textStart ;
60
+
61
+ // Corresponding offsets within the search string
62
+ const searchOverlapStart = overlapStart - matchStart ;
63
+ const searchOverlapEnd = overlapEnd - matchStart ;
64
+
65
+ const textNodePath = [ ...path , textIndex ] ;
66
+
67
+ ranges . push ( {
68
+ anchor : {
69
+ path : textNodePath ,
70
+ offset : anchorOffset ,
71
+ } ,
72
+ focus : {
73
+ path : textNodePath ,
74
+ offset : focusOffset ,
75
+ } ,
76
+ search : search . substring ( searchOverlapStart , searchOverlapEnd ) ,
77
+ [ type ] : true ,
78
+ } ) ;
79
+ }
80
+
81
+ // If the match ends within the current text, move to the next match
82
+ if ( matchEnd <= textEnd ) {
83
+ matchIndex ++ ;
84
+ } else {
85
+ // The match continues in the next text node
86
+ break ;
87
+ }
62
88
}
63
89
64
- // Update the cumulative position for the next iteration
65
90
cumulativePosition = textEnd ;
66
91
}
67
92
0 commit comments