Skip to content

Commit 3ff7b26

Browse files
authored
Workspace configuration (#62)
* use onInitialize * apply connection setting from workspace/configuration * apply lint setting from workspace/configuration * strip unexpected console.log from pg library * fix failed test * add test for configObject option * Update README * update package.json
1 parent ead55a3 commit 3ff7b26

File tree

22 files changed

+309
-87
lines changed

22 files changed

+309
-87
lines changed

.vscode/settings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,5 @@
99
"out": true
1010
},
1111
"typescript.tsdk": "./node_modules/typescript/lib",
12-
"typescript.tsc.autoDetect": "off"
12+
"typescript.tsc.autoDetect": "off",
1313
}

README.md

Lines changed: 98 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -79,14 +79,13 @@ $ sql-language-server up [options] run sql-language-server
7979
$ sql-language-server up --method stdio
8080
```
8181

82-
### Connect to database
83-
84-
#### Using Configuration Files
82+
### Configuration
8583

8684
There are two ways to use configuration files.
8785

8886
- Set personal configuration file(~/.config/sql-language-server/.sqllsrc.json)
8987
- Set project configuration file on your project root(\${YOUR_PROJECT/.sqllsrc.json})
88+
- Use workspace/configuration according to LSP specification
9089

9190
#### Example for personal configuration file
9291

@@ -135,7 +134,7 @@ There are two ways to use configuration files.
135134

136135
Please restart sql-language-server process after create .sqllsrc.json.
137136

138-
#### Parameters
137+
#### Parameters of connections
139138

140139
| Key | Description | value | required | default |
141140
| ------------ | ------------------------------------------------------------------------------------------------------------------------- | ----------------------- | -------- | --------------------------------- |
@@ -231,6 +230,101 @@ It will merge them as following:
231230
}
232231
```
233232

233+
#### workspace/configuration
234+
235+
##### Parameters of workspace configuration
236+
237+
- connections: It's the same as `connections` params of personal config file
238+
- lint: It's the same as configuration of [sqlint](https://github.com/joe-re/sql-language-server/tree/release/packages/sqlint#configuration).
239+
240+
241+
##### Example of workspace configuration
242+
243+
- [coc.nvim](https://github.com/neoclide/coc.nvim)
244+
245+
~/.config/nvim/coc-settings.json
246+
```json
247+
{
248+
"languageserver": {
249+
"sql": {
250+
"command": "sql-language-server",
251+
"args": ["up", "--method", "stdio"],
252+
"filetypes": ["sql"],
253+
"settings": {
254+
"sqlLanguageServer": {
255+
"connections": [
256+
{
257+
"name": "mysql_project",
258+
"adapter": "mysql",
259+
"host": "127.0.0.1",
260+
"port": 3306,
261+
"user": "sqlls",
262+
"password": "sqlls",
263+
"database": "mysql_db",
264+
"projectPaths": ["/Users/joe_re/src/MysqlProject"],
265+
"ssh": {
266+
"user": "ubuntu",
267+
"remoteHost": "xxx-xx-xxx-xxx-xxx.ap-southeast-1.compute.amazonaws.com",
268+
"dbHost": "127.0.0.1",
269+
"port": 3306
270+
}
271+
}
272+
],
273+
"lint": {
274+
"rules": {
275+
"align-column-to-the-first": "error",
276+
"column-new-line": "error",
277+
"linebreak-after-clause-keyword": "off",
278+
"reserved-word-case": ["error", "upper"],
279+
"space-surrounding-operators": "error",
280+
"where-clause-new-line": "error",
281+
"align-where-clause-to-the-first": "error"
282+
}
283+
}
284+
}
285+
}
286+
}
287+
}
288+
}
289+
```
290+
291+
- vscode workspace setting
292+
293+
```json
294+
"settings": {
295+
"sqlLanguageServer.connections": [
296+
{
297+
"name": "mysql_project",
298+
"adapter": "mysql",
299+
"host": "127.0.0.1",
300+
"port": 3306,
301+
"user": "sqlls",
302+
"password": "sqlls",
303+
"database": "mysql_db",
304+
"projectPaths": ["/Users/joe_re/src/MysqlProject"],
305+
"ssh": {
306+
"user": "ubuntu",
307+
"remoteHost": "xxx-xx-xxx-xxx-xxx.ap-southeast-1.compute.amazonaws.com",
308+
"dbHost": "127.0.0.1",
309+
"port": 3306
310+
}
311+
}
312+
],
313+
"sqlLanguageServer.lint": {
314+
"rules": {
315+
"align-column-to-the-first": "off",
316+
"column-new-line": "error",
317+
"linebreak-after-clause-keyword": "error",
318+
"reserved-word-case": ["error", "upper"],
319+
"space-surrounding-operators": "error",
320+
"where-clause-new-line": "error",
321+
"align-where-clause-to-the-first": "error",
322+
}
323+
}
324+
}
325+
```
326+
327+
234328
#### Inject envitonment variables
235329

236330
${env:VARIABLE_NAME} syntax allows you to replace configuration value with enviroment variable.

example/monaco_editor/src/client/App.svelte

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@
22
import {
33
executeSwitchDatabaseCommand,
44
executeFixAllFixableProblemsCommand,
5+
executeWorkspaceConfig,
56
getConnectionList,
6-
getCurrecntConnection
7+
getCurrecntConnection,
78
} from './client'
89
910
const commands = [
1011
{ id: 'fixAllFixableProblems', text: 'fixAllFixableProblems' },
11-
// TODO
1212
{ id: 'switchDatabaseConnection', text: 'switchDatabaseConnection' },
13+
{ id: 'workspace/configuration', text: 'workspace/configuration' },
1314
]
1415
1516
let command = commands[0]
@@ -19,6 +20,8 @@
1920
executeFixAllFixableProblemsCommand()
2021
} else if (command.id === 'switchDatabaseConnection') {
2122
executeSwitchDatabaseCommand(connection)
23+
} else if (command.id === 'workspace/configuration') {
24+
executeWorkspaceConfig(connection)
2225
}
2326
}
2427

example/monaco_editor/src/client/client.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ export function executeFixAllFixableProblemsCommand() {
9999
const params: ExecuteCommandParams = {
100100
command: 'fixAllFixableProblems',
101101
arguments: ['inmemory://model.sql']
102-
}
102+
}
103103
languageClient.sendRequest('workspace/executeCommand', params)
104104
}
105105

@@ -111,6 +111,14 @@ export function executeSwitchDatabaseCommand(db: string) {
111111
languageClient.sendRequest('workspace/executeCommand', params)
112112
}
113113

114+
export function executeWorkspaceConfig(db: string) {
115+
// TODO: implement
116+
// languageClient.sendRequest('workspace/configuration', { test: 'test' }).catch(e => {
117+
// console.log('--- error ---')
118+
// console.log(e)
119+
// })
120+
}
121+
114122
export function getConnectionList() {
115123
return connectionNames
116124
}

package.json

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"main": "./packages/client/out/extension",
77
"repository": {
88
"type": "git",
9-
"url": "https://github.com/joe-re/sql-language-server"
9+
"url": "git+https://github.com/joe-re/sql-language-server.git"
1010
},
1111
"keywords": [
1212
"sql",
@@ -57,7 +57,25 @@
5757
"title": "Rebuild SQLite3 Client",
5858
"category": "SQLLanguageServer"
5959
}
60-
]
60+
],
61+
"configuration": {
62+
"type": "object",
63+
"title": "sql-language-server configuration",
64+
"properties": {
65+
"sqlLanguageServer.connections": {
66+
"scope": "resource",
67+
"type": "array",
68+
"default": [],
69+
"description": "connection setting"
70+
},
71+
"sqlLanguageServer.lint": {
72+
"scope": "resource",
73+
"type": "object",
74+
"default": {},
75+
"description": "lint setting"
76+
}
77+
}
78+
}
6179
},
6280
"private": true,
6381
"workspaces": [

packages/client/extension.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export function activate(context: ExtensionContext) {
2626
diagnosticCollectionName: 'sqlLanguageServer',
2727
synchronize: {
2828
configurationSection: 'sqlLanguageServer',
29-
fileEvents: workspace.createFileSystemWatcher('**/.sqllsrc.json')
29+
// fileEvents: workspace.createFileSystemWatcher('**/.sqllsrc.json')
3030
}
3131
}
3232

packages/server/package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@
77
},
88
"repository": {
99
"type": "git",
10-
"url": "https://github.com/joe-re/sql-language-server"
10+
"url": "git+https://github.com/joe-re/sql-language-server.git"
11+
},
12+
"bugs": {
13+
"url": "https://github.com/joe-re/sql-language-server/issues"
1114
},
1215
"keywords": [
1316
"sql",
@@ -44,6 +47,7 @@
4447
"sqlite3": "^4.2.0",
4548
"vscode-languageclient": "^6.1.3",
4649
"vscode-languageserver": "^6.1.1",
50+
"vscode-languageserver-protocol": "^3.15.3",
4751
"vscode-languageserver-textdocument": "^1.0.1",
4852
"yargs": "^12.0.1"
4953
},

packages/server/rollup.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export default {
2121
replace({
2222
delimiters: ['', ''],
2323
values: {
24+
"console.log('using faster connection')": '',
2425
'require(\'readable-stream/transform\')': 'require(\'stream\').Transform',
2526
'require("readable-stream/transform")': 'require("stream").Transform',
2627
'readable-stream': 'stream',

packages/server/src/SettingStore.ts

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export type SSHConfig = {
1313
passphrase?: string
1414
identityFile?: string
1515
}
16-
export type Settings = {
16+
export type Connection = {
1717
name: string | null,
1818
adapter: 'mysql' | 'postgresql' | 'postgres' | 'sqlite3' | null,
1919
host: string | null
@@ -27,7 +27,7 @@ export type Settings = {
2727
}
2828

2929
type PersonalConfig = {
30-
connections: Settings[]
30+
connections: Connection[]
3131
}
3232

3333
function fileExists(path: string) {
@@ -47,7 +47,7 @@ function readFile(filePath: string) {
4747

4848
export default class SettingStore extends EventEmitter.EventEmitter {
4949
private personalConfig: PersonalConfig = { connections: []}
50-
private state: Settings = {
50+
private state: Connection = {
5151
name: null,
5252
adapter: null,
5353
host: null,
@@ -83,7 +83,7 @@ export default class SettingStore extends EventEmitter.EventEmitter {
8383
const config = this.personalConfig.connections.find(v => v.name === connectionName)
8484
if (!config) {
8585
const errorMessage = `not find connection name: ${connectionName}`
86-
logger.error(`not find connection name: ${connectionName}`)
86+
logger.error(errorMessage)
8787
throw new Error(errorMessage)
8888
}
8989
this.setSetting(config)
@@ -93,8 +93,8 @@ export default class SettingStore extends EventEmitter.EventEmitter {
9393
personalConfigPath: string,
9494
projectConfigPath: string,
9595
projectPath: string
96-
): Promise<Settings | null> {
97-
let personalConfig = { connections: [] } as PersonalConfig, projectConfig = {} as Settings
96+
): Promise<Connection | null> {
97+
let personalConfig = { connections: [] } as PersonalConfig, projectConfig = {} as Connection
9898
if (fileExists(personalConfigPath)) {
9999
personalConfig = JSON.parse(readFile(personalConfigPath))
100100
this.personalConfig = personalConfig
@@ -107,21 +107,25 @@ export default class SettingStore extends EventEmitter.EventEmitter {
107107
logger.debug(`There isn't project config file., ${projectConfigPath}`)
108108
}
109109
const extractedPersonalConfig = projectConfig.name
110-
? personalConfig.connections.find((v: Settings) => v.name === projectConfig.name)
111-
: personalConfig.connections.find((v: Settings) => v.projectPaths?.includes(projectPath))
112-
if (!extractedPersonalConfig) {
113-
logger.debug(`Failed to extract personal config, { path: ${projectPath}, projectName: ${projectConfig.name} }`)
114-
}
110+
? personalConfig.connections.find((v: Connection) => v.name === projectConfig.name)
111+
: this.extractPersonalConfigMatchedProjectPath(projectPath)
115112

116113
const sshConfig = { ...extractedPersonalConfig?.ssh || {}, ...projectConfig?.ssh || {} } as SSHConfig
117114
const config = { ...extractedPersonalConfig, ...projectConfig }
118115
config.ssh = sshConfig
119-
logger.debug(`Set config: ${JSON.stringify(config)}`)
120116
this.setSetting(config)
121117
return this.getSetting()
122118
}
123119

124-
setSetting(setting: Partial<Settings>) {
120+
async setSettingFromWorkspaceConfig(connections: Connection[], projectPath: string = '') {
121+
this.personalConfig = { connections }
122+
const extractedPersonalConfig = this.extractPersonalConfigMatchedProjectPath(projectPath)
123+
this.setSetting(extractedPersonalConfig || {})
124+
return this.getSetting()
125+
}
126+
127+
setSetting(setting: Partial<Connection>) {
128+
logger.debug(`Set config: ${JSON.stringify(setting)}`)
125129
const replaceEnv = (v: { [key: string]: any }) => {
126130
for (const k in v) {
127131
if (v[k] && typeof v[k] === 'object') {
@@ -141,4 +145,12 @@ export default class SettingStore extends EventEmitter.EventEmitter {
141145
logger.debug('setting store, emit "change"')
142146
this.emit('change', this.state)
143147
}
148+
149+
private extractPersonalConfigMatchedProjectPath(projectPath: string) {
150+
const con = this.personalConfig.connections.find((v: Connection) => v.projectPaths?.includes(projectPath))
151+
if (!con) {
152+
logger.debug(`Not to extract personal config, { path: ${projectPath}, projectName: ${projectPath} }`)
153+
}
154+
return con
155+
}
144156
}

packages/server/src/createDiagnostics.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,16 @@ import { parse } from '@joe-re/sql-parser'
22
import log4js from 'log4js';
33
import { PublishDiagnosticsParams, Diagnostic } from 'vscode-languageserver'
44
import { DiagnosticSeverity }from 'vscode-languageserver-types'
5-
import { lint, ErrorLevel, LintResult } from 'sqlint'
5+
import { lint, ErrorLevel, LintResult, RawConfig } from 'sqlint'
66
import cache, { LintCache } from './cache'
77

88
const logger = log4js.getLogger()
99

10-
function doLint(uri: string, sql: string): Diagnostic[] {
10+
function doLint(uri: string, sql: string, config?: RawConfig | null): Diagnostic[] {
1111
if (!sql) {
1212
return []
1313
}
14-
const result: LintResult[] = JSON.parse(lint({ configPath: process.cwd(), formatType: 'json', text: sql }))
14+
const result: LintResult[] = JSON.parse(lint({ configPath: process.cwd(), formatType: 'json', text: sql, configObject: config }))
1515
const lintDiagnostics = result.map(v => v.diagnostics).flat()
1616
const lintCache: LintCache[] = []
1717
const diagnostics = lintDiagnostics.map(v => {
@@ -32,13 +32,13 @@ function doLint(uri: string, sql: string): Diagnostic[] {
3232
return diagnostics
3333
}
3434

35-
export default function createDiagnostics(uri: string, sql: string): PublishDiagnosticsParams {
35+
export default function createDiagnostics(uri: string, sql: string, config?: RawConfig | null): PublishDiagnosticsParams {
3636
logger.debug(`createDiagnostics`)
3737
let diagnostics: Diagnostic[] = []
3838
try {
3939
const ast = parse(sql)
4040
logger.debug(`ast: ${JSON.stringify(ast)}`)
41-
diagnostics = doLint(uri, sql)
41+
diagnostics = doLint(uri, sql, config)
4242
} catch (e) {
4343
logger.debug('parse error')
4444
logger.debug(e)

0 commit comments

Comments
 (0)