diff --git a/package-lock.json b/package-lock.json
index 8f03c31..0884fc3 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -25,7 +25,8 @@
"d3": "^7.9.0",
"dayjs": "^1.11.13",
"normalize.css": "^8.0.1",
- "p-debounce": "^4.0.0"
+ "p-debounce": "^4.0.0",
+ "sql-formatter": "^15.4.9"
},
"devDependencies": {
"@sveltejs/adapter-static": "^3.0.8",
@@ -1567,6 +1568,12 @@
"acorn": ">=8.9.0"
}
},
+ "node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "license": "Python-2.0"
+ },
"node_modules/aria-query": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.1.tgz",
@@ -2080,6 +2087,12 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/discontinuous-range": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/discontinuous-range/-/discontinuous-range-1.0.0.tgz",
+ "integrity": "sha512-c68LpLbO+7kP/b1Hr1qs8/BJ09F5khZGTxqxZuhzxpmwJKOgRFHJWIb9/KmqnqHhLdO55aOxFH/EGBvUQbL/RQ==",
+ "license": "MIT"
+ },
"node_modules/esbuild": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.2.tgz",
@@ -2166,6 +2179,18 @@
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
+ "node_modules/get-stdin": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz",
+ "integrity": "sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/globalyzer": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/globalyzer/-/globalyzer-0.1.0.tgz",
@@ -2246,6 +2271,12 @@
"@jridgewell/sourcemap-codec": "^1.5.0"
}
},
+ "node_modules/moo": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/moo/-/moo-0.5.2.tgz",
+ "integrity": "sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==",
+ "license": "BSD-3-Clause"
+ },
"node_modules/mri": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz",
@@ -2292,6 +2323,34 @@
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
+ "node_modules/nearley": {
+ "version": "2.20.1",
+ "resolved": "https://registry.npmjs.org/nearley/-/nearley-2.20.1.tgz",
+ "integrity": "sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ==",
+ "license": "MIT",
+ "dependencies": {
+ "commander": "^2.19.0",
+ "moo": "^0.5.0",
+ "railroad-diagrams": "^1.0.0",
+ "randexp": "0.4.6"
+ },
+ "bin": {
+ "nearley-railroad": "bin/nearley-railroad.js",
+ "nearley-test": "bin/nearley-test.js",
+ "nearley-unparse": "bin/nearley-unparse.js",
+ "nearleyc": "bin/nearleyc.js"
+ },
+ "funding": {
+ "type": "individual",
+ "url": "https://nearley.js.org/#give-to-nearley"
+ }
+ },
+ "node_modules/nearley/node_modules/commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+ "license": "MIT"
+ },
"node_modules/normalize.css": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/normalize.css/-/normalize.css-8.0.1.tgz",
@@ -2370,6 +2429,25 @@
"svelte": "^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0"
}
},
+ "node_modules/railroad-diagrams": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz",
+ "integrity": "sha512-cz93DjNeLY0idrCNOH6PviZGRN9GJhsdm9hpn1YCS879fj4W+x5IFJhhkRZcwVgMmFF7R82UA/7Oh+R8lLZg6A==",
+ "license": "CC0-1.0"
+ },
+ "node_modules/randexp": {
+ "version": "0.4.6",
+ "resolved": "https://registry.npmjs.org/randexp/-/randexp-0.4.6.tgz",
+ "integrity": "sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==",
+ "license": "MIT",
+ "dependencies": {
+ "discontinuous-range": "1.0.0",
+ "ret": "~0.1.10"
+ },
+ "engines": {
+ "node": ">=0.12"
+ }
+ },
"node_modules/readdirp": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.2.tgz",
@@ -2384,6 +2462,15 @@
"url": "https://paulmillr.com/funding/"
}
},
+ "node_modules/ret": {
+ "version": "0.1.15",
+ "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
+ "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.12"
+ }
+ },
"node_modules/robust-predicates": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz",
@@ -2485,6 +2572,20 @@
"node": ">=0.10.0"
}
},
+ "node_modules/sql-formatter": {
+ "version": "15.4.9",
+ "resolved": "https://registry.npmjs.org/sql-formatter/-/sql-formatter-15.4.9.tgz",
+ "integrity": "sha512-5vmt2HlCAVozxsBZuXWkAki/KGawaK+b5GG5x+BtXOFVpN/8cqppblFUxHl4jxdA0cvo14lABhM+KBnrUapOlw==",
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^2.0.1",
+ "get-stdin": "=8.0.0",
+ "nearley": "^2.20.1"
+ },
+ "bin": {
+ "sql-formatter": "bin/sql-formatter-cli.cjs"
+ }
+ },
"node_modules/style-mod": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.2.tgz",
diff --git a/package.json b/package.json
index 9a99f9f..4cd8e49 100644
--- a/package.json
+++ b/package.json
@@ -31,7 +31,8 @@
"d3": "^7.9.0",
"dayjs": "^1.11.13",
"normalize.css": "^8.0.1",
- "p-debounce": "^4.0.0"
+ "p-debounce": "^4.0.0",
+ "sql-formatter": "^15.4.9"
},
"devDependencies": {
"@sveltejs/adapter-static": "^3.0.8",
diff --git a/src/lib/icons/ClipboardText.svelte b/src/lib/icons/ClipboardText.svelte
new file mode 100644
index 0000000..3104d79
--- /dev/null
+++ b/src/lib/icons/ClipboardText.svelte
@@ -0,0 +1,50 @@
+
+
+
diff --git a/src/lib/icons/Copy.svelte b/src/lib/icons/Copy.svelte
new file mode 100644
index 0000000..b2b9ef8
--- /dev/null
+++ b/src/lib/icons/Copy.svelte
@@ -0,0 +1,31 @@
+
+
+
diff --git a/src/lib/icons/MagicWand.svelte b/src/lib/icons/MagicWand.svelte
new file mode 100644
index 0000000..363ca10
--- /dev/null
+++ b/src/lib/icons/MagicWand.svelte
@@ -0,0 +1,102 @@
+
+
+
diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte
index 5455a82..fb13af8 100644
--- a/src/routes/+page.svelte
+++ b/src/routes/+page.svelte
@@ -11,6 +11,9 @@
import { setAppContext } from '$lib/context';
import { tablesToSQLNamespace } from '$lib/editor';
import Bars3 from '$lib/icons/Bars3.svelte';
+ import ClipboardText from '$lib/icons/ClipboardText.svelte';
+ import Copy from '$lib/icons/Copy.svelte';
+ import MagicWand from '$lib/icons/MagicWand.svelte';
import PanelBottom from '$lib/icons/PanelBottom.svelte';
import PanelLeft from '$lib/icons/PanelLeft.svelte';
import Play from '$lib/icons/Play.svelte';
@@ -26,6 +29,7 @@
import { ClickHouseDialect, extendsDialectKeywords } from '@agnosticeng/editor/dialect';
import { SplitPane } from '@rich_harris/svelte-split-pane';
import debounce from 'p-debounce';
+ import { format } from 'sql-formatter';
import { tick, type ComponentProps } from 'svelte';
import type { PageData } from './$types';
@@ -98,6 +102,7 @@
}
if (event.key === 'Enter' && event.metaKey) handleExec();
+ if (event.key === 'i' && event.metaKey) handleFormat();
}
async function handleCreateQuery({
@@ -227,6 +232,18 @@
bottomPanelTab = 'logs';
}
});
+
+ function handleFormat() {
+ if (!currentTab.contents) return;
+
+ currentTab.contents = format(currentTab.contents, {
+ keywordCase: 'lower',
+ tabWidth: 2,
+ useTabs: true,
+ expressionWidth: 80,
+ language: 'postgresql'
+ });
+ }