diff --git a/apps/desktop/src/components/editor/EditorSettingsDialog.vue b/apps/desktop/src/components/editor/EditorSettingsDialog.vue index 3af4abc1b9..5f33255133 100644 --- a/apps/desktop/src/components/editor/EditorSettingsDialog.vue +++ b/apps/desktop/src/components/editor/EditorSettingsDialog.vue @@ -114,6 +114,7 @@ import { normalizeMcpGlobalPolicy, type ClickTableNavigationTarget, type EditorSettings, + type SnippetTriggerKey, type SqlCompletionTriggerMode, type SqlTableCompletionSchemaQualification, type TableHoverLookupMode, @@ -967,6 +968,7 @@ function editableSnippet(snippet: SqlSnippet): SqlSnippet { } const editSnippets = ref(settingsStore.editorSettings.snippets.map(editableSnippet)); +const editSnippetTriggerKey = ref(settingsStore.editorSettings.snippetTriggerKey); function editableSqlShortcut(action: SqlShortcutAction): SqlShortcutAction { const next: SqlShortcutAction = { ...action, enabled: action.enabled !== false }; @@ -1179,6 +1181,7 @@ function currentEditorSettingsDraft(): EditorSettingsDraft { updateDownloadSource: editUpdateDownloadSource.value, toolbarItems: { ...editToolbarItems.value }, snippets: editSnippets.value, + snippetTriggerKey: editSnippetTriggerKey.value, sqlShortcuts: editSqlShortcuts.value, sqlVariableSubstitutionEnabled: editSqlVariableSubstitutionEnabled.value, sqlVariableSyntaxOverrides: editSqlVariableSyntaxOverrides.value, @@ -1851,6 +1854,7 @@ function syncEditorSettingsDraftFromStore() { editUpdateDownloadSource.value = settingsStore.editorSettings.updateDownloadSource; editToolbarItems.value = { ...settingsStore.editorSettings.toolbarItems }; editSnippets.value = settingsStore.editorSettings.snippets.map(editableSnippet); + editSnippetTriggerKey.value = settingsStore.editorSettings.snippetTriggerKey; editSqlShortcuts.value = mergeDefaultSqlShortcuts(settingsStore.editorSettings.sqlShortcuts.map(editableSqlShortcut)); editSqlVariableSubstitutionEnabled.value = settingsStore.editorSettings.sqlVariableSubstitutionEnabled; editSqlVariableSyntaxOverrides.value = normalizeSqlVariableSyntaxOverrides(settingsStore.editorSettings.sqlVariableSyntaxOverrides); @@ -1997,6 +2001,7 @@ const editorSettingsDraftRefs: EditorSettingsDraftRefMap = { updateDownloadSource: editUpdateDownloadSource, toolbarItems: editToolbarItems, snippets: editSnippets, + snippetTriggerKey: editSnippetTriggerKey, sqlShortcuts: editSqlShortcuts, sqlVariableSubstitutionEnabled: editSqlVariableSubstitutionEnabled, sqlVariableSyntaxOverrides: editSqlVariableSyntaxOverrides, @@ -2544,6 +2549,7 @@ function resetDefaultsForTab(tab: SettingsCategory) { editShortcuts.value = normalizeShortcutSettings(DEFAULT_EDITOR_SETTINGS.shortcuts); } else if (tab === "snippets") { editSnippets.value = DEFAULT_SQL_SNIPPETS.map((s) => ({ ...s })); + editSnippetTriggerKey.value = DEFAULT_EDITOR_SETTINGS.snippetTriggerKey; } else if (tab === "updates") { editAutoUpdateApp.value = DEFAULT_EDITOR_SETTINGS.autoUpdateApp; editAutoUpdateDrivers.value = DEFAULT_EDITOR_SETTINGS.autoUpdateDrivers; @@ -2694,6 +2700,7 @@ function resetAllDefaults() { editUpdateDownloadSource.value = DEFAULT_EDITOR_SETTINGS.updateDownloadSource; editToolbarItems.value = { ...DEFAULT_EDITOR_SETTINGS.toolbarItems }; editSnippets.value = DEFAULT_SQL_SNIPPETS.map((s) => ({ ...s })); + editSnippetTriggerKey.value = DEFAULT_EDITOR_SETTINGS.snippetTriggerKey; editSqlShortcuts.value = DEFAULT_SQL_SHORTCUTS.map(editableSqlShortcut); } @@ -2835,6 +2842,12 @@ function onCompletionTriggerModeChange(v: any) { } } +function onSnippetTriggerKeyChange(v: any) { + if (v === "tab" || v === "space" || v === "both") { + editSnippetTriggerKey.value = v; + } +} + function onTableCompletionSchemaQualificationChange(v: any) { if (v === "never" || v === "collision" || v === "always") { editTableCompletionSchemaQualification.value = v; @@ -9395,6 +9408,26 @@ onUnmounted(() => { {{ t("settings.snippetsAdd") }} + +
+
+ +

+ {{ t("settings.snippetTriggerKeyDescription") }} +

+
+ +
+

{{ t("settings.snippetsPlaceholderHint") }}

diff --git a/apps/desktop/src/components/editor/QueryEditor.vue b/apps/desktop/src/components/editor/QueryEditor.vue
index 7d5625a3ab..fb3ba09252 100644
--- a/apps/desktop/src/components/editor/QueryEditor.vue
+++ b/apps/desktop/src/components/editor/QueryEditor.vue
@@ -1812,7 +1812,7 @@ const completion = useQueryEditorCompletion({
 });
 const { triggerSqlCompletion, shouldTriggerSqlCompletionForPosition, scheduleSqlCompletionStart, consumeSqlCompletionAutoStartSuppression, scheduleDeferredCompletionTrigger, clearDeferredCompletionTrigger } = completion;
 
-const { editorIndentUnit, handleTab, handleEnter, acceptCompletionOrNextSnippetField, acceptSqlServerCompletionOnSpace, clearPendingCompletionEnter, clearPendingCompletionTab } = useQueryEditorCompletionKeys({
+const { editorIndentUnit, handleTab, handleEnter, acceptCompletionOrNextSnippetField, handleSpace, clearPendingCompletionEnter, clearPendingCompletionTab } = useQueryEditorCompletionKeys({
   props,
   settingsStore,
   runtime: codeMirrorRuntime,
@@ -1998,7 +1998,7 @@ const codeMirrorLifecycle = useQueryEditorCodeMirror({
         initializedRuntime.vimModeComp.of(vimModeExtension(initialSettings.vimModeEnabled)),
         initializedRuntime.defaultKeymapComp.of(defaultKeymapExtension()),
         keymap.of([...searchKeymapWithoutModD(searchKeymap), ...historyKeymap, ...foldKeymapWithoutAllBindings(foldKeymap, initializedRuntime.codeMirrorFoldAll, initializedRuntime.codeMirrorUnfoldAll), ...completionKeymap]),
-        Prec.highest(keymap.of([{ key: "Space", run: acceptSqlServerCompletionOnSpace }])),
+        Prec.highest(keymap.of([{ key: "Space", run: handleSpace }])),
         initializedRuntime.sqlLanguageComp.of(sqlExtensions.buildSqlLanguageExtension()),
         initializedRuntime.sqlSemanticHighlightComp.of(sqlExtensions.buildSqlSemanticHighlightExtension()),
         createSqlUnknownObjectHighlights({
diff --git a/apps/desktop/src/components/editor/__tests__/EditorSettingsDialog.debugLoggingSearchTarget.spec.ts b/apps/desktop/src/components/editor/__tests__/EditorSettingsDialog.debugLoggingSearchTarget.spec.ts
index 168f79f490..ec5598ac0e 100644
--- a/apps/desktop/src/components/editor/__tests__/EditorSettingsDialog.debugLoggingSearchTarget.spec.ts
+++ b/apps/desktop/src/components/editor/__tests__/EditorSettingsDialog.debugLoggingSearchTarget.spec.ts
@@ -33,4 +33,10 @@ describe("EditorSettingsDialog settings search targets", () => {
     expect(entry?.category).toBe(owningTab);
     expect(dialogSource).toContain(`data-settings-search-id="${entry?.targetId}"`);
   });
+
+  it("renders the snippet trigger key setting in the tab its search entry points at", () => {
+    const entry = SETTINGS_SEARCH_DEFINITIONS.find((definition) => definition.id === "snippet-trigger-key");
+    expect(entry?.category).toBe("snippets");
+    expect(dialogSource).toContain(`data-settings-search-id="${entry?.targetId}"`);
+  });
 });
diff --git a/apps/desktop/src/components/editor/__tests__/useQueryEditorCompletionKeys.spec.ts b/apps/desktop/src/components/editor/__tests__/useQueryEditorCompletionKeys.spec.ts
index 6fb4f620d4..1342b43cde 100644
--- a/apps/desktop/src/components/editor/__tests__/useQueryEditorCompletionKeys.spec.ts
+++ b/apps/desktop/src/components/editor/__tests__/useQueryEditorCompletionKeys.spec.ts
@@ -45,7 +45,7 @@ function createHarness(overrides: Partial = {}) {
     codeMirrorSelectFirstCompletion: vi.fn(() => false),
     codeMirrorInsertNewlineKeepIndent: newline,
   });
-  const settings = reactive({ editorSettings: { sqlFormatter: { useTabs: false, tabWidth: 2 }, shortcuts: { acceptCompletion: "Tab" }, selectFirstCompletionOnOpen: false, sqlServerSpaceConfirmsCompletion: true } });
+  const settings = reactive({ editorSettings: { sqlFormatter: { useTabs: false, tabWidth: 2 }, shortcuts: { acceptCompletion: "Tab" }, selectFirstCompletionOnOpen: false, sqlServerSpaceConfirmsCompletion: true, snippetTriggerKey: "tab" as const } });
   const completion = { suppressAutoStartUntil: 0 };
   const keys = useQueryEditorCompletionKeys({ props, settingsStore: settings as Options["settingsStore"], runtime, completion, batchSelection: { applySelectedBatchColumnSelection: batch }, isEditorComposing: composing, retryDelayMs: 5, tabMaxWaitMs: 40, enterMaxWaitMs: 20 });
   cleanups.push(() => {
@@ -174,4 +174,69 @@ describe("QueryEditor completion-key ownership", () => {
     expect(view.state.doc.toString()).toBe("users ");
     expect(view.state.selection.main.head).toBe(6);
   });
+
+  it("accepts snippet completion on Tab when snippetTriggerKey is tab or both", () => {
+    const { keys, view, status, runtime, accept } = createHarness();
+    status.mockReturnValue("active");
+    runtime.codeMirrorSelectedCompletionIndex = () => 0;
+    runtime.codeMirrorSelectedCompletion = () => ({ label: "sel", type: "snippet" });
+    accept.mockReturnValue(true);
+
+    expect(keys.handleTab(view)).toBe(true);
+    expect(accept).toHaveBeenCalledOnce();
+  });
+
+  it("does not accept snippet completion on Tab when snippetTriggerKey is space", () => {
+    const { keys, view, status, runtime, accept, settings } = createHarness();
+    settings.editorSettings.snippetTriggerKey = "space";
+    status.mockReturnValue("active");
+    runtime.codeMirrorSelectedCompletionIndex = () => 0;
+    runtime.codeMirrorSelectedCompletion = () => ({ label: "sel", type: "snippet" });
+    accept.mockReturnValue(true);
+
+    expect(keys.handleTab(view)).toBe(true);
+    expect(accept).not.toHaveBeenCalled();
+    expect(view.state.doc.toString()).toBe("abc  ");
+  });
+
+  it("accepts snippet completion on Space when snippetTriggerKey is space or both", () => {
+    const { keys, view, status, runtime, accept, settings } = createHarness();
+    settings.editorSettings.snippetTriggerKey = "space";
+    status.mockReturnValue("active");
+    runtime.codeMirrorSelectedCompletionIndex = () => 0;
+    runtime.codeMirrorSelectedCompletion = () => ({ label: "sel", type: "snippet" });
+    accept.mockReturnValue(true);
+
+    expect(keys.handleSpace(view)).toBe(true);
+    expect(accept).toHaveBeenCalledOnce();
+
+    accept.mockClear();
+    settings.editorSettings.snippetTriggerKey = "both";
+    expect(keys.handleSpace(view)).toBe(true);
+    expect(accept).toHaveBeenCalledOnce();
+  });
+
+  it("does not accept snippet completion on Space when snippetTriggerKey is tab", () => {
+    const { keys, view, status, runtime, accept, settings } = createHarness();
+    settings.editorSettings.snippetTriggerKey = "tab";
+    status.mockReturnValue("active");
+    runtime.codeMirrorSelectedCompletionIndex = () => 0;
+    runtime.codeMirrorSelectedCompletion = () => ({ label: "sel", type: "snippet" });
+    accept.mockReturnValue(true);
+
+    expect(keys.handleSpace(view)).toBe(false);
+    expect(accept).not.toHaveBeenCalled();
+  });
+
+  it("ignores Space for snippet completion while an IME composition is active", () => {
+    const { keys, view, status, runtime, accept, composing, settings } = createHarness();
+    settings.editorSettings.snippetTriggerKey = "space";
+    status.mockReturnValue("active");
+    runtime.codeMirrorSelectedCompletionIndex = () => 0;
+    runtime.codeMirrorSelectedCompletion = () => ({ label: "sel", type: "snippet" });
+    composing.mockReturnValue(true);
+
+    expect(keys.handleSpace(view)).toBe(false);
+    expect(accept).not.toHaveBeenCalled();
+  });
 });
diff --git a/apps/desktop/src/components/editor/useQueryEditorCompletionKeys.ts b/apps/desktop/src/components/editor/useQueryEditorCompletionKeys.ts
index afbf5564e2..b70560da4d 100644
--- a/apps/desktop/src/components/editor/useQueryEditorCompletionKeys.ts
+++ b/apps/desktop/src/components/editor/useQueryEditorCompletionKeys.ts
@@ -129,6 +129,24 @@ export function useQueryEditorCompletionKeys(options: QueryEditorCompletionKeysO
     return handled;
   }
 
+  function getTargetCompletion(view: EditorViewType): QueryCompletionOption | null {
+    const selected = codeMirrorRuntime.codeMirrorSelectedCompletion?.(view.state) as QueryCompletionOption | null | undefined;
+    if (selected) return selected;
+    const current = codeMirrorRuntime.codeMirrorCurrentCompletions?.(view.state) as QueryCompletionOption[] | undefined;
+    if (current && current.length > 0) {
+      return current[0] ?? null;
+    }
+    return null;
+  }
+
+  function canAcceptCurrentCompletionOnTab(view: EditorViewType): boolean {
+    if (settingsStore.editorSettings.snippetTriggerKey === "space") {
+      const target = getTargetCompletion(view);
+      if (target?.type === "snippet") return false;
+    }
+    return true;
+  }
+
   function acceptCompletionOrNextSnippetField(view: EditorViewType): boolean {
     // A non-empty selection belongs to snippet navigation or block indentation,
     // not word completion. A popup opened by an indent edit must not hijack Tab.
@@ -136,12 +154,26 @@ export function useQueryEditorCompletionKeys(options: QueryEditorCompletionKeysO
     if (view.state.selection.ranges.every((range) => range.empty)) {
       const completionStatus = codeMirrorRuntime.codeMirrorCompletionStatus?.(view.state) ?? null;
       if (isBatchColumnSelectionCompletionActive(completionStatus) && applySelectedBatchColumnSelection(view)) return true;
-      if (completionStatus === "active" && acceptSelectedOrFirstCompletion(view, codeMirrorRuntime.codeMirrorAcceptCompletion, codeMirrorRuntime.codeMirrorSelectedCompletionIndex, codeMirrorRuntime.codeMirrorSelectFirstCompletion)) return true;
-      if (completionStatus) return waitForCompletionTab(view);
+      if (completionStatus === "active" && canAcceptCurrentCompletionOnTab(view) && acceptSelectedOrFirstCompletion(view, codeMirrorRuntime.codeMirrorAcceptCompletion, codeMirrorRuntime.codeMirrorSelectedCompletionIndex, codeMirrorRuntime.codeMirrorSelectFirstCompletion)) return true;
+      if (completionStatus && canAcceptCurrentCompletionOnTab(view)) return waitForCompletionTab(view);
     }
     return codeMirrorRuntime.codeMirrorNextSnippetField?.(view) ?? false;
   }
 
+  function acceptSnippetCompletionOnSpace(view: EditorViewType): boolean {
+    if (isEditorComposing(view)) return false;
+    const triggerKey = settingsStore.editorSettings.snippetTriggerKey;
+    if (triggerKey !== "space" && triggerKey !== "both") return false;
+    if (codeMirrorRuntime.codeMirrorCompletionStatus?.(view.state) !== "active") return false;
+    if (!view.state.selection.main.empty) return false;
+
+    const target = getTargetCompletion(view);
+    if (target?.type !== "snippet") return false;
+    if (target?.dbxBatchColumnSelection || target?.dbxBatchColumnSelectionAction) return false;
+
+    return acceptSelectedOrFirstCompletion(view, codeMirrorRuntime.codeMirrorAcceptCompletion, codeMirrorRuntime.codeMirrorSelectedCompletionIndex, codeMirrorRuntime.codeMirrorSelectFirstCompletion);
+  }
+
   function acceptSqlServerCompletionOnSpace(view: EditorViewType): boolean {
     if (isEditorComposing(view)) return false;
     if (props.databaseType !== "sqlserver" || !settingsStore.editorSettings.sqlServerSpaceConfirmsCompletion) return false;
@@ -174,6 +206,13 @@ export function useQueryEditorCompletionKeys(options: QueryEditorCompletionKeysO
     return true;
   }
 
+  function handleSpace(view: EditorViewType): boolean {
+    if (isEditorComposing(view)) return false;
+    if (!view.state.selection.main.empty) return false;
+    if (acceptSnippetCompletionOnSpace(view)) return true;
+    return acceptSqlServerCompletionOnSpace(view);
+  }
+
   function clearPendingCompletionTab() {
     if (pendingCompletionTabTimer === null) return;
     clearTimeout(pendingCompletionTabTimer);
@@ -196,8 +235,8 @@ export function useQueryEditorCompletionKeys(options: QueryEditorCompletionKeysO
       if (view.state.doc !== initialDoc || selectionRanges.length !== initialSelectionRanges.length || selectionRanges.some((range, index) => !range.empty || range.anchor !== initialSelectionRanges[index]?.anchor || range.head !== initialSelectionRanges[index]?.head)) return;
 
       const completionStatus = codeMirrorRuntime.codeMirrorCompletionStatus?.(view.state) ?? null;
-      if (completionStatus === "active" && acceptSelectedOrFirstCompletion(view, codeMirrorRuntime.codeMirrorAcceptCompletion, codeMirrorRuntime.codeMirrorSelectedCompletionIndex, codeMirrorRuntime.codeMirrorSelectFirstCompletion)) return;
-      if (completionStatus && Date.now() - startedAt < COMPLETION_TAB_MAX_WAIT_MS) {
+      if (completionStatus === "active" && canAcceptCurrentCompletionOnTab(view) && acceptSelectedOrFirstCompletion(view, codeMirrorRuntime.codeMirrorAcceptCompletion, codeMirrorRuntime.codeMirrorSelectedCompletionIndex, codeMirrorRuntime.codeMirrorSelectFirstCompletion)) return;
+      if (completionStatus && Date.now() - startedAt < COMPLETION_TAB_MAX_WAIT_MS && canAcceptCurrentCompletionOnTab(view)) {
         pendingCompletionTabTimer = setTimeout(retry, COMPLETION_TAB_RETRY_DELAY_MS);
         return;
       }
@@ -212,5 +251,5 @@ export function useQueryEditorCompletionKeys(options: QueryEditorCompletionKeysO
     return true;
   }
 
-  return { editorIndentUnit, handleTab, handleEnter, acceptCompletionOrNextSnippetField, acceptSqlServerCompletionOnSpace, clearPendingCompletionEnter, clearPendingCompletionTab };
+  return { editorIndentUnit, handleTab, handleEnter, acceptCompletionOrNextSnippetField, acceptSqlServerCompletionOnSpace, acceptSnippetCompletionOnSpace, handleSpace, clearPendingCompletionEnter, clearPendingCompletionTab };
 }
diff --git a/apps/desktop/src/i18n/locales/az.ts b/apps/desktop/src/i18n/locales/az.ts
index 86a7c17dfd..df7ce487b3 100644
--- a/apps/desktop/src/i18n/locales/az.ts
+++ b/apps/desktop/src/i18n/locales/az.ts
@@ -7382,6 +7382,11 @@ export default withEnglishFallback({
     sidebarFontSize: "Yan panel şriftinin ölçüsü",
     sidebarFontSizeDescription: "Yan paneldəki obyekt ağacının şrift ölçüsü, piksellə. Redaktorun şrift ölçüsündən asılı deyil.",
     snippetsDescription: "Redaktorda işə salınan SQL kod parçası şablonlarını fərdiləşdirin.",
+    snippetTriggerKey: "Kod parçası işə salma düyməsi",
+    snippetTriggerKeyDescription: "Redaktorda kod parçasının genişləndirilməsini işə salmaq üçün istifadə olunan düyməni seçin (Tab, Boşluq və ya Hər ikisi).",
+    snippetTriggerKeyTab: "Tab",
+    snippetTriggerKeySpace: "Boşluq",
+    snippetTriggerKeyBoth: "Tab və ya Boşluq",
     snippetsPlaceholderHint: "Redaktə edilə bilən yer tutucu yaratmaq üçün {'${number:default text}'} sintaksisindən istifadə edin. Eyni nömrəli yer tutucular əlaqəli qalır; məsələn:",
     snippetsAdd: "Kod parçası əlavə et",
     snippetsLabel: "Etiket",
diff --git a/apps/desktop/src/i18n/locales/en.ts b/apps/desktop/src/i18n/locales/en.ts
index c7ea127420..a3839caf5c 100644
--- a/apps/desktop/src/i18n/locales/en.ts
+++ b/apps/desktop/src/i18n/locales/en.ts
@@ -8276,6 +8276,11 @@ export default {
     sidebarFontSize: "Sidebar font size",
     sidebarFontSizeDescription: "Font size for the sidebar object tree, in pixels. Independent of the editor font size.",
     snippetsDescription: "Customize SQL snippet templates triggered in the editor.",
+    snippetTriggerKey: "Snippet trigger key",
+    snippetTriggerKeyDescription: "Choose the key used to trigger snippet expansion in the editor (Tab, Space, or Both).",
+    snippetTriggerKeyTab: "Tab",
+    snippetTriggerKeySpace: "Space",
+    snippetTriggerKeyBoth: "Tab or Space",
     snippetsPlaceholderHint: "Use {'${number:default text}'} to create an editable placeholder. Placeholders with the same number stay linked; for example:",
     snippetsAdd: "Add Snippet",
     snippetsLabel: "Label",
diff --git a/apps/desktop/src/i18n/locales/es.ts b/apps/desktop/src/i18n/locales/es.ts
index 7ea0742963..eb51a66b53 100644
--- a/apps/desktop/src/i18n/locales/es.ts
+++ b/apps/desktop/src/i18n/locales/es.ts
@@ -7599,6 +7599,11 @@ export default withEnglishFallback({
     sidebarShowTooltips: "Sugerencias al pasar el cursor en la barra lateral",
     sidebarShowTooltipsDescription: "Muestra sugerencias con detalles al pasar el cursor sobre conexiones y objetos de la base de datos en la barra lateral. Desactívalo para ocultarlas.",
     snippetsDescription: "Personaliza plantillas SQL activadas en el editor.",
+    snippetTriggerKey: "Tecla de activación de fragmentos",
+    snippetTriggerKeyDescription: "Elija la tecla utilizada para activar la expansión de fragmentos en el editor (Tab, Espacio o Ambos).",
+    snippetTriggerKeyTab: "Tab",
+    snippetTriggerKeySpace: "Espacio",
+    snippetTriggerKeyBoth: "Tab o Espacio",
     syncSnippetProviderGitee: "Fragmentos de Gitee",
     syncGitLabInstance: "URL de la instancia de GitLab",
     syncGitLabInstanceInvalid: "Introduce una URL HTTP o HTTPS sin credenciales, parámetros de consulta ni fragmentos.",
diff --git a/apps/desktop/src/i18n/locales/id.ts b/apps/desktop/src/i18n/locales/id.ts
index d386fb3cb9..c91042a6b9 100644
--- a/apps/desktop/src/i18n/locales/id.ts
+++ b/apps/desktop/src/i18n/locales/id.ts
@@ -8069,6 +8069,11 @@ export default withEnglishFallback({
     sidebarFontSize: "Ukuran font bilah sisi",
     sidebarFontSizeDescription: "Ukuran font untuk pohon objek bilah sisi, dalam piksel. Independen dari ukuran font editor.",
     snippetsDescription: "Sesuaikan template snippet SQL yang dipicu di editor.",
+    snippetTriggerKey: "Tombol pemicu snippet",
+    snippetTriggerKeyDescription: "Pilih tombol yang digunakan untuk memicu perluasan snippet di editor (Tab, Spasi, atau Keduanya).",
+    snippetTriggerKeyTab: "Tab",
+    snippetTriggerKeySpace: "Spasi",
+    snippetTriggerKeyBoth: "Tab atau Spasi",
     snippetsPlaceholderHint: "Gunakan {'${number:default text}'} untuk membuat placeholder yang dapat diedit. Placeholder dengan nomor yang sama akan tetap tertaut; contohnya:",
     snippetsAdd: "Tambah Snippet",
     snippetsLabel: "Label",
diff --git a/apps/desktop/src/i18n/locales/it.ts b/apps/desktop/src/i18n/locales/it.ts
index 039946316d..56660d6542 100644
--- a/apps/desktop/src/i18n/locales/it.ts
+++ b/apps/desktop/src/i18n/locales/it.ts
@@ -7377,6 +7377,11 @@ export default withEnglishFallback({
     sidebarShowTooltips: "Tooltip al passaggio del mouse nella barra laterale",
     sidebarShowTooltipsDescription: "Mostra tooltip con dettagli al passaggio del mouse su connessioni e oggetti del database nella barra laterale. Disattiva per nasconderli.",
     snippetsDescription: "Personalizza i modelli di snippet SQL attivati nell'editor.",
+    snippetTriggerKey: "Tasto di attivazione dello snippet",
+    snippetTriggerKeyDescription: "Scegli il tasto utilizzato per attivare l'espansione dello snippet nell'editor (Tab, Spazio o Entrambi).",
+    snippetTriggerKeyTab: "Tab",
+    snippetTriggerKeySpace: "Spazio",
+    snippetTriggerKeyBoth: "Tab o Spazio",
     syncSnippetProviderGitee: "Snippet Gitee",
     syncGitLabInstance: "URL dell'istanza GitLab",
     syncGitLabInstanceInvalid: "Inserisci un URL HTTP o HTTPS senza credenziali, parametri di query o frammenti.",
diff --git a/apps/desktop/src/i18n/locales/ja.ts b/apps/desktop/src/i18n/locales/ja.ts
index 0709a8017e..235c68d873 100644
--- a/apps/desktop/src/i18n/locales/ja.ts
+++ b/apps/desktop/src/i18n/locales/ja.ts
@@ -7392,6 +7392,11 @@ export default withEnglishFallback({
     sidebarShowTooltips: "サイドバーのホバーツールチップ",
     sidebarShowTooltipsDescription: "サイドバーで接続やデータベースオブジェクトにホバーしたときに詳細ツールチップを表示します。オフにすると表示されません。",
     snippetsDescription: "エディタでトリガーされるSQLスニペットテンプレートをカスタマイズします。",
+    snippetTriggerKey: "スニペットトリガーキー",
+    snippetTriggerKeyDescription: "エディタでスニペットを展開するトリガーキーを設定します(Tab、スペース、または両方)。",
+    snippetTriggerKeyTab: "Tab",
+    snippetTriggerKeySpace: "スペース",
+    snippetTriggerKeyBoth: "Tab またはスペース",
     syncSnippetProviderGitee: "Gitee コードスニペット",
     syncGitLabInstance: "GitLab インスタンス URL",
     syncGitLabInstanceInvalid: "認証情報、クエリ、フラグメントを含まない HTTP または HTTPS の URL を入力してください。",
diff --git a/apps/desktop/src/i18n/locales/ko.ts b/apps/desktop/src/i18n/locales/ko.ts
index bb4b15c957..d550a6878d 100644
--- a/apps/desktop/src/i18n/locales/ko.ts
+++ b/apps/desktop/src/i18n/locales/ko.ts
@@ -7178,6 +7178,11 @@ export default withEnglishFallback({
     sidebarShowTooltips: "사이드바 호버 툴팁",
     sidebarShowTooltipsDescription: "사이드바에서 연결 및 데이터베이스 개체에 마우스를 올리면 상세 툴팁을 표시합니다. 끄면 표시되지 않습니다.",
     snippetsDescription: "편집기에서 트리거되는 SQL 스니펫 템플릿을 사용자 정의합니다.",
+    snippetTriggerKey: "스니펫 트리거 키",
+    snippetTriggerKeyDescription: "편집기에서 스니펫 확장을 트리거하는 키를 설정합니다(Tab, 스페이스 또는 둘 다).",
+    snippetTriggerKeyTab: "Tab",
+    snippetTriggerKeySpace: "스페이스",
+    snippetTriggerKeyBoth: "Tab 또는 스페이스",
     snippetsPlaceholderHint: "{'${번호:기본 텍스트}'}를 사용하여 편집 가능한 자리 표시자를 만듭니다. 같은 번호의 자리 표시자는 함께 수정됩니다. 예:",
     snippetsAdd: "스니펫 추가",
     snippetsLabel: "라벨",
diff --git a/apps/desktop/src/i18n/locales/pt-BR.ts b/apps/desktop/src/i18n/locales/pt-BR.ts
index de1cd071a8..fd98a8bd23 100644
--- a/apps/desktop/src/i18n/locales/pt-BR.ts
+++ b/apps/desktop/src/i18n/locales/pt-BR.ts
@@ -7379,6 +7379,11 @@ export default withEnglishFallback({
     sidebarShowTooltips: "Dicas ao passar o mouse na barra lateral",
     sidebarShowTooltipsDescription: "Mostra dicas com detalhes ao passar o mouse sobre conexões e objetos do banco de dados na barra lateral. Desative para ocultá-las.",
     snippetsDescription: "Personalize os modelos de snippets SQL acionados no editor.",
+    snippetTriggerKey: "Tecla de ativação de snippets",
+    snippetTriggerKeyDescription: "Escolha a tecla usada para acionar a expansão de snippets no editor (Tab, Espaço ou Ambos).",
+    snippetTriggerKeyTab: "Tab",
+    snippetTriggerKeySpace: "Espaço",
+    snippetTriggerKeyBoth: "Tab ou Espaço",
     syncSnippetProviderGitee: "Snippets do Gitee",
     syncGitLabInstance: "URL da instância do GitLab",
     syncGitLabInstanceInvalid: "Insira uma URL HTTP ou HTTPS sem credenciais, parâmetros de consulta ou fragmentos.",
diff --git a/apps/desktop/src/i18n/locales/ru.ts b/apps/desktop/src/i18n/locales/ru.ts
index 20161d0658..9b3b31d649 100644
--- a/apps/desktop/src/i18n/locales/ru.ts
+++ b/apps/desktop/src/i18n/locales/ru.ts
@@ -8592,6 +8592,11 @@ export default withEnglishFallback({
     sidebarFontSize: "Размер шрифта боковой панели",
     sidebarFontSizeDescription: "Размер шрифта дерева объектов боковой панели в пикселях. Не зависит от размера шрифта редактора.",
     snippetsDescription: "Настройка шаблонов SQL-сниппетов, вызываемых в редакторе.",
+    snippetTriggerKey: "Клавиша вызова сниппетов",
+    snippetTriggerKeyDescription: "Выберите клавишу для вставки сниппетов в редакторе (Tab, пробел или оба).",
+    snippetTriggerKeyTab: "Tab",
+    snippetTriggerKeySpace: "Пробел",
+    snippetTriggerKeyBoth: "Tab или пробел",
     snippetsPlaceholderHint: "Используйте {'${number:default text}'} для создания редактируемого заполнителя. Заполнители с одинаковым номером остаются связанными; например:",
     snippetsAdd: "Добавить сниппет",
     snippetsLabel: "Название",
diff --git a/apps/desktop/src/i18n/locales/tr.ts b/apps/desktop/src/i18n/locales/tr.ts
index 623cd080a9..c4fb1f9449 100644
--- a/apps/desktop/src/i18n/locales/tr.ts
+++ b/apps/desktop/src/i18n/locales/tr.ts
@@ -7281,6 +7281,11 @@ export default withEnglishFallback({
     sidebarFontSize: "Kenar çubuğu yazı tipi boyutu",
     sidebarFontSizeDescription: "Kenar çubuğu nesne ağacı için piksel cinsinden yazı tipi boyutu. Düzenleyici yazı tipi boyutundan bağımsızdır.",
     snippetsDescription: "Düzenleyicide tetiklenen SQL parçacığı şablonlarını özelleştirin.",
+    snippetTriggerKey: "Kod parçacığı tetikleme tuşu",
+    snippetTriggerKeyDescription: "Düzenleyicide kod parçacığı genişletmesini tetiklemek için kullanılan tuşu seçin (Tab, Boşluk veya Her İkisi).",
+    snippetTriggerKeyTab: "Tab",
+    snippetTriggerKeySpace: "Boşluk",
+    snippetTriggerKeyBoth: "Tab veya Boşluk",
     snippetsAdd: "Parçacık Ekle",
     snippetsLabel: "Etiket",
     snippetsPrefix: "Önek",
diff --git a/apps/desktop/src/i18n/locales/zh-CN.ts b/apps/desktop/src/i18n/locales/zh-CN.ts
index 8d0b964e9d..6c73b134c9 100644
--- a/apps/desktop/src/i18n/locales/zh-CN.ts
+++ b/apps/desktop/src/i18n/locales/zh-CN.ts
@@ -8261,6 +8261,11 @@ export default withEnglishFallback({
     sidebarFontSize: "侧边栏字体大小",
     sidebarFontSizeDescription: "侧边栏对象树的字体大小(像素),独立于编辑器字体大小。",
     snippetsDescription: "自定义编辑器中触发的 SQL 代码片段模板。",
+    snippetTriggerKey: "代码片段触发键",
+    snippetTriggerKeyDescription: "设置在编辑器中触发插入代码片段的按键(Tab、空格或两者均可)。",
+    snippetTriggerKeyTab: "Tab",
+    snippetTriggerKeySpace: "空格",
+    snippetTriggerKeyBoth: "Tab 或空格",
     snippetsPlaceholderHint: "使用 {'${编号:默认文本}'} 创建可编辑占位符;编号相同的占位符会同步修改,例如:",
     snippetsAdd: "添加片段",
     snippetsLabel: "显示名",
diff --git a/apps/desktop/src/i18n/locales/zh-TW.ts b/apps/desktop/src/i18n/locales/zh-TW.ts
index c62d07bb32..08b43be53e 100644
--- a/apps/desktop/src/i18n/locales/zh-TW.ts
+++ b/apps/desktop/src/i18n/locales/zh-TW.ts
@@ -6726,6 +6726,11 @@ export default withEnglishFallback({
     sidebarShowTooltips: "側邊欄懸浮提示",
     sidebarShowTooltipsDescription: "在側邊欄的連線與資料庫物件上懸浮時顯示詳細提示;關閉後懸浮不再彈出。",
     snippetsDescription: "自訂編輯器中觸發的 SQL 程式碼片段範本。",
+    snippetTriggerKey: "程式碼片段觸發鍵",
+    snippetTriggerKeyDescription: "設定在編輯器中觸發插入程式碼片段的按鍵(Tab、空格或兩者皆可)。",
+    snippetTriggerKeyTab: "Tab",
+    snippetTriggerKeySpace: "空格",
+    snippetTriggerKeyBoth: "Tab 或空格",
     syncSnippetProviderGitee: "Gitee 程式碼片段",
     syncGitLabInstance: "GitLab 執行個體網址",
     syncGitLabInstanceInvalid: "請輸入不含認證資訊、查詢參數或片段的 HTTP 或 HTTPS 網址。",
diff --git a/apps/desktop/src/lib/__tests__/editor/queryEditorCompletionKeymap.spec.ts b/apps/desktop/src/lib/__tests__/editor/queryEditorCompletionKeymap.spec.ts
index e3eeb63a2c..52e86b82af 100644
--- a/apps/desktop/src/lib/__tests__/editor/queryEditorCompletionKeymap.spec.ts
+++ b/apps/desktop/src/lib/__tests__/editor/queryEditorCompletionKeymap.spec.ts
@@ -91,7 +91,7 @@ function createHarness(options: {
     "let cancelPendingCompletionEnter: (() => void) | null = null;",
     "let suppressNextSqlCompletionAutoStartUntil = 0;",
     "const completion = { get suppressAutoStartUntil() { return suppressNextSqlCompletionAutoStartUntil; }, set suppressAutoStartUntil(value) { suppressNextSqlCompletionAutoStartUntil = value; } };",
-    "const codeMirrorRuntime = { codeMirrorCompletionStatus, codeMirrorSelectedCompletion, codeMirrorAcceptCompletion, codeMirrorSelectedCompletionIndex, codeMirrorSelectFirstCompletion, codeMirrorCloseCompletion, codeMirrorInsertNewlineKeepIndent, codeMirrorNextSnippetField, codeMirrorIndentMore };",
+    "const codeMirrorRuntime = { codeMirrorCompletionStatus, codeMirrorSelectedCompletion, codeMirrorCurrentCompletions: typeof codeMirrorCurrentCompletions !== 'undefined' ? codeMirrorCurrentCompletions : undefined, codeMirrorAcceptCompletion, codeMirrorSelectedCompletionIndex, codeMirrorSelectFirstCompletion, codeMirrorCloseCompletion, codeMirrorInsertNewlineKeepIndent, codeMirrorNextSnippetField, codeMirrorIndentMore };",
     extractFunction("editorIndentUnit"),
     extractFunction("handleTab"),
     extractFunction("tabKeyAcceptsCompletion"),
@@ -100,8 +100,12 @@ function createHarness(options: {
     extractFunction("handleEnter"),
     extractFunction("clearPendingCompletionEnter"),
     extractFunction("insertNewlineWithoutCompletion"),
+    extractFunction("getTargetCompletion"),
+    extractFunction("canAcceptCurrentCompletionOnTab"),
     extractFunction("acceptCompletionOrNextSnippetField"),
+    extractFunction("acceptSnippetCompletionOnSpace"),
     extractFunction("acceptSqlServerCompletionOnSpace"),
+    extractFunction("handleSpace"),
     extractFunction("clearPendingCompletionTab"),
     extractFunction("waitForCompletionTab"),
     extractFunction("consumeSqlCompletionAutoStartSuppression"),
diff --git a/apps/desktop/src/lib/settings/__tests__/editorSettingsDraft.spec.ts b/apps/desktop/src/lib/settings/__tests__/editorSettingsDraft.spec.ts
index 4767dd6414..35b902bb3f 100644
--- a/apps/desktop/src/lib/settings/__tests__/editorSettingsDraft.spec.ts
+++ b/apps/desktop/src/lib/settings/__tests__/editorSettingsDraft.spec.ts
@@ -99,6 +99,12 @@ describe("EDITOR_SETTINGS_DRAFT_KEYS", () => {
     expect(EDITOR_SETTINGS_DRAFT_KEYS).toContain("openDataTabsNextToActive");
   });
 
+  it("includes snippet trigger key setting in draft keys and draft conversion", () => {
+    expect(EDITOR_SETTINGS_DRAFT_KEYS).toContain("snippetTriggerKey");
+    const draft = editorSettingsDraftFromSettings(makeSettings({ snippetTriggerKey: "space" }));
+    expect(draft.snippetTriggerKey).toBe("space");
+  });
+
   it("includes data grid type colors", () => {
     expect(EDITOR_SETTINGS_DRAFT_KEYS).toContain("colorizeDataGridCellTypes");
   });
diff --git a/apps/desktop/src/lib/settings/__tests__/settingsTransfer.spec.ts b/apps/desktop/src/lib/settings/__tests__/settingsTransfer.spec.ts
index 41fa6f28ac..1710f5f1af 100644
--- a/apps/desktop/src/lib/settings/__tests__/settingsTransfer.spec.ts
+++ b/apps/desktop/src/lib/settings/__tests__/settingsTransfer.spec.ts
@@ -273,12 +273,14 @@ describe("settingsTransfer", () => {
       theme: DEFAULT_EDITOR_SETTINGS.theme,
       pageSize: 200,
       snippets: [{ id: "s1", label: "L", prefix: "p", body: "SELECT 1", enabled: true }],
+      snippetTriggerKey: "space",
     } as EditorSettings;
     const result = parseSettingsTransferFile(serializeSettingsTransfer(settings));
     expect(result.ok).toBe(true);
     if (!result.ok) return;
     expect(result.value.editorSettings.pageSize).toBe(200);
     expect(result.value.editorSettings.snippets).toEqual(settings.snippets);
+    expect(result.value.editorSettings.snippetTriggerKey).toBe("space");
   });
 
   it("round-trips table default sorting settings", () => {
diff --git a/apps/desktop/src/lib/settings/editorSettingsDraft.ts b/apps/desktop/src/lib/settings/editorSettingsDraft.ts
index cc7d742f12..26edc1756b 100644
--- a/apps/desktop/src/lib/settings/editorSettingsDraft.ts
+++ b/apps/desktop/src/lib/settings/editorSettingsDraft.ts
@@ -30,6 +30,7 @@ export const EDITOR_SETTINGS_DRAFT_KEYS = [
   "tableCompletionSchemaQualification",
   "insertSpaceAfterCompletion",
   "sqlServerSpaceConfirmsCompletion",
+  "snippetTriggerKey",
   "sortCompletionColumnsAlphabetically",
   "selectFirstCompletionOnOpen",
   "wordWrap",
@@ -177,6 +178,7 @@ function normalizedDraftValue(key: EditorSettingsDraftKey, value: unknown): unkn
   if (key === "redisKeyTemplates") return normalizeRedisKeyTemplates(value);
   if (key === "csvNullMode") return normalizeCsvNullMode(value);
   if (key === "backgroundImage") return normalizeBackgroundImageSettings(value);
+  if (key === "snippetTriggerKey") return value === "space" || value === "both" ? value : "tab";
   return value;
 }
 
diff --git a/apps/desktop/src/lib/settings/settingsSearch.ts b/apps/desktop/src/lib/settings/settingsSearch.ts
index aa5824497b..b6911e74c5 100644
--- a/apps/desktop/src/lib/settings/settingsSearch.ts
+++ b/apps/desktop/src/lib/settings/settingsSearch.ts
@@ -272,6 +272,7 @@ export const SETTINGS_SEARCH_DEFINITIONS: readonly SettingsSearchDefinition[] =
   { id: "shortcuts", category: "shortcuts", titleKey: "settings.shortcutsTab", targetId: "shortcuts" },
   { id: "sql-shortcuts", category: "shortcuts", titleKey: "settings.sqlShortcutsTitle", descriptionKey: "settings.sqlShortcutsDescription", targetId: "sql-shortcuts" },
   { id: "snippets", category: "snippets", titleKey: "settings.snippetsTab", descriptionKey: "settings.snippetsDescription", targetId: "snippets" },
+  { id: "snippet-trigger-key", category: "snippets", titleKey: "settings.snippetTriggerKey", descriptionKey: "settings.snippetTriggerKeyDescription", targetId: "snippet-trigger-key" },
   { id: "sync-webdav", category: "sync", titleKey: "settings.syncWebDavTitle", descriptionKey: "settings.syncWebDavDescription", targetId: "sync-webdav", route: { syncMethodTab: "webdav" } },
   { id: "sync-webdav-endpoint", category: "sync", titleKey: "settings.syncEndpoint", targetId: "sync-webdav", route: { syncMethodTab: "webdav" } },
   { id: "sync-webdav-username", category: "sync", titleKey: "settings.syncUsername", targetId: "sync-webdav", route: { syncMethodTab: "webdav" } },
diff --git a/apps/desktop/src/lib/settings/settingsTransfer.ts b/apps/desktop/src/lib/settings/settingsTransfer.ts
index a958087607..5c0e842637 100644
--- a/apps/desktop/src/lib/settings/settingsTransfer.ts
+++ b/apps/desktop/src/lib/settings/settingsTransfer.ts
@@ -195,7 +195,7 @@ const SETTINGS_TRANSFER_CATEGORY_KEYS: Record {
     expect(normalizeEditorSettings({ sqlServerSpaceConfirmsCompletion: "yes" as unknown as boolean }).sqlServerSpaceConfirmsCompletion).toBe(false);
   });
 
+  it("defaults snippetTriggerKey to tab and preserves valid options while falling back on invalid values", () => {
+    expect(normalizeEditorSettings({}).snippetTriggerKey).toBe("tab");
+    expect(normalizeEditorSettings({ snippetTriggerKey: "tab" }).snippetTriggerKey).toBe("tab");
+    expect(normalizeEditorSettings({ snippetTriggerKey: "space" }).snippetTriggerKey).toBe("space");
+    expect(normalizeEditorSettings({ snippetTriggerKey: "both" }).snippetTriggerKey).toBe("both");
+    expect(normalizeEditorSettings({ snippetTriggerKey: "enter" as any }).snippetTriggerKey).toBe("tab");
+  });
+
   it("selects the first completion candidate by default and preserves the opt-out", () => {
     expect(normalizeEditorSettings({}).selectFirstCompletionOnOpen).toBe(true);
     expect(normalizeEditorSettings({ selectFirstCompletionOnOpen: true }).selectFirstCompletionOnOpen).toBe(true);
diff --git a/apps/desktop/src/stores/settingsStore.ts b/apps/desktop/src/stores/settingsStore.ts
index 6f6b655d4b..5ced829640 100644
--- a/apps/desktop/src/stores/settingsStore.ts
+++ b/apps/desktop/src/stores/settingsStore.ts
@@ -851,7 +851,10 @@ export interface RememberedConnectionDatabase {
   dbType: string;
 }
 
+export type SnippetTriggerKey = "tab" | "space" | "both";
+
 export interface EditorSettings {
+  snippetTriggerKey: SnippetTriggerKey;
   fontFamily: string;
   fontSize: number;
   uiFontFamily: string;
@@ -1205,6 +1208,7 @@ export const DEFAULT_EDITOR_SETTINGS: EditorSettings = {
   tableCompletionSchemaQualification: DEFAULT_SQL_TABLE_COMPLETION_SCHEMA_QUALIFICATION,
   insertSpaceAfterCompletion: true,
   sqlServerSpaceConfirmsCompletion: false,
+  snippetTriggerKey: "tab",
   sortCompletionColumnsAlphabetically: true,
   selectFirstCompletionOnOpen: true,
   wordWrap: false,
@@ -1798,6 +1802,7 @@ export function normalizeEditorSettings(settings: Partial, exist
     tableCompletionSchemaQualification: normalizeSqlTableCompletionSchemaQualification(settings.tableCompletionSchemaQualification),
     insertSpaceAfterCompletion: typeof settings.insertSpaceAfterCompletion === "boolean" ? settings.insertSpaceAfterCompletion : DEFAULT_EDITOR_SETTINGS.insertSpaceAfterCompletion,
     sqlServerSpaceConfirmsCompletion: typeof settings.sqlServerSpaceConfirmsCompletion === "boolean" ? settings.sqlServerSpaceConfirmsCompletion : DEFAULT_EDITOR_SETTINGS.sqlServerSpaceConfirmsCompletion,
+    snippetTriggerKey: settings.snippetTriggerKey === "space" || settings.snippetTriggerKey === "both" ? settings.snippetTriggerKey : DEFAULT_EDITOR_SETTINGS.snippetTriggerKey,
     sortCompletionColumnsAlphabetically: typeof settings.sortCompletionColumnsAlphabetically === "boolean" ? settings.sortCompletionColumnsAlphabetically : DEFAULT_EDITOR_SETTINGS.sortCompletionColumnsAlphabetically,
     selectFirstCompletionOnOpen: typeof settings.selectFirstCompletionOnOpen === "boolean" ? settings.selectFirstCompletionOnOpen : DEFAULT_EDITOR_SETTINGS.selectFirstCompletionOnOpen,
     wordWrap: settings.wordWrap ?? DEFAULT_EDITOR_SETTINGS.wordWrap,
@@ -2645,6 +2650,7 @@ export const useSettingsStore = defineStore("settings", () => {
     if (partial.tableCompletionSchemaQualification !== undefined) editorSettings.value.tableCompletionSchemaQualification = normalizeSqlTableCompletionSchemaQualification(partial.tableCompletionSchemaQualification);
     if (partial.insertSpaceAfterCompletion !== undefined) editorSettings.value.insertSpaceAfterCompletion = partial.insertSpaceAfterCompletion === true;
     if (partial.sqlServerSpaceConfirmsCompletion !== undefined) editorSettings.value.sqlServerSpaceConfirmsCompletion = partial.sqlServerSpaceConfirmsCompletion === true;
+    if (partial.snippetTriggerKey !== undefined) editorSettings.value.snippetTriggerKey = partial.snippetTriggerKey === "space" || partial.snippetTriggerKey === "both" ? partial.snippetTriggerKey : "tab";
     if (partial.sortCompletionColumnsAlphabetically !== undefined) editorSettings.value.sortCompletionColumnsAlphabetically = partial.sortCompletionColumnsAlphabetically === true;
     if (partial.selectFirstCompletionOnOpen !== undefined) editorSettings.value.selectFirstCompletionOnOpen = partial.selectFirstCompletionOnOpen === true;
     if (partial.wordWrap !== undefined) editorSettings.value.wordWrap = partial.wordWrap;