@herb-tools/language-server 0.3.0 → 0.4.0

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.
Files changed (56) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/README.md +81 -8
  3. package/bin/herb-language-server +3 -0
  4. package/dist/cli.js +27 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/config.js.map +1 -1
  7. package/dist/diagnostics.js +17 -50
  8. package/dist/diagnostics.js.map +1 -1
  9. package/dist/formatting_service.js +126 -0
  10. package/dist/formatting_service.js.map +1 -0
  11. package/dist/herb-language-server.js +15260 -6068
  12. package/dist/herb-language-server.js.map +1 -1
  13. package/dist/index.cjs +29736 -0
  14. package/dist/index.cjs.map +1 -0
  15. package/dist/index.js +27 -0
  16. package/dist/index.js.map +1 -0
  17. package/dist/linter_service.js +44 -0
  18. package/dist/linter_service.js.map +1 -0
  19. package/dist/parser_service.js +47 -0
  20. package/dist/parser_service.js.map +1 -0
  21. package/dist/project.js +2 -1
  22. package/dist/project.js.map +1 -1
  23. package/dist/server.js +72 -61
  24. package/dist/server.js.map +1 -1
  25. package/dist/service.js +16 -4
  26. package/dist/service.js.map +1 -1
  27. package/dist/settings.js +8 -1
  28. package/dist/settings.js.map +1 -1
  29. package/dist/types/cli.d.ts +4 -0
  30. package/dist/types/config.d.ts +9 -1
  31. package/dist/types/diagnostics.d.ts +9 -7
  32. package/dist/types/formatting_service.d.ts +19 -0
  33. package/dist/types/herb-language-server.d.ts +2 -0
  34. package/dist/types/index.d.ts +10 -0
  35. package/dist/types/linter_service.d.ts +14 -0
  36. package/dist/types/parser_service.d.ts +10 -0
  37. package/dist/types/project.d.ts +2 -0
  38. package/dist/types/server.d.ts +7 -1
  39. package/dist/types/service.d.ts +6 -0
  40. package/dist/types/settings.d.ts +11 -0
  41. package/package.json +28 -19
  42. package/src/cli.ts +23 -0
  43. package/src/config.ts +109 -0
  44. package/src/diagnostics.ts +59 -0
  45. package/src/document_service.ts +35 -0
  46. package/src/formatting_service.ts +151 -0
  47. package/src/herb-language-server.ts +6 -0
  48. package/src/index.ts +10 -0
  49. package/src/linter_service.ts +63 -0
  50. package/src/parser_service.ts +59 -0
  51. package/src/project.ts +22 -0
  52. package/src/server.ts +111 -0
  53. package/src/service.ts +66 -0
  54. package/src/settings.ts +82 -0
  55. package/src/utils.ts +11 -0
  56. package/dist/herb-language-server +0 -20526
package/src/server.ts ADDED
@@ -0,0 +1,111 @@
1
+ import {
2
+ createConnection,
3
+ ProposedFeatures,
4
+ InitializeParams,
5
+ DidChangeConfigurationNotification,
6
+ DidChangeWatchedFilesNotification,
7
+ TextDocumentSyncKind,
8
+ InitializeResult,
9
+ Connection,
10
+ DocumentFormattingParams,
11
+ } from "vscode-languageserver/node"
12
+
13
+ import { Service } from "./service"
14
+ import { HerbSettings } from "./settings"
15
+
16
+ export class Server {
17
+ private service!: Service
18
+ private connection: Connection
19
+
20
+ constructor() {
21
+ this.connection = createConnection(ProposedFeatures.all)
22
+ this.setupEventHandlers()
23
+ }
24
+
25
+ private setupEventHandlers() {
26
+ this.connection.onInitialize(async (params: InitializeParams) => {
27
+ this.service = new Service(this.connection, params)
28
+
29
+ await this.service.init()
30
+
31
+ const result: InitializeResult = {
32
+ capabilities: {
33
+ textDocumentSync: TextDocumentSyncKind.Incremental,
34
+ documentFormattingProvider: true,
35
+ },
36
+ }
37
+
38
+ if (this.service.settings.hasWorkspaceFolderCapability) {
39
+ result.capabilities.workspace = {
40
+ workspaceFolders: {
41
+ supported: true,
42
+ },
43
+ }
44
+ }
45
+
46
+ return result
47
+ })
48
+
49
+ this.connection.onInitialized(() => {
50
+ if (this.service.settings.hasConfigurationCapability) {
51
+ // Register for all configuration changes.
52
+ this.connection.client.register(DidChangeConfigurationNotification.type, undefined)
53
+ }
54
+
55
+ if (this.service.settings.hasWorkspaceFolderCapability) {
56
+ this.connection.workspace.onDidChangeWorkspaceFolders((_event) => {
57
+ this.connection.console.log("Workspace folder change event received.")
58
+ })
59
+ }
60
+
61
+ this.connection.client.register(DidChangeWatchedFilesNotification.type, {
62
+ watchers: [
63
+ { globPattern: `**/**/*.html.erb` },
64
+ { globPattern: `**/**/.herb-lsp/config.json` },
65
+ ],
66
+ })
67
+ })
68
+
69
+ this.connection.onDidChangeConfiguration(async (change) => {
70
+ if (this.service.settings.hasConfigurationCapability) {
71
+ // Reset all cached document settings
72
+ this.service.settings.documentSettings.clear()
73
+ } else {
74
+ this.service.settings.globalSettings = (
75
+ (change.settings.languageServerHerb || this.service.settings.defaultSettings)
76
+ ) as HerbSettings
77
+ }
78
+
79
+ await this.service.refresh()
80
+ })
81
+
82
+ this.connection.onDidOpenTextDocument(async (params) => {
83
+ const document = this.service.documentService.get(params.textDocument.uri)
84
+
85
+ if (document) {
86
+ await this.service.diagnostics.refreshDocument(document)
87
+ }
88
+ })
89
+
90
+ this.connection.onDidChangeWatchedFiles((params) => {
91
+ params.changes.forEach(async (event) => {
92
+ if (event.uri.endsWith("/.herb-lsp/config.json")) {
93
+ await this.service.refreshConfig()
94
+
95
+ const documents = this.service.documentService.getAll()
96
+ await Promise.all(documents.map(document =>
97
+ this.service.diagnostics.refreshDocument(document)
98
+ ))
99
+ }
100
+ })
101
+ })
102
+
103
+ this.connection.onDocumentFormatting((params: DocumentFormattingParams) => {
104
+ return this.service.formatting.formatDocument(params)
105
+ })
106
+ }
107
+
108
+ listen() {
109
+ this.connection.listen()
110
+ }
111
+ }
package/src/service.ts ADDED
@@ -0,0 +1,66 @@
1
+ import { Connection, InitializeParams } from "vscode-languageserver/node"
2
+
3
+ import { Settings, HerbSettings } from "./settings"
4
+ import { DocumentService } from "./document_service"
5
+ import { Diagnostics } from "./diagnostics"
6
+ import { ParserService } from "./parser_service"
7
+ import { LinterService } from "./linter_service"
8
+ import { Config } from "./config"
9
+ import { Project } from "./project"
10
+ import { FormattingService } from "./formatting_service"
11
+
12
+ export class Service {
13
+ connection: Connection
14
+ settings: Settings
15
+ diagnostics: Diagnostics
16
+ documentService: DocumentService
17
+ parserService: ParserService
18
+ linterService: LinterService
19
+ project: Project
20
+ config?: Config
21
+ formatting: FormattingService
22
+
23
+ constructor(connection: Connection, params: InitializeParams) {
24
+ this.connection = connection
25
+ this.settings = new Settings(params, this.connection)
26
+ this.documentService = new DocumentService(this.connection)
27
+ this.project = new Project(connection, this.settings.projectPath.replace("file://", ""))
28
+ this.parserService = new ParserService()
29
+ this.linterService = new LinterService(this.settings)
30
+ this.formatting = new FormattingService(this.connection, this.documentService.documents, this.project, this.settings)
31
+ this.diagnostics = new Diagnostics(this.connection, this.documentService, this.parserService, this.linterService)
32
+
33
+ // Initialize global settings from initialization options
34
+ if (params.initializationOptions) {
35
+ this.settings.globalSettings = params.initializationOptions as HerbSettings
36
+ }
37
+ }
38
+
39
+ async init() {
40
+ await this.project.initialize()
41
+ await this.formatting.initialize()
42
+
43
+ this.config = await Config.fromPathOrNew(this.project.projectPath)
44
+
45
+ // Only keep settings for open documents
46
+ this.documentService.onDidClose((change) => {
47
+ this.settings.documentSettings.delete(change.document.uri)
48
+ })
49
+
50
+ // The content of a text document has changed. This event is emitted
51
+ // when the text document first opened or when its content has changed.
52
+ this.documentService.onDidChangeContent(async (change) => {
53
+ await this.diagnostics.refreshDocument(change.document)
54
+ })
55
+ }
56
+
57
+ async refresh() {
58
+ await this.project.refresh()
59
+ await this.diagnostics.refreshAllDocuments()
60
+ }
61
+
62
+ async refreshConfig() {
63
+ this.config = await Config.fromPathOrNew(this.project.projectPath)
64
+ await this.formatting.refreshConfig()
65
+ }
66
+ }
@@ -0,0 +1,82 @@
1
+ import { ClientCapabilities, Connection, InitializeParams } from "vscode-languageserver/node"
2
+ import { defaultFormatOptions } from "@herb-tools/formatter"
3
+
4
+ export interface HerbSettings {
5
+ trace?: {
6
+ server?: string
7
+ }
8
+ linter?: {
9
+ enabled?: boolean
10
+ }
11
+ formatter?: {
12
+ enabled?: boolean
13
+ indentWidth?: number
14
+ maxLineLength?: number
15
+ }
16
+ }
17
+
18
+ export class Settings {
19
+ // The global settings, used when the `workspace/configuration` request is not supported by the client.
20
+ // Please note that this is not the case when using this server with the client provided in this example
21
+ // but could happen with other clients.
22
+ defaultSettings: HerbSettings = {
23
+ formatter: {
24
+ enabled: false,
25
+ indentWidth: defaultFormatOptions.indentWidth,
26
+ maxLineLength: defaultFormatOptions.maxLineLength
27
+ }
28
+ }
29
+ globalSettings: HerbSettings = this.defaultSettings
30
+ documentSettings: Map<string, Thenable<HerbSettings>> = new Map()
31
+
32
+ hasConfigurationCapability = false
33
+ hasWorkspaceFolderCapability = false
34
+ hasDiagnosticRelatedInformationCapability = false
35
+
36
+ params: InitializeParams
37
+ capabilities: ClientCapabilities
38
+ connection: Connection
39
+
40
+ constructor(params: InitializeParams, connection: Connection) {
41
+ this.params = params
42
+ this.capabilities = params.capabilities
43
+ this.connection = connection
44
+
45
+ // Does the client support the `workspace/configuration` request?
46
+ // If not, we fall back using global settings.
47
+ this.hasConfigurationCapability = !!(this.capabilities.workspace && !!this.capabilities.workspace.configuration)
48
+
49
+ this.hasWorkspaceFolderCapability = !!(
50
+ this.capabilities.workspace && !!this.capabilities.workspace.workspaceFolders
51
+ )
52
+
53
+ this.hasDiagnosticRelatedInformationCapability = !!(
54
+ this.capabilities.textDocument &&
55
+ this.capabilities.textDocument.publishDiagnostics &&
56
+ this.capabilities.textDocument.publishDiagnostics.relatedInformation
57
+ )
58
+ }
59
+
60
+ get projectPath(): string {
61
+ return this.params.workspaceFolders?.at(0)?.uri || ""
62
+ }
63
+
64
+ getDocumentSettings(resource: string): Thenable<HerbSettings> {
65
+ if (!this.hasConfigurationCapability) {
66
+ return Promise.resolve(this.globalSettings)
67
+ }
68
+
69
+ let result = this.documentSettings.get(resource)
70
+
71
+ if (!result) {
72
+ result = this.connection.workspace.getConfiguration({
73
+ scopeUri: resource,
74
+ section: "languageServerHerb",
75
+ })
76
+
77
+ this.documentSettings.set(resource, result)
78
+ }
79
+
80
+ return result
81
+ }
82
+ }
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
+ }