feat(redis): add in-content search for string values

This commit is contained in:
onenewcode authored and GitHub committed 2026-07-24 09:32:30 +08:00
1 parent 405dca5bb4
commit 192d874554
7 files changed
+753 -23

No files matched your search

@@ -1,5 +1,5 @@
import { shallowRef, onBeforeUnmount, getCurrentInstance, type ShallowRef, createApp, watch } from "vue";
import { EditorState, Compartment } from "@codemirror/state";
import { EditorSelection, EditorState, Compartment } from "@codemirror/state";
import { EditorView, keymap, drawSelection, dropCursor, highlightSpecialChars, highlightActiveLine, highlightActiveLineGutter, lineNumbers } from "@codemirror/view";
import { json } from "@codemirror/lang-json";
import { search as cmSearch } from "@codemirror/search";
@@ -31,6 +31,11 @@ export interface UseCellDetailEditorOptions {
lineWrapping?: () => boolean;
/** Add CodeMirror fold controls and keyboard bindings for structured source. */
folding?: boolean;
/**
* When false, Mod+F / replace are not bound so a parent (e.g. Redis value viewer)
* can own the unified find surface. Default true for grid cell editors.
*/
enableBuiltinFind?: boolean;
editorTheme: () => EditorTheme;
appAppearance: () => AppThemeAppearance;
appPalette: () => AppThemePalette;
@@ -44,6 +49,7 @@ export interface UseCellDetailEditorReturn {
getValue: () => string;
openSearch: () => boolean;
openReplace: () => boolean;
selectRange: (from: number, to: number, options?: { focus?: boolean }) => boolean;
destroy: () => void;
view: Readonly<ShallowRef<EditorView | null>>;
}
@@ -224,16 +230,21 @@ export function useCellDetailEditor(options: UseCellDetailEditorOptions): UseCel
...defaultKeymap,
...historyKeymap,
...(options.folding ? foldKeymap : []),
{
key: shortcutToCodeMirrorKey(shortcuts.find),
preventDefault: true,
run: () => openSearch(),
},
{
key: shortcutToCodeMirrorKey(shortcuts.replace),
preventDefault: true,
run: () => openReplace(),
},
// Callers may disable find so a parent surface (e.g. RedisValueViewer) owns Mod+F.
...(options.enableBuiltinFind === false
? []
: [
{
key: shortcutToCodeMirrorKey(shortcuts.find),
preventDefault: true,
run: () => openSearch(),
},
{
key: shortcutToCodeMirrorKey(shortcuts.replace),
preventDefault: true,
run: () => openReplace(),
},
]),
]),
languageComp.of(currentIsJson ? json() : []),
lineWrappingComp.of(options.lineWrapping?.() ? EditorView.lineWrapping : []),
@@ -339,6 +350,21 @@ export function useCellDetailEditor(options: UseCellDetailEditorOptions): UseCel
return (searchInstance as any)?.openReplace?.() ?? false;
}
function selectRange(from: number, to: number, options?: { focus?: boolean }): boolean {
const editor = view.value;
if (!editor || destroyed) return false;
const max = editor.state.doc.length;
const start = Math.max(0, Math.min(from, max));
const end = Math.max(0, Math.min(to, max));
editor.dispatch({
selection: EditorSelection.range(start, end),
scrollIntoView: true,
});
// Default focus for explicit navigation; callers can pass focus:false while typing in a find box.
if (options?.focus !== false) editor.focus();
return true;
}
function destroy() {
const alreadyDestroyed = destroyed;
destroyed = true;
@@ -365,5 +391,5 @@ export function useCellDetailEditor(options: UseCellDetailEditorOptions): UseCel
});
}
return { create, setValue, getValue, openSearch, openReplace, destroy, view };
return { create, setValue, getValue, openSearch, openReplace, selectRange, destroy, view };
}