feat(editor): add occurrence selection actions
This commit is contained in:
1 parent
91f309f6c7
commit
7d92433689
13 files changed
+284
-1
No files matched your search
@@ -0,0 +1,97 @@
|
||||
// @vitest-environment happy-dom
|
||||
|
||||
import { EditorSelection, EditorState } from "@codemirror/state";
|
||||
import { EditorView } from "@codemirror/view";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { addNextQueryEditorSelectionOccurrence, selectAllQueryEditorSelectionOccurrences } from "@/lib/editor/queryEditorOccurrenceSelection";
|
||||
|
||||
function createView(doc: string, selection: EditorSelection) {
|
||||
return new EditorView({
|
||||
parent: document.createElement("div"),
|
||||
state: EditorState.create({
|
||||
doc,
|
||||
selection,
|
||||
extensions: [EditorState.allowMultipleSelections.of(true)],
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
function ranges(view: EditorView) {
|
||||
return view.state.selection.ranges.map(({ from, to }) => ({ from, to }));
|
||||
}
|
||||
|
||||
describe("queryEditorOccurrenceSelection", () => {
|
||||
it("adds the next occurrence of the current selection", () => {
|
||||
const view = createView("foo bar foo baz foo", EditorSelection.single(0, 3));
|
||||
|
||||
expect(addNextQueryEditorSelectionOccurrence(view)).toBe(true);
|
||||
|
||||
expect(ranges(view)).toEqual([
|
||||
{ from: 0, to: 3 },
|
||||
{ from: 8, to: 11 },
|
||||
]);
|
||||
});
|
||||
|
||||
it("expands an empty cursor to the current word before adding the next occurrence", () => {
|
||||
const view = createView("select id from users where id = 1", EditorSelection.cursor(8));
|
||||
|
||||
expect(addNextQueryEditorSelectionOccurrence(view)).toBe(true);
|
||||
|
||||
expect(ranges(view)).toEqual([
|
||||
{ from: 7, to: 9 },
|
||||
{ from: 27, to: 29 },
|
||||
]);
|
||||
});
|
||||
|
||||
it("word-expands every empty range when multiple cursors are active", () => {
|
||||
// One cursor sits on a word, the second (Alt+click) is empty on another
|
||||
// word: the single-cursor helper cannot run, so the native command
|
||||
// word-expands every empty range and adds the next occurrence.
|
||||
const view = createView("foo bar foo", EditorSelection.create([EditorSelection.range(0, 3), EditorSelection.cursor(4)]));
|
||||
|
||||
expect(addNextQueryEditorSelectionOccurrence(view)).toBe(true);
|
||||
|
||||
// "bar" has no further occurrence, so the selection stays at the two
|
||||
// word ranges — the point is the empty cursor was expanded, not dropped.
|
||||
expect(ranges(view)).toEqual([
|
||||
{ from: 0, to: 3 },
|
||||
{ from: 4, to: 7 },
|
||||
]);
|
||||
});
|
||||
|
||||
it("selects every occurrence of the selected text", () => {
|
||||
const view = createView("foo bar foo baz foo", EditorSelection.single(0, 3));
|
||||
|
||||
expect(selectAllQueryEditorSelectionOccurrences(view)).toBe(true);
|
||||
|
||||
expect(ranges(view)).toEqual([
|
||||
{ from: 0, to: 3 },
|
||||
{ from: 8, to: 11 },
|
||||
{ from: 16, to: 19 },
|
||||
]);
|
||||
});
|
||||
|
||||
it("selects every occurrence after the next occurrence was already added", () => {
|
||||
const view = createView("foo bar foo baz foo", EditorSelection.single(0, 3));
|
||||
|
||||
expect(addNextQueryEditorSelectionOccurrence(view)).toBe(true);
|
||||
expect(selectAllQueryEditorSelectionOccurrences(view)).toBe(true);
|
||||
|
||||
expect(ranges(view)).toEqual([
|
||||
{ from: 0, to: 3 },
|
||||
{ from: 8, to: 11 },
|
||||
{ from: 16, to: 19 },
|
||||
]);
|
||||
});
|
||||
|
||||
it("selects all occurrences of the word around an empty cursor", () => {
|
||||
const view = createView("select id from users where id = 1", EditorSelection.cursor(8));
|
||||
|
||||
expect(selectAllQueryEditorSelectionOccurrences(view)).toBe(true);
|
||||
|
||||
expect(ranges(view)).toEqual([
|
||||
{ from: 7, to: 9 },
|
||||
{ from: 27, to: 29 },
|
||||
]);
|
||||
});
|
||||
});
|
||||
@@ -1,5 +1,16 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { closeOtherTabsDefaultShortcut, DEFAULT_SHORTCUT_SETTINGS, SHORTCUT_DEFINITIONS, findShortcutConflict, formatShortcut, normalizeModifierOnlyShortcut, normalizeShortcutSettings, shortcutToCodeMirrorKey, type ShortcutActionId } from "@/lib/editor/shortcutRegistry";
|
||||
import {
|
||||
closeOtherTabsDefaultShortcut,
|
||||
DEFAULT_SHORTCUT_SETTINGS,
|
||||
SHORTCUT_DEFINITIONS,
|
||||
findShortcutConflict,
|
||||
formatShortcut,
|
||||
normalizeModifierOnlyShortcut,
|
||||
normalizeShortcutSettings,
|
||||
selectionOccurrenceDefaultShortcut,
|
||||
shortcutToCodeMirrorKey,
|
||||
type ShortcutActionId,
|
||||
} from "@/lib/editor/shortcutRegistry";
|
||||
|
||||
describe("shortcutRegistry editor actions", () => {
|
||||
const formatterEditorActionIds: ShortcutActionId[] = [
|
||||
@@ -18,6 +29,8 @@ describe("shortcutRegistry editor actions", () => {
|
||||
"undo",
|
||||
"redo",
|
||||
"selectAll",
|
||||
"addNextSelectionOccurrence",
|
||||
"selectAllSelectionOccurrences",
|
||||
"uppercaseSelection",
|
||||
"lowercaseSelection",
|
||||
"exPasteSqlInCondition",
|
||||
@@ -233,6 +246,9 @@ describe("shortcutRegistry editor actions", () => {
|
||||
expect(shortcuts.redo).toBe("Shift+Mod+Z");
|
||||
expect(shortcuts.selectAll).toBe("Mod+A");
|
||||
expect(shortcuts.extendSelection).toBe("Alt+W");
|
||||
// 测试平台(Linux runner)解析为非 mac 默认键;mac 变体在下方单独断言。
|
||||
expect(shortcuts.addNextSelectionOccurrence).toBe(selectionOccurrenceDefaultShortcut("addNextSelectionOccurrence"));
|
||||
expect(shortcuts.selectAllSelectionOccurrences).toBe(selectionOccurrenceDefaultShortcut("selectAllSelectionOccurrences"));
|
||||
expect(shortcuts.uppercaseSelection).toBe("Shift+Alt+U");
|
||||
expect(shortcuts.lowercaseSelection).toBe("Shift+Alt+L");
|
||||
expect(shortcuts.exPasteSqlInCondition).toBe("");
|
||||
@@ -246,6 +262,29 @@ describe("shortcutRegistry editor actions", () => {
|
||||
expect(DEFAULT_SHORTCUT_SETTINGS.extendSelection).toBe("Alt+W");
|
||||
});
|
||||
|
||||
it("registers occurrence selection shortcuts for query editor multi-selection", () => {
|
||||
const next = SHORTCUT_DEFINITIONS.find((item) => item.id === "addNextSelectionOccurrence");
|
||||
const all = SHORTCUT_DEFINITIONS.find((item) => item.id === "selectAllSelectionOccurrences");
|
||||
|
||||
expect(next).toMatchObject({ scope: "editor", defaultShortcut: "Ctrl+G" });
|
||||
expect(all).toMatchObject({ scope: "editor", defaultShortcut: "Ctrl+Mod+G" });
|
||||
expect(findShortcutConflict("selectAllSelectionOccurrences", DEFAULT_SHORTCUT_SETTINGS.selectAllSelectionOccurrences, DEFAULT_SHORTCUT_SETTINGS)).toBeNull();
|
||||
});
|
||||
|
||||
it("resolves occurrence selection defaults per platform", () => {
|
||||
expect(selectionOccurrenceDefaultShortcut("addNextSelectionOccurrence", "MacIntel")).toBe("Ctrl+G");
|
||||
expect(selectionOccurrenceDefaultShortcut("selectAllSelectionOccurrences", "MacIntel")).toBe("Ctrl+Mod+G");
|
||||
// Ctrl+Mod+G is unreachable on non-mac (CodeMirror expands Mod→Ctrl) and
|
||||
// Ctrl+G there is find-next, so Windows/Linux use the JetBrains-style keys.
|
||||
expect(selectionOccurrenceDefaultShortcut("addNextSelectionOccurrence", "Win32")).toBe("Alt+J");
|
||||
expect(selectionOccurrenceDefaultShortcut("selectAllSelectionOccurrences", "Win32")).toBe("Ctrl+Alt+Shift+J");
|
||||
expect(selectionOccurrenceDefaultShortcut("addNextSelectionOccurrence", "Linux x86_64")).toBe("Alt+J");
|
||||
expect(selectionOccurrenceDefaultShortcut("selectAllSelectionOccurrences", "Linux x86_64")).toBe("Ctrl+Alt+Shift+J");
|
||||
// Both platform defaults must survive the CodeMirror key conversion.
|
||||
expect(shortcutToCodeMirrorKey(selectionOccurrenceDefaultShortcut("selectAllSelectionOccurrences", "Win32"))).toBe("Ctrl-Alt-Shift-j");
|
||||
expect(shortcutToCodeMirrorKey(selectionOccurrenceDefaultShortcut("addNextSelectionOccurrence", "Win32"))).toBe("Alt-j");
|
||||
});
|
||||
|
||||
it("registers an IDEA/DataGrip-style Alt+/ shortcut for manually triggering completion", () => {
|
||||
const definition = SHORTCUT_DEFINITIONS.find((item) => item.id === "triggerCompletion");
|
||||
|
||||
|
||||
Reference in new issue
Block a user