@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/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ ## 0.3.1 (2025-06-23)
2
+
3
+ This was a version bump only for @herb-tools/language-server to align it with other projects, there were no code changes.
4
+
1
5
  ## 0.3.0 (2025-06-21)
2
6
 
3
7
  This was a version bump only for @herb-tools/language-server to align it with other projects, there were no code changes.
package/README.md CHANGED
@@ -1,27 +1,57 @@
1
- # Herb Language Server (`@herb-tools/language-server`)
1
+ # Herb Language Server
2
2
 
3
- [Language Server Protocol](https://github.com/Microsoft/language-server-protocol) integration for HTML-aware ERB using the [Herb Parser](https://herb-tools.dev).
3
+ **Package**: [`@herb-tools/language-server`](https://www.npmjs.com/package/@herb-tools/language-server)
4
4
 
5
- ![](./assets/herb-lsp.png)
5
+ ---
6
6
 
7
- Used by the [Herb LSP](https://marketplace.visualstudio.com/items?itemName=marcoroth.herb-lsp) Visual Studio Code extension.
7
+ [Language Server Protocol](https://github.com/Microsoft/language-server-protocol) integration for HTML-aware ERB parsing using the [Herb Parser](/projects/parser).
8
8
 
9
- ## Install
9
+ ![Herb Language Server in action](https://github.com/marcoroth/herb/raw/main/javascript/packages/language-server/assets/herb-lsp.png)
10
+
11
+ ### Installation
12
+
13
+ #### Visual Studio Code
14
+
15
+ Install the [Herb LSP extension](https://marketplace.visualstudio.com/items?itemName=marcoroth.herb-lsp) from the Visual Studio Marketplace.
16
+
17
+ #### Cursor (Open VSX Registry)
18
+
19
+ Install the [Herb LSP extension](https://open-vsx.org/extension/marcoroth/herb-lsp) from the Open VSX Registry.
20
+
21
+ #### Zed
22
+
23
+ The Herb Language Server is part of the official [Ruby extension for Zed](https://github.com/zed-extensions/ruby). Just install the Ruby extension in Zed and you should be good to go.
24
+
25
+ Read more in the [documentation](https://zed.dev/docs/languages/ruby).
26
+
27
+ #### Neovim (using `nvim-lspconfig`)
28
+
29
+ Coming soon, see [#3925](https://github.com/neovim/nvim-lspconfig/pull/3925).
30
+
31
+ #### Manual Installation
32
+
33
+ You can use the language server in any editor that supports the [Language Server Protocol](https://microsoft.github.io/language-server-protocol/).
34
+
35
+ ###### NPM (Global)
10
36
 
11
37
  ```bash
12
- npm install -g herb-language-server
38
+ npm install -g @herb-tools/language-server
13
39
  ```
14
40
 
41
+ ###### Yarn (Global)
42
+
15
43
  ```bash
16
- yarn global add herb-language-server
44
+ yarn global add @herb-tools/language-server
17
45
  ```
18
46
 
19
- ## Run
47
+ ##### Run
20
48
 
21
49
  ```bash
22
50
  herb-language-server --stdio
23
51
  ```
24
52
 
53
+ ##### Usage
54
+
25
55
  ```
26
56
  Usage: herb-language-server [options]
27
57
 
@@ -31,3 +61,46 @@ Options:
31
61
  --node-ipc use node-ipc
32
62
  --socket=<port> use socket
33
63
  ```
64
+
65
+ ##### NPX
66
+
67
+ Alternatively you can also run the language server directly with `npx` without installing anything:
68
+
69
+ ```bash
70
+ npx @herb-tools/language-server --stdio
71
+ ```
72
+
73
+ ## Configuration
74
+
75
+ The language server can be configured using a `.herb-lsp/config.json` file in your project root. This file is automatically created when the language server starts if it doesn't exist.
76
+
77
+ ### Formatting Configuration
78
+
79
+ You can configure formatting behavior by adding a `formatting` section to your config:
80
+
81
+ ```json
82
+ {
83
+ "version": "0.3.1",
84
+ "createdAt": "2025-06-29T00:00:00.000Z",
85
+ "updatedAt": "2025-06-29T00:00:00.000Z",
86
+ "options": {
87
+ "formatting": {
88
+ "enabled": true,
89
+ "include": ["**/*.html.erb"],
90
+ "exclude": ["**/node_modules/**", "**/dist/**", "**/*.min.html.erb"],
91
+ "indentWidth": 2,
92
+ "maxLineLength": 80
93
+ }
94
+ }
95
+ }
96
+ ```
97
+
98
+ #### `formatting` Options
99
+
100
+ - `enabled` (`boolean`): Enable or disable formatting for this project. Defaults to `false`.
101
+ - `include` (`string[]`): Glob patterns for files to include in formatting. If specified, only matching files will be formatted.
102
+ - `exclude` (`string[]`): Glob patterns for files to exclude from formatting. Takes precedence over `include` patterns.
103
+ - `indentWidth` (`number`): Number of spaces for each indentation level. Defaults to `2`.
104
+ - `maxLineLength` (`number`): Maximum line length before wrapping. Defaults to `80`.
105
+
106
+ **Note**: VS Code users can also control formatting globally through the `languageServerHerb.formatting.enabled` setting in VS Code preferences. Formatting is currently in **Beta** and disabled by default.
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+
3
+ import("../dist/herb-language-server.js")
package/dist/cli.js ADDED
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CLI = void 0;
4
+ const server_1 = require("./server");
5
+ class CLI {
6
+ constructor() {
7
+ this.usage = `
8
+ Usage: herb-language-server [options]
9
+
10
+ Options:
11
+ --stdio use stdio
12
+ --node-ipc use node-ipc
13
+ --socket=<port> use socket
14
+ `;
15
+ }
16
+ run() {
17
+ if (process.argv.length <= 2) {
18
+ console.error(`Error: Connection input stream is not set. Set command line parameters: '--node-ipc', '--stdio' or '--socket=<port>'`);
19
+ console.error(this.usage);
20
+ process.exit(1);
21
+ }
22
+ const server = new server_1.Server();
23
+ server.listen();
24
+ }
25
+ }
26
+ exports.CLI = CLI;
27
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";;;AAAA,qCAAiC;AAEjC,MAAa,GAAG;IAAhB;QACU,UAAK,GAAG;;;;;;;CAOjB,CAAA;IAYD,CAAC;IAVC,GAAG;QACD,IAAI,OAAO,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;YAC7B,OAAO,CAAC,KAAK,CAAC,sHAAsH,CAAC,CAAA;YACrI,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;YACzB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;QACjB,CAAC;QAED,MAAM,MAAM,GAAG,IAAI,eAAM,EAAE,CAAA;QAC3B,MAAM,CAAC,MAAM,EAAE,CAAA;IACjB,CAAC;CACF;AApBD,kBAoBC"}
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -1,63 +1,30 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Diagnostics = void 0;
4
- const node_1 = require("vscode-languageserver/node");
5
- const node_wasm_1 = require("@herb-tools/node-wasm");
6
- class ErrorVisitor extends node_wasm_1.Visitor {
7
- constructor(diagnostics, textDocument) {
8
- super();
9
- this.diagnostics = diagnostics;
10
- this.textDocument = textDocument;
11
- }
12
- visitChildNodes(node) {
13
- super.visitChildNodes(node);
14
- node.errors.forEach(error => this.publishDiagnosticForError(error, node));
15
- }
16
- publishDiagnosticForError(error, node) {
17
- this.diagnostics.pushDiagnostic(error.message, error.type, this.rangeFromHerbError(error), this.textDocument, {
18
- error: error.toJSON(),
19
- node: node.toJSON()
20
- }, node_1.DiagnosticSeverity.Error);
21
- }
22
- rangeFromHerbError(error) {
23
- return node_1.Range.create(node_1.Position.create(error.location.start.line - 1, error.location.start.column), node_1.Position.create(error.location.end.line - 1, error.location.end.column));
24
- }
25
- }
26
4
  class Diagnostics {
27
- constructor(connection, documentService) {
28
- this.diagnosticsSource = "Herb LSP ";
5
+ constructor(connection, documentService, parserService, linterService) {
29
6
  this.diagnostics = new Map();
30
7
  this.connection = connection;
31
8
  this.documentService = documentService;
32
- }
33
- validate(textDocument) {
34
- const content = textDocument.getText();
35
- const result = node_wasm_1.Herb.parse(content);
36
- const visitor = new ErrorVisitor(this, textDocument);
37
- result.visit(visitor);
9
+ this.parserService = parserService;
10
+ this.linterService = linterService;
11
+ }
12
+ async validate(textDocument) {
13
+ const parseResult = this.parserService.parseDocument(textDocument);
14
+ const lintResult = await this.linterService.lintDocument(parseResult.document, textDocument);
15
+ const allDiagnostics = [
16
+ ...parseResult.diagnostics,
17
+ ...lintResult.diagnostics,
18
+ ];
19
+ this.diagnostics.set(textDocument, allDiagnostics);
38
20
  this.sendDiagnosticsFor(textDocument);
39
21
  }
40
- refreshDocument(document) {
41
- this.validate(document);
42
- }
43
- refreshAllDocuments() {
44
- this.documentService.getAll().forEach((document) => {
45
- this.refreshDocument(document);
46
- });
22
+ async refreshDocument(document) {
23
+ await this.validate(document);
47
24
  }
48
- pushDiagnostic(message, code, range, textDocument, data = {}, severity = node_1.DiagnosticSeverity.Error) {
49
- const diagnostic = {
50
- source: this.diagnosticsSource,
51
- severity,
52
- range,
53
- message,
54
- code,
55
- data,
56
- };
57
- const diagnostics = this.diagnostics.get(textDocument) || [];
58
- diagnostics.push(diagnostic);
59
- this.diagnostics.set(textDocument, diagnostics);
60
- return diagnostic;
25
+ async refreshAllDocuments() {
26
+ const documents = this.documentService.getAll();
27
+ await Promise.all(documents.map(document => this.refreshDocument(document)));
61
28
  }
62
29
  sendDiagnosticsFor(textDocument) {
63
30
  const diagnostics = this.diagnostics.get(textDocument) || [];
@@ -1 +1 @@
1
- {"version":3,"file":"diagnostics.js","sourceRoot":"","sources":["../src/diagnostics.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"diagnostics.js","sourceRoot":"","sources":["../src/diagnostics.ts"],"names":[],"mappings":";;;AAOA,MAAa,WAAW;IAOtB,YACE,UAAsB,EACtB,eAAgC,EAChC,aAA4B,EAC5B,aAA4B;QANtB,gBAAW,GAAoC,IAAI,GAAG,EAAE,CAAA;QAQ9D,IAAI,CAAC,UAAU,GAAG,UAAU,CAAA;QAC5B,IAAI,CAAC,eAAe,GAAG,eAAe,CAAA;QACtC,IAAI,CAAC,aAAa,GAAG,aAAa,CAAA;QAClC,IAAI,CAAC,aAAa,GAAG,aAAa,CAAA;IACpC,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,YAA0B;QACvC,MAAM,WAAW,GAAG,IAAI,CAAC,aAAa,CAAC,aAAa,CAAC,YAAY,CAAC,CAAA;QAClE,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,YAAY,CAAC,WAAW,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAA;QAE5F,MAAM,cAAc,GAAG;YACrB,GAAG,WAAW,CAAC,WAAW;YAC1B,GAAG,UAAU,CAAC,WAAW;SAC1B,CAAA;QAED,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,YAAY,EAAE,cAAc,CAAC,CAAA;QAClD,IAAI,CAAC,kBAAkB,CAAC,YAAY,CAAC,CAAA;IACvC,CAAC;IAED,KAAK,CAAC,eAAe,CAAC,QAAsB;QAC1C,MAAM,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAA;IAC/B,CAAC;IAED,KAAK,CAAC,mBAAmB;QACvB,MAAM,SAAS,GAAG,IAAI,CAAC,eAAe,CAAC,MAAM,EAAE,CAAA;QAC/C,MAAM,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAA;IAC9E,CAAC;IAEO,kBAAkB,CAAC,YAA0B;QACnD,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,YAAY,CAAC,IAAI,EAAE,CAAA;QAE5D,IAAI,CAAC,UAAU,CAAC,eAAe,CAAC;YAC9B,GAAG,EAAE,YAAY,CAAC,GAAG;YACrB,WAAW;SACZ,CAAC,CAAA;QAEF,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,YAAY,CAAC,CAAA;IACvC,CAAC;CACF;AAnDD,kCAmDC"}
@@ -0,0 +1,126 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FormattingService = void 0;
4
+ const node_1 = require("vscode-languageserver/node");
5
+ const formatter_1 = require("@herb-tools/formatter");
6
+ const config_1 = require("./config");
7
+ const glob_1 = require("glob");
8
+ class FormattingService {
9
+ constructor(connection, documents, project, settings) {
10
+ this.connection = connection;
11
+ this.documents = documents;
12
+ this.project = project;
13
+ this.settings = settings;
14
+ }
15
+ async initialize() {
16
+ try {
17
+ this.config = await config_1.Config.fromPathOrNew(this.project.projectPath);
18
+ this.connection.console.log("Herb formatter initialized successfully");
19
+ }
20
+ catch (error) {
21
+ this.connection.console.error(`Failed to initialize Herb formatter: ${error}`);
22
+ }
23
+ }
24
+ async refreshConfig() {
25
+ this.config = await config_1.Config.fromPathOrNew(this.project.projectPath);
26
+ }
27
+ async shouldFormatFile(filePath) {
28
+ var _a;
29
+ if (!((_a = this.config) === null || _a === void 0 ? void 0 : _a.options.formatter)) {
30
+ return true;
31
+ }
32
+ const formatter = this.config.options.formatter;
33
+ // Check if formatting is disabled in project config
34
+ if (formatter.enabled === false) {
35
+ return false;
36
+ }
37
+ // Check exclude patterns first
38
+ if (formatter.exclude) {
39
+ for (const pattern of formatter.exclude) {
40
+ try {
41
+ const matches = await new Promise((resolve, reject) => {
42
+ (0, glob_1.glob)(pattern, { cwd: this.project.projectPath }).then(resolve).catch(reject);
43
+ });
44
+ if (Array.isArray(matches) && matches.some((match) => filePath.includes(match) || filePath.endsWith(match))) {
45
+ return false;
46
+ }
47
+ }
48
+ catch (error) {
49
+ continue;
50
+ }
51
+ }
52
+ }
53
+ if (formatter.include && formatter.include.length > 0) {
54
+ for (const pattern of formatter.include) {
55
+ try {
56
+ const matches = await new Promise((resolve, reject) => {
57
+ (0, glob_1.glob)(pattern, { cwd: this.project.projectPath }).then(resolve).catch(reject);
58
+ });
59
+ if (Array.isArray(matches) && matches.some((match) => filePath.includes(match) || filePath.endsWith(match))) {
60
+ return true;
61
+ }
62
+ }
63
+ catch (error) {
64
+ continue;
65
+ }
66
+ }
67
+ return false;
68
+ }
69
+ return true;
70
+ }
71
+ async getFormatterOptions(uri) {
72
+ var _a, _b, _c, _d, _e, _f, _g;
73
+ // Get VS Code settings
74
+ const settings = await this.settings.getDocumentSettings(uri);
75
+ // Get project config options
76
+ const projectFormatter = ((_a = this.config) === null || _a === void 0 ? void 0 : _a.options.formatter) || {};
77
+ // Merge options with precedence: project config > VS Code settings > defaults
78
+ return {
79
+ indentWidth: (_d = (_b = projectFormatter.indentWidth) !== null && _b !== void 0 ? _b : (_c = settings.formatter) === null || _c === void 0 ? void 0 : _c.indentWidth) !== null && _d !== void 0 ? _d : formatter_1.defaultFormatOptions.indentWidth,
80
+ maxLineLength: (_g = (_e = projectFormatter.maxLineLength) !== null && _e !== void 0 ? _e : (_f = settings.formatter) === null || _f === void 0 ? void 0 : _f.maxLineLength) !== null && _g !== void 0 ? _g : formatter_1.defaultFormatOptions.maxLineLength
81
+ };
82
+ }
83
+ async performFormatting(params) {
84
+ const document = this.documents.get(params.textDocument.uri);
85
+ if (!document) {
86
+ return [];
87
+ }
88
+ try {
89
+ const options = await this.getFormatterOptions(params.textDocument.uri);
90
+ const formatter = new formatter_1.Formatter(this.project.herbBackend, options);
91
+ const text = document.getText();
92
+ const newText = formatter.format(text);
93
+ if (newText === text) {
94
+ return [];
95
+ }
96
+ const range = {
97
+ start: node_1.Position.create(0, 0),
98
+ end: node_1.Position.create(document.lineCount, 0)
99
+ };
100
+ return [{ range, newText }];
101
+ }
102
+ catch (error) {
103
+ this.connection.console.error(`Formatting failed: ${error}`);
104
+ return [];
105
+ }
106
+ }
107
+ async formatDocument(params) {
108
+ var _a;
109
+ // Check VS Code settings first
110
+ const settings = await this.settings.getDocumentSettings(params.textDocument.uri);
111
+ if (((_a = settings.formatter) === null || _a === void 0 ? void 0 : _a.enabled) === false) {
112
+ return [];
113
+ }
114
+ // Check project config and file patterns
115
+ const filePath = params.textDocument.uri.replace(/^file:\/\//, '');
116
+ if (!(await this.shouldFormatFile(filePath))) {
117
+ return [];
118
+ }
119
+ return this.performFormatting(params);
120
+ }
121
+ async formatDocumentIgnoreConfig(params) {
122
+ return this.performFormatting(params);
123
+ }
124
+ }
125
+ exports.FormattingService = FormattingService;
126
+ //# sourceMappingURL=formatting_service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formatting_service.js","sourceRoot":"","sources":["../src/formatting_service.ts"],"names":[],"mappings":"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"}