mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-07-10 15:03:12 +08:00
🐛 fix(query-editor): 限制 macOS 搜索仅响应 Cmd+F
- 拦截 Monaco 内置 Cmd+E find-with-selection 行为 - 保留默认 Cmd+E 当前语句选择逻辑,禁用或改键时不触发搜索 - 增加 SQL 编辑器快捷键回归测试并加固 Monaco 测试包装
This commit is contained in:
@@ -168,7 +168,8 @@ const installOceanBaseOracleNavigationFallback = (editor: any) => {
|
||||
};
|
||||
|
||||
const patchQueryEditorAiInlineRightArrowFallback = (editor: any, monaco: any) => {
|
||||
const originalAddCommand = editor?.addCommand?.bind?.(editor);
|
||||
const rawAddCommand = editor?.addCommand;
|
||||
const originalAddCommand = rawAddCommand?.bind?.(editor);
|
||||
if (!originalAddCommand || !monaco?.KeyCode?.RightArrow) {
|
||||
return;
|
||||
}
|
||||
@@ -180,7 +181,7 @@ const patchQueryEditorAiInlineRightArrowFallback = (editor: any, monaco: any) =>
|
||||
configurable: true,
|
||||
});
|
||||
|
||||
editor.addCommand = (keybinding: any, handler: any, context: any) => {
|
||||
const patchedAddCommand = (keybinding: any, handler: any, context: any) => {
|
||||
if (
|
||||
keybinding === monaco.KeyCode.RightArrow
|
||||
&& context === QUERY_EDITOR_AI_INLINE_CONTEXT_KEY
|
||||
@@ -200,6 +201,32 @@ const patchQueryEditorAiInlineRightArrowFallback = (editor: any, monaco: any) =>
|
||||
}
|
||||
return originalAddCommand(keybinding, handler, context);
|
||||
};
|
||||
|
||||
if (rawAddCommand?.mock) {
|
||||
for (const propertyName of [
|
||||
'mock',
|
||||
'mockClear',
|
||||
'mockReset',
|
||||
'mockRestore',
|
||||
'mockImplementation',
|
||||
'mockImplementationOnce',
|
||||
'mockName',
|
||||
'getMockName',
|
||||
]) {
|
||||
if (!(propertyName in rawAddCommand)) {
|
||||
continue;
|
||||
}
|
||||
Object.defineProperty(patchedAddCommand, propertyName, {
|
||||
configurable: true,
|
||||
get: () => {
|
||||
const value = rawAddCommand[propertyName];
|
||||
return typeof value === 'function' ? value.bind(rawAddCommand) : value;
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
editor.addCommand = patchedAddCommand;
|
||||
};
|
||||
|
||||
const installPrintableInputFallback = (editor: any, monaco: any) => {
|
||||
@@ -207,8 +234,9 @@ const installPrintableInputFallback = (editor: any, monaco: any) => {
|
||||
if (!editorDomNode || editor.__gonaviPrintableInputFallbackInstalled) {
|
||||
return;
|
||||
}
|
||||
const TextAreaElement = typeof HTMLTextAreaElement === 'undefined' ? null : HTMLTextAreaElement;
|
||||
const input = editorDomNode.querySelector?.('textarea.inputarea, .inputarea textarea, textarea') as HTMLTextAreaElement | null;
|
||||
if (!(input instanceof HTMLTextAreaElement)) {
|
||||
if (!TextAreaElement || !(input instanceof TextAreaElement)) {
|
||||
return;
|
||||
}
|
||||
Object.defineProperty(editor, '__gonaviPrintableInputFallbackInstalled', {
|
||||
|
||||
@@ -4481,6 +4481,39 @@ describe('QueryEditor external SQL save', () => {
|
||||
).not.toContain('gonavi:find-active-query');
|
||||
});
|
||||
|
||||
it('keeps SQL editor search on Cmd+F only and suppresses Monaco Cmd+E find-with-selection', async () => {
|
||||
storeState.shortcutOptions.selectCurrentStatement.mac = { enabled: false, combo: '' };
|
||||
storeState.shortcutOptions.selectCurrentStatement.windows = { enabled: false, combo: '' };
|
||||
|
||||
await act(async () => {
|
||||
create(<QueryEditor tab={createTab({
|
||||
query: 'SELECT 1;\nSELECT 2 AS two;\nSELECT 3;',
|
||||
readOnly: true,
|
||||
})} />);
|
||||
});
|
||||
(window.dispatchEvent as any).mockClear();
|
||||
(document.execCommand as any).mockClear();
|
||||
|
||||
expect(findEditorAction('gonavi.findInEditor')).toMatchObject({
|
||||
keybindings: [2048 | 70],
|
||||
});
|
||||
|
||||
const suppressMacFindAction = findEditorAction('gonavi.suppressMacFindWithSelection');
|
||||
expect(suppressMacFindAction).toMatchObject({
|
||||
keybindings: [2048 | 69],
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
suppressMacFindAction.run();
|
||||
await Promise.resolve();
|
||||
});
|
||||
|
||||
expect(
|
||||
(window.dispatchEvent as any).mock.calls.map((call: any[]) => call[0]?.type),
|
||||
).not.toContain('gonavi:find-active-query');
|
||||
expect(document.execCommand).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('intercepts Ctrl/Cmd+D at window level and duplicates the current line below', async () => {
|
||||
storeState.shortcutOptions.duplicateCurrentLine.mac = { enabled: true, combo: 'Meta+D' };
|
||||
storeState.shortcutOptions.duplicateCurrentLine.windows = { enabled: true, combo: 'Ctrl+D' };
|
||||
|
||||
@@ -170,6 +170,8 @@ const QUERY_EDITOR_MONACO_FIND_OPTIONS = {
|
||||
addExtraSpaceOnTop: true,
|
||||
} as const;
|
||||
const QUERY_EDITOR_NATIVE_SELECT_CURRENT_LINE_EVENT = 'gonavi:native-select-current-line';
|
||||
const QUERY_EDITOR_MAC_FIND_WITH_SELECTION_COMBO = 'Meta+E';
|
||||
const QUERY_EDITOR_MAC_FIND_WITH_SELECTION_GUARD_ACTION_ID = 'gonavi.suppressMacFindWithSelection';
|
||||
const QUERY_EDITOR_AI_INLINE_DEBOUNCE_MS = 90;
|
||||
const QUERY_EDITOR_AI_INLINE_CONTEXT_KEY = 'gonaviAiInlineSuggestionVisible';
|
||||
|
||||
@@ -1131,6 +1133,7 @@ const QueryEditor: React.FC<{ tab: TabData; isActive?: boolean }> = ({ tab, isAc
|
||||
const monacoRef = useRef<any>(null);
|
||||
const runQueryActionRef = useRef<any>(null);
|
||||
const selectCurrentStatementActionRef = useRef<any>(null);
|
||||
const macFindWithSelectionGuardActionRef = useRef<any>(null);
|
||||
const duplicateCurrentLineActionRef = useRef<any>(null);
|
||||
const saveQueryActionRef = useRef<any>(null);
|
||||
const findInEditorActionRef = useRef<any>(null);
|
||||
@@ -4308,6 +4311,8 @@ const QueryEditor: React.FC<{ tab: TabData; isActive?: boolean }> = ({ tab, isAc
|
||||
objectHoverActionRef.current = null;
|
||||
triggerSqlAiCompletionActionRef.current?.dispose?.();
|
||||
triggerSqlAiCompletionActionRef.current = null;
|
||||
macFindWithSelectionGuardActionRef.current?.dispose?.();
|
||||
macFindWithSelectionGuardActionRef.current = null;
|
||||
triggerSqlAiCompletionKeydownDisposableRef.current?.dispose?.();
|
||||
triggerSqlAiCompletionKeydownDisposableRef.current = null;
|
||||
triggerAiInlineCompletionRef.current = null;
|
||||
@@ -4369,6 +4374,30 @@ const QueryEditor: React.FC<{ tab: TabData; isActive?: boolean }> = ({ tab, isAc
|
||||
}
|
||||
}
|
||||
|
||||
const macFindWithSelectionGuardKeyBinding = activeShortcutPlatform === 'mac'
|
||||
? comboToMonacoKeyBinding(
|
||||
QUERY_EDITOR_MAC_FIND_WITH_SELECTION_COMBO, monaco.KeyMod, monaco.KeyCode,
|
||||
)
|
||||
: null;
|
||||
if (macFindWithSelectionGuardKeyBinding) {
|
||||
macFindWithSelectionGuardActionRef.current = editor.addAction({
|
||||
id: QUERY_EDITOR_MAC_FIND_WITH_SELECTION_GUARD_ACTION_ID,
|
||||
label: 'GoNavi: Suppress macOS Cmd+E Find with Selection',
|
||||
keybindings: [
|
||||
macFindWithSelectionGuardKeyBinding.keyMod
|
||||
| macFindWithSelectionGuardKeyBinding.keyCode,
|
||||
],
|
||||
run: () => {
|
||||
if (
|
||||
selectStatementBinding?.enabled
|
||||
&& normalizeShortcutCombo(selectStatementBinding.combo) === QUERY_EDITOR_MAC_FIND_WITH_SELECTION_COMBO
|
||||
) {
|
||||
void handleSelectCurrentStatement();
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const duplicateLineBinding = duplicateCurrentLineShortcutBinding;
|
||||
if (duplicateLineBinding?.enabled && duplicateLineBinding.combo) {
|
||||
const keyBinding = comboToMonacoKeyBinding(
|
||||
@@ -6734,21 +6763,51 @@ const QueryEditor: React.FC<{ tab: TabData; isActive?: boolean }> = ({ tab, isAc
|
||||
selectCurrentStatementActionRef.current.dispose();
|
||||
selectCurrentStatementActionRef.current = null;
|
||||
}
|
||||
if (macFindWithSelectionGuardActionRef.current) {
|
||||
macFindWithSelectionGuardActionRef.current.dispose();
|
||||
macFindWithSelectionGuardActionRef.current = null;
|
||||
}
|
||||
|
||||
const editor = editorRef.current;
|
||||
const monaco = monacoRef.current;
|
||||
if (!editor || !monaco) return;
|
||||
|
||||
const binding = selectCurrentStatementShortcutBinding;
|
||||
if (!binding?.enabled || !binding.combo) return;
|
||||
if (binding?.enabled && binding.combo) {
|
||||
const keyBinding = comboToMonacoKeyBinding(binding.combo, monaco.KeyMod, monaco.KeyCode);
|
||||
if (keyBinding) {
|
||||
selectCurrentStatementActionRef.current = editor.addAction({
|
||||
id: 'gonavi.selectCurrentStatement',
|
||||
label: buildQueryEditorMonacoActionLabel('app.shortcuts.action.selectCurrentStatement.label'),
|
||||
keybindings: [keyBinding.keyMod | keyBinding.keyCode],
|
||||
run: handleSelectCurrentStatement,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const keyBinding = comboToMonacoKeyBinding(binding.combo, monaco.KeyMod, monaco.KeyCode);
|
||||
if (keyBinding) {
|
||||
selectCurrentStatementActionRef.current = editor.addAction({
|
||||
id: 'gonavi.selectCurrentStatement',
|
||||
label: buildQueryEditorMonacoActionLabel('app.shortcuts.action.selectCurrentStatement.label'),
|
||||
keybindings: [keyBinding.keyMod | keyBinding.keyCode],
|
||||
run: handleSelectCurrentStatement,
|
||||
const macFindWithSelectionGuardKeyBinding = activeShortcutPlatform === 'mac'
|
||||
? comboToMonacoKeyBinding(
|
||||
QUERY_EDITOR_MAC_FIND_WITH_SELECTION_COMBO,
|
||||
monaco.KeyMod,
|
||||
monaco.KeyCode,
|
||||
)
|
||||
: null;
|
||||
if (macFindWithSelectionGuardKeyBinding) {
|
||||
macFindWithSelectionGuardActionRef.current = editor.addAction({
|
||||
id: QUERY_EDITOR_MAC_FIND_WITH_SELECTION_GUARD_ACTION_ID,
|
||||
label: 'GoNavi: Suppress macOS Cmd+E Find with Selection',
|
||||
keybindings: [
|
||||
macFindWithSelectionGuardKeyBinding.keyMod
|
||||
| macFindWithSelectionGuardKeyBinding.keyCode,
|
||||
],
|
||||
run: () => {
|
||||
if (
|
||||
binding?.enabled
|
||||
&& normalizeShortcutCombo(binding.combo) === QUERY_EDITOR_MAC_FIND_WITH_SELECTION_COMBO
|
||||
) {
|
||||
void handleSelectCurrentStatement();
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -6757,8 +6816,12 @@ const QueryEditor: React.FC<{ tab: TabData; isActive?: boolean }> = ({ tab, isAc
|
||||
selectCurrentStatementActionRef.current.dispose();
|
||||
selectCurrentStatementActionRef.current = null;
|
||||
}
|
||||
if (macFindWithSelectionGuardActionRef.current) {
|
||||
macFindWithSelectionGuardActionRef.current.dispose();
|
||||
macFindWithSelectionGuardActionRef.current = null;
|
||||
}
|
||||
};
|
||||
}, [languagePreference, selectCurrentStatementShortcutBinding, handleSelectCurrentStatement]);
|
||||
}, [activeShortcutPlatform, languagePreference, selectCurrentStatementShortcutBinding, handleSelectCurrentStatement]);
|
||||
|
||||
useEffect(() => {
|
||||
if (duplicateCurrentLineActionRef.current) {
|
||||
|
||||
Reference in New Issue
Block a user