diff --git a/src/components/editor/EditorSettingsDialog.vue b/src/components/editor/EditorSettingsDialog.vue
index c1bf6eee90..2a8b07c6b4 100644
--- a/src/components/editor/EditorSettingsDialog.vue
+++ b/src/components/editor/EditorSettingsDialog.vue
@@ -30,6 +30,7 @@ const editFontFamily = ref(settingsStore.editorSettings.fontFamily);
const editFontSize = ref(settingsStore.editorSettings.fontSize);
const editTheme = ref(settingsStore.editorSettings.theme);
const editExecuteMode = ref(settingsStore.editorSettings.executeMode);
+const editWordWrap = ref(settingsStore.editorSettings.wordWrap);
// Sync from store when dialog opens
watch(
@@ -40,6 +41,7 @@ watch(
editFontSize.value = settingsStore.editorSettings.fontSize;
editTheme.value = settingsStore.editorSettings.theme;
editExecuteMode.value = settingsStore.editorSettings.executeMode;
+ editWordWrap.value = settingsStore.editorSettings.wordWrap;
}
},
);
@@ -49,7 +51,8 @@ function hasChanges(): boolean {
editFontFamily.value !== settingsStore.editorSettings.fontFamily ||
editFontSize.value !== settingsStore.editorSettings.fontSize ||
editTheme.value !== settingsStore.editorSettings.theme ||
- editExecuteMode.value !== settingsStore.editorSettings.executeMode
+ editExecuteMode.value !== settingsStore.editorSettings.executeMode ||
+ editWordWrap.value !== settingsStore.editorSettings.wordWrap
);
}
@@ -59,6 +62,7 @@ function applySettings() {
fontSize: editFontSize.value,
theme: editTheme.value,
executeMode: editExecuteMode.value,
+ wordWrap: editWordWrap.value,
});
emit("update:open", false);
}
@@ -68,6 +72,7 @@ function resetDefaults() {
editFontSize.value = DEFAULT_EDITOR_SETTINGS.fontSize;
editTheme.value = DEFAULT_EDITOR_SETTINGS.theme;
editExecuteMode.value = DEFAULT_EDITOR_SETTINGS.executeMode;
+ editWordWrap.value = DEFAULT_EDITOR_SETTINGS.wordWrap;
}
function onExecuteModeChange(v: any) {
@@ -340,6 +345,16 @@ watch(
+
+
+
+
{{ t("settings.wordWrapDescription") }}
+
+
+
+
+
+
diff --git a/src/components/editor/QueryEditor.vue b/src/components/editor/QueryEditor.vue
index f6e9b934ef..01ba0d2b63 100644
--- a/src/components/editor/QueryEditor.vue
+++ b/src/components/editor/QueryEditor.vue
@@ -15,7 +15,7 @@ const props = defineProps<{
modelValue: string;
connectionId?: string;
database?: string;
- dialect?: "mysql" | "postgres";
+ dialect?: "mysql" | "postgres" | "sqlserver";
formatDialect?: SqlFormatDialect;
formatRequestId?: number;
}>();
@@ -40,6 +40,7 @@ const MAX_FONT_SIZE = 24;
let editorViewModule: typeof import("@codemirror/view") | null = null;
let fontThemeComp: import("@codemirror/state").Compartment | null = null;
let codeMirrorTheme: import("@codemirror/state").Compartment | null = null;
+let wordWrapComp: import("@codemirror/state").Compartment | null = null;
// Completion cache
let cachedTables: Array<{ name: string; schema?: string; type?: "table" | "view" }> = [];
@@ -206,8 +207,8 @@ onMounted(async () => {
const [
{ EditorView, keymap },
- { EditorState, Compartment },
- { sql, MySQL, PostgreSQL, SQLDialect },
+ { EditorState, Compartment, Prec },
+ { sql, MSSQL, MySQL, PostgreSQL, SQLDialect },
{ basicSetup },
{ autocompletion, startCompletion },
{ indentWithTab },
@@ -222,10 +223,11 @@ onMounted(async () => {
editorViewModule = { EditorView, keymap } as typeof import("@codemirror/view");
fontThemeComp = new Compartment();
codeMirrorTheme = new Compartment();
+ wordWrapComp = new Compartment();
const ss = settingsStore.editorSettings;
- const baseDialect = props.dialect === "postgres" ? PostgreSQL : MySQL;
+ const baseDialect = props.dialect === "postgres" ? PostgreSQL : props.dialect === "sqlserver" ? MSSQL : MySQL;
const extraKeywords =
"PIVOT UNPIVOT EXCLUDE REPLACE QUALIFY ASOF POSITIONAL ANTI SEMI SAMPLE TABLESAMPLE STRUCT MAP LIST ARRAY LAMBDA UNNEST LATERAL FILTER RECURSIVE SUMMARIZE PRAGMA READ_CSV READ_PARQUET READ_JSON DESCRIBE SHOW COPY EXPORT IMPORT";
const dialect = SQLDialect.define({
@@ -283,8 +285,9 @@ onMounted(async () => {
override: [async (context: CompletionContext) => provideSqlCompletions(context.state, context.pos)],
}),
codeMirrorTheme.of(theme),
- keymap.of([indentWithTab]),
+ Prec.highest(keymap.of([indentWithTab])),
runKeymap,
+ wordWrapComp.of(ss.wordWrap ? EditorView.lineWrapping : []),
EditorView.updateListener.of((update) => {
if (update.docChanged) {
emit("update:modelValue", update.state.doc.toString());
@@ -486,11 +489,12 @@ watch(
watch(
() => settingsStore.editorSettings,
async (ss) => {
- if (!view.value || !codeMirrorTheme || !fontThemeComp || !editorViewModule) return;
+ if (!view.value || !codeMirrorTheme || !fontThemeComp || !wordWrapComp || !editorViewModule) return;
const themeExt = await loadEditorTheme(ss.theme);
view.value.dispatch({
effects: [
codeMirrorTheme.reconfigure(themeExt),
+ wordWrapComp.reconfigure(ss.wordWrap ? editorViewModule.EditorView.lineWrapping : []),
fontThemeComp.reconfigure(
editorFontTheme(editorViewModule.EditorView, ss.fontSize, ss.fontFamily, {
fixedHeight: true,
diff --git a/src/components/layout/ContentArea.vue b/src/components/layout/ContentArea.vue
index 7abe6cdd68..1bcee57967 100644
--- a/src/components/layout/ContentArea.vue
+++ b/src/components/layout/ContentArea.vue
@@ -71,9 +71,11 @@ const activeSqlFormatDialect = computed(() => {
}
});
-const editorDialect = computed<"mysql" | "postgres">(() =>
- props.activeConnection?.db_type === "postgres" ? "postgres" : "mysql",
-);
+const editorDialect = computed<"mysql" | "postgres" | "sqlserver">(() => {
+ if (props.activeConnection?.db_type === "postgres") return "postgres";
+ if (props.activeConnection?.db_type === "sqlserver") return "sqlserver";
+ return "mysql";
+});
const hasNumericData = computed(() => {
const r = props.activeTab.result;
diff --git a/src/i18n/locales/en.ts b/src/i18n/locales/en.ts
index 60229dd431..b8b9176f0d 100644
--- a/src/i18n/locales/en.ts
+++ b/src/i18n/locales/en.ts
@@ -737,6 +737,8 @@ export default {
executeMode: "Execute Mode (Cmd+Enter)",
executeModeAll: "Execute all SQL",
executeModeCurrent: "Execute statement at cursor",
+ wordWrap: "Word wrap",
+ wordWrapDescription: "Wrap long SQL lines within the editor width",
preview: "Live Preview",
aboutDescription: "An open-source, lightweight database management tool.",
community: "Community",
diff --git a/src/i18n/locales/zh-CN.ts b/src/i18n/locales/zh-CN.ts
index 76da0152a2..17149b2f55 100644
--- a/src/i18n/locales/zh-CN.ts
+++ b/src/i18n/locales/zh-CN.ts
@@ -725,6 +725,8 @@ export default {
executeMode: "执行模式 (Cmd+Enter)",
executeModeAll: "执行全部 SQL",
executeModeCurrent: "执行光标所在语句",
+ wordWrap: "自动换行",
+ wordWrapDescription: "长 SQL 在编辑器宽度内自动折行显示",
preview: "实时预览",
aboutDescription: "开源、轻量的数据库管理工具。",
community: "社区",
diff --git a/src/lib/sqlCompletion.ts b/src/lib/sqlCompletion.ts
index 8863a507fd..0cdda30c76 100644
--- a/src/lib/sqlCompletion.ts
+++ b/src/lib/sqlCompletion.ts
@@ -84,6 +84,15 @@ const SQL_KEYWORDS = [
"APPLY",
"CROSS APPLY",
"OUTER APPLY",
+ "ISJSON",
+ "JSON_ARRAY",
+ "JSON_ARRAYAGG",
+ "JSON_MODIFY",
+ "JSON_OBJECT",
+ "JSON_OBJECTAGG",
+ "JSON_PATH_EXISTS",
+ "JSON_QUERY",
+ "JSON_VALUE",
"OPENJSON",
"OPENXML",
"OPENROWSET",
diff --git a/src/stores/settingsStore.ts b/src/stores/settingsStore.ts
index f14409feb0..7d375900a4 100644
--- a/src/stores/settingsStore.ts
+++ b/src/stores/settingsStore.ts
@@ -45,6 +45,7 @@ export interface EditorSettings {
fontSize: number;
theme: EditorTheme;
executeMode: "all" | "current";
+ wordWrap: boolean;
}
export const EDITOR_THEMES: { value: EditorTheme; label: string; dark: boolean }[] = [
@@ -74,6 +75,7 @@ export const DEFAULT_EDITOR_SETTINGS: EditorSettings = {
fontSize: 13,
theme: "one-dark",
executeMode: "all",
+ wordWrap: false,
};
export const STORAGE_KEY = "dbx-editor-settings";
@@ -90,6 +92,7 @@ function loadEditorSettings(): EditorSettings {
fontSize: parsed.fontSize ?? DEFAULT_EDITOR_SETTINGS.fontSize,
theme: parsed.theme ?? DEFAULT_EDITOR_SETTINGS.theme,
executeMode: parsed.executeMode ?? DEFAULT_EDITOR_SETTINGS.executeMode,
+ wordWrap: parsed.wordWrap ?? DEFAULT_EDITOR_SETTINGS.wordWrap,
};
}
} catch {