Skip to content
This repository was archived by the owner on Jun 24, 2025. It is now read-only.

Commit 307d94a

Browse files
authored
Merge pull request #1876 from TriliumNext/find_replace
Make the find function for read-only code scroll correctly.
2 parents 696784b + 553b07a commit 307d94a

File tree

4 files changed

+54
-31
lines changed

4 files changed

+54
-31
lines changed

apps/client/src/stylesheets/style.css

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,14 @@ body .CodeMirror {
438438
background-color: #eeeeee;
439439
}
440440

441+
.cm-matchhighlight.ck-find-result{
442+
background: var(--ck-color-highlight-background);
443+
}
444+
445+
.cm-matchhighlight.ck-find-result_selected {
446+
background-color: #ff9633;
447+
}
448+
441449
.CodeMirror pre.CodeMirror-placeholder {
442450
color: #999 !important;
443451
}

apps/client/src/widgets/find.ts

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -143,9 +143,9 @@ export default class FindWidget extends NoteContextAwareWidget {
143143
this.$currentFound = this.$widget.find(".find-widget-current-found");
144144
this.$totalFound = this.$widget.find(".find-widget-total-found");
145145
this.$caseSensitiveCheckbox = this.$widget.find(".find-widget-case-sensitive-checkbox");
146-
this.$caseSensitiveCheckbox.change(() => this.performFind());
146+
this.$caseSensitiveCheckbox.on("change", () => this.performFind());
147147
this.$matchWordsCheckbox = this.$widget.find(".find-widget-match-words-checkbox");
148-
this.$matchWordsCheckbox.change(() => this.performFind());
148+
this.$matchWordsCheckbox.on("change", () => this.performFind());
149149
this.$previousButton = this.$widget.find(".find-widget-previous-button");
150150
this.$previousButton.on("click", () => this.findNext(-1));
151151
this.$nextButton = this.$widget.find(".find-widget-next-button");
@@ -160,7 +160,7 @@ export default class FindWidget extends NoteContextAwareWidget {
160160
this.$replaceButton = this.$widget.find(".replace-widget-replace-button");
161161
this.$replaceButton.on("click", () => this.replace());
162162

163-
this.$input.keydown(async (e) => {
163+
this.$input.on("keydown", async (e) => {
164164
if ((e.metaKey || e.ctrlKey) && (e.key === "F" || e.key === "f")) {
165165
// If ctrl+f is pressed when the findbox is shown, select the
166166
// whole input to find
@@ -172,7 +172,7 @@ export default class FindWidget extends NoteContextAwareWidget {
172172
}
173173
});
174174

175-
this.$widget.keydown(async (e) => {
175+
this.$widget.on("keydown", async (e) => {
176176
if (e.key === "Escape") {
177177
await this.closeSearch();
178178
}
@@ -197,9 +197,14 @@ export default class FindWidget extends NoteContextAwareWidget {
197197
const isReadOnly = await this.noteContext?.isReadOnly();
198198

199199
let selectedText = "";
200-
if (this.note?.type === "code" && !isReadOnly && this.noteContext) {
201-
const codeEditor = await this.noteContext.getCodeEditor();
202-
selectedText = codeEditor.getSelection();
200+
if (this.note?.type === "code" && this.noteContext) {
201+
if (isReadOnly){
202+
const $content = await this.noteContext.getContentElement();
203+
selectedText = $content.find('.cm-matchhighlight').first().text();
204+
} else {
205+
const codeEditor = await this.noteContext.getCodeEditor();
206+
selectedText = codeEditor.getSelection();
207+
}
203208
} else {
204209
selectedText = window.getSelection()?.toString() || "";
205210
}
@@ -235,6 +240,12 @@ export default class FindWidget extends NoteContextAwareWidget {
235240
}
236241
}
237242

243+
async readOnlyTemporarilyDisabledEvent({ noteContext }: EventData<"readOnlyTemporarilyDisabled">) {
244+
if (this.isNoteContext(noteContext.ntxId)) {
245+
await this.closeSearch();
246+
}
247+
}
248+
238249
async getHandler() {
239250
if (this.note?.type === "render") {
240251
return this.htmlHandler;

apps/client/src/widgets/find_in_html.ts

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
// uses for highlighting matches, use the same one on CodeMirror
33
// for consistency
44
import utils from "../services/utils.js";
5-
import appContext from "../components/app_context.js";
65
import type FindWidget from "./find.js";
76
import type { FindResult } from "./find.js";
87

@@ -39,12 +38,16 @@ export default class FindInHtml {
3938
caseSensitive: matchCase,
4039
done: async () => {
4140
this.$results = $content.find(`.${FIND_RESULT_CSS_CLASSNAME}`);
42-
this.currentIndex = 0;
41+
const scrollingContainer = $content[0].closest('.scrolling-container');
42+
const containerTop = scrollingContainer?.getBoundingClientRect().top ?? 0;
43+
const closestIndex = this.$results.toArray().findIndex(el => el.getBoundingClientRect().top >= containerTop);
44+
this.currentIndex = closestIndex >= 0 ? closestIndex : 0;
45+
4346
await this.jumpTo();
4447

4548
res({
4649
totalFound: this.$results.length,
47-
currentFound: Math.min(1, this.$results.length)
50+
currentFound: this.$results.length > 0 ? this.currentIndex + 1 : 0
4851
});
4952
}
5053
});
@@ -71,27 +74,17 @@ export default class FindInHtml {
7174

7275
async findBoxClosed(totalFound: number, currentFound: number) {
7376
const $content = await this.parent?.noteContext?.getContentElement();
74-
if ($content) {
77+
if (typeof $content?.unmark === 'function') {
7578
$content.unmark();
7679
}
7780
}
7881

7982
async jumpTo() {
8083
if (this.$results?.length) {
81-
const offsetTop = 100;
8284
const $current = this.$results.eq(this.currentIndex);
8385
this.$results.removeClass(FIND_RESULT_SELECTED_CSS_CLASSNAME);
84-
85-
if ($current.length) {
86-
$current.addClass(FIND_RESULT_SELECTED_CSS_CLASSNAME);
87-
const position = $current.position().top - offsetTop;
88-
89-
const $content = await this.parent.noteContext?.getContentElement();
90-
if ($content) {
91-
const $contentWidget = appContext.getComponentByEl($content[0]);
92-
$contentWidget.triggerCommand("scrollContainerTo", { position });
93-
}
94-
}
86+
$current[0].scrollIntoView();
87+
$current.addClass(FIND_RESULT_SELECTED_CSS_CLASSNAME);
9588
}
9689
}
9790
}

apps/client/src/widgets/find_in_text.ts

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -55,15 +55,26 @@ export default class FindInText {
5555
const options = { matchCase: matchCase, wholeWords: wholeWord };
5656
findResult = textEditor.execute("find", searchTerm, options);
5757
totalFound = findResult.results.length;
58-
// Find the result beyond the cursor
59-
const cursorPos = model.document.selection.getLastPosition();
60-
for (let i = 0; i < findResult.results.length; ++i) {
61-
const marker = findResult.results.get(i)?.marker;
62-
const fromPos = marker?.getStart();
63-
if (cursorPos && fromPos && fromPos.compareWith(cursorPos) !== "before") {
64-
currentFound = i;
65-
break;
58+
const selection = model.document.selection;
59+
// If text is selected, highlight the corresponding result;
60+
// otherwise, highlight the first visible result in the scrolling container.
61+
if (!selection.isCollapsed) {
62+
const cursorPos = selection.getFirstPosition();
63+
for (let i = 0; i < findResult.results.length; ++i) {
64+
const marker = findResult.results.get(i)?.marker;
65+
const fromPos = marker?.getStart();
66+
if (cursorPos && fromPos?.compareWith(cursorPos) !== "before") {
67+
currentFound = i;
68+
break;
69+
}
6670
}
71+
} else {
72+
const editorEl = textEditor?.sourceElement;
73+
const findResultElement = editorEl?.querySelectorAll(".ck-find-result");
74+
const scrollingContainer = editorEl?.closest('.scrolling-container');
75+
const containerTop = scrollingContainer?.getBoundingClientRect().top ?? 0;
76+
const closestIndex = Array.from(findResultElement ?? []).findIndex((el) => el.getBoundingClientRect().top >= containerTop);
77+
currentFound = closestIndex >= 0 ? closestIndex : 0;
6778
}
6879
}
6980

0 commit comments

Comments
 (0)