Skip to content

Commit 48b7f56

Browse files
feat(editor): add highlighting for search terms (#606)
1 parent 6563870 commit 48b7f56

File tree

2 files changed

+79
-0
lines changed

2 files changed

+79
-0
lines changed

src/renderer/components/editor/Editor.vue

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ const {
2929
isEmpty,
3030
selectedSnippetIds,
3131
isAvailableToCodePreview,
32+
searchQuery,
3233
} = useSnippets()
3334
const {
3435
isShowMarkdown,
@@ -43,6 +44,7 @@ const { addToUpdateContentQueue } = useSnippetUpdate()
4344
4445
const isDark = useDark()
4546
let editor: CodeMirror.Editor | null = null
47+
let currentSearchOverlay: any = null
4648
4749
const isProgrammaticChange = ref(false)
4850
@@ -208,6 +210,11 @@ async function init() {
208210
watch(selectedSnippetContent, (v) => {
209211
nextTick(() => {
210212
setValue(v?.value || '')
213+
nextTick(() => {
214+
if (searchQuery.value) {
215+
updateSearchOverlay()
216+
}
217+
})
211218
})
212219
})
213220
@@ -328,8 +335,72 @@ function onSplitterLayout() {
328335
editor?.refresh()
329336
}
330337
338+
function createSearchOverlay(query: string) {
339+
if (!query)
340+
return null
341+
342+
let regexp: RegExp
343+
344+
try {
345+
regexp = new RegExp(query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'gi')
346+
}
347+
catch {
348+
return null
349+
}
350+
351+
return {
352+
token: (stream: any) => {
353+
regexp.lastIndex = stream.pos
354+
const match = regexp.exec(stream.string)
355+
if (match && match.index === stream.pos) {
356+
stream.pos += match[0].length
357+
return 'searching'
358+
}
359+
else if (match) {
360+
stream.pos = match.index
361+
}
362+
else {
363+
stream.skipToEnd()
364+
}
365+
},
366+
}
367+
}
368+
369+
function updateSearchOverlay() {
370+
if (!editor)
371+
return
372+
373+
if (currentSearchOverlay) {
374+
editor.removeOverlay(currentSearchOverlay)
375+
currentSearchOverlay = null
376+
}
377+
378+
if (searchQuery.value) {
379+
currentSearchOverlay = createSearchOverlay(searchQuery.value)
380+
if (currentSearchOverlay) {
381+
editor.addOverlay(currentSearchOverlay)
382+
383+
// Scroll to the first match
384+
const cursor = editor.getSearchCursor(
385+
searchQuery.value,
386+
{ line: 0, ch: 0 },
387+
true,
388+
)
389+
if (cursor.findNext()) {
390+
editor.scrollIntoView(cursor.from(), 50)
391+
}
392+
}
393+
}
394+
}
395+
331396
onMounted(() => {
332397
init()
398+
399+
watch(searchQuery, () => {
400+
nextTick(() => {
401+
updateSearchOverlay()
402+
})
403+
})
333404
})
334405
</script>
335406

@@ -430,4 +501,10 @@ onMounted(() => {
430501
.CodeMirror-scrollbar-filler {
431502
background-color: transparent;
432503
}
504+
505+
.CodeMirror .cm-searching {
506+
background-color: var(--color-text-highlight);
507+
color: black !important;
508+
border-radius: 2px;
509+
}
433510
</style>

src/renderer/styles.css

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ body {
5959

6060
--color-text: oklch(20% 0 0);
6161
--color-text-muted: oklch(60% 0 0);
62+
--color-text-highlight: oklch(90.5% 0.182 98.111);
6263

6364
--color-list-selection: oklch(92% 0 0);
6465
--color-list-selection-fg: oklch(20% 0 0);
@@ -93,6 +94,7 @@ html.dark {
9394

9495
--color-text: oklch(75% 0 0);
9596
--color-text-muted: oklch(60% 0 0);
97+
--color-text-highlight: oklch(90.5% 0.182 98.111);
9698

9799
--color-list-selection: oklch(32% 0 0);
98100
--color-list-selection-fg: oklch(95% 0 0);

0 commit comments

Comments
 (0)