@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 +50 -39
- package/package.json +8 -4
- package/src/definition.js +30 -32
- package/src/diagnostics.js +83 -1829
- package/src/formatting.js +97 -0
- package/src/server.js +56 -46
- package/src/syntax.js +124 -0
- package/vendor/tree-sitter-sed-gnu.wasm +0 -0
- package/vendor/tree-sitter-sed-posix.wasm +0 -0
- package/src/completion.js +0 -148
- package/src/document-structure.js +0 -774
- package/src/sed-syntax.js +0 -2321
- package/src/syntax-profile.js +0 -95
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { collectSyntaxIssueNodes, syntaxTreeFor } from "./syntax.js";
|
|
2
|
+
|
|
3
|
+
function formatCommandList(node, indentation, lineEnding, depth) {
|
|
4
|
+
return node.children
|
|
5
|
+
.filter((child) => child.type === "command")
|
|
6
|
+
.map((command) => formatCommand(command, indentation, lineEnding, depth))
|
|
7
|
+
.join(lineEnding);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function formatCommand(command, indentation, lineEnding, depth) {
|
|
11
|
+
const prefix = indentation.repeat(depth);
|
|
12
|
+
const body = command.childForFieldName("body");
|
|
13
|
+
if (body.type !== "block_command") {
|
|
14
|
+
return `${prefix}${command.text}`;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const openingBrace = body.childForFieldName("name");
|
|
18
|
+
const opening = command.text.slice(
|
|
19
|
+
0,
|
|
20
|
+
openingBrace.endIndex - command.startIndex,
|
|
21
|
+
);
|
|
22
|
+
const commandList = body.childForFieldName("argument");
|
|
23
|
+
if (commandList === null) {
|
|
24
|
+
return `${prefix}${opening}}`;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const formattedCommands = formatCommandList(
|
|
28
|
+
commandList,
|
|
29
|
+
indentation,
|
|
30
|
+
lineEnding,
|
|
31
|
+
depth + 1,
|
|
32
|
+
);
|
|
33
|
+
const content =
|
|
34
|
+
formattedCommands === "" ? "" : `${formattedCommands}${lineEnding}`;
|
|
35
|
+
|
|
36
|
+
return `${prefix}${opening}${lineEnding}${content}${prefix}}`;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function indentationFor(options) {
|
|
40
|
+
return options.insertSpaces ? " ".repeat(options.tabSize) : "\t";
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function formatScript(rootNode, source, options) {
|
|
44
|
+
const lineEnding = source.match(/\r\n|\n/)?.[0] ?? "\n";
|
|
45
|
+
const indentation = indentationFor(options);
|
|
46
|
+
const firstLine = rootNode.children.find(
|
|
47
|
+
(child) => child.type === "first_line_silent",
|
|
48
|
+
);
|
|
49
|
+
const commandList = rootNode.children.find(
|
|
50
|
+
(child) => child.type === "command_list",
|
|
51
|
+
);
|
|
52
|
+
const sections = [];
|
|
53
|
+
|
|
54
|
+
if (firstLine !== undefined) {
|
|
55
|
+
sections.push(firstLine.text);
|
|
56
|
+
}
|
|
57
|
+
if (commandList !== undefined) {
|
|
58
|
+
const formattedCommands = formatCommandList(
|
|
59
|
+
commandList,
|
|
60
|
+
indentation,
|
|
61
|
+
lineEnding,
|
|
62
|
+
0,
|
|
63
|
+
);
|
|
64
|
+
if (formattedCommands !== "") {
|
|
65
|
+
sections.push(formattedCommands);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
let formatted = sections.join(lineEnding);
|
|
70
|
+
if (source.endsWith("\n") && !formatted.endsWith("\n")) {
|
|
71
|
+
formatted += lineEnding;
|
|
72
|
+
}
|
|
73
|
+
return formatted;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function createFormattingEdits(document, dialect, options) {
|
|
77
|
+
const source = document.getText();
|
|
78
|
+
const tree = syntaxTreeFor(document, dialect);
|
|
79
|
+
if (collectSyntaxIssueNodes(tree.rootNode).length > 0) {
|
|
80
|
+
return [];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const formatted = formatScript(tree.rootNode, source, options);
|
|
84
|
+
if (formatted === source) {
|
|
85
|
+
return [];
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return [
|
|
89
|
+
{
|
|
90
|
+
range: {
|
|
91
|
+
start: document.positionAt(0),
|
|
92
|
+
end: document.positionAt(source.length),
|
|
93
|
+
},
|
|
94
|
+
newText: formatted,
|
|
95
|
+
},
|
|
96
|
+
];
|
|
97
|
+
}
|
package/src/server.js
CHANGED
|
@@ -11,17 +11,10 @@ import {
|
|
|
11
11
|
TextDocuments,
|
|
12
12
|
} from "vscode-languageserver/node";
|
|
13
13
|
import { TextDocument } from "vscode-languageserver-textdocument";
|
|
14
|
-
import {
|
|
15
|
-
completionProviderOptions,
|
|
16
|
-
createCompletionHandler,
|
|
17
|
-
} from "./completion.js";
|
|
18
|
-
import { createDefinitionHandler, definitionProvider } from "./definition.js";
|
|
14
|
+
import { createDefinitionLocations } from "./definition.js";
|
|
19
15
|
import { createDiagnostics } from "./diagnostics.js";
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
defaultSyntaxProfile,
|
|
23
|
-
resolveSyntaxProfile,
|
|
24
|
-
} from "./syntax-profile.js";
|
|
16
|
+
import { createFormattingEdits } from "./formatting.js";
|
|
17
|
+
import { invalidateSyntaxTreeCache } from "./syntax.js";
|
|
25
18
|
|
|
26
19
|
if (process.argv.length === 2) {
|
|
27
20
|
process.argv.push("--stdio");
|
|
@@ -29,19 +22,31 @@ if (process.argv.length === 2) {
|
|
|
29
22
|
|
|
30
23
|
const connection = createConnection(ProposedFeatures.all);
|
|
31
24
|
const documents = new TextDocuments(TextDocument);
|
|
32
|
-
|
|
25
|
+
const defaultDialect = "posix";
|
|
26
|
+
let activeDialect = defaultDialect;
|
|
27
|
+
|
|
28
|
+
function resolveDialect(options) {
|
|
29
|
+
if (options === undefined || options === null) {
|
|
30
|
+
return { dialect: defaultDialect };
|
|
31
|
+
}
|
|
32
|
+
if (typeof options !== "object" || Array.isArray(options)) {
|
|
33
|
+
return {
|
|
34
|
+
error: "Syntax options must be provided as an object or null.",
|
|
35
|
+
};
|
|
36
|
+
}
|
|
33
37
|
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
38
|
+
const dialect =
|
|
39
|
+
options.dialect === undefined ? defaultDialect : options.dialect;
|
|
40
|
+
if (dialect !== "posix" && dialect !== "gnu") {
|
|
41
|
+
return {
|
|
42
|
+
error: 'The syntax dialect must be either "posix" or "gnu".',
|
|
43
|
+
};
|
|
44
|
+
}
|
|
39
45
|
|
|
40
|
-
|
|
41
|
-
return errors.map(({ message }) => message).join(" ");
|
|
46
|
+
return { dialect };
|
|
42
47
|
}
|
|
43
48
|
|
|
44
|
-
function
|
|
49
|
+
function configurationOptions(settings) {
|
|
45
50
|
if (
|
|
46
51
|
settings !== null &&
|
|
47
52
|
typeof settings === "object" &&
|
|
@@ -54,63 +59,68 @@ function configurationProfileOptions(settings) {
|
|
|
54
59
|
return settings;
|
|
55
60
|
}
|
|
56
61
|
|
|
57
|
-
function publishDiagnostics(document
|
|
62
|
+
function publishDiagnostics(document) {
|
|
58
63
|
return connection.sendDiagnostics({
|
|
59
64
|
uri: document.uri,
|
|
60
65
|
version: document.version,
|
|
61
|
-
diagnostics: createDiagnostics(document,
|
|
66
|
+
diagnostics: createDiagnostics(document, activeDialect),
|
|
62
67
|
});
|
|
63
68
|
}
|
|
64
69
|
|
|
65
70
|
connection.onInitialize(({ initializationOptions }) => {
|
|
66
|
-
const result =
|
|
67
|
-
if (
|
|
68
|
-
return new ResponseError(
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
{ retry: false },
|
|
72
|
-
);
|
|
71
|
+
const result = resolveDialect(initializationOptions);
|
|
72
|
+
if (result.error !== undefined) {
|
|
73
|
+
return new ResponseError(ErrorCodes.InvalidParams, result.error, {
|
|
74
|
+
retry: false,
|
|
75
|
+
});
|
|
73
76
|
}
|
|
74
77
|
|
|
75
|
-
|
|
78
|
+
activeDialect = result.dialect;
|
|
76
79
|
return {
|
|
77
|
-
capabilities:
|
|
80
|
+
capabilities: {
|
|
81
|
+
textDocumentSync: TextDocumentSyncKind.Incremental,
|
|
82
|
+
definitionProvider: true,
|
|
83
|
+
documentFormattingProvider: true,
|
|
84
|
+
},
|
|
78
85
|
};
|
|
79
86
|
});
|
|
80
87
|
|
|
81
88
|
connection.onDidChangeConfiguration(async ({ settings }) => {
|
|
82
|
-
const result =
|
|
83
|
-
if (
|
|
89
|
+
const result = resolveDialect(configurationOptions(settings));
|
|
90
|
+
if (result.error !== undefined) {
|
|
84
91
|
connection.sendNotification(ShowMessageNotification.type, {
|
|
85
92
|
type: MessageType.Error,
|
|
86
|
-
message:
|
|
93
|
+
message: result.error,
|
|
87
94
|
});
|
|
88
95
|
return;
|
|
89
96
|
}
|
|
90
97
|
|
|
91
|
-
|
|
92
|
-
activeSyntaxProfile = nextSyntaxProfile;
|
|
93
|
-
invalidateDocumentStructureCache();
|
|
98
|
+
activeDialect = result.dialect;
|
|
94
99
|
await Promise.all(
|
|
95
|
-
documents
|
|
96
|
-
.all()
|
|
97
|
-
.map((document) => publishDiagnostics(document, nextSyntaxProfile)),
|
|
100
|
+
documents.all().map((document) => publishDiagnostics(document)),
|
|
98
101
|
);
|
|
99
102
|
});
|
|
100
103
|
|
|
101
|
-
connection.
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
);
|
|
104
|
+
connection.onDefinition(({ textDocument, position }) => {
|
|
105
|
+
const document = documents.get(textDocument.uri);
|
|
106
|
+
return document === undefined
|
|
107
|
+
? null
|
|
108
|
+
: createDefinitionLocations(document, position, activeDialect);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
connection.onDocumentFormatting(({ textDocument, options }) => {
|
|
112
|
+
const document = documents.get(textDocument.uri);
|
|
113
|
+
return document === undefined
|
|
114
|
+
? []
|
|
115
|
+
: createFormattingEdits(document, activeDialect, options);
|
|
116
|
+
});
|
|
107
117
|
|
|
108
118
|
documents.onDidChangeContent(({ document }) => {
|
|
109
119
|
publishDiagnostics(document);
|
|
110
120
|
});
|
|
111
121
|
|
|
112
122
|
documents.onDidClose(({ document }) => {
|
|
113
|
-
|
|
123
|
+
invalidateSyntaxTreeCache(document);
|
|
114
124
|
connection.sendDiagnostics({ uri: document.uri, diagnostics: [] });
|
|
115
125
|
});
|
|
116
126
|
|
package/src/syntax.js
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { fileURLToPath } from "node:url";
|
|
2
|
+
import { Language, Parser } from "web-tree-sitter";
|
|
3
|
+
|
|
4
|
+
await Parser.init();
|
|
5
|
+
|
|
6
|
+
async function createParser(dialect) {
|
|
7
|
+
const path = fileURLToPath(
|
|
8
|
+
new URL(`../vendor/tree-sitter-sed-${dialect}.wasm`, import.meta.url),
|
|
9
|
+
);
|
|
10
|
+
return new Parser().setLanguage(await Language.load(path));
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const parsers = {
|
|
14
|
+
posix: await createParser("posix"),
|
|
15
|
+
gnu: await createParser("gnu"),
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const documentTrees = new Map();
|
|
19
|
+
|
|
20
|
+
export function syntaxTreeFor(document, dialect) {
|
|
21
|
+
const source = document.getText();
|
|
22
|
+
let trees = documentTrees.get(document.uri);
|
|
23
|
+
|
|
24
|
+
if (trees === undefined) {
|
|
25
|
+
trees = new Map();
|
|
26
|
+
documentTrees.set(document.uri, trees);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const cached = trees.get(dialect);
|
|
30
|
+
if (
|
|
31
|
+
cached !== undefined &&
|
|
32
|
+
cached.version === document.version &&
|
|
33
|
+
cached.source === source
|
|
34
|
+
) {
|
|
35
|
+
return cached.tree;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
cached?.tree.delete();
|
|
39
|
+
const tree = parsers[dialect].parse(source);
|
|
40
|
+
if (tree === null) {
|
|
41
|
+
throw new Error(`Failed to parse ${dialect} sed source.`);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
trees.set(dialect, {
|
|
45
|
+
source,
|
|
46
|
+
tree,
|
|
47
|
+
version: document.version,
|
|
48
|
+
});
|
|
49
|
+
return tree;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function invalidateSyntaxTreeCache(document) {
|
|
53
|
+
const trees = documentTrees.get(document.uri);
|
|
54
|
+
if (trees !== undefined) {
|
|
55
|
+
for (const { tree } of trees.values()) {
|
|
56
|
+
tree.delete();
|
|
57
|
+
}
|
|
58
|
+
documentTrees.delete(document.uri);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function isSpecificSyntaxIssue(node) {
|
|
63
|
+
return (
|
|
64
|
+
node.isMissing ||
|
|
65
|
+
node.type.startsWith("incomplete_") ||
|
|
66
|
+
node.type.startsWith("invalid_") ||
|
|
67
|
+
node.type === "unclosed_bracket" ||
|
|
68
|
+
node.type === "unexpected_text"
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function collectSyntaxIssueNodes(rootNode) {
|
|
73
|
+
const issues = [];
|
|
74
|
+
const stack = [
|
|
75
|
+
{
|
|
76
|
+
node: rootNode,
|
|
77
|
+
children: rootNode.children,
|
|
78
|
+
childIndex: 0,
|
|
79
|
+
hasDescendantIssue: false,
|
|
80
|
+
issueStartIndex: 0,
|
|
81
|
+
},
|
|
82
|
+
];
|
|
83
|
+
|
|
84
|
+
while (stack.length > 0) {
|
|
85
|
+
const frame = stack.at(-1);
|
|
86
|
+
if (frame.childIndex < frame.children.length) {
|
|
87
|
+
const child = frame.children[frame.childIndex];
|
|
88
|
+
frame.childIndex += 1;
|
|
89
|
+
stack.push({
|
|
90
|
+
node: child,
|
|
91
|
+
children: child.children,
|
|
92
|
+
childIndex: 0,
|
|
93
|
+
hasDescendantIssue: false,
|
|
94
|
+
issueStartIndex: issues.length,
|
|
95
|
+
});
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const specific = isSpecificSyntaxIssue(frame.node);
|
|
100
|
+
const selected =
|
|
101
|
+
specific || (frame.node.isError && !frame.hasDescendantIssue);
|
|
102
|
+
if (specific) {
|
|
103
|
+
issues.length = frame.issueStartIndex;
|
|
104
|
+
}
|
|
105
|
+
if (selected) {
|
|
106
|
+
issues.push(frame.node);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
stack.pop();
|
|
110
|
+
const parent = stack.at(-1);
|
|
111
|
+
if (parent !== undefined) {
|
|
112
|
+
parent.hasDescendantIssue ||= frame.hasDescendantIssue || selected;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return issues;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function rangeForNode(document, node) {
|
|
120
|
+
return {
|
|
121
|
+
start: document.positionAt(node.startIndex),
|
|
122
|
+
end: document.positionAt(node.endIndex),
|
|
123
|
+
};
|
|
124
|
+
}
|
|
Binary file
|
|
Binary file
|
package/src/completion.js
DELETED
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
CompletionItemKind,
|
|
3
|
-
InsertTextFormat,
|
|
4
|
-
} from "vscode-languageserver/node";
|
|
5
|
-
import { getDocumentStructure } from "./document-structure.js";
|
|
6
|
-
import {
|
|
7
|
-
commandSpecificationsFor,
|
|
8
|
-
substituteFlagSpecificationsFor,
|
|
9
|
-
} from "./sed-syntax.js";
|
|
10
|
-
import {
|
|
11
|
-
defaultSyntaxProfile,
|
|
12
|
-
requireSyntaxProfile,
|
|
13
|
-
} from "./syntax-profile.js";
|
|
14
|
-
|
|
15
|
-
function plainCompletion(label, kind, documentation) {
|
|
16
|
-
return {
|
|
17
|
-
label,
|
|
18
|
-
kind,
|
|
19
|
-
insertText: label,
|
|
20
|
-
insertTextFormat: InsertTextFormat.PlainText,
|
|
21
|
-
documentation,
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const commandCompletionsByProfile = new Map();
|
|
26
|
-
const substituteFlagCompletionsByProfile = new Map();
|
|
27
|
-
|
|
28
|
-
function commandCompletionsFor(syntaxProfile) {
|
|
29
|
-
let completions = commandCompletionsByProfile.get(syntaxProfile);
|
|
30
|
-
if (completions !== undefined) {
|
|
31
|
-
return completions;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
completions = [
|
|
35
|
-
plainCompletion(
|
|
36
|
-
";",
|
|
37
|
-
CompletionItemKind.Keyword,
|
|
38
|
-
"Insert an empty command and begin the next command.",
|
|
39
|
-
),
|
|
40
|
-
...commandSpecificationsFor(syntaxProfile).map(
|
|
41
|
-
({ command, documentation }) =>
|
|
42
|
-
plainCompletion(command, CompletionItemKind.Keyword, documentation),
|
|
43
|
-
),
|
|
44
|
-
];
|
|
45
|
-
commandCompletionsByProfile.set(syntaxProfile, completions);
|
|
46
|
-
return completions;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function substituteFlagCompletionsFor(syntaxProfile) {
|
|
50
|
-
let completions = substituteFlagCompletionsByProfile.get(syntaxProfile);
|
|
51
|
-
if (completions !== undefined) {
|
|
52
|
-
return completions;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
completions = [
|
|
56
|
-
...Array.from({ length: 9 }, (_, index) => {
|
|
57
|
-
const occurrence = String(index + 1);
|
|
58
|
-
return plainCompletion(
|
|
59
|
-
occurrence,
|
|
60
|
-
CompletionItemKind.Value,
|
|
61
|
-
`Replace only occurrence number ${occurrence}.`,
|
|
62
|
-
);
|
|
63
|
-
}),
|
|
64
|
-
...substituteFlagSpecificationsFor(syntaxProfile).map(
|
|
65
|
-
({ flag, documentation }) =>
|
|
66
|
-
plainCompletion(flag, CompletionItemKind.Keyword, documentation),
|
|
67
|
-
),
|
|
68
|
-
];
|
|
69
|
-
substituteFlagCompletionsByProfile.set(syntaxProfile, completions);
|
|
70
|
-
return completions;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
export const completionProviderOptions = Object.freeze({});
|
|
74
|
-
|
|
75
|
-
function branchLabelEditRange(document, context, offset) {
|
|
76
|
-
const replacementRange = context.replacementRange ?? {
|
|
77
|
-
startOffset: offset,
|
|
78
|
-
endOffset: offset,
|
|
79
|
-
};
|
|
80
|
-
const startOffset = Math.min(replacementRange.startOffset, offset);
|
|
81
|
-
const endOffset = Math.max(replacementRange.endOffset, offset);
|
|
82
|
-
|
|
83
|
-
return {
|
|
84
|
-
start: document.positionAt(startOffset),
|
|
85
|
-
end: document.positionAt(endOffset),
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
export function createCompletions(
|
|
90
|
-
document,
|
|
91
|
-
position,
|
|
92
|
-
options = defaultSyntaxProfile,
|
|
93
|
-
) {
|
|
94
|
-
const syntaxProfile = requireSyntaxProfile(options);
|
|
95
|
-
const structure = getDocumentStructure(document, syntaxProfile);
|
|
96
|
-
const offset = document.offsetAt(position);
|
|
97
|
-
const context = structure.contextDetailsAt(offset);
|
|
98
|
-
|
|
99
|
-
if (context?.kind === "command") {
|
|
100
|
-
return commandCompletionsFor(syntaxProfile).map((completion) => ({
|
|
101
|
-
...completion,
|
|
102
|
-
}));
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
if (context?.kind === "substitute-flag") {
|
|
106
|
-
return substituteFlagCompletionsFor(syntaxProfile).map((completion) => ({
|
|
107
|
-
...completion,
|
|
108
|
-
}));
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
if (context?.kind !== "branch-label") {
|
|
112
|
-
return [];
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
const names = new Set();
|
|
116
|
-
const completions = [];
|
|
117
|
-
const editRange = branchLabelEditRange(document, context, offset);
|
|
118
|
-
for (const definition of structure.labelDefinitions) {
|
|
119
|
-
if (names.has(definition.name)) {
|
|
120
|
-
continue;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
names.add(definition.name);
|
|
124
|
-
completions.push({
|
|
125
|
-
label: definition.name,
|
|
126
|
-
kind: CompletionItemKind.Reference,
|
|
127
|
-
insertTextFormat: InsertTextFormat.PlainText,
|
|
128
|
-
documentation: "Branch label defined in this document.",
|
|
129
|
-
textEdit: {
|
|
130
|
-
range: editRange,
|
|
131
|
-
newText: definition.name,
|
|
132
|
-
},
|
|
133
|
-
});
|
|
134
|
-
}
|
|
135
|
-
return completions;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
export function createCompletionHandler(
|
|
139
|
-
documents,
|
|
140
|
-
getSyntaxProfile = () => defaultSyntaxProfile,
|
|
141
|
-
) {
|
|
142
|
-
return ({ textDocument, position }) => {
|
|
143
|
-
const document = documents.get(textDocument.uri);
|
|
144
|
-
return document === undefined
|
|
145
|
-
? null
|
|
146
|
-
: createCompletions(document, position, getSyntaxProfile());
|
|
147
|
-
};
|
|
148
|
-
}
|