Skip to content

Commit

Permalink
VSCodeの設定UIからurborosql-fmtのオプションを指定できるようにする (#52)
Browse files Browse the repository at this point in the history
* 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 <saitou@ialab.cs.tsukuba.ac.jp>
Co-authored-by: Toshiya Saito <t.saito.ab@future.co.jp>
Co-authored-by: Yosuke Ota <otameshiyo23@gmail.com>
  • Loading branch information
4 people committed May 2, 2024
1 parent 5df3a5c commit ee4d6d4
Show file tree
Hide file tree
Showing 6 changed files with 381 additions and 90 deletions.
5 changes: 5 additions & 0 deletions .changeset/strong-needles-joke.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"uroborosql-fmt": minor
---

Added setting options to apply options of urborosql-fmt.
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.

174 changes: 174 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,179 @@
"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": [
"integer",
"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 +241,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

0 comments on commit ee4d6d4

Please sign in to comment.