feat(editor): optional space completion confirmation for SQL Server

This commit is contained in:
zipg authored and GitHub committed 2026-09-18 23:43:58 +08:00
1 parent 54652951d1
commit 9d29f53e37
24 files changed
+202 -2

No files matched your search

@@ -253,6 +253,7 @@ import {
import { applyEditorSettingsDraftToRefs, type EditorSettingsDraftRefMap } from "@/lib/settings/applyEditorSettingsDraft";
import { serializeSettingsTransfer, sortTransferCategories, transferCategoryForKey, type SettingsTransferCategoryId } from "@/lib/settings/settingsTransfer";
import { useConnectionStore } from "@/stores/connectionStore";
import { effectiveDatabaseTypeForConnection } from "@/lib/database/jdbcDialect";
import { useSavedSqlStore } from "@/stores/savedSqlStore";
import { usePromptTemplateStore } from "@/stores/promptTemplateStore";
import { useTunnelProfileStore } from "@/stores/tunnelProfileStore";
@@ -291,6 +292,7 @@ const { t, locale } = useI18n();
const { toast } = useToast();
const settingsStore = useSettingsStore();
const connectionStore = useConnectionStore();
const hasSqlServerConnection = computed(() => connectionStore.connections.some((connection) => effectiveDatabaseTypeForConnection(connection) === "sqlserver"));
const savedSqlStore = useSavedSqlStore();
const promptTemplateStore = usePromptTemplateStore();
const tunnelProfileStore = useTunnelProfileStore();
@@ -568,6 +570,8 @@ const editShowCurrentStatementFrame = ref(settingsStore.editorSettings.showCurre
const editShowInsertValueHints = ref(settingsStore.editorSettings.showInsertValueHints);
const editAutoAliasTables = ref(settingsStore.editorSettings.autoAliasTables);
const editInsertSpaceAfterCompletion = ref(settingsStore.editorSettings.insertSpaceAfterCompletion);
const editSqlServerSpaceConfirmsCompletion = ref(settingsStore.editorSettings.sqlServerSpaceConfirmsCompletion);
const showSqlServerSpaceConfirmsCompletion = computed(() => hasSqlServerConnection.value || settingsStore.editorSettings.sqlServerSpaceConfirmsCompletion || editSqlServerSpaceConfirmsCompletion.value);
const editSortCompletionColumnsAlphabetically = ref(settingsStore.editorSettings.sortCompletionColumnsAlphabetically);
const editSelectFirstCompletionOnOpen = ref(settingsStore.editorSettings.selectFirstCompletionOnOpen);
const editCompletionTriggerMode = ref<SqlCompletionTriggerMode>(settingsStore.editorSettings.completionTriggerMode);
@@ -904,6 +908,7 @@ function currentEditorSettingsDraft(): EditorSettingsDraft {
showInsertValueHints: editShowInsertValueHints.value,
autoAliasTables: editAutoAliasTables.value,
insertSpaceAfterCompletion: editInsertSpaceAfterCompletion.value,
sqlServerSpaceConfirmsCompletion: editSqlServerSpaceConfirmsCompletion.value,
sortCompletionColumnsAlphabetically: editSortCompletionColumnsAlphabetically.value,
selectFirstCompletionOnOpen: editSelectFirstCompletionOnOpen.value,
completionTriggerMode: editCompletionTriggerMode.value,
@@ -1541,6 +1546,7 @@ function syncEditorSettingsDraftFromStore() {
editShowInsertValueHints.value = settingsStore.editorSettings.showInsertValueHints;
editAutoAliasTables.value = settingsStore.editorSettings.autoAliasTables;
editInsertSpaceAfterCompletion.value = settingsStore.editorSettings.insertSpaceAfterCompletion;
editSqlServerSpaceConfirmsCompletion.value = settingsStore.editorSettings.sqlServerSpaceConfirmsCompletion;
editSortCompletionColumnsAlphabetically.value = settingsStore.editorSettings.sortCompletionColumnsAlphabetically;
editSelectFirstCompletionOnOpen.value = settingsStore.editorSettings.selectFirstCompletionOnOpen;
editCompletionTriggerMode.value = settingsStore.editorSettings.completionTriggerMode;
@@ -1671,6 +1677,7 @@ const editorSettingsDraftRefs: EditorSettingsDraftRefMap = {
showInsertValueHints: editShowInsertValueHints,
autoAliasTables: editAutoAliasTables,
insertSpaceAfterCompletion: editInsertSpaceAfterCompletion,
sqlServerSpaceConfirmsCompletion: editSqlServerSpaceConfirmsCompletion,
sortCompletionColumnsAlphabetically: editSortCompletionColumnsAlphabetically,
selectFirstCompletionOnOpen: editSelectFirstCompletionOnOpen,
wordWrap: editWordWrap,
@@ -2114,6 +2121,7 @@ function resetDefaultsForTab(tab: SettingsCategory) {
editShowInsertValueHints.value = DEFAULT_EDITOR_SETTINGS.showInsertValueHints;
editAutoAliasTables.value = DEFAULT_EDITOR_SETTINGS.autoAliasTables;
editInsertSpaceAfterCompletion.value = DEFAULT_EDITOR_SETTINGS.insertSpaceAfterCompletion;
editSqlServerSpaceConfirmsCompletion.value = DEFAULT_EDITOR_SETTINGS.sqlServerSpaceConfirmsCompletion;
editSortCompletionColumnsAlphabetically.value = DEFAULT_EDITOR_SETTINGS.sortCompletionColumnsAlphabetically;
editSelectFirstCompletionOnOpen.value = DEFAULT_EDITOR_SETTINGS.selectFirstCompletionOnOpen;
editCompletionTriggerMode.value = DEFAULT_EDITOR_SETTINGS.completionTriggerMode;
@@ -2259,6 +2267,7 @@ function resetAllDefaults() {
editShowInsertValueHints.value = DEFAULT_EDITOR_SETTINGS.showInsertValueHints;
editAutoAliasTables.value = DEFAULT_EDITOR_SETTINGS.autoAliasTables;
editInsertSpaceAfterCompletion.value = DEFAULT_EDITOR_SETTINGS.insertSpaceAfterCompletion;
editSqlServerSpaceConfirmsCompletion.value = DEFAULT_EDITOR_SETTINGS.sqlServerSpaceConfirmsCompletion;
editSortCompletionColumnsAlphabetically.value = DEFAULT_EDITOR_SETTINGS.sortCompletionColumnsAlphabetically;
editSelectFirstCompletionOnOpen.value = DEFAULT_EDITOR_SETTINGS.selectFirstCompletionOnOpen;
editWordWrap.value = DEFAULT_EDITOR_SETTINGS.wordWrap;
@@ -2792,6 +2801,8 @@ const settingsSearchEntries = computed(() =>
[...SETTINGS_SEARCH_DEFINITIONS, ...createShortcutSettingsSearchDefinitions(SHORTCUT_DEFINITIONS)],
{
isWeb,
hasSqlServerConnection: hasSqlServerConnection.value,
sqlServerSpaceConfirmsCompletionEnabled: settingsStore.editorSettings.sqlServerSpaceConfirmsCompletion || editSqlServerSpaceConfirmsCompletion.value,
visibleCategories: new Set(settingsCategoryNav.value.map((category) => category.value)),
},
translateWithExecuteShortcut,
@@ -6044,6 +6055,16 @@ onUnmounted(() => {
<Switch id="editor-insert-space-after-completion" v-model="editInsertSpaceAfterCompletion" class="mt-0.5" />
</div>
<div v-if="showSqlServerSpaceConfirmsCompletion" class="settings-item flex items-center justify-between gap-4 rounded-md border bg-muted/20 px-3 py-2">
<div class="space-y-1">
<Label for="editor-sqlserver-space-confirms-completion">{{ t("settings.sqlServerSpaceConfirmsCompletion") }}</Label>
<p class="text-xs text-muted-foreground">
{{ t("settings.sqlServerSpaceConfirmsCompletionDescription") }}
</p>
</div>
<Switch id="editor-sqlserver-space-confirms-completion" v-model="editSqlServerSpaceConfirmsCompletion" class="mt-0.5" />
</div>
<div class="settings-item flex items-center justify-between gap-4 rounded-md border bg-muted/20 px-3 py-2">
<div class="space-y-1">
<Label for="editor-sort-completion-columns-alphabetically">{{ t("settings.sortCompletionColumnsAlphabetically") }}</Label>
@@ -2705,6 +2705,38 @@ function acceptCompletionOrNextSnippetField(view: EditorViewType): boolean {
return codeMirrorNextSnippetField?.(view) ?? false;
}
function acceptSqlServerCompletionOnSpace(view: EditorViewType): boolean {
if (isEditorComposing(view)) return false;
if (props.databaseType !== "sqlserver" || !settingsStore.editorSettings.sqlServerSpaceConfirmsCompletion) return false;
if (codeMirrorCompletionStatus?.(view.state) !== "active") return false;
// A non-empty selection belongs to block editing, not word completion.
if (!view.state.selection.main.empty) return false;
const selected = codeMirrorSelectedCompletion?.(view.state) as QueryCompletionOption | null | undefined;
const completionType = selected?.type;
if (completionType !== "keyword" && completionType !== "table" && completionType !== "column") return false;
// Batch-selection rows own Space for checkbox toggling; this Prec.highest binding outranks their keymap.
if (selected?.dbxBatchColumnSelection || selected?.dbxBatchColumnSelectionAction) return false;
if (!(codeMirrorAcceptCompletion?.(view) ?? false)) return false;
const selection = view.state.selection.main;
if (!selection.empty) return true;
const cursor = selection.head;
const previousCharacter = cursor > 0 ? view.state.sliceDoc(cursor - 1, cursor) : "";
if (/\s/.test(previousCharacter)) return true;
const nextCharacter = view.state.sliceDoc(cursor, cursor + 1);
if (/\s/.test(nextCharacter)) {
view.dispatch({ selection: { anchor: cursor + 1 }, scrollIntoView: true });
} else {
view.dispatch({
changes: { from: cursor, insert: " " },
selection: { anchor: cursor + 1 },
scrollIntoView: true,
});
}
return true;
}
function clearPendingCompletionTab() {
if (pendingCompletionTabTimer === null) return;
clearTimeout(pendingCompletionTabTimer);
@@ -6942,6 +6974,7 @@ onMounted(async () => {
vimModeComp.of(vimModeExtension(initialSettings.vimModeEnabled)),
defaultKeymapComp.of(defaultKeymapExtension()),
keymap.of([...searchKeymapWithoutModD(searchKeymap), ...historyKeymap, ...foldKeymap, ...completionKeymap]),
Prec.highest(keymap.of([{ key: "Space", run: acceptSqlServerCompletionOnSpace }])),
sqlLanguageComp.of(buildSqlLanguageExtension()),
sqlSemanticHighlightComp.of(buildSqlSemanticHighlightExtension()),
tooltips({ parent: tooltipParent }),