@herb-tools/language-server 0.3.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/README.md +45 -7
- package/bin/herb-language-server +19 -0
- package/dist/herb-language-server.js +1 -1
- package/dist/herb-language-server.js.map +1 -1
- package/package.json +22 -7
- package/src/config.ts +101 -0
- package/src/diagnostics.ts +116 -0
- package/src/document_service.ts +35 -0
- package/src/project.ts +20 -0
- package/src/server.ts +93 -0
- package/src/service.ts +51 -0
- package/src/settings.ts +63 -0
- package/src/utils.ts +11 -0
- package/dist/herb-language-server +0 -20526
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@herb-tools/language-server",
|
|
3
|
-
"description": "
|
|
4
|
-
"version": "0.3.
|
|
3
|
+
"description": "Herb HTML+ERB Language Tools and Language Server Protocol integration.",
|
|
4
|
+
"version": "0.3.1",
|
|
5
5
|
"author": "Marco Roth",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"engines": {
|
|
@@ -13,24 +13,39 @@
|
|
|
13
13
|
"url": "https://github.com/marcoroth/herb.git",
|
|
14
14
|
"directory": "javascript/packages/language-server"
|
|
15
15
|
},
|
|
16
|
+
"main": "./dist/herb-language-server.cjs",
|
|
17
|
+
"module": "./dist/server.js",
|
|
18
|
+
"types": "./dist/types/server.d.ts",
|
|
19
|
+
"exports": {
|
|
20
|
+
"./package.json": "./package.json",
|
|
21
|
+
".": {
|
|
22
|
+
"types": "./dist/types/server.d.ts",
|
|
23
|
+
"import": "./dist/server.js",
|
|
24
|
+
"require": "./dist/herb-language-server.cjs",
|
|
25
|
+
"default": "./dist/server.js"
|
|
26
|
+
}
|
|
27
|
+
},
|
|
16
28
|
"homepage": "https://herb-tools.dev",
|
|
17
29
|
"bin": {
|
|
18
|
-
"herb-language-server": "./
|
|
30
|
+
"herb-language-server": "./bin/herb-language-server"
|
|
19
31
|
},
|
|
20
32
|
"scripts": {
|
|
21
33
|
"clean": "rimraf dist",
|
|
22
34
|
"prebuild": "yarn run clean",
|
|
23
35
|
"build": "tsc -b && rollup -c rollup.config.mjs",
|
|
24
|
-
"postbuild": "node scripts/executable.mjs",
|
|
25
36
|
"watch": "tsc -b -w",
|
|
26
|
-
"test": "echo 'TODO'",
|
|
37
|
+
"test": "echo 'TODO: add tests'",
|
|
27
38
|
"prepublishOnly": "yarn clean && yarn build && yarn test"
|
|
28
39
|
},
|
|
29
40
|
"files": [
|
|
30
|
-
"
|
|
41
|
+
"package.json",
|
|
42
|
+
"README.md",
|
|
43
|
+
"src/",
|
|
44
|
+
"bin/",
|
|
45
|
+
"dist/"
|
|
31
46
|
],
|
|
32
47
|
"dependencies": {
|
|
33
|
-
"@herb-tools/node-wasm": "0.3.
|
|
48
|
+
"@herb-tools/node-wasm": "0.3.1",
|
|
34
49
|
"dedent": "^1.6.0",
|
|
35
50
|
"typescript": "^5.8.3",
|
|
36
51
|
"vscode-languageserver": "^9.0.1",
|
package/src/config.ts
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
export type HerbConfigOptions = {}
|
|
2
|
+
|
|
3
|
+
export type HerbLSPConfig = {
|
|
4
|
+
version: string
|
|
5
|
+
createdAt: string
|
|
6
|
+
updatedAt: string
|
|
7
|
+
options: HerbConfigOptions
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
import path from "path"
|
|
11
|
+
import { version } from "../package.json"
|
|
12
|
+
import { promises as fs } from "fs"
|
|
13
|
+
|
|
14
|
+
export class Config {
|
|
15
|
+
static configPath = ".herb-lsp/config.json"
|
|
16
|
+
|
|
17
|
+
public readonly path: string
|
|
18
|
+
public config: HerbLSPConfig
|
|
19
|
+
|
|
20
|
+
constructor(projectPath: string, config: HerbLSPConfig) {
|
|
21
|
+
this.path = Config.configPathFromProjectPath(projectPath)
|
|
22
|
+
this.config = config
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
get version(): string {
|
|
26
|
+
return this.config.version
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
get createdAt(): Date {
|
|
30
|
+
return new Date(this.config.createdAt)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
get updatedAt(): Date {
|
|
34
|
+
return new Date(this.config.updatedAt)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
get options(): HerbConfigOptions {
|
|
38
|
+
return this.config.options
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
public toJSON() {
|
|
42
|
+
return JSON.stringify(this.config, null, " ")
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
private updateTimestamp() {
|
|
46
|
+
this.config.updatedAt = new Date().toISOString()
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
private updateVersion() {
|
|
50
|
+
this.config.version = version
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async write() {
|
|
54
|
+
this.updateVersion()
|
|
55
|
+
this.updateTimestamp()
|
|
56
|
+
|
|
57
|
+
const folder = path.dirname(this.path)
|
|
58
|
+
|
|
59
|
+
fs.stat(folder)
|
|
60
|
+
.then(() => {})
|
|
61
|
+
.catch(async () => await fs.mkdir(folder))
|
|
62
|
+
.finally(async () => await fs.writeFile(this.path, this.toJSON()))
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async read() {
|
|
66
|
+
return await fs.readFile(this.path, "utf8")
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
static configPathFromProjectPath(projectPath: string) {
|
|
70
|
+
return path.join(projectPath, this.configPath)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
static async fromPathOrNew(projectPath: string) {
|
|
74
|
+
try {
|
|
75
|
+
return await this.fromPath(projectPath)
|
|
76
|
+
} catch (error: any) {
|
|
77
|
+
return Config.newConfig(projectPath)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
static async fromPath(projectPath: string) {
|
|
82
|
+
const configPath = Config.configPathFromProjectPath(projectPath)
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
const config = JSON.parse(await fs.readFile(configPath, "utf8"))
|
|
86
|
+
|
|
87
|
+
return new Config(projectPath, config)
|
|
88
|
+
} catch (error: any) {
|
|
89
|
+
throw new Error(`Error reading config file at: ${configPath}. Error: ${error.message}`)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
static newConfig(projectPath: string): Config {
|
|
94
|
+
return new Config(projectPath, {
|
|
95
|
+
version,
|
|
96
|
+
createdAt: new Date().toISOString(),
|
|
97
|
+
updatedAt: new Date().toISOString(),
|
|
98
|
+
options: {}
|
|
99
|
+
})
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { Connection, Diagnostic, DiagnosticSeverity, Range, Position } from "vscode-languageserver/node"
|
|
2
|
+
import { TextDocument } from "vscode-languageserver-textdocument"
|
|
3
|
+
import { Herb, Visitor } from "@herb-tools/node-wasm"
|
|
4
|
+
|
|
5
|
+
import { DocumentService } from "./document_service"
|
|
6
|
+
|
|
7
|
+
import type { Node, HerbError } from "@herb-tools/node-wasm"
|
|
8
|
+
|
|
9
|
+
class ErrorVisitor extends Visitor {
|
|
10
|
+
private diagnostics: Diagnostics
|
|
11
|
+
private textDocument: TextDocument
|
|
12
|
+
|
|
13
|
+
constructor(diagnostics: Diagnostics, textDocument: TextDocument) {
|
|
14
|
+
super()
|
|
15
|
+
this.diagnostics = diagnostics
|
|
16
|
+
this.textDocument = textDocument
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
visitChildNodes(node: Node) {
|
|
20
|
+
super.visitChildNodes(node)
|
|
21
|
+
|
|
22
|
+
node.errors.forEach(error => this.publishDiagnosticForError(error, node))
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
private publishDiagnosticForError(error: HerbError, node: Node): void {
|
|
26
|
+
this.diagnostics.pushDiagnostic(
|
|
27
|
+
error.message,
|
|
28
|
+
error.type,
|
|
29
|
+
this.rangeFromHerbError(error),
|
|
30
|
+
this.textDocument,
|
|
31
|
+
{
|
|
32
|
+
error: error.toJSON(),
|
|
33
|
+
node: node.toJSON()
|
|
34
|
+
},
|
|
35
|
+
DiagnosticSeverity.Error
|
|
36
|
+
)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
private rangeFromHerbError(error: HerbError): Range {
|
|
40
|
+
return Range.create(
|
|
41
|
+
Position.create(error.location.start.line - 1, error.location.start.column),
|
|
42
|
+
Position.create(error.location.end.line - 1, error.location.end.column),
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export class Diagnostics {
|
|
48
|
+
private readonly connection: Connection
|
|
49
|
+
private readonly documentService: DocumentService
|
|
50
|
+
private readonly diagnosticsSource = "Herb LSP "
|
|
51
|
+
private diagnostics: Map<TextDocument, Diagnostic[]> = new Map()
|
|
52
|
+
|
|
53
|
+
constructor(
|
|
54
|
+
connection: Connection,
|
|
55
|
+
documentService: DocumentService,
|
|
56
|
+
) {
|
|
57
|
+
this.connection = connection
|
|
58
|
+
this.documentService = documentService
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
validate(textDocument: TextDocument) {
|
|
62
|
+
const content = textDocument.getText()
|
|
63
|
+
const result = Herb.parse(content)
|
|
64
|
+
const visitor = new ErrorVisitor(this, textDocument)
|
|
65
|
+
|
|
66
|
+
result.visit(visitor)
|
|
67
|
+
|
|
68
|
+
this.sendDiagnosticsFor(textDocument)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
refreshDocument(document: TextDocument) {
|
|
72
|
+
this.validate(document)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
refreshAllDocuments() {
|
|
76
|
+
this.documentService.getAll().forEach((document) => {
|
|
77
|
+
this.refreshDocument(document)
|
|
78
|
+
})
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
pushDiagnostic(
|
|
82
|
+
message: string,
|
|
83
|
+
code: string,
|
|
84
|
+
range: Range,
|
|
85
|
+
textDocument: TextDocument,
|
|
86
|
+
data = {},
|
|
87
|
+
severity: DiagnosticSeverity = DiagnosticSeverity.Error,
|
|
88
|
+
) {
|
|
89
|
+
const diagnostic: Diagnostic = {
|
|
90
|
+
source: this.diagnosticsSource,
|
|
91
|
+
severity,
|
|
92
|
+
range,
|
|
93
|
+
message,
|
|
94
|
+
code,
|
|
95
|
+
data,
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const diagnostics = this.diagnostics.get(textDocument) || []
|
|
99
|
+
diagnostics.push(diagnostic)
|
|
100
|
+
|
|
101
|
+
this.diagnostics.set(textDocument, diagnostics)
|
|
102
|
+
|
|
103
|
+
return diagnostic
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
private sendDiagnosticsFor(textDocument: TextDocument) {
|
|
107
|
+
const diagnostics = this.diagnostics.get(textDocument) || []
|
|
108
|
+
|
|
109
|
+
this.connection.sendDiagnostics({
|
|
110
|
+
uri: textDocument.uri,
|
|
111
|
+
diagnostics,
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
this.diagnostics.delete(textDocument)
|
|
115
|
+
}
|
|
116
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Connection, TextDocuments } from "vscode-languageserver/node"
|
|
2
|
+
import { TextDocument } from "vscode-languageserver-textdocument"
|
|
3
|
+
|
|
4
|
+
export class DocumentService {
|
|
5
|
+
public documents: TextDocuments<TextDocument>
|
|
6
|
+
document?: TextDocument
|
|
7
|
+
|
|
8
|
+
constructor(connection: Connection) {
|
|
9
|
+
this.documents = new TextDocuments(TextDocument)
|
|
10
|
+
|
|
11
|
+
// Make the text document manager listen on the connection
|
|
12
|
+
// for open, change and close text document events
|
|
13
|
+
this.documents.listen(connection)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
get(uri: string) {
|
|
17
|
+
return this.documents.get(uri)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
getAll() {
|
|
21
|
+
return this.documents.all()
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
get onDidChangeContent() {
|
|
25
|
+
return this.documents.onDidChangeContent
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
get onDidOpen() {
|
|
29
|
+
return this.documents.onDidOpen
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
get onDidClose() {
|
|
33
|
+
return this.documents.onDidClose
|
|
34
|
+
}
|
|
35
|
+
}
|
package/src/project.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Herb } from "@herb-tools/node-wasm"
|
|
2
|
+
import { Connection } from "vscode-languageserver/node"
|
|
3
|
+
|
|
4
|
+
export class Project {
|
|
5
|
+
connection: Connection
|
|
6
|
+
projectPath: string
|
|
7
|
+
|
|
8
|
+
constructor(connection: Connection, projectPath: string) {
|
|
9
|
+
this.projectPath = projectPath
|
|
10
|
+
this.connection = connection
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async initialize() {
|
|
14
|
+
await Herb.load()
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async refresh() {
|
|
18
|
+
// TODO
|
|
19
|
+
}
|
|
20
|
+
}
|
package/src/server.ts
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createConnection,
|
|
3
|
+
ProposedFeatures,
|
|
4
|
+
InitializeParams,
|
|
5
|
+
DidChangeConfigurationNotification,
|
|
6
|
+
DidChangeWatchedFilesNotification,
|
|
7
|
+
TextDocumentSyncKind,
|
|
8
|
+
InitializeResult,
|
|
9
|
+
} from "vscode-languageserver/node"
|
|
10
|
+
|
|
11
|
+
import { Service } from "./service"
|
|
12
|
+
import { HerbSettings } from "./settings"
|
|
13
|
+
|
|
14
|
+
let service: Service
|
|
15
|
+
const connection = createConnection(ProposedFeatures.all)
|
|
16
|
+
|
|
17
|
+
connection.onInitialize(async (params: InitializeParams) => {
|
|
18
|
+
service = new Service(connection, params)
|
|
19
|
+
await service.init()
|
|
20
|
+
|
|
21
|
+
const result: InitializeResult = {
|
|
22
|
+
capabilities: {
|
|
23
|
+
textDocumentSync: TextDocumentSyncKind.Incremental,
|
|
24
|
+
},
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (service.settings.hasWorkspaceFolderCapability) {
|
|
28
|
+
result.capabilities.workspace = {
|
|
29
|
+
workspaceFolders: {
|
|
30
|
+
supported: true,
|
|
31
|
+
},
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return result
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
connection.onInitialized(() => {
|
|
39
|
+
if (service.settings.hasConfigurationCapability) {
|
|
40
|
+
// Register for all configuration changes.
|
|
41
|
+
connection.client.register(DidChangeConfigurationNotification.type, undefined)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (service.settings.hasWorkspaceFolderCapability) {
|
|
45
|
+
connection.workspace.onDidChangeWorkspaceFolders((_event) => {
|
|
46
|
+
connection.console.log("Workspace folder change event received.")
|
|
47
|
+
})
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
connection.client.register(DidChangeWatchedFilesNotification.type, {
|
|
51
|
+
watchers: [
|
|
52
|
+
{ globPattern: `**/**/*.html.erb` },
|
|
53
|
+
{ globPattern: `**/**/.herb-lsp/config.json` },
|
|
54
|
+
],
|
|
55
|
+
})
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
connection.onDidChangeConfiguration((change) => {
|
|
59
|
+
if (service.settings.hasConfigurationCapability) {
|
|
60
|
+
// Reset all cached document settings
|
|
61
|
+
service.settings.documentSettings.clear()
|
|
62
|
+
} else {
|
|
63
|
+
service.settings.globalSettings = (
|
|
64
|
+
(change.settings.languageServerHerb || service.settings.defaultSettings)
|
|
65
|
+
) as HerbSettings
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
service.refresh()
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
connection.onDidOpenTextDocument((params) => {
|
|
72
|
+
console.error(params)
|
|
73
|
+
const document = service.documentService.get(params.textDocument.uri)
|
|
74
|
+
|
|
75
|
+
if (document) {
|
|
76
|
+
service.diagnostics.refreshDocument(document)
|
|
77
|
+
}
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
connection.onDidChangeWatchedFiles((params) => {
|
|
81
|
+
params.changes.forEach(async (event) => {
|
|
82
|
+
if (event.uri.endsWith("/.herb-lsp/config.json")) {
|
|
83
|
+
await service.refreshConfig()
|
|
84
|
+
|
|
85
|
+
service.documentService.getAll().forEach((document) => {
|
|
86
|
+
service.diagnostics.refreshDocument(document)
|
|
87
|
+
})
|
|
88
|
+
}
|
|
89
|
+
})
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
// Listen on the connection
|
|
93
|
+
connection.listen()
|
package/src/service.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { Connection, InitializeParams } from "vscode-languageserver/node"
|
|
2
|
+
|
|
3
|
+
import { Settings } from "./settings"
|
|
4
|
+
import { DocumentService } from "./document_service"
|
|
5
|
+
import { Diagnostics } from "./diagnostics"
|
|
6
|
+
import { Config } from "./config"
|
|
7
|
+
import { Project } from "./project"
|
|
8
|
+
|
|
9
|
+
export class Service {
|
|
10
|
+
connection: Connection
|
|
11
|
+
settings: Settings
|
|
12
|
+
diagnostics: Diagnostics
|
|
13
|
+
documentService: DocumentService
|
|
14
|
+
project: Project
|
|
15
|
+
config?: Config
|
|
16
|
+
|
|
17
|
+
constructor(connection: Connection, params: InitializeParams) {
|
|
18
|
+
this.connection = connection
|
|
19
|
+
this.settings = new Settings(params, this.connection)
|
|
20
|
+
this.documentService = new DocumentService(this.connection)
|
|
21
|
+
this.project = new Project(connection, this.settings.projectPath.replace("file://", ""))
|
|
22
|
+
this.diagnostics = new Diagnostics(this.connection, this.documentService)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async init() {
|
|
26
|
+
await this.project.initialize()
|
|
27
|
+
|
|
28
|
+
this.config = await Config.fromPathOrNew(this.project.projectPath)
|
|
29
|
+
|
|
30
|
+
// Only keep settings for open documents
|
|
31
|
+
this.documentService.onDidClose((change) => {
|
|
32
|
+
this.settings.documentSettings.delete(change.document.uri)
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
// The content of a text document has changed. This event is emitted
|
|
36
|
+
// when the text document first opened or when its content has changed.
|
|
37
|
+
this.documentService.onDidChangeContent((change) => {
|
|
38
|
+
this.diagnostics.refreshDocument(change.document)
|
|
39
|
+
})
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async refresh() {
|
|
43
|
+
await this.project.refresh()
|
|
44
|
+
|
|
45
|
+
this.diagnostics.refreshAllDocuments()
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async refreshConfig() {
|
|
49
|
+
this.config = await Config.fromPathOrNew(this.project.projectPath)
|
|
50
|
+
}
|
|
51
|
+
}
|
package/src/settings.ts
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { ClientCapabilities, Connection, InitializeParams } from "vscode-languageserver/node"
|
|
2
|
+
|
|
3
|
+
export interface HerbSettings {}
|
|
4
|
+
|
|
5
|
+
export class Settings {
|
|
6
|
+
// The global settings, used when the `workspace/configuration` request is not supported by the client.
|
|
7
|
+
// Please note that this is not the case when using this server with the client provided in this example
|
|
8
|
+
// but could happen with other clients.
|
|
9
|
+
defaultSettings: HerbSettings = {}
|
|
10
|
+
globalSettings: HerbSettings = this.defaultSettings
|
|
11
|
+
documentSettings: Map<string, Thenable<HerbSettings>> = new Map()
|
|
12
|
+
|
|
13
|
+
hasConfigurationCapability = false
|
|
14
|
+
hasWorkspaceFolderCapability = false
|
|
15
|
+
hasDiagnosticRelatedInformationCapability = false
|
|
16
|
+
|
|
17
|
+
params: InitializeParams
|
|
18
|
+
capabilities: ClientCapabilities
|
|
19
|
+
connection: Connection
|
|
20
|
+
|
|
21
|
+
constructor(params: InitializeParams, connection: Connection) {
|
|
22
|
+
this.params = params
|
|
23
|
+
this.capabilities = params.capabilities
|
|
24
|
+
this.connection = connection
|
|
25
|
+
|
|
26
|
+
// Does the client support the `workspace/configuration` request?
|
|
27
|
+
// If not, we fall back using global settings.
|
|
28
|
+
this.hasConfigurationCapability = !!(this.capabilities.workspace && !!this.capabilities.workspace.configuration)
|
|
29
|
+
|
|
30
|
+
this.hasWorkspaceFolderCapability = !!(
|
|
31
|
+
this.capabilities.workspace && !!this.capabilities.workspace.workspaceFolders
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
this.hasDiagnosticRelatedInformationCapability = !!(
|
|
35
|
+
this.capabilities.textDocument &&
|
|
36
|
+
this.capabilities.textDocument.publishDiagnostics &&
|
|
37
|
+
this.capabilities.textDocument.publishDiagnostics.relatedInformation
|
|
38
|
+
)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
get projectPath(): string {
|
|
42
|
+
return this.params.workspaceFolders?.at(0)?.uri || ""
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
getDocumentSettings(resource: string): Thenable<HerbSettings> {
|
|
46
|
+
if (!this.hasConfigurationCapability) {
|
|
47
|
+
return Promise.resolve(this.globalSettings)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
let result = this.documentSettings.get(resource)
|
|
51
|
+
|
|
52
|
+
if (!result) {
|
|
53
|
+
result = this.connection.workspace.getConfiguration({
|
|
54
|
+
scopeUri: resource,
|
|
55
|
+
section: "languageServerHerb",
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
this.documentSettings.set(resource, result)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return result
|
|
62
|
+
}
|
|
63
|
+
}
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export function camelize(value: string) {
|
|
2
|
+
return value.replace(/(?:[_-])([a-z0-9])/g, (_, char) => char.toUpperCase())
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export function dasherize(value: string) {
|
|
6
|
+
return value.replace(/([A-Z])/g, (_, char) => `-${char.toLowerCase()}`)
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function capitalize(value: string) {
|
|
10
|
+
return value.charAt(0).toUpperCase() + value.slice(1)
|
|
11
|
+
}
|