@pellux/goodvibes-sdk 0.25.0 → 0.25.2
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/dist/_internal/daemon/otlp-protobuf.d.ts +3 -0
- package/dist/_internal/daemon/otlp-protobuf.d.ts.map +1 -0
- package/dist/_internal/daemon/otlp-protobuf.js +968 -0
- package/dist/_internal/daemon/telemetry-routes.d.ts.map +1 -1
- package/dist/_internal/daemon/telemetry-routes.js +22 -13
- package/dist/_internal/platform/intelligence/lsp/service.d.ts.map +1 -1
- package/dist/_internal/platform/intelligence/lsp/service.js +18 -9
- package/dist/_internal/platform/providers/anthropic-vertex.d.ts.map +1 -1
- package/dist/_internal/platform/providers/anthropic-vertex.js +135 -2
- package/dist/_internal/platform/version.js +1 -1
- package/dist/_internal/platform/watchers/registry.d.ts.map +1 -1
- package/dist/_internal/platform/watchers/registry.js +4 -1
- package/package.json +9 -8
- package/vendor/bash-language-server/CHANGELOG.md +453 -0
- package/vendor/bash-language-server/GOODVIBES_PATCH.md +22 -0
- package/vendor/bash-language-server/README.md +230 -0
- package/vendor/bash-language-server/out/analyser.d.ts +187 -0
- package/vendor/bash-language-server/out/analyser.js +782 -0
- package/vendor/bash-language-server/out/analyser.js.map +1 -0
- package/vendor/bash-language-server/out/builtins.d.ts +2 -0
- package/vendor/bash-language-server/out/builtins.js +71 -0
- package/vendor/bash-language-server/out/builtins.js.map +1 -0
- package/vendor/bash-language-server/out/cli.d.ts +3 -0
- package/vendor/bash-language-server/out/cli.js +74 -0
- package/vendor/bash-language-server/out/cli.js.map +1 -0
- package/vendor/bash-language-server/out/config.d.ts +88 -0
- package/vendor/bash-language-server/out/config.js +96 -0
- package/vendor/bash-language-server/out/config.js.map +1 -0
- package/vendor/bash-language-server/out/executables.d.ts +19 -0
- package/vendor/bash-language-server/out/executables.js +86 -0
- package/vendor/bash-language-server/out/executables.js.map +1 -0
- package/vendor/bash-language-server/out/parser.d.ts +2 -0
- package/vendor/bash-language-server/out/parser.js +36 -0
- package/vendor/bash-language-server/out/parser.js.map +1 -0
- package/vendor/bash-language-server/out/reserved-words.d.ts +2 -0
- package/vendor/bash-language-server/out/reserved-words.js +33 -0
- package/vendor/bash-language-server/out/reserved-words.js.map +1 -0
- package/vendor/bash-language-server/out/server.d.ts +56 -0
- package/vendor/bash-language-server/out/server.js +756 -0
- package/vendor/bash-language-server/out/server.js.map +1 -0
- package/vendor/bash-language-server/out/shellcheck/config.d.ts +5 -0
- package/vendor/bash-language-server/out/shellcheck/config.js +17 -0
- package/vendor/bash-language-server/out/shellcheck/config.js.map +1 -0
- package/vendor/bash-language-server/out/shellcheck/directive.d.ts +18 -0
- package/vendor/bash-language-server/out/shellcheck/directive.js +62 -0
- package/vendor/bash-language-server/out/shellcheck/directive.js.map +1 -0
- package/vendor/bash-language-server/out/shellcheck/index.d.ts +22 -0
- package/vendor/bash-language-server/out/shellcheck/index.js +229 -0
- package/vendor/bash-language-server/out/shellcheck/index.js.map +1 -0
- package/vendor/bash-language-server/out/shellcheck/types.d.ts +175 -0
- package/vendor/bash-language-server/out/shellcheck/types.js +35 -0
- package/vendor/bash-language-server/out/shellcheck/types.js.map +1 -0
- package/vendor/bash-language-server/out/shfmt/index.d.ts +18 -0
- package/vendor/bash-language-server/out/shfmt/index.js +151 -0
- package/vendor/bash-language-server/out/shfmt/index.js.map +1 -0
- package/vendor/bash-language-server/out/snippets.d.ts +2 -0
- package/vendor/bash-language-server/out/snippets.js +671 -0
- package/vendor/bash-language-server/out/snippets.js.map +1 -0
- package/vendor/bash-language-server/out/types.d.ts +13 -0
- package/vendor/bash-language-server/out/types.js +12 -0
- package/vendor/bash-language-server/out/types.js.map +1 -0
- package/vendor/bash-language-server/out/util/array.d.ts +14 -0
- package/vendor/bash-language-server/out/util/array.js +36 -0
- package/vendor/bash-language-server/out/util/array.js.map +1 -0
- package/vendor/bash-language-server/out/util/async.d.ts +13 -0
- package/vendor/bash-language-server/out/util/async.js +24 -0
- package/vendor/bash-language-server/out/util/async.js.map +1 -0
- package/vendor/bash-language-server/out/util/declarations.d.ts +88 -0
- package/vendor/bash-language-server/out/util/declarations.js +295 -0
- package/vendor/bash-language-server/out/util/declarations.js.map +1 -0
- package/vendor/bash-language-server/out/util/discriminate.d.ts +1 -0
- package/vendor/bash-language-server/out/util/discriminate.js +7 -0
- package/vendor/bash-language-server/out/util/discriminate.js.map +1 -0
- package/vendor/bash-language-server/out/util/fs.d.ts +6 -0
- package/vendor/bash-language-server/out/util/fs.js +73 -0
- package/vendor/bash-language-server/out/util/fs.js.map +1 -0
- package/vendor/bash-language-server/out/util/logger.d.ts +35 -0
- package/vendor/bash-language-server/out/util/logger.js +105 -0
- package/vendor/bash-language-server/out/util/logger.js.map +1 -0
- package/vendor/bash-language-server/out/util/lsp.d.ts +5 -0
- package/vendor/bash-language-server/out/util/lsp.js +13 -0
- package/vendor/bash-language-server/out/util/lsp.js.map +1 -0
- package/vendor/bash-language-server/out/util/platform.d.ts +1 -0
- package/vendor/bash-language-server/out/util/platform.js +7 -0
- package/vendor/bash-language-server/out/util/platform.js.map +1 -0
- package/vendor/bash-language-server/out/util/sh.d.ts +16 -0
- package/vendor/bash-language-server/out/util/sh.js +132 -0
- package/vendor/bash-language-server/out/util/sh.js.map +1 -0
- package/vendor/bash-language-server/out/util/shebang.d.ts +10 -0
- package/vendor/bash-language-server/out/util/shebang.js +53 -0
- package/vendor/bash-language-server/out/util/shebang.js.map +1 -0
- package/vendor/bash-language-server/out/util/sourcing.d.ts +15 -0
- package/vendor/bash-language-server/out/util/sourcing.js +182 -0
- package/vendor/bash-language-server/out/util/sourcing.js.map +1 -0
- package/vendor/bash-language-server/out/util/tree-sitter.d.ts +22 -0
- package/vendor/bash-language-server/out/util/tree-sitter.js +110 -0
- package/vendor/bash-language-server/out/util/tree-sitter.js.map +1 -0
- package/vendor/bash-language-server/package.json +52 -0
- package/vendor/bash-language-server/parser.info +2 -0
- package/vendor/bash-language-server/tree-sitter-bash.wasm +0 -0
- package/dist/_internal/platform/runtime/contracts/index.d.ts +0 -40
- package/dist/_internal/platform/runtime/contracts/index.d.ts.map +0 -1
- package/dist/_internal/platform/runtime/contracts/index.js +0 -133
- package/dist/_internal/platform/runtime/contracts/migrations/index.d.ts +0 -75
- package/dist/_internal/platform/runtime/contracts/migrations/index.d.ts.map +0 -1
- package/dist/_internal/platform/runtime/contracts/migrations/index.js +0 -158
- package/dist/_internal/platform/runtime/contracts/migrations/schemas.d.ts +0 -57
- package/dist/_internal/platform/runtime/contracts/migrations/schemas.d.ts.map +0 -1
- package/dist/_internal/platform/runtime/contracts/migrations/schemas.js +0 -157
- package/dist/_internal/platform/runtime/contracts/types.d.ts +0 -123
- package/dist/_internal/platform/runtime/contracts/types.d.ts.map +0 -1
- package/dist/_internal/platform/runtime/contracts/types.js +0 -41
- package/dist/_internal/platform/runtime/contracts/validators/event-envelope.d.ts +0 -24
- package/dist/_internal/platform/runtime/contracts/validators/event-envelope.d.ts.map +0 -1
- package/dist/_internal/platform/runtime/contracts/validators/event-envelope.js +0 -104
- package/dist/_internal/platform/runtime/contracts/validators/index.d.ts +0 -11
- package/dist/_internal/platform/runtime/contracts/validators/index.d.ts.map +0 -1
- package/dist/_internal/platform/runtime/contracts/validators/index.js +0 -10
- package/dist/_internal/platform/runtime/contracts/validators/runtime-state.d.ts +0 -23
- package/dist/_internal/platform/runtime/contracts/validators/runtime-state.d.ts.map +0 -1
- package/dist/_internal/platform/runtime/contracts/validators/runtime-state.js +0 -101
- package/dist/_internal/platform/runtime/contracts/validators/session.d.ts +0 -24
- package/dist/_internal/platform/runtime/contracts/validators/session.d.ts.map +0 -1
- package/dist/_internal/platform/runtime/contracts/validators/session.js +0 -103
- package/dist/_internal/platform/runtime/contracts/version.d.ts +0 -84
- package/dist/_internal/platform/runtime/contracts/version.d.ts.map +0 -1
- package/dist/_internal/platform/runtime/contracts/version.js +0 -41
- package/scripts/postinstall-patch-minimatch.mjs +0 -285
|
@@ -0,0 +1,782 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
const fs = require("fs");
|
|
13
|
+
const FuzzySearch = require("fuzzy-search");
|
|
14
|
+
const node_fetch_1 = require("node-fetch");
|
|
15
|
+
const url = require("url");
|
|
16
|
+
const util_1 = require("util");
|
|
17
|
+
const LSP = require("vscode-languageserver/node");
|
|
18
|
+
const vscode_languageserver_textdocument_1 = require("vscode-languageserver-textdocument");
|
|
19
|
+
const array_1 = require("./util/array");
|
|
20
|
+
const declarations_1 = require("./util/declarations");
|
|
21
|
+
const fs_1 = require("./util/fs");
|
|
22
|
+
const logger_1 = require("./util/logger");
|
|
23
|
+
const lsp_1 = require("./util/lsp");
|
|
24
|
+
const shebang_1 = require("./util/shebang");
|
|
25
|
+
const sourcing = require("./util/sourcing");
|
|
26
|
+
const TreeSitterUtil = require("./util/tree-sitter");
|
|
27
|
+
/**
|
|
28
|
+
* The Analyzer uses the Abstract Syntax Trees (ASTs) that are provided by
|
|
29
|
+
* tree-sitter to find definitions, reference, etc.
|
|
30
|
+
*/
|
|
31
|
+
class Analyzer {
|
|
32
|
+
constructor({ enableSourceErrorDiagnostics = false, includeAllWorkspaceSymbols = false, parser, workspaceFolder, }) {
|
|
33
|
+
this.uriToAnalyzedDocument = {};
|
|
34
|
+
this.enableSourceErrorDiagnostics = enableSourceErrorDiagnostics;
|
|
35
|
+
this.includeAllWorkspaceSymbols = includeAllWorkspaceSymbols;
|
|
36
|
+
this.parser = parser;
|
|
37
|
+
this.workspaceFolder = workspaceFolder;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Analyze the given document, cache the tree-sitter AST, and iterate over the
|
|
41
|
+
* tree to find declarations.
|
|
42
|
+
*/
|
|
43
|
+
analyze({ document, uri, // NOTE: we don't use document.uri to make testing easier
|
|
44
|
+
}) {
|
|
45
|
+
const diagnostics = [];
|
|
46
|
+
const fileContent = document.getText();
|
|
47
|
+
const tree = this.parser.parse(fileContent);
|
|
48
|
+
const globalDeclarations = (0, declarations_1.getGlobalDeclarations)({ tree, uri });
|
|
49
|
+
const sourceCommands = sourcing.getSourceCommands({
|
|
50
|
+
fileUri: uri,
|
|
51
|
+
rootPath: this.workspaceFolder,
|
|
52
|
+
tree,
|
|
53
|
+
});
|
|
54
|
+
const sourcedUris = new Set(sourceCommands
|
|
55
|
+
.map((sourceCommand) => sourceCommand.uri)
|
|
56
|
+
.filter((uri) => uri !== null));
|
|
57
|
+
this.uriToAnalyzedDocument[uri] = {
|
|
58
|
+
document,
|
|
59
|
+
globalDeclarations,
|
|
60
|
+
sourcedUris,
|
|
61
|
+
sourceCommands: sourceCommands.filter((sourceCommand) => !sourceCommand.error),
|
|
62
|
+
tree,
|
|
63
|
+
};
|
|
64
|
+
if (!this.includeAllWorkspaceSymbols) {
|
|
65
|
+
sourceCommands
|
|
66
|
+
.filter((sourceCommand) => sourceCommand.error)
|
|
67
|
+
.forEach((sourceCommand) => {
|
|
68
|
+
logger_1.logger.warn(`${uri} line ${sourceCommand.range.start.line}: ${sourceCommand.error}`);
|
|
69
|
+
if (this.enableSourceErrorDiagnostics) {
|
|
70
|
+
diagnostics.push(LSP.Diagnostic.create(sourceCommand.range, [
|
|
71
|
+
`Source command could not be analyzed: ${sourceCommand.error}.\n`,
|
|
72
|
+
'Consider adding a ShellCheck directive above this line to fix or ignore this:',
|
|
73
|
+
'# shellcheck source=/my-file.sh # specify the file to source',
|
|
74
|
+
'# shellcheck source-path=my_script_folder # specify the folder to search in',
|
|
75
|
+
'# shellcheck source=/dev/null # to ignore the error',
|
|
76
|
+
'',
|
|
77
|
+
'Disable this message by changing the configuration option "enableSourceErrorDiagnostics"',
|
|
78
|
+
].join('\n'), LSP.DiagnosticSeverity.Information, undefined, 'bash-language-server'));
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
if (tree.rootNode.hasError) {
|
|
83
|
+
logger_1.logger.warn(`Error while parsing ${uri}: syntax error`);
|
|
84
|
+
}
|
|
85
|
+
return diagnostics;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Initiates a background analysis of the files in the workspaceFolder to
|
|
89
|
+
* enable features across files.
|
|
90
|
+
*
|
|
91
|
+
* NOTE that when the source aware feature is enabled files are also parsed
|
|
92
|
+
* when they are found.
|
|
93
|
+
*/
|
|
94
|
+
initiateBackgroundAnalysis(_a) {
|
|
95
|
+
return __awaiter(this, arguments, void 0, function* ({ backgroundAnalysisMaxFiles, globPattern, }) {
|
|
96
|
+
const rootPath = this.workspaceFolder;
|
|
97
|
+
if (!rootPath) {
|
|
98
|
+
return { filesParsed: 0 };
|
|
99
|
+
}
|
|
100
|
+
if (backgroundAnalysisMaxFiles <= 0) {
|
|
101
|
+
logger_1.logger.info(`BackgroundAnalysis: skipping as backgroundAnalysisMaxFiles was 0...`);
|
|
102
|
+
return { filesParsed: 0 };
|
|
103
|
+
}
|
|
104
|
+
logger_1.logger.info(`BackgroundAnalysis: resolving glob "${globPattern}" inside "${rootPath}"...`);
|
|
105
|
+
const lookupStartTime = Date.now();
|
|
106
|
+
const getTimePassed = () => `${(Date.now() - lookupStartTime) / 1000} seconds`;
|
|
107
|
+
let filePaths = [];
|
|
108
|
+
try {
|
|
109
|
+
filePaths = yield (0, fs_1.getFilePaths)({
|
|
110
|
+
globPattern,
|
|
111
|
+
rootPath,
|
|
112
|
+
maxItems: backgroundAnalysisMaxFiles,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
const errorMessage = error instanceof Error ? error.message : error;
|
|
117
|
+
logger_1.logger.warn(`BackgroundAnalysis: failed resolved glob "${globPattern}". The experience across files will be degraded. Error: ${errorMessage}`);
|
|
118
|
+
return { filesParsed: 0 };
|
|
119
|
+
}
|
|
120
|
+
logger_1.logger.info(`BackgroundAnalysis: Glob resolved with ${filePaths.length} files after ${getTimePassed()}`);
|
|
121
|
+
for (const filePath of filePaths) {
|
|
122
|
+
const uri = url.pathToFileURL(filePath).href;
|
|
123
|
+
try {
|
|
124
|
+
const fileContent = yield fs.promises.readFile(filePath, 'utf8');
|
|
125
|
+
const { shebang, shellDialect } = (0, shebang_1.analyzeShebang)(fileContent);
|
|
126
|
+
if (shebang && !shellDialect) {
|
|
127
|
+
logger_1.logger.info(`BackgroundAnalysis: Skipping file ${uri} with shebang "${shebang}"`);
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
this.analyze({
|
|
131
|
+
document: vscode_languageserver_textdocument_1.TextDocument.create(uri, 'shell', 1, fileContent),
|
|
132
|
+
uri,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
const errorMessage = error instanceof Error ? error.message : error;
|
|
137
|
+
logger_1.logger.warn(`BackgroundAnalysis: Failed analyzing ${uri}. Error: ${errorMessage}`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
logger_1.logger.info(`BackgroundAnalysis: Completed after ${getTimePassed()}.`);
|
|
141
|
+
return {
|
|
142
|
+
filesParsed: filePaths.length,
|
|
143
|
+
};
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Find all the locations where the word was declared.
|
|
148
|
+
*/
|
|
149
|
+
findDeclarationLocations({ position, uri, word, }) {
|
|
150
|
+
var _a;
|
|
151
|
+
// If the word is sourced, return the location of the source file
|
|
152
|
+
const sourcedUri = (_a = this.uriToAnalyzedDocument[uri]) === null || _a === void 0 ? void 0 : _a.sourceCommands.filter((sourceCommand) => (0, lsp_1.isPositionIncludedInRange)(position, sourceCommand.range)).map((sourceCommand) => sourceCommand.uri)[0];
|
|
153
|
+
if (sourcedUri) {
|
|
154
|
+
return [LSP.Location.create(sourcedUri, LSP.Range.create(0, 0, 0, 0))];
|
|
155
|
+
}
|
|
156
|
+
return this.findDeclarationsMatchingWord({
|
|
157
|
+
exactMatch: true,
|
|
158
|
+
position,
|
|
159
|
+
uri,
|
|
160
|
+
word,
|
|
161
|
+
}).map((symbol) => symbol.location);
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Find all the declaration symbols in the workspace matching the query using fuzzy search.
|
|
165
|
+
*/
|
|
166
|
+
findDeclarationsWithFuzzySearch(query) {
|
|
167
|
+
const searcher = new FuzzySearch(this.getAllDeclarations(), ['name'], {
|
|
168
|
+
caseSensitive: true,
|
|
169
|
+
});
|
|
170
|
+
return searcher.search(query);
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Find declarations for the given word and position.
|
|
174
|
+
*/
|
|
175
|
+
findDeclarationsMatchingWord({ exactMatch, position, uri, word, }) {
|
|
176
|
+
return this.getAllDeclarations({ uri, position }).filter((symbol) => {
|
|
177
|
+
if (exactMatch) {
|
|
178
|
+
return symbol.name === word;
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
return symbol.name.startsWith(word);
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Find a symbol's original declaration and parent scope based on its original
|
|
187
|
+
* definition with respect to its scope.
|
|
188
|
+
*/
|
|
189
|
+
findOriginalDeclaration(params) {
|
|
190
|
+
var _a;
|
|
191
|
+
const node = this.nodeAtPoint(params.uri, params.position.line, params.position.character);
|
|
192
|
+
if (!node) {
|
|
193
|
+
return { declaration: null, parent: null };
|
|
194
|
+
}
|
|
195
|
+
const otherInfo = {
|
|
196
|
+
currentUri: params.uri,
|
|
197
|
+
boundary: params.position.line,
|
|
198
|
+
};
|
|
199
|
+
let parent = this.parentScope(node);
|
|
200
|
+
let declaration;
|
|
201
|
+
let continueSearching = false;
|
|
202
|
+
// Search for local declaration within parents
|
|
203
|
+
while (parent) {
|
|
204
|
+
if (params.kind === LSP.SymbolKind.Variable &&
|
|
205
|
+
parent.type === 'function_definition' &&
|
|
206
|
+
parent.lastChild) {
|
|
207
|
+
;
|
|
208
|
+
({ declaration, continueSearching } = (0, declarations_1.findDeclarationUsingLocalSemantics)({
|
|
209
|
+
baseNode: parent.lastChild,
|
|
210
|
+
symbolInfo: params,
|
|
211
|
+
otherInfo,
|
|
212
|
+
}));
|
|
213
|
+
}
|
|
214
|
+
else if (parent.type === 'subshell') {
|
|
215
|
+
;
|
|
216
|
+
({ declaration, continueSearching } = (0, declarations_1.findDeclarationUsingGlobalSemantics)({
|
|
217
|
+
baseNode: parent,
|
|
218
|
+
symbolInfo: params,
|
|
219
|
+
otherInfo,
|
|
220
|
+
}));
|
|
221
|
+
}
|
|
222
|
+
if (declaration && !continueSearching) {
|
|
223
|
+
break;
|
|
224
|
+
}
|
|
225
|
+
// Update boundary since any other instance within or below the current
|
|
226
|
+
// parent can now be considered local to that parent or out of scope.
|
|
227
|
+
otherInfo.boundary = parent.startPosition.row;
|
|
228
|
+
parent = this.parentScope(parent);
|
|
229
|
+
}
|
|
230
|
+
// Search for global declaration within files
|
|
231
|
+
if (!parent && (!declaration || continueSearching)) {
|
|
232
|
+
for (const uri of this.getOrderedReachableUris({ fromUri: params.uri })) {
|
|
233
|
+
const root = (_a = this.uriToAnalyzedDocument[uri]) === null || _a === void 0 ? void 0 : _a.tree.rootNode;
|
|
234
|
+
if (!root) {
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
otherInfo.currentUri = uri;
|
|
238
|
+
otherInfo.boundary =
|
|
239
|
+
uri === params.uri
|
|
240
|
+
? // Reset boundary so globally defined variables within any
|
|
241
|
+
// functions already searched can be found.
|
|
242
|
+
params.position.line
|
|
243
|
+
: // Set boundary to EOF since any position taken from the original
|
|
244
|
+
// URI/file does not apply to other URIs/files.
|
|
245
|
+
root.endPosition.row;
|
|
246
|
+
({ declaration, continueSearching } = (0, declarations_1.findDeclarationUsingGlobalSemantics)({
|
|
247
|
+
baseNode: root,
|
|
248
|
+
symbolInfo: params,
|
|
249
|
+
otherInfo,
|
|
250
|
+
}));
|
|
251
|
+
if (declaration && !continueSearching) {
|
|
252
|
+
break;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
return {
|
|
257
|
+
declaration: declaration
|
|
258
|
+
? LSP.Location.create(otherInfo.currentUri, TreeSitterUtil.range(declaration))
|
|
259
|
+
: null,
|
|
260
|
+
parent: parent
|
|
261
|
+
? LSP.Location.create(params.uri, TreeSitterUtil.range(parent))
|
|
262
|
+
: null,
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Find all the locations where the given word was defined or referenced.
|
|
267
|
+
* This will include commands, functions, variables, etc.
|
|
268
|
+
*
|
|
269
|
+
* It's currently not scope-aware, see findOccurrences.
|
|
270
|
+
*/
|
|
271
|
+
findReferences(word) {
|
|
272
|
+
const uris = Object.keys(this.uriToAnalyzedDocument);
|
|
273
|
+
return (0, array_1.flattenArray)(uris.map((uri) => this.findOccurrences(uri, word)));
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Find all occurrences of a word in the given file.
|
|
277
|
+
* It's currently not scope-aware.
|
|
278
|
+
*
|
|
279
|
+
* This will include commands, functions, variables, etc.
|
|
280
|
+
*
|
|
281
|
+
* It's currently not scope-aware, meaning references does include
|
|
282
|
+
* references to functions and variables that has the same name but
|
|
283
|
+
* are defined in different files.
|
|
284
|
+
*/
|
|
285
|
+
findOccurrences(uri, word) {
|
|
286
|
+
const analyzedDocument = this.uriToAnalyzedDocument[uri];
|
|
287
|
+
if (!analyzedDocument) {
|
|
288
|
+
return [];
|
|
289
|
+
}
|
|
290
|
+
const { tree } = analyzedDocument;
|
|
291
|
+
const locations = [];
|
|
292
|
+
TreeSitterUtil.forEach(tree.rootNode, (n) => {
|
|
293
|
+
let namedNode = null;
|
|
294
|
+
if (TreeSitterUtil.isReference(n)) {
|
|
295
|
+
// NOTE: a reference can be a command, variable, function, etc.
|
|
296
|
+
namedNode = n.firstNamedChild || n;
|
|
297
|
+
}
|
|
298
|
+
else if (TreeSitterUtil.isDefinition(n)) {
|
|
299
|
+
namedNode = n.firstNamedChild;
|
|
300
|
+
}
|
|
301
|
+
if (namedNode && namedNode.text === word) {
|
|
302
|
+
const range = TreeSitterUtil.range(namedNode);
|
|
303
|
+
const alreadyInLocations = locations.some((loc) => {
|
|
304
|
+
return (0, util_1.isDeepStrictEqual)(loc.range, range);
|
|
305
|
+
});
|
|
306
|
+
if (!alreadyInLocations) {
|
|
307
|
+
locations.push(LSP.Location.create(uri, range));
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
});
|
|
311
|
+
return locations;
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* A more scope-aware version of findOccurrences that differentiates between
|
|
315
|
+
* functions and variables.
|
|
316
|
+
*/
|
|
317
|
+
findOccurrencesWithin({ uri, word, kind, start, scope, }) {
|
|
318
|
+
var _a;
|
|
319
|
+
const scopeNode = scope
|
|
320
|
+
? this.nodeAtPoints(uri, { row: scope.start.line, column: scope.start.character }, { row: scope.end.line, column: scope.end.character })
|
|
321
|
+
: null;
|
|
322
|
+
const baseNode = scopeNode && (kind === LSP.SymbolKind.Variable || scopeNode.type === 'subshell')
|
|
323
|
+
? scopeNode
|
|
324
|
+
: (_a = this.uriToAnalyzedDocument[uri]) === null || _a === void 0 ? void 0 : _a.tree.rootNode;
|
|
325
|
+
if (!baseNode) {
|
|
326
|
+
return [];
|
|
327
|
+
}
|
|
328
|
+
const typeOfDescendants = kind === LSP.SymbolKind.Variable
|
|
329
|
+
? ['variable_name', 'word']
|
|
330
|
+
: ['function_definition', 'command_name'];
|
|
331
|
+
const startPosition = start
|
|
332
|
+
? { row: start.line, column: start.character }
|
|
333
|
+
: baseNode.startPosition;
|
|
334
|
+
const ignoredRanges = [];
|
|
335
|
+
const filterVariables = (n) => {
|
|
336
|
+
var _a;
|
|
337
|
+
if (n.text !== word ||
|
|
338
|
+
(n.type === 'word' && !TreeSitterUtil.isVariableInReadCommand(n))) {
|
|
339
|
+
return false;
|
|
340
|
+
}
|
|
341
|
+
const definition = TreeSitterUtil.findParentOfType(n, 'variable_assignment');
|
|
342
|
+
const definedVariable = definition === null || definition === void 0 ? void 0 : definition.descendantsOfType('variable_name').at(0);
|
|
343
|
+
// For self-assignment `var=$var` cases; this decides whether `$var` is an
|
|
344
|
+
// occurrence or not.
|
|
345
|
+
if ((definedVariable === null || definedVariable === void 0 ? void 0 : definedVariable.text) === word && !n.equals(definedVariable)) {
|
|
346
|
+
// `start.line` is assumed to be the same as the variable's original
|
|
347
|
+
// declaration line; handles cases where `$var` shouldn't be considered
|
|
348
|
+
// an occurrence.
|
|
349
|
+
if ((definition === null || definition === void 0 ? void 0 : definition.startPosition.row) === (start === null || start === void 0 ? void 0 : start.line)) {
|
|
350
|
+
return false;
|
|
351
|
+
}
|
|
352
|
+
// Returning true here is a good enough heuristic for most cases. It
|
|
353
|
+
// breaks down when redeclaration happens in multiple nested scopes,
|
|
354
|
+
// handling those more complex situations can be done later on if use
|
|
355
|
+
// cases arise.
|
|
356
|
+
return true;
|
|
357
|
+
}
|
|
358
|
+
const parent = this.parentScope(n);
|
|
359
|
+
if (!parent || baseNode.equals(parent)) {
|
|
360
|
+
return true;
|
|
361
|
+
}
|
|
362
|
+
const includeDeclaration = !ignoredRanges.some((r) => n.startPosition.row > r.start.line && n.endPosition.row < r.end.line);
|
|
363
|
+
const declarationCommand = TreeSitterUtil.findParentOfType(n, 'declaration_command');
|
|
364
|
+
const isLocal =
|
|
365
|
+
// Local `variable_name`s
|
|
366
|
+
(((definedVariable === null || definedVariable === void 0 ? void 0 : definedVariable.text) === word || !!(!definition && declarationCommand)) &&
|
|
367
|
+
(parent.type === 'subshell' ||
|
|
368
|
+
['local', 'declare', 'typeset'].includes((_a = declarationCommand === null || declarationCommand === void 0 ? void 0 : declarationCommand.firstChild) === null || _a === void 0 ? void 0 : _a.text))) ||
|
|
369
|
+
// Local variables within `read` command that are typed as `word`
|
|
370
|
+
(parent.type === 'subshell' && n.type === 'word');
|
|
371
|
+
if (isLocal) {
|
|
372
|
+
if (includeDeclaration) {
|
|
373
|
+
ignoredRanges.push(TreeSitterUtil.range(parent));
|
|
374
|
+
}
|
|
375
|
+
return false;
|
|
376
|
+
}
|
|
377
|
+
return includeDeclaration;
|
|
378
|
+
};
|
|
379
|
+
const filterFunctions = (n) => {
|
|
380
|
+
var _a;
|
|
381
|
+
const text = n.type === 'function_definition' ? (_a = n.firstNamedChild) === null || _a === void 0 ? void 0 : _a.text : n.text;
|
|
382
|
+
if (text !== word) {
|
|
383
|
+
return false;
|
|
384
|
+
}
|
|
385
|
+
const parentScope = TreeSitterUtil.findParentOfType(n, 'subshell');
|
|
386
|
+
if (!parentScope || baseNode.equals(parentScope)) {
|
|
387
|
+
return true;
|
|
388
|
+
}
|
|
389
|
+
const includeDeclaration = !ignoredRanges.some((r) => n.startPosition.row > r.start.line && n.endPosition.row < r.end.line);
|
|
390
|
+
if (n.type === 'function_definition') {
|
|
391
|
+
if (includeDeclaration) {
|
|
392
|
+
ignoredRanges.push(TreeSitterUtil.range(parentScope));
|
|
393
|
+
}
|
|
394
|
+
return false;
|
|
395
|
+
}
|
|
396
|
+
return includeDeclaration;
|
|
397
|
+
};
|
|
398
|
+
return baseNode
|
|
399
|
+
.descendantsOfType(typeOfDescendants, startPosition)
|
|
400
|
+
.filter(kind === LSP.SymbolKind.Variable ? filterVariables : filterFunctions)
|
|
401
|
+
.map((n) => {
|
|
402
|
+
if (n.type === 'function_definition' && n.firstNamedChild) {
|
|
403
|
+
return TreeSitterUtil.range(n.firstNamedChild);
|
|
404
|
+
}
|
|
405
|
+
return TreeSitterUtil.range(n);
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
getAllVariables({ position, uri, }) {
|
|
409
|
+
return this.getAllDeclarations({ uri, position }).filter((symbol) => symbol.kind === LSP.SymbolKind.Variable);
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* Get all symbol declarations in the given file. This is used for generating an outline.
|
|
413
|
+
*
|
|
414
|
+
* TODO: convert to DocumentSymbol[] which is a hierarchy of symbols found in a given text document.
|
|
415
|
+
*/
|
|
416
|
+
getDeclarationsForUri({ uri }) {
|
|
417
|
+
var _a;
|
|
418
|
+
const tree = (_a = this.uriToAnalyzedDocument[uri]) === null || _a === void 0 ? void 0 : _a.tree;
|
|
419
|
+
if (!(tree === null || tree === void 0 ? void 0 : tree.rootNode)) {
|
|
420
|
+
return [];
|
|
421
|
+
}
|
|
422
|
+
return (0, declarations_1.getAllDeclarationsInTree)({ uri, tree });
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
425
|
+
* Get the document for the given URI.
|
|
426
|
+
*/
|
|
427
|
+
getDocument(uri) {
|
|
428
|
+
var _a;
|
|
429
|
+
return (_a = this.uriToAnalyzedDocument[uri]) === null || _a === void 0 ? void 0 : _a.document;
|
|
430
|
+
}
|
|
431
|
+
// TODO: move somewhere else than the analyzer...
|
|
432
|
+
getExplainshellDocumentation(_a) {
|
|
433
|
+
return __awaiter(this, arguments, void 0, function* ({ params, endpoint, }) {
|
|
434
|
+
const analyzedDocument = this.uriToAnalyzedDocument[params.textDocument.uri];
|
|
435
|
+
const leafNode = analyzedDocument === null || analyzedDocument === void 0 ? void 0 : analyzedDocument.tree.rootNode.descendantForPosition({
|
|
436
|
+
row: params.position.line,
|
|
437
|
+
column: params.position.character,
|
|
438
|
+
});
|
|
439
|
+
if (!leafNode || !analyzedDocument) {
|
|
440
|
+
return {};
|
|
441
|
+
}
|
|
442
|
+
// explainshell needs the whole command, not just the "word" (tree-sitter
|
|
443
|
+
// parlance) that the user hovered over. A relatively successful heuristic
|
|
444
|
+
// is to simply go up one level in the AST. If you go up too far, you'll
|
|
445
|
+
// start to include newlines, and explainshell completely balks when it
|
|
446
|
+
// encounters newlines.
|
|
447
|
+
const interestingNode = leafNode.type === 'word' ? leafNode.parent : leafNode;
|
|
448
|
+
if (!interestingNode) {
|
|
449
|
+
return {};
|
|
450
|
+
}
|
|
451
|
+
const searchParams = new URLSearchParams({ cmd: interestingNode.text }).toString();
|
|
452
|
+
const url = `${endpoint}/explain?${searchParams}`;
|
|
453
|
+
const explainshellRawResponse = yield (0, node_fetch_1.default)(url);
|
|
454
|
+
const explainshellResponse = (yield explainshellRawResponse.json());
|
|
455
|
+
if (!explainshellRawResponse.ok) {
|
|
456
|
+
throw new Error(`HTTP request failed: ${url}`);
|
|
457
|
+
}
|
|
458
|
+
else if (!explainshellResponse.matches) {
|
|
459
|
+
return {};
|
|
460
|
+
}
|
|
461
|
+
else {
|
|
462
|
+
const offsetOfMousePointerInCommand = analyzedDocument.document.offsetAt(params.position) - interestingNode.startIndex;
|
|
463
|
+
const match = explainshellResponse.matches.find((helpItem) => helpItem.start <= offsetOfMousePointerInCommand &&
|
|
464
|
+
offsetOfMousePointerInCommand < helpItem.end);
|
|
465
|
+
return { helpHTML: match && match.helpHTML };
|
|
466
|
+
}
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
/**
|
|
470
|
+
* Find the name of the command at the given point.
|
|
471
|
+
*/
|
|
472
|
+
commandNameAtPoint(uri, line, column) {
|
|
473
|
+
let node = this.nodeAtPoint(uri, line, column);
|
|
474
|
+
while (node && node.type !== 'command') {
|
|
475
|
+
node = node.parent;
|
|
476
|
+
}
|
|
477
|
+
if (!node) {
|
|
478
|
+
return null;
|
|
479
|
+
}
|
|
480
|
+
const firstChild = node.firstNamedChild;
|
|
481
|
+
if (!firstChild || firstChild.type !== 'command_name') {
|
|
482
|
+
return null;
|
|
483
|
+
}
|
|
484
|
+
return firstChild.text.trim();
|
|
485
|
+
}
|
|
486
|
+
/**
|
|
487
|
+
* Find a block of comments above a line position
|
|
488
|
+
*/
|
|
489
|
+
commentsAbove(uri, line) {
|
|
490
|
+
var _a;
|
|
491
|
+
const doc = (_a = this.uriToAnalyzedDocument[uri]) === null || _a === void 0 ? void 0 : _a.document;
|
|
492
|
+
if (!doc) {
|
|
493
|
+
return null;
|
|
494
|
+
}
|
|
495
|
+
const commentBlock = [];
|
|
496
|
+
// start from the line above
|
|
497
|
+
let commentBlockIndex = line - 1;
|
|
498
|
+
// will return the comment string without the comment '#'
|
|
499
|
+
// and without leading whitespace, or null if the line 'l'
|
|
500
|
+
// is not a comment line
|
|
501
|
+
const getComment = (l) => {
|
|
502
|
+
// this regexp has to be defined within the function
|
|
503
|
+
const commentRegExp = /^\s*#\s?(.*)/g;
|
|
504
|
+
const matches = commentRegExp.exec(l);
|
|
505
|
+
return matches ? matches[1].trimRight() : null;
|
|
506
|
+
};
|
|
507
|
+
let currentLine = doc.getText({
|
|
508
|
+
start: { line: commentBlockIndex, character: 0 },
|
|
509
|
+
end: { line: commentBlockIndex + 1, character: 0 },
|
|
510
|
+
});
|
|
511
|
+
// iterate on every line above and including
|
|
512
|
+
// the current line until getComment returns null
|
|
513
|
+
let currentComment = '';
|
|
514
|
+
while ((currentComment = getComment(currentLine)) !== null) {
|
|
515
|
+
commentBlock.push(currentComment);
|
|
516
|
+
commentBlockIndex -= 1;
|
|
517
|
+
currentLine = doc.getText({
|
|
518
|
+
start: { line: commentBlockIndex, character: 0 },
|
|
519
|
+
end: { line: commentBlockIndex + 1, character: 0 },
|
|
520
|
+
});
|
|
521
|
+
}
|
|
522
|
+
if (commentBlock.length) {
|
|
523
|
+
commentBlock.push('```txt');
|
|
524
|
+
// since we searched from bottom up, we then reverse
|
|
525
|
+
// the lines so that it reads top down.
|
|
526
|
+
commentBlock.reverse();
|
|
527
|
+
commentBlock.push('```');
|
|
528
|
+
return commentBlock.join('\n');
|
|
529
|
+
}
|
|
530
|
+
// no comments found above line:
|
|
531
|
+
return null;
|
|
532
|
+
}
|
|
533
|
+
/**
|
|
534
|
+
* Find the full word at the given point.
|
|
535
|
+
*/
|
|
536
|
+
wordAtPoint(uri, line, column) {
|
|
537
|
+
const node = this.nodeAtPoint(uri, line, column);
|
|
538
|
+
if (!node || node.childCount > 0 || node.text.trim() === '') {
|
|
539
|
+
return null;
|
|
540
|
+
}
|
|
541
|
+
return node.text.trim();
|
|
542
|
+
}
|
|
543
|
+
wordAtPointFromTextPosition(params) {
|
|
544
|
+
return this.wordAtPoint(params.textDocument.uri, params.position.line, params.position.character);
|
|
545
|
+
}
|
|
546
|
+
symbolAtPointFromTextPosition(params) {
|
|
547
|
+
var _a;
|
|
548
|
+
const node = this.nodeAtPoint(params.textDocument.uri, params.position.line, params.position.character);
|
|
549
|
+
if (!node) {
|
|
550
|
+
return null;
|
|
551
|
+
}
|
|
552
|
+
if (node.type === 'variable_name' ||
|
|
553
|
+
(node.type === 'word' &&
|
|
554
|
+
['function_definition', 'command_name'].includes((_a = node.parent) === null || _a === void 0 ? void 0 : _a.type))) {
|
|
555
|
+
return {
|
|
556
|
+
word: node.text,
|
|
557
|
+
range: TreeSitterUtil.range(node),
|
|
558
|
+
kind: node.type === 'variable_name'
|
|
559
|
+
? LSP.SymbolKind.Variable
|
|
560
|
+
: LSP.SymbolKind.Function,
|
|
561
|
+
};
|
|
562
|
+
}
|
|
563
|
+
if (TreeSitterUtil.isVariableInReadCommand(node)) {
|
|
564
|
+
return {
|
|
565
|
+
word: node.text,
|
|
566
|
+
range: TreeSitterUtil.range(node),
|
|
567
|
+
kind: LSP.SymbolKind.Variable,
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
return null;
|
|
571
|
+
}
|
|
572
|
+
setEnableSourceErrorDiagnostics(enableSourceErrorDiagnostics) {
|
|
573
|
+
this.enableSourceErrorDiagnostics = enableSourceErrorDiagnostics;
|
|
574
|
+
}
|
|
575
|
+
setIncludeAllWorkspaceSymbols(includeAllWorkspaceSymbols) {
|
|
576
|
+
this.includeAllWorkspaceSymbols = includeAllWorkspaceSymbols;
|
|
577
|
+
}
|
|
578
|
+
/**
|
|
579
|
+
* If includeAllWorkspaceSymbols is true, this returns all URIs from the
|
|
580
|
+
* background analysis, else, it returns the URIs of the files that are
|
|
581
|
+
* linked to `uri` via sourcing.
|
|
582
|
+
*/
|
|
583
|
+
findAllLinkedUris(uri) {
|
|
584
|
+
if (this.includeAllWorkspaceSymbols) {
|
|
585
|
+
return Object.keys(this.uriToAnalyzedDocument).filter((u) => u !== uri);
|
|
586
|
+
}
|
|
587
|
+
const uriToAnalyzedDocument = Object.entries(this.uriToAnalyzedDocument);
|
|
588
|
+
const uris = [];
|
|
589
|
+
let continueSearching = true;
|
|
590
|
+
while (continueSearching) {
|
|
591
|
+
continueSearching = false;
|
|
592
|
+
for (const [analyzedUri, analyzedDocument] of uriToAnalyzedDocument) {
|
|
593
|
+
if (!analyzedDocument) {
|
|
594
|
+
continue;
|
|
595
|
+
}
|
|
596
|
+
for (const sourcedUri of analyzedDocument.sourcedUris.values()) {
|
|
597
|
+
if ((sourcedUri === uri || uris.includes(sourcedUri)) &&
|
|
598
|
+
!uris.includes(analyzedUri)) {
|
|
599
|
+
uris.push(analyzedUri);
|
|
600
|
+
continueSearching = true;
|
|
601
|
+
break;
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
return uris;
|
|
607
|
+
}
|
|
608
|
+
// Private methods
|
|
609
|
+
/**
|
|
610
|
+
* Returns all reachable URIs from the given URI based on sourced commands
|
|
611
|
+
* If no URI is given, all URIs from the background analysis are returned.
|
|
612
|
+
* If the includeAllWorkspaceSymbols flag is set, all URIs from the background analysis are also included.
|
|
613
|
+
*/
|
|
614
|
+
getReachableUris({ fromUri } = {}) {
|
|
615
|
+
if (!fromUri) {
|
|
616
|
+
return Object.keys(this.uriToAnalyzedDocument);
|
|
617
|
+
}
|
|
618
|
+
const urisBasedOnSourcing = [
|
|
619
|
+
fromUri,
|
|
620
|
+
...Array.from(this.findAllSourcedUris({ uri: fromUri })),
|
|
621
|
+
];
|
|
622
|
+
if (this.includeAllWorkspaceSymbols) {
|
|
623
|
+
return Array.from(new Set([...urisBasedOnSourcing, ...Object.keys(this.uriToAnalyzedDocument)]));
|
|
624
|
+
}
|
|
625
|
+
else {
|
|
626
|
+
return urisBasedOnSourcing;
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
/**
|
|
630
|
+
* Returns all reachable URIs from `fromUri` based on source commands in
|
|
631
|
+
* descending order starting from the top of the sourcing tree, this list
|
|
632
|
+
* includes `fromUri`. If includeAllWorkspaceSymbols is true, other URIs from
|
|
633
|
+
* the background analysis are also included after the ordered URIs in no
|
|
634
|
+
* particular order.
|
|
635
|
+
*/
|
|
636
|
+
getOrderedReachableUris({ fromUri }) {
|
|
637
|
+
let uris = this.findAllSourcedUris({ uri: fromUri });
|
|
638
|
+
for (const u1 of uris) {
|
|
639
|
+
for (const u2 of this.findAllSourcedUris({ uri: u1 })) {
|
|
640
|
+
if (uris.has(u2)) {
|
|
641
|
+
uris.delete(u2);
|
|
642
|
+
uris.add(u2);
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
uris = Array.from(uris);
|
|
647
|
+
uris.reverse();
|
|
648
|
+
uris.push(fromUri);
|
|
649
|
+
if (this.includeAllWorkspaceSymbols) {
|
|
650
|
+
uris.push(...Object.keys(this.uriToAnalyzedDocument).filter((u) => !uris.includes(u)));
|
|
651
|
+
}
|
|
652
|
+
return uris;
|
|
653
|
+
}
|
|
654
|
+
getAnalyzedReachableUris({ fromUri } = {}) {
|
|
655
|
+
return this.ensureUrisAreAnalyzed(this.getReachableUris({ fromUri }));
|
|
656
|
+
}
|
|
657
|
+
ensureUrisAreAnalyzed(uris) {
|
|
658
|
+
return uris.filter((uri) => {
|
|
659
|
+
if (!this.uriToAnalyzedDocument[uri]) {
|
|
660
|
+
// Either the background analysis didn't run or the file is outside
|
|
661
|
+
// the workspace. Let us try to analyze the file.
|
|
662
|
+
try {
|
|
663
|
+
logger_1.logger.debug(`Analyzing file not covered by background analysis ${uri}`);
|
|
664
|
+
const fileContent = fs.readFileSync(new URL(uri), 'utf8');
|
|
665
|
+
this.analyze({
|
|
666
|
+
document: vscode_languageserver_textdocument_1.TextDocument.create(uri, 'shell', 1, fileContent),
|
|
667
|
+
uri,
|
|
668
|
+
});
|
|
669
|
+
}
|
|
670
|
+
catch (err) {
|
|
671
|
+
logger_1.logger.warn(`Error while analyzing file ${uri}: ${err}`);
|
|
672
|
+
return false;
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
return true;
|
|
676
|
+
});
|
|
677
|
+
}
|
|
678
|
+
/**
|
|
679
|
+
* Get all declaration symbols (function or variables) from the given file/position
|
|
680
|
+
* or from all files in the workspace. It will take into account the given position
|
|
681
|
+
* to filter out irrelevant symbols.
|
|
682
|
+
*
|
|
683
|
+
* Note that this can return duplicates across the workspace.
|
|
684
|
+
*/
|
|
685
|
+
getAllDeclarations({ uri: fromUri, position, } = {}) {
|
|
686
|
+
return this.getAnalyzedReachableUris({ fromUri }).reduce((symbols, uri) => {
|
|
687
|
+
var _a;
|
|
688
|
+
const analyzedDocument = this.uriToAnalyzedDocument[uri];
|
|
689
|
+
if (analyzedDocument) {
|
|
690
|
+
if (uri !== fromUri || !position) {
|
|
691
|
+
// We use the global declarations for external files or if we do not have a position
|
|
692
|
+
const { globalDeclarations } = analyzedDocument;
|
|
693
|
+
Object.values(globalDeclarations).forEach((symbol) => symbols.push(symbol));
|
|
694
|
+
}
|
|
695
|
+
// For the current file we find declarations based on the current scope
|
|
696
|
+
if (uri === fromUri && position) {
|
|
697
|
+
const node = (_a = analyzedDocument.tree.rootNode) === null || _a === void 0 ? void 0 : _a.descendantForPosition({
|
|
698
|
+
row: position.line,
|
|
699
|
+
column: position.character,
|
|
700
|
+
});
|
|
701
|
+
const localDeclarations = (0, declarations_1.getLocalDeclarations)({
|
|
702
|
+
node,
|
|
703
|
+
rootNode: analyzedDocument.tree.rootNode,
|
|
704
|
+
uri,
|
|
705
|
+
});
|
|
706
|
+
Object.keys(localDeclarations).map((name) => {
|
|
707
|
+
const symbolsMatchingWord = localDeclarations[name];
|
|
708
|
+
// Find the latest definition
|
|
709
|
+
let closestSymbol = null;
|
|
710
|
+
symbolsMatchingWord.forEach((symbol) => {
|
|
711
|
+
// Skip if the symbol is defined in the current file after the requested position
|
|
712
|
+
if (symbol.location.range.start.line > position.line) {
|
|
713
|
+
return;
|
|
714
|
+
}
|
|
715
|
+
if (closestSymbol === null ||
|
|
716
|
+
symbol.location.range.start.line > closestSymbol.location.range.start.line) {
|
|
717
|
+
closestSymbol = symbol;
|
|
718
|
+
}
|
|
719
|
+
});
|
|
720
|
+
if (closestSymbol) {
|
|
721
|
+
symbols.push(closestSymbol);
|
|
722
|
+
}
|
|
723
|
+
});
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
return symbols;
|
|
727
|
+
}, []);
|
|
728
|
+
}
|
|
729
|
+
findAllSourcedUris({ uri }) {
|
|
730
|
+
const allSourcedUris = new Set([]);
|
|
731
|
+
const addSourcedFilesFromUri = (fromUri) => {
|
|
732
|
+
var _a;
|
|
733
|
+
const sourcedUris = (_a = this.uriToAnalyzedDocument[fromUri]) === null || _a === void 0 ? void 0 : _a.sourcedUris;
|
|
734
|
+
if (!sourcedUris) {
|
|
735
|
+
return;
|
|
736
|
+
}
|
|
737
|
+
sourcedUris.forEach((sourcedUri) => {
|
|
738
|
+
if (!allSourcedUris.has(sourcedUri)) {
|
|
739
|
+
allSourcedUris.add(sourcedUri);
|
|
740
|
+
addSourcedFilesFromUri(sourcedUri);
|
|
741
|
+
}
|
|
742
|
+
});
|
|
743
|
+
};
|
|
744
|
+
addSourcedFilesFromUri(uri);
|
|
745
|
+
return allSourcedUris;
|
|
746
|
+
}
|
|
747
|
+
/**
|
|
748
|
+
* Returns the parent `subshell` or `function_definition` of the given `node`.
|
|
749
|
+
* To disambiguate between regular `subshell`s and `subshell`s that serve as a
|
|
750
|
+
* `function_definition`'s body, this only returns a `function_definition` if
|
|
751
|
+
* its body is a `compound_statement`.
|
|
752
|
+
*/
|
|
753
|
+
parentScope(node) {
|
|
754
|
+
return TreeSitterUtil.findParent(node, (n) => {
|
|
755
|
+
var _a;
|
|
756
|
+
return n.type === 'subshell' ||
|
|
757
|
+
(n.type === 'function_definition' && ((_a = n.lastChild) === null || _a === void 0 ? void 0 : _a.type) === 'compound_statement');
|
|
758
|
+
});
|
|
759
|
+
}
|
|
760
|
+
/**
|
|
761
|
+
* Find the node at the given point.
|
|
762
|
+
*/
|
|
763
|
+
nodeAtPoint(uri, line, column) {
|
|
764
|
+
var _a;
|
|
765
|
+
const tree = (_a = this.uriToAnalyzedDocument[uri]) === null || _a === void 0 ? void 0 : _a.tree;
|
|
766
|
+
if (!(tree === null || tree === void 0 ? void 0 : tree.rootNode)) {
|
|
767
|
+
// Check for lacking rootNode (due to failed parse?)
|
|
768
|
+
return null;
|
|
769
|
+
}
|
|
770
|
+
return tree.rootNode.descendantForPosition({ row: line, column });
|
|
771
|
+
}
|
|
772
|
+
nodeAtPoints(uri, start, end) {
|
|
773
|
+
var _a;
|
|
774
|
+
const rootNode = (_a = this.uriToAnalyzedDocument[uri]) === null || _a === void 0 ? void 0 : _a.tree.rootNode;
|
|
775
|
+
if (!rootNode) {
|
|
776
|
+
return null;
|
|
777
|
+
}
|
|
778
|
+
return rootNode.descendantForPosition(start, end);
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
exports.default = Analyzer;
|
|
782
|
+
//# sourceMappingURL=analyser.js.map
|