Skip to content

Commit ee4d6d4

Browse files
abcd-tsToshiya SaitoToshiya Saitoota-meshi
authored
VSCodeの設定UIからurborosql-fmtのオプションを指定できるようにする (#52)
* Support options specified by settings.json * Refactor settings and workspaceFolder from being obtained in multiple places. * exec `npm i --save-dev ts-case-convert` * refactor map -> filter * improve settings UI for boolean configurations * fix typo * improve tabSize type (number -> integer) and set mininum * add description when option value is null * modify maxCharPerLine properties * Create strong-needles-joke.md --------- Co-authored-by: Toshiya Saito <[email protected]> Co-authored-by: Toshiya Saito <[email protected]> Co-authored-by: Yosuke Ota <[email protected]>
1 parent 5df3a5c commit ee4d6d4

File tree

6 files changed

+381
-90
lines changed

6 files changed

+381
-90
lines changed

.changeset/strong-needles-joke.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"uroborosql-fmt": minor
3+
---
4+
5+
Added setting options to apply options of urborosql-fmt.

package-lock.json

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,179 @@
3939
"uroborosql-fmt.configurationFilePath": {
4040
"type": "string",
4141
"description": "The path of configuration file. File extension must be `.json`. If you don't specify the path and `./.uroborosqlfmtrc.json` exists, formatter will use `./.uroborosqlfmtrc.json`. If you doesn't specify and `.uroborosqlfmtrc.json` doesn't exist, formatter will use formatters default configurations."
42+
},
43+
"uroborosql-fmt.debug": {
44+
"type": [
45+
"boolean",
46+
"null"
47+
],
48+
"enum": [
49+
true,
50+
false,
51+
null
52+
],
53+
"default": null,
54+
"description": "Run in debug mode. If this value is null, uroborosql-fmt refers the configuration file or uses the default value."
55+
},
56+
"uroborosql-fmt.tabSize": {
57+
"type": [
58+
"integer",
59+
"null"
60+
],
61+
"minimum": 0,
62+
"default": null,
63+
"description": "Tab size used for formatting. If this value is null, uroborosql-fmt refers the configuration file or uses the default value."
64+
},
65+
"uroborosql-fmt.complementAlias": {
66+
"type": [
67+
"boolean",
68+
"null"
69+
],
70+
"enum": [
71+
true,
72+
false,
73+
null
74+
],
75+
"default": null,
76+
"markdownDescription": "Complement aliases. Currently, column names are auto-completed with the same name. (e.g. `COL1` → `COL1 AS COL1`) If this value is null, uroborosql-fmt refers the configuration file or uses the default value."
77+
},
78+
"uroborosql-fmt.trimBindParam": {
79+
"type": [
80+
"boolean",
81+
"null"
82+
],
83+
"enum": [
84+
true,
85+
false,
86+
null
87+
],
88+
"default": null,
89+
"markdownDescription": "Trim the contents of the [bind parameters](https://future-architect.github.io/uroborosql-doc/background/#%E3%83%8F%E3%82%99%E3%82%A4%E3%83%B3%E3%83%88%E3%82%99%E3%83%8F%E3%82%9A%E3%83%A9%E3%83%A1%E3%83%BC%E3%82%BF). (e.g. `/* foo */` → `/*foo*/`) If this value is null, uroborosql-fmt refers the configuration file or uses the default value."
90+
},
91+
"uroborosql-fmt.keywordCase": {
92+
"type": [
93+
"string",
94+
"null"
95+
],
96+
"default": null,
97+
"enum": [
98+
"upper",
99+
"lower",
100+
"preserve"
101+
],
102+
"markdownDescription": "Unify the case of keywords. (No conversion in case of `\"preserve\"`) If this value is null, uroborosql-fmt refers the configuration file or uses the default value."
103+
},
104+
"uroborosql-fmt.identifierCase": {
105+
"type": [
106+
"string",
107+
"null"
108+
],
109+
"default": null,
110+
"enum": [
111+
"upper",
112+
"lower",
113+
"preserve"
114+
],
115+
"markdownDescription": "Unify the case of identifiers. (No conversion in case of `\"preserve\"`) If this value is null, uroborosql-fmt refers the configuration file or uses the default value."
116+
},
117+
"uroborosql-fmt.maxCharPerLine": {
118+
"type": [
119+
"integer",
120+
"null"
121+
],
122+
"default": null,
123+
"markdownDescription": "If the total number of characters in the function name and arguments exceeds `max_char_per_line`, the arguments are formatted with new lines. If this value is null, uroborosql-fmt refers the configuration file or uses the default value."
124+
},
125+
"uroborosql-fmt.complementOuterKeyword": {
126+
"type": [
127+
"boolean",
128+
"null"
129+
],
130+
"enum": [
131+
true,
132+
false,
133+
null
134+
],
135+
"default": null,
136+
"markdownDescription": "Complement the optional OUTER. (e.g. `LEFT JOIN` → `LEFT OUTER JOIN`) If this value is null, uroborosql-fmt refers the configuration file or uses the default value."
137+
},
138+
"uroborosql-fmt.complementColumnAsKeyword": {
139+
"type": [
140+
"boolean",
141+
"null"
142+
],
143+
"enum": [
144+
true,
145+
false,
146+
null
147+
],
148+
"default": null,
149+
"markdownDescription": "Complement `AS` in column aliases. If this value is null, uroborosql-fmt refers the configuration file or uses the default value."
150+
},
151+
"uroborosql-fmt.removeTableAsKeyword": {
152+
"type": [
153+
"boolean",
154+
"null"
155+
],
156+
"enum": [
157+
true,
158+
false,
159+
null
160+
],
161+
"default": null,
162+
"markdownDescription": "Remove `AS` in table aliases. If this value is null, uroborosql-fmt refers the configuration file or uses the default value."
163+
},
164+
"uroborosql-fmt.removeRedundantNest": {
165+
"type": [
166+
"boolean",
167+
"null"
168+
],
169+
"enum": [
170+
true,
171+
false,
172+
null
173+
],
174+
"default": null,
175+
"markdownDescription": "Remove redundant parentheses. (e.g. (`((foo))`) → `(foo)`) If this value is null, uroborosql-fmt refers the configuration file or uses the default value."
176+
},
177+
"uroborosql-fmt.complementSqlId": {
178+
"type": [
179+
"boolean",
180+
"null"
181+
],
182+
"enum": [
183+
true,
184+
false,
185+
null
186+
],
187+
"default": null,
188+
"description": "Complement SQL ID. If this value is null, uroborosql-fmt refers the configuration file or uses the default value."
189+
},
190+
"uroborosql-fmt.convertDoubleColonCast": {
191+
"type": [
192+
"boolean",
193+
"null"
194+
],
195+
"enum": [
196+
true,
197+
false,
198+
null
199+
],
200+
"default": null,
201+
"markdownDescription": "Convert casts by `X::type` to the form `CAST(X AS type)`. If this value is null, uroborosql-fmt refers the configuration file or uses the default value."
202+
},
203+
"uroborosql-fmt.unifyNotEqual": {
204+
"type": [
205+
"boolean",
206+
"null"
207+
],
208+
"enum": [
209+
true,
210+
false,
211+
null
212+
],
213+
"default": null,
214+
"markdownDescription": "Convert comparison operator `<>` to `!=`. If this value is null, uroborosql-fmt refers the configuration file or uses the default value."
42215
}
43216
}
44217
}
@@ -68,6 +241,7 @@
68241
"eslint": "^8.50.0",
69242
"mocha": "^10.2.0",
70243
"prettier": "~3.2.0",
244+
"ts-case-convert": "^2.0.7",
71245
"typescript": "^5.2.2",
72246
"undici": "^6.0.0"
73247
}

server/package-lock.json

Lines changed: 1 addition & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

server/src/server.ts

Lines changed: 54 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,13 @@ import {
1717

1818
import { TextDocument } from "vscode-languageserver-textdocument";
1919

20-
import { runfmt } from "uroborosql-fmt-napi";
20+
import { runfmtWithSettings } from "uroborosql-fmt-napi";
2121
import * as fs from "fs";
2222

2323
import { performance } from "perf_hooks";
2424
import path = require("path");
2525
import { URI } from "vscode-uri";
26+
import { objectToSnake } from "ts-case-convert";
2627
// Create a connection for the server, using Node's IPC as a transport.
2728
// Also include all preview / proposed LSP features.
2829
const connection = createConnection(ProposedFeatures.all);
@@ -89,6 +90,20 @@ connection.onInitialized(() => {
8990

9091
type ConfigurationSettings = {
9192
configurationFilePath: string;
93+
debug: boolean | null | undefined;
94+
tabSize: number | null | undefined;
95+
complementAlias: boolean | null | undefined;
96+
trimBindParam: boolean | null | undefined;
97+
keywordCase: string | null | undefined;
98+
identifierCase: string | null | undefined;
99+
maxCharPerLine: number | null | undefined;
100+
complementOuterKeyword: boolean | null | undefined;
101+
complementColumnAsKeyword: boolean | null | undefined;
102+
removeTableAsKeyword: boolean | null | undefined;
103+
removeRedundantNest: boolean | null | undefined;
104+
complementSqlId: boolean | null | undefined;
105+
convertDoubleColonCast: boolean | null | undefined;
106+
unifyNotEqual: boolean | null | undefined;
92107
};
93108

94109
function getSettings(resource: string): Thenable<ConfigurationSettings> {
@@ -129,21 +144,38 @@ async function getWorkspaceFolder(
129144
return undefined;
130145
}
131146

132-
async function determineConfigPath(
133-
uri: string,
134-
textDocument: TextDocument,
135-
): Promise<string | null> {
136-
const workspaceFolder: string | undefined =
137-
await getWorkspaceFolder(textDocument);
147+
function getVSCodeOptions(
148+
settings: ConfigurationSettings,
149+
workspaceFolder: string | undefined,
150+
): Partial<ConfigurationSettings> | null {
151+
if (!workspaceFolder) {
152+
return null;
153+
}
154+
155+
// remove configurationFilePath
156+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
157+
const { configurationFilePath, ...restConfiguration } = settings;
158+
159+
// translate null (that means unsupecified option) to undefined
160+
const removedNullSettings = Object.fromEntries(
161+
Object.entries(restConfiguration).filter(([, value]) => value != null),
162+
);
163+
164+
// to snake case for uroborosql-fmt
165+
return objectToSnake(removedNullSettings);
166+
}
138167

168+
function determineConfigPath(
169+
settings: ConfigurationSettings,
170+
workspaceFolder: string | undefined,
171+
): string | null {
139172
if (!workspaceFolder) {
140173
return null;
141174
}
142175

143176
// remove scheme
144177
const workspaceFolderPath = URI.parse(workspaceFolder).fsPath;
145178

146-
const settings: ConfigurationSettings = await getSettings(uri);
147179
if (!settings.configurationFilePath) {
148180
const defaultConfigPath = path.join(
149181
workspaceFolderPath,
@@ -179,10 +211,15 @@ async function formatText(
179211
version: number,
180212
selections: Range[],
181213
): Promise<TextEdit[]> {
214+
const workspaceFolder: string | undefined =
215+
await getWorkspaceFolder(textDocument);
216+
217+
const settings: ConfigurationSettings = await getSettings(uri);
218+
182219
let configPath: string | null;
183220

184221
try {
185-
configPath = await determineConfigPath(uri, textDocument);
222+
configPath = determineConfigPath(settings, workspaceFolder);
186223
} catch (e) {
187224
if (e instanceof Error) {
188225
connection.window.showErrorMessage(e.message);
@@ -196,6 +233,12 @@ async function formatText(
196233
return [];
197234
}
198235

236+
// settings specified by vscode ui
237+
const specifiedSettings = getVSCodeOptions(settings, workspaceFolder);
238+
const settingsString = specifiedSettings
239+
? JSON.stringify(specifiedSettings)
240+
: "{}";
241+
console.log(settingsString);
199242
const changes: TextEdit[] = [];
200243

201244
// 全ての選択範囲に対して実行
@@ -209,7 +252,7 @@ async function formatText(
209252
let formattedText: string;
210253

211254
try {
212-
formattedText = runfmt(text, configPath);
255+
formattedText = runfmtWithSettings(text, settingsString, configPath);
213256
// ステータスバーの背景を通常色に変更
214257
connection.sendRequest("custom/normal", []);
215258
} catch (e) {
@@ -230,7 +273,7 @@ async function formatText(
230273
let formattedText: string;
231274
const startTime = performance.now();
232275
try {
233-
formattedText = runfmt(text, configPath);
276+
formattedText = runfmtWithSettings(text, settingsString, configPath);
234277
// ステータスバーの背景を通常色に変更
235278
connection.sendRequest("custom/normal", []);
236279
} catch (e) {

0 commit comments

Comments
 (0)