@tsuzuri-lab/sed-language-server 0.1.2 → 0.3.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.
package/README.md CHANGED
@@ -2,13 +2,7 @@
2
2
 
3
3
  [![CI](https://github.com/tsuzuri-lab/sed-language-server/actions/workflows/ci.yml/badge.svg)](https://github.com/tsuzuri-lab/sed-language-server/actions/workflows/ci.yml)
4
4
 
5
- A [Language Server Protocol](https://microsoft.github.io/language-server-protocol/)
6
- implementation for
7
- [POSIX `sed`](https://pubs.opengroup.org/onlinepubs/9799919799.2024edition/utilities/sed.html)
8
- with opt-in GNU sed 4.10 syntax support.
9
-
10
- The default profile is POSIX with basic regular expressions. BSD and
11
- other implementation-specific extensions are outside the current scope.
5
+ A Language Server Protocol implementation for POSIX and GNU `sed`.
12
6
 
13
7
  ## Installation
14
8
 
@@ -18,7 +12,7 @@ Requires Node.js 22 or later.
18
12
  npm install --global @tsuzuri-lab/sed-language-server
19
13
  ```
20
14
 
21
- Configure the LSP client to start the server with:
15
+ Start the server with:
22
16
 
23
17
  ```sh
24
18
  sed-language-server --stdio
@@ -26,49 +20,60 @@ sed-language-server --stdio
26
20
 
27
21
  ## Features
28
22
 
29
- The server provides:
30
-
31
- - profile-aware diagnostics for supported POSIX and GNU sed syntax
32
- - profile-aware command, substitute-flag, and branch-label completion
23
+ - Tree-sitter-based diagnostics
33
24
  - go to definition from `b`, `t`, and GNU `T` label references
25
+ - document formatting for command lists and nested blocks
34
26
 
35
- It analyzes scripts without executing them, running shell commands, or
36
- accessing referenced files.
37
-
38
- ## Syntax profiles
27
+ Scripts are analyzed without executing them or accessing referenced files.
39
28
 
40
- The supported profile values are:
29
+ ## Configuration
41
30
 
42
- - `dialect`: `posix` or `gnu`
43
- - `regexpMode`: `bre` or `ere`
44
-
45
- Clients can select a profile during initialization:
31
+ The default dialect is `posix`. To use GNU syntax, pass the following LSP
32
+ initialization options:
46
33
 
47
34
  ```json
48
35
  {
49
- "initializationOptions": {
50
- "dialect": "gnu",
51
- "regexpMode": "bre"
52
- }
36
+ "dialect": "gnu"
53
37
  }
54
38
  ```
55
39
 
56
- They can change it for all open documents through
57
- `workspace/didChangeConfiguration`:
40
+ The supported dialects are `posix` and `gnu`.
58
41
 
59
- ```json
60
- {
61
- "settings": {
62
- "sedLanguageServer": {
63
- "dialect": "gnu",
64
- "regexpMode": "ere"
65
- }
66
- }
67
- }
42
+ ## Editor setup
43
+
44
+ Configure the LSP client to use:
45
+
46
+ - command: `sed-language-server --stdio`
47
+ - language ID or filetype: `sed`
48
+ - initialization options: `{"dialect":"gnu"}` when using GNU syntax
49
+
50
+ For example, with Emacs and Eglot:
51
+
52
+ ```elisp
53
+ (require 'eglot)
54
+
55
+ (add-to-list 'eglot-server-programs
56
+ '((sed-ts-mode sed-mode) .
57
+ ("sed-language-server" "--stdio"
58
+ :initializationOptions (:dialect "gnu"))))
59
+ ```
60
+
61
+ Run `M-x eglot` in a `sed-ts-mode` or `sed-mode` buffer. Omit
62
+ `:initializationOptions` to use the default POSIX dialect.
63
+
64
+ For Neovim 0.11 or later:
65
+
66
+ ```lua
67
+ vim.lsp.config("sed_language_server", {
68
+ cmd = { "sed-language-server", "--stdio" },
69
+ filetypes = { "sed" },
70
+ init_options = { dialect = "gnu" },
71
+ })
72
+
73
+ vim.lsp.enable("sed_language_server")
68
74
  ```
69
75
 
70
- Omitted settings select `posix` and `bre`. The server does not infer a dialect
71
- from document contents.
76
+ Omit `init_options` to use the default POSIX dialect.
72
77
 
73
78
  ## Development
74
79
 
@@ -76,7 +81,13 @@ from document contents.
76
81
  npm ci
77
82
  npm run check
78
83
  npm test
79
- npm start
84
+ ```
85
+
86
+ The published package includes prebuilt POSIX and GNU grammar Wasm files.
87
+ Rebuild them from the pinned `tree-sitter-sed` revision with:
88
+
89
+ ```sh
90
+ npm run build:grammar -- /path/to/tree-sitter-sed
80
91
  ```
81
92
 
82
93
  ## License
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tsuzuri-lab/sed-language-server",
3
- "version": "0.1.2",
3
+ "version": "0.3.0",
4
4
  "description": "Language server for POSIX sed with opt-in GNU syntax support.",
5
5
  "keywords": [
6
6
  "sed",
@@ -9,12 +9,14 @@
9
9
  ],
10
10
  "type": "module",
11
11
  "files": [
12
- "src"
12
+ "src",
13
+ "vendor"
13
14
  ],
14
15
  "bin": {
15
16
  "sed-language-server": "src/server.js"
16
17
  },
17
18
  "scripts": {
19
+ "build:grammar": "node scripts/build-tree-sitter-sed-wasm.js",
18
20
  "check": "biome check .",
19
21
  "check:write": "biome check --write .",
20
22
  "prepublishOnly": "npm run check && npm test",
@@ -38,9 +40,11 @@
38
40
  },
39
41
  "dependencies": {
40
42
  "vscode-languageserver": "10.1.0",
41
- "vscode-languageserver-textdocument": "1.0.12"
43
+ "vscode-languageserver-textdocument": "1.0.12",
44
+ "web-tree-sitter": "0.26.11"
42
45
  },
43
46
  "devDependencies": {
44
- "@biomejs/biome": "2.5.5"
47
+ "@biomejs/biome": "2.5.5",
48
+ "tree-sitter-cli": "0.26.11"
45
49
  }
46
50
  }
package/src/definition.js CHANGED
@@ -1,46 +1,44 @@
1
- import { getDocumentStructure } from "./document-structure.js";
2
- import { defaultSyntaxProfile } from "./syntax-profile.js";
1
+ import { rangeForNode, syntaxTreeFor } from "./syntax.js";
3
2
 
4
- export const definitionProvider = true;
3
+ function collectLabels(rootNode, offset) {
4
+ const definitions = [];
5
+ let reference;
6
+ const stack = [rootNode];
5
7
 
6
- function containsCursor(range, offset) {
7
- return range.startOffset <= offset && offset <= range.endOffset;
8
+ while (stack.length > 0) {
9
+ const node = stack.pop();
10
+ if (node.type === "label_definition") {
11
+ definitions.push(node);
12
+ } else if (
13
+ node.type === "label_reference" &&
14
+ node.startIndex <= offset &&
15
+ offset < node.endIndex
16
+ ) {
17
+ reference = node;
18
+ }
19
+ stack.push(...node.children.toReversed());
20
+ }
21
+
22
+ return { definitions, reference };
8
23
  }
9
24
 
10
- export function createDefinitionLocations(
11
- document,
12
- position,
13
- syntaxProfile = defaultSyntaxProfile,
14
- ) {
15
- const structure = getDocumentStructure(document, syntaxProfile);
16
- const offset = document.offsetAt(position);
17
- const reference = structure.labelReferences.find(({ range }) =>
18
- containsCursor(range, offset),
25
+ export function createDefinitionLocations(document, position, dialect) {
26
+ const tree = syntaxTreeFor(document, dialect);
27
+ const { definitions, reference } = collectLabels(
28
+ tree.rootNode,
29
+ document.offsetAt(position),
19
30
  );
20
31
 
21
32
  if (reference === undefined) {
22
- return [];
33
+ return null;
23
34
  }
24
35
 
25
- return structure.labelDefinitions
26
- .filter((definition) => definition.name === reference.name)
36
+ const locations = definitions
37
+ .filter((definition) => definition.text === reference.text)
27
38
  .map((definition) => ({
28
39
  uri: document.uri,
29
- range: {
30
- start: document.positionAt(definition.range.startOffset),
31
- end: document.positionAt(definition.range.endOffset),
32
- },
40
+ range: rangeForNode(document, definition),
33
41
  }));
34
- }
35
42
 
36
- export function createDefinitionHandler(
37
- documents,
38
- getSyntaxProfile = () => defaultSyntaxProfile,
39
- ) {
40
- return ({ textDocument, position }) => {
41
- const document = documents.get(textDocument.uri);
42
- return document === undefined
43
- ? null
44
- : createDefinitionLocations(document, position, getSyntaxProfile());
45
- };
43
+ return locations.length === 0 ? null : locations;
46
44
  }