@tsuzuri-lab/sed-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/package.json +1 -1
- package/src/definition.js +11 -20
- package/src/diagnostics.js +90 -20
- package/src/formatting.js +6 -3
- package/src/server.js +0 -31
- package/src/syntax.js +1 -1
package/package.json
CHANGED
package/src/definition.js
CHANGED
|
@@ -1,39 +1,30 @@
|
|
|
1
1
|
import { rangeForNode, syntaxTreeFor } from "./syntax.js";
|
|
2
2
|
|
|
3
|
-
function
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
while (stack.length > 0) {
|
|
9
|
-
const node = stack.pop();
|
|
10
|
-
if (node.type === "label_definition") {
|
|
11
|
-
definitions.push(node);
|
|
12
|
-
} else if (
|
|
3
|
+
function labelReferenceAt(rootNode, offset) {
|
|
4
|
+
let node = rootNode.namedDescendantForIndex(offset);
|
|
5
|
+
while (node !== null) {
|
|
6
|
+
if (
|
|
13
7
|
node.type === "label_reference" &&
|
|
14
8
|
node.startIndex <= offset &&
|
|
15
9
|
offset < node.endIndex
|
|
16
10
|
) {
|
|
17
|
-
|
|
11
|
+
return node;
|
|
18
12
|
}
|
|
19
|
-
|
|
13
|
+
node = node.parent;
|
|
20
14
|
}
|
|
21
|
-
|
|
22
|
-
return { definitions, reference };
|
|
15
|
+
return undefined;
|
|
23
16
|
}
|
|
24
17
|
|
|
25
18
|
export function createDefinitionLocations(document, position, dialect) {
|
|
26
|
-
const
|
|
27
|
-
const
|
|
28
|
-
tree.rootNode,
|
|
29
|
-
document.offsetAt(position),
|
|
30
|
-
);
|
|
19
|
+
const rootNode = syntaxTreeFor(document, dialect).rootNode;
|
|
20
|
+
const reference = labelReferenceAt(rootNode, document.offsetAt(position));
|
|
31
21
|
|
|
32
22
|
if (reference === undefined) {
|
|
33
23
|
return null;
|
|
34
24
|
}
|
|
35
25
|
|
|
36
|
-
const locations =
|
|
26
|
+
const locations = rootNode
|
|
27
|
+
.descendantsOfType("label_definition")
|
|
37
28
|
.filter((definition) => definition.text === reference.text)
|
|
38
29
|
.map((definition) => ({
|
|
39
30
|
uri: document.uri,
|
package/src/diagnostics.js
CHANGED
|
@@ -40,6 +40,10 @@ const issueDescriptions = {
|
|
|
40
40
|
code: "invalid-substitution-flag",
|
|
41
41
|
message: `Unknown ${dialectName(dialect)} sed substitution flag: \`${node.text}\`.`,
|
|
42
42
|
}),
|
|
43
|
+
invalid_flags: (node, dialect) => ({
|
|
44
|
+
code: "invalid-substitution-flag",
|
|
45
|
+
message: `The ${dialectName(dialect)} sed substitution flags \`${node.text}\` contain invalid characters.`,
|
|
46
|
+
}),
|
|
43
47
|
unexpected_text: (_node, dialect) => ({
|
|
44
48
|
code: "unexpected-text",
|
|
45
49
|
message: `Unexpected text after this ${dialectName(dialect)} sed command.`,
|
|
@@ -71,7 +75,8 @@ function descriptionFor(node, dialect) {
|
|
|
71
75
|
message: `Invalid ${dialectName(dialect)} sed syntax.`,
|
|
72
76
|
};
|
|
73
77
|
}
|
|
74
|
-
|
|
78
|
+
|
|
79
|
+
function issueSpecificity(node) {
|
|
75
80
|
if (node.isError) {
|
|
76
81
|
return 0;
|
|
77
82
|
}
|
|
@@ -81,6 +86,10 @@ function issuePriority(node) {
|
|
|
81
86
|
return 2;
|
|
82
87
|
}
|
|
83
88
|
|
|
89
|
+
function compareIssueRanges(left, right) {
|
|
90
|
+
return left.startIndex - right.startIndex || left.endIndex - right.endIndex;
|
|
91
|
+
}
|
|
92
|
+
|
|
84
93
|
function preferSpecificIssues(nodes) {
|
|
85
94
|
const issuesByRange = new Map();
|
|
86
95
|
|
|
@@ -89,31 +98,92 @@ function preferSpecificIssues(nodes) {
|
|
|
89
98
|
const existing = issuesByRange.get(key);
|
|
90
99
|
if (
|
|
91
100
|
existing === undefined ||
|
|
92
|
-
|
|
101
|
+
issueSpecificity(node) > issueSpecificity(existing)
|
|
93
102
|
) {
|
|
94
103
|
issuesByRange.set(key, node);
|
|
95
104
|
}
|
|
96
105
|
}
|
|
97
106
|
|
|
98
|
-
return [...issuesByRange.values()].sort(
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
107
|
+
return [...issuesByRange.values()].sort(compareIssueRanges);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function collapseInvalidFlags(nodes) {
|
|
111
|
+
const groupedByParent = new Map();
|
|
112
|
+
const issues = [];
|
|
113
|
+
|
|
114
|
+
for (const node of nodes) {
|
|
115
|
+
const parent = node.parent;
|
|
116
|
+
if (node.type !== "invalid_flag" || parent?.type !== "substitute_flags") {
|
|
117
|
+
issues.push(node);
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const key = `${parent.startIndex}:${parent.endIndex}`;
|
|
122
|
+
const group = groupedByParent.get(key);
|
|
123
|
+
if (group === undefined) {
|
|
124
|
+
groupedByParent.set(key, [node]);
|
|
125
|
+
} else {
|
|
126
|
+
group.push(node);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
for (const invalidFlags of groupedByParent.values()) {
|
|
131
|
+
if (invalidFlags.length === 1) {
|
|
132
|
+
issues.push(invalidFlags[0]);
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const first = invalidFlags[0];
|
|
137
|
+
const last = invalidFlags.at(-1);
|
|
138
|
+
issues.push({
|
|
139
|
+
type: "invalid_flags",
|
|
140
|
+
startIndex: first.startIndex,
|
|
141
|
+
endIndex: last.endIndex,
|
|
142
|
+
text: first.parent.text,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return issues.sort(compareIssueRanges);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function mergeAdjacentIssues(nodes) {
|
|
150
|
+
const merged = [];
|
|
151
|
+
|
|
152
|
+
for (const node of nodes) {
|
|
153
|
+
const previous = merged.at(-1);
|
|
154
|
+
if (previous === undefined || node.startIndex > previous.endIndex) {
|
|
155
|
+
merged.push(node);
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
merged[merged.length - 1] = {
|
|
160
|
+
type: previous.type,
|
|
161
|
+
text: previous.text,
|
|
162
|
+
isMissing: previous.isMissing,
|
|
163
|
+
startIndex: previous.startIndex,
|
|
164
|
+
endIndex: Math.max(previous.endIndex, node.endIndex),
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return merged;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function normalizeIssues(nodes) {
|
|
172
|
+
return mergeAdjacentIssues(collapseInvalidFlags(preferSpecificIssues(nodes)));
|
|
102
173
|
}
|
|
103
174
|
|
|
104
175
|
export function createDiagnostics(document, dialect) {
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
);
|
|
176
|
+
const rootNode = syntaxTreeFor(document, dialect).rootNode;
|
|
177
|
+
const issueNodes = normalizeIssues(collectSyntaxIssueNodes(rootNode));
|
|
178
|
+
|
|
179
|
+
return issueNodes.map((node) => {
|
|
180
|
+
const { code, message } = descriptionFor(node, dialect);
|
|
181
|
+
return {
|
|
182
|
+
severity: DiagnosticSeverity.Error,
|
|
183
|
+
range: rangeForNode(document, node),
|
|
184
|
+
message,
|
|
185
|
+
code,
|
|
186
|
+
source: diagnosticSource,
|
|
187
|
+
};
|
|
188
|
+
});
|
|
119
189
|
}
|
package/src/formatting.js
CHANGED
|
@@ -75,12 +75,15 @@ function formatScript(rootNode, source, options) {
|
|
|
75
75
|
|
|
76
76
|
export function createFormattingEdits(document, dialect, options) {
|
|
77
77
|
const source = document.getText();
|
|
78
|
-
const
|
|
79
|
-
if (collectSyntaxIssueNodes(
|
|
78
|
+
const rootNode = syntaxTreeFor(document, dialect).rootNode;
|
|
79
|
+
if (rootNode.hasError || collectSyntaxIssueNodes(rootNode).length > 0) {
|
|
80
80
|
return [];
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
const formatted = formatScript(
|
|
83
|
+
const formatted = formatScript(rootNode, source, options);
|
|
84
|
+
if (!source.startsWith("#n") && formatted.startsWith("#n")) {
|
|
85
|
+
return [];
|
|
86
|
+
}
|
|
84
87
|
if (formatted === source) {
|
|
85
88
|
return [];
|
|
86
89
|
}
|
package/src/server.js
CHANGED
|
@@ -3,10 +3,8 @@
|
|
|
3
3
|
import {
|
|
4
4
|
createConnection,
|
|
5
5
|
ErrorCodes,
|
|
6
|
-
MessageType,
|
|
7
6
|
ProposedFeatures,
|
|
8
7
|
ResponseError,
|
|
9
|
-
ShowMessageNotification,
|
|
10
8
|
TextDocumentSyncKind,
|
|
11
9
|
TextDocuments,
|
|
12
10
|
} from "vscode-languageserver/node";
|
|
@@ -46,19 +44,6 @@ function resolveDialect(options) {
|
|
|
46
44
|
return { dialect };
|
|
47
45
|
}
|
|
48
46
|
|
|
49
|
-
function configurationOptions(settings) {
|
|
50
|
-
if (
|
|
51
|
-
settings !== null &&
|
|
52
|
-
typeof settings === "object" &&
|
|
53
|
-
!Array.isArray(settings) &&
|
|
54
|
-
Object.hasOwn(settings, "sedLanguageServer")
|
|
55
|
-
) {
|
|
56
|
-
return settings.sedLanguageServer;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
return settings;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
47
|
function publishDiagnostics(document) {
|
|
63
48
|
return connection.sendDiagnostics({
|
|
64
49
|
uri: document.uri,
|
|
@@ -85,22 +70,6 @@ connection.onInitialize(({ initializationOptions }) => {
|
|
|
85
70
|
};
|
|
86
71
|
});
|
|
87
72
|
|
|
88
|
-
connection.onDidChangeConfiguration(async ({ settings }) => {
|
|
89
|
-
const result = resolveDialect(configurationOptions(settings));
|
|
90
|
-
if (result.error !== undefined) {
|
|
91
|
-
connection.sendNotification(ShowMessageNotification.type, {
|
|
92
|
-
type: MessageType.Error,
|
|
93
|
-
message: result.error,
|
|
94
|
-
});
|
|
95
|
-
return;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
activeDialect = result.dialect;
|
|
99
|
-
await Promise.all(
|
|
100
|
-
documents.all().map((document) => publishDiagnostics(document)),
|
|
101
|
-
);
|
|
102
|
-
});
|
|
103
|
-
|
|
104
73
|
connection.onDefinition(({ textDocument, position }) => {
|
|
105
74
|
const document = documents.get(textDocument.uri);
|
|
106
75
|
return document === undefined
|
package/src/syntax.js
CHANGED
|
@@ -98,7 +98,7 @@ export function collectSyntaxIssueNodes(rootNode) {
|
|
|
98
98
|
|
|
99
99
|
const specific = isSpecificSyntaxIssue(frame.node);
|
|
100
100
|
const selected =
|
|
101
|
-
specific || (frame.node.
|
|
101
|
+
specific || (frame.node.hasError && !frame.hasDescendantIssue);
|
|
102
102
|
if (specific) {
|
|
103
103
|
issues.length = frame.issueStartIndex;
|
|
104
104
|
}
|