Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

VSCodeの設定UIからurborosql-fmtのオプションを指定できるようにする #52

Merged
merged 10 commits into from
May 2, 2024
7 changes: 7 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

179 changes: 179 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,184 @@
"uroborosql-fmt.configurationFilePath": {
"type": "string",
"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."
},
"uroborosql-fmt.debug": {
"type": [
"boolean",
"null"
],
"enum": [
true,
false,
null
],
"default": null,
"description": "Run in debug mode. If this value is null, uroborosql-fmt refers the configuration file or uses the default value."
},
"uroborosql-fmt.tabSize": {
"type": [
"integer",
"null"
],
"minimum": 0,
"default": null,
"description": "Tab size used for formatting. If this value is null, uroborosql-fmt refers the configuration file or uses the default value."
},
"uroborosql-fmt.complementAlias": {
"type": [
"boolean",
"null"
],
"enum": [
true,
false,
null
],
"default": null,
"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."
},
"uroborosql-fmt.trimBindParam": {
"type": [
"boolean",
"null"
],
"enum": [
true,
false,
null
],
"default": null,
"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."
},
"uroborosql-fmt.keywordCase": {
"type": [
"string",
"null"
],
"default": null,
"enum": [
"upper",
"lower",
"preserve"
],
"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."
},
"uroborosql-fmt.identifierCase": {
"type": [
"string",
"null"
],
"default": null,
"enum": [
"upper",
"lower",
"preserve"
],
"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."
},
"uroborosql-fmt.maxCharPerLine": {
"type": [
"number",
"null"
],
"enum": [
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ここはnumberなのでenumいらない気がします。

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

見落としていました、ありがとうございます。修正しておきました。

また、ここも整数でよさそうなので、numberからintegerに変更しました。

true,
false,
null
],
"default": null,
"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."
},
"uroborosql-fmt.complementOuterKeyword": {
"type": [
"boolean",
"null"
],
"enum": [
true,
false,
null
],
"default": null,
"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."
},
"uroborosql-fmt.complementColumnAsKeyword": {
"type": [
"boolean",
"null"
],
"enum": [
true,
false,
null
],
"default": null,
"markdownDescription": "Complement `AS` in column aliases. If this value is null, uroborosql-fmt refers the configuration file or uses the default value."
},
"uroborosql-fmt.removeTableAsKeyword": {
"type": [
"boolean",
"null"
],
"enum": [
true,
false,
null
],
"default": null,
"markdownDescription": "Remove `AS` in table aliases. If this value is null, uroborosql-fmt refers the configuration file or uses the default value."
},
"uroborosql-fmt.removeRedundantNest": {
"type": [
"boolean",
"null"
],
"enum": [
true,
false,
null
],
"default": null,
"markdownDescription": "Remove redundant parentheses. (e.g. (`((foo))`) → `(foo)`) If this value is null, uroborosql-fmt refers the configuration file or uses the default value."
},
"uroborosql-fmt.complementSqlId": {
"type": [
"boolean",
"null"
],
"enum": [
true,
false,
null
],
"default": null,
"description": "Complement SQL ID. If this value is null, uroborosql-fmt refers the configuration file or uses the default value."
},
"uroborosql-fmt.convertDoubleColonCast": {
"type": [
"boolean",
"null"
],
"enum": [
true,
false,
null
],
"default": null,
"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."
},
"uroborosql-fmt.unifyNotEqual": {
"type": [
"boolean",
"null"
],
"enum": [
true,
false,
null
],
"default": null,
"markdownDescription": "Convert comparison operator `<>` to `!=`. If this value is null, uroborosql-fmt refers the configuration file or uses the default value."
}
}
}
Expand Down Expand Up @@ -68,6 +246,7 @@
"eslint": "^8.50.0",
"mocha": "^10.2.0",
"prettier": "~3.2.0",
"ts-case-convert": "^2.0.7",
"typescript": "^5.2.2",
"undici": "^6.0.0"
}
Expand Down
3 changes: 1 addition & 2 deletions server/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

65 changes: 54 additions & 11 deletions server/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,13 @@ import {

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

import { runfmt } from "uroborosql-fmt-napi";
import { runfmtWithSettings } from "uroborosql-fmt-napi";
import * as fs from "fs";

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

type ConfigurationSettings = {
configurationFilePath: string;
debug: boolean | null | undefined;
tabSize: number | null | undefined;
complementAlias: boolean | null | undefined;
trimBindParam: boolean | null | undefined;
keywordCase: string | null | undefined;
identifierCase: string | null | undefined;
maxCharPerLine: number | null | undefined;
complementOuterKeyword: boolean | null | undefined;
complementColumnAsKeyword: boolean | null | undefined;
removeTableAsKeyword: boolean | null | undefined;
removeRedundantNest: boolean | null | undefined;
complementSqlId: boolean | null | undefined;
convertDoubleColonCast: boolean | null | undefined;
unifyNotEqual: boolean | null | undefined;
};

function getSettings(resource: string): Thenable<ConfigurationSettings> {
Expand Down Expand Up @@ -129,21 +144,38 @@ async function getWorkspaceFolder(
return undefined;
}

async function determineConfigPath(
uri: string,
textDocument: TextDocument,
): Promise<string | null> {
const workspaceFolder: string | undefined =
await getWorkspaceFolder(textDocument);
function getVSCodeOptions(
settings: ConfigurationSettings,
workspaceFolder: string | undefined,
): Partial<ConfigurationSettings> | null {
if (!workspaceFolder) {
return null;
}

// remove configurationFilePath
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { configurationFilePath, ...restConfiguration } = settings;

// translate null (that means unsupecified option) to undefined
const removedNullSettings = Object.fromEntries(
Object.entries(restConfiguration).filter(([, value]) => value != null),
);

// to snake case for uroborosql-fmt
return objectToSnake(removedNullSettings);
}

function determineConfigPath(
settings: ConfigurationSettings,
workspaceFolder: string | undefined,
): string | null {
if (!workspaceFolder) {
return null;
}

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

const settings: ConfigurationSettings = await getSettings(uri);
if (!settings.configurationFilePath) {
const defaultConfigPath = path.join(
workspaceFolderPath,
Expand Down Expand Up @@ -179,10 +211,15 @@ async function formatText(
version: number,
selections: Range[],
): Promise<TextEdit[]> {
const workspaceFolder: string | undefined =
await getWorkspaceFolder(textDocument);

const settings: ConfigurationSettings = await getSettings(uri);

let configPath: string | null;

try {
configPath = await determineConfigPath(uri, textDocument);
configPath = determineConfigPath(settings, workspaceFolder);
} catch (e) {
if (e instanceof Error) {
connection.window.showErrorMessage(e.message);
Expand All @@ -196,6 +233,12 @@ async function formatText(
return [];
}

// settings specified by vscode ui
const specifiedSettings = getVSCodeOptions(settings, workspaceFolder);
const settingsString = specifiedSettings
? JSON.stringify(specifiedSettings)
: "{}";
console.log(settingsString);
const changes: TextEdit[] = [];

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

try {
formattedText = runfmt(text, configPath);
formattedText = runfmtWithSettings(text, settingsString, configPath);
// ステータスバーの背景を通常色に変更
connection.sendRequest("custom/normal", []);
} catch (e) {
Expand All @@ -230,7 +273,7 @@ async function formatText(
let formattedText: string;
const startTime = performance.now();
try {
formattedText = runfmt(text, configPath);
formattedText = runfmtWithSettings(text, settingsString, configPath);
// ステータスバーの背景を通常色に変更
connection.sendRequest("custom/normal", []);
} catch (e) {
Expand Down
Loading