@@ -18,13 +18,15 @@ import upArrow from '../images/up-arrow.svg?byContent';
18
18
import exitIcon from '../images/exit.svg?byContent' ;
19
19
20
20
function searchOverlay ( query , caseInsensitive ) {
21
- if ( typeof query == 'string' )
21
+ // if the query is a string, we need to convert it into a regular expression
22
+ if ( typeof query == 'string' ) {
22
23
query = new RegExp (
23
24
query . replace ( / [ \- \[ \] \/ \{ \} \( \) \* \+ \? \. \\ \^ \$ \| ] / g, '\\$&' ) ,
24
25
caseInsensitive ? 'gi' : 'g'
25
26
) ;
26
- else if ( ! query . global )
27
+ } else if ( ! query . global ) {
27
28
query = new RegExp ( query . source , query . ignoreCase ? 'gi' : 'g' ) ;
29
+ }
28
30
29
31
return {
30
32
token : function ( stream ) {
@@ -42,13 +44,15 @@ function searchOverlay(query, caseInsensitive) {
42
44
} ;
43
45
}
44
46
47
+ // SearchState is a constructor function that initializes an object to keep track of search-related settings
45
48
function SearchState ( ) {
46
49
this . posFrom = this . posTo = this . lastQuery = this . query = null ;
47
50
this . overlay = null ;
48
51
this . regexp = false ;
49
52
this . caseInsensitive = true ;
50
53
this . wholeWord = false ;
51
54
this . replaceStarted = false ;
55
+ this . lastFileName = 'sketch.js' ;
52
56
}
53
57
54
58
function getSearchState ( cm ) {
@@ -60,6 +64,51 @@ function getSearchCursor(cm, query, pos) {
60
64
return cm . getSearchCursor ( query , pos , getSearchState ( cm ) . caseInsensitive ) ;
61
65
}
62
66
67
+ function watchFileChanges ( cm , searchState , searchField ) {
68
+ let observer = null ;
69
+
70
+ function setupObserver ( ) {
71
+ var fileNameElement = document . querySelector ( '.editor__file-name span' ) ;
72
+
73
+ if ( ! fileNameElement ) {
74
+ setTimeout ( setupObserver , 500 ) ;
75
+ return ;
76
+ }
77
+
78
+ if ( observer ) {
79
+ return ;
80
+ }
81
+
82
+ observer = new MutationObserver ( ( ) => {
83
+ if ( searchField . value . length > 1 ) {
84
+ startSearch ( cm , searchState , searchField . value ) ;
85
+ }
86
+ } ) ;
87
+
88
+ observer . observe ( fileNameElement , { characterData : true , subtree : true } ) ;
89
+ }
90
+
91
+ function disconnectObserver ( ) {
92
+ if ( observer ) {
93
+ observer . disconnect ( ) ;
94
+ observer = null ;
95
+ }
96
+ }
97
+
98
+ setupObserver ( ) ;
99
+
100
+ // continuously check for the dialog's existence (every 500ms)
101
+ setInterval ( ( ) => {
102
+ var searchDialog = document . querySelector ( '.CodeMirror-dialog' ) ;
103
+ if ( ! searchDialog && observer ) {
104
+ disconnectObserver ( ) ;
105
+ return ;
106
+ } else if ( searchDialog && ! observer ) {
107
+ setupObserver ( ) ;
108
+ }
109
+ } , 500 ) ;
110
+ }
111
+
63
112
function isMouseClick ( event ) {
64
113
if ( event . detail > 0 ) return true ;
65
114
else return false ;
@@ -88,6 +137,9 @@ function persistentDialog(cm, text, deflt, onEnter, replaceOpened, onKeyDown) {
88
137
89
138
var state = getSearchState ( cm ) ;
90
139
140
+ watchFileChanges ( cm , getSearchState ( cm ) , searchField ) ;
141
+
142
+ // this runs when the user types in the search box
91
143
CodeMirror . on ( searchField , 'keyup' , function ( e ) {
92
144
state . replaceStarted = false ;
93
145
if ( e . keyCode !== 13 && searchField . value . length > 1 ) {
@@ -101,8 +153,8 @@ function persistentDialog(cm, text, deflt, onEnter, replaceOpened, onKeyDown) {
101
153
} ) ;
102
154
103
155
CodeMirror . on ( closeButton , 'click' , function ( ) {
104
- clearSearch ( cm ) ;
105
156
dialog . parentNode . removeChild ( dialog ) ;
157
+ clearSearch ( cm ) ;
106
158
cm . focus ( ) ;
107
159
} ) ;
108
160
@@ -349,44 +401,66 @@ function parseQuery(query, state) {
349
401
}
350
402
351
403
function startSearch ( cm , state , query ) {
352
- state . queryText = query ;
353
- state . lastQuery = query ;
354
- state . query = parseQuery ( query , state ) ;
355
- cm . removeOverlay ( state . overlay , state . caseInsensitive ) ;
356
- state . overlay = searchOverlay ( state . query , state . caseInsensitive ) ;
357
- cm . addOverlay ( state . overlay ) ;
358
- if ( cm . showMatchesOnScrollbar ) {
359
- if ( state . annotate ) {
360
- state . annotate . clear ( ) ;
361
- state . annotate = null ;
404
+ var searchDialog = document . querySelector ( '.CodeMirror-dialog' ) ;
405
+ if ( searchDialog ) {
406
+ // check if the file has changed
407
+ let currentFileName = document . querySelector ( '.editor__file-name span' )
408
+ ?. innerText ;
409
+
410
+ if ( state . lastFileName !== currentFileName ) {
411
+ state . lastFileName = currentFileName ; // update stored filename
412
+ state . queryText = null ;
413
+ state . lastQuery = null ;
414
+ state . query = null ;
415
+ cm . removeOverlay ( state . overlay ) ;
416
+ state . overlay = null ;
417
+
418
+ if ( searchDialog ) {
419
+ cm . display . wrapper . querySelector (
420
+ '.CodeMirror-search-results'
421
+ ) . innerText = '0/0' ;
422
+ }
362
423
}
363
- state . annotate = cm . showMatchesOnScrollbar (
364
- state . query ,
365
- state . caseInsensitive
366
- ) ;
367
- }
368
424
369
- //Updating the UI everytime the search input changes
370
- var cursor = getSearchCursor ( cm , state . query ) ;
371
- cursor . findNext ( ) ;
372
- var num_match = cm . state . search . annotate . matches . length ;
373
- //no matches found
374
- if ( num_match == 0 ) {
375
- cm . display . wrapper . querySelector (
376
- '.CodeMirror-search-results'
377
- ) . innerText = i18n . t ( 'CodemirrorFindAndReplace.NoResults' ) ;
425
+ state . queryText = query ;
426
+ state . lastQuery = query ;
427
+ state . query = parseQuery ( query , state ) ;
378
428
cm . removeOverlay ( state . overlay , state . caseInsensitive ) ;
379
- } else {
380
- var next =
381
- cm . state . search . annotate . matches . findIndex ( ( s ) => {
382
- return (
383
- s . from . ch === cursor . from ( ) . ch && s . from . line === cursor . from ( ) . line
384
- ) ;
385
- } ) + 1 ;
386
- var text_match = next + '/' + num_match ;
387
- cm . display . wrapper . querySelector (
388
- '.CodeMirror-search-results'
389
- ) . innerText = text_match ;
429
+ state . overlay = searchOverlay ( state . query , state . caseInsensitive ) ;
430
+ cm . addOverlay ( state . overlay ) ;
431
+ if ( cm . showMatchesOnScrollbar ) {
432
+ if ( state . annotate ) {
433
+ state . annotate . clear ( ) ;
434
+ state . annotate = null ;
435
+ }
436
+ state . annotate = cm . showMatchesOnScrollbar (
437
+ state . query ,
438
+ state . caseInsensitive
439
+ ) ;
440
+ }
441
+
442
+ // Updating the UI everytime the search input changes
443
+ var cursor = getSearchCursor ( cm , state . query ) ;
444
+ cursor . findNext ( ) ;
445
+ var num_match = cm . state . search . annotate . matches . length ;
446
+ // no matches found
447
+ if ( num_match == 0 ) {
448
+ cm . display . wrapper . querySelector (
449
+ '.CodeMirror-search-results'
450
+ ) . innerText = i18n . t ( 'CodemirrorFindAndReplace.NoResults' ) ;
451
+ cm . removeOverlay ( state . overlay , state . caseInsensitive ) ; // removes any existing search highlights
452
+ } else {
453
+ var next =
454
+ cm . state . search . annotate . matches . findIndex ( ( s ) => {
455
+ return (
456
+ s . from . ch === cursor . from ( ) . ch && s . from . line === cursor . from ( ) . line
457
+ ) ;
458
+ } ) + 1 ;
459
+ var text_match = next + '/' + num_match ;
460
+ cm . display . wrapper . querySelector (
461
+ '.CodeMirror-search-results'
462
+ ) . innerText = text_match ;
463
+ }
390
464
}
391
465
}
392
466
0 commit comments