Skip to content

Commit 8c52e95

Browse files
committed
fix: reset search match counter when switching files (issue #3361)
1 parent 235441d commit 8c52e95

File tree

1 file changed

+112
-38
lines changed

1 file changed

+112
-38
lines changed

client/utils/codemirror-search.js

Lines changed: 112 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,15 @@ import upArrow from '../images/up-arrow.svg?byContent';
1818
import exitIcon from '../images/exit.svg?byContent';
1919

2020
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') {
2223
query = new RegExp(
2324
query.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&'),
2425
caseInsensitive ? 'gi' : 'g'
2526
);
26-
else if (!query.global)
27+
} else if (!query.global) {
2728
query = new RegExp(query.source, query.ignoreCase ? 'gi' : 'g');
29+
}
2830

2931
return {
3032
token: function (stream) {
@@ -42,13 +44,15 @@ function searchOverlay(query, caseInsensitive) {
4244
};
4345
}
4446

47+
// SearchState is a constructor function that initializes an object to keep track of search-related settings
4548
function SearchState() {
4649
this.posFrom = this.posTo = this.lastQuery = this.query = null;
4750
this.overlay = null;
4851
this.regexp = false;
4952
this.caseInsensitive = true;
5053
this.wholeWord = false;
5154
this.replaceStarted = false;
55+
this.lastFileName = 'sketch.js';
5256
}
5357

5458
function getSearchState(cm) {
@@ -60,6 +64,51 @@ function getSearchCursor(cm, query, pos) {
6064
return cm.getSearchCursor(query, pos, getSearchState(cm).caseInsensitive);
6165
}
6266

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+
63112
function isMouseClick(event) {
64113
if (event.detail > 0) return true;
65114
else return false;
@@ -88,6 +137,9 @@ function persistentDialog(cm, text, deflt, onEnter, replaceOpened, onKeyDown) {
88137

89138
var state = getSearchState(cm);
90139

140+
watchFileChanges(cm, getSearchState(cm), searchField);
141+
142+
// this runs when the user types in the search box
91143
CodeMirror.on(searchField, 'keyup', function (e) {
92144
state.replaceStarted = false;
93145
if (e.keyCode !== 13 && searchField.value.length > 1) {
@@ -101,8 +153,8 @@ function persistentDialog(cm, text, deflt, onEnter, replaceOpened, onKeyDown) {
101153
});
102154

103155
CodeMirror.on(closeButton, 'click', function () {
104-
clearSearch(cm);
105156
dialog.parentNode.removeChild(dialog);
157+
clearSearch(cm);
106158
cm.focus();
107159
});
108160

@@ -349,44 +401,66 @@ function parseQuery(query, state) {
349401
}
350402

351403
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+
}
362423
}
363-
state.annotate = cm.showMatchesOnScrollbar(
364-
state.query,
365-
state.caseInsensitive
366-
);
367-
}
368424

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);
378428
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+
}
390464
}
391465
}
392466

0 commit comments

Comments
 (0)