@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.
Files changed (128) hide show
  1. package/dist/_internal/daemon/otlp-protobuf.d.ts +3 -0
  2. package/dist/_internal/daemon/otlp-protobuf.d.ts.map +1 -0
  3. package/dist/_internal/daemon/otlp-protobuf.js +968 -0
  4. package/dist/_internal/daemon/telemetry-routes.d.ts.map +1 -1
  5. package/dist/_internal/daemon/telemetry-routes.js +22 -13
  6. package/dist/_internal/platform/intelligence/lsp/service.d.ts.map +1 -1
  7. package/dist/_internal/platform/intelligence/lsp/service.js +18 -9
  8. package/dist/_internal/platform/providers/anthropic-vertex.d.ts.map +1 -1
  9. package/dist/_internal/platform/providers/anthropic-vertex.js +135 -2
  10. package/dist/_internal/platform/version.js +1 -1
  11. package/dist/_internal/platform/watchers/registry.d.ts.map +1 -1
  12. package/dist/_internal/platform/watchers/registry.js +4 -1
  13. package/package.json +9 -8
  14. package/vendor/bash-language-server/CHANGELOG.md +453 -0
  15. package/vendor/bash-language-server/GOODVIBES_PATCH.md +22 -0
  16. package/vendor/bash-language-server/README.md +230 -0
  17. package/vendor/bash-language-server/out/analyser.d.ts +187 -0
  18. package/vendor/bash-language-server/out/analyser.js +782 -0
  19. package/vendor/bash-language-server/out/analyser.js.map +1 -0
  20. package/vendor/bash-language-server/out/builtins.d.ts +2 -0
  21. package/vendor/bash-language-server/out/builtins.js +71 -0
  22. package/vendor/bash-language-server/out/builtins.js.map +1 -0
  23. package/vendor/bash-language-server/out/cli.d.ts +3 -0
  24. package/vendor/bash-language-server/out/cli.js +74 -0
  25. package/vendor/bash-language-server/out/cli.js.map +1 -0
  26. package/vendor/bash-language-server/out/config.d.ts +88 -0
  27. package/vendor/bash-language-server/out/config.js +96 -0
  28. package/vendor/bash-language-server/out/config.js.map +1 -0
  29. package/vendor/bash-language-server/out/executables.d.ts +19 -0
  30. package/vendor/bash-language-server/out/executables.js +86 -0
  31. package/vendor/bash-language-server/out/executables.js.map +1 -0
  32. package/vendor/bash-language-server/out/parser.d.ts +2 -0
  33. package/vendor/bash-language-server/out/parser.js +36 -0
  34. package/vendor/bash-language-server/out/parser.js.map +1 -0
  35. package/vendor/bash-language-server/out/reserved-words.d.ts +2 -0
  36. package/vendor/bash-language-server/out/reserved-words.js +33 -0
  37. package/vendor/bash-language-server/out/reserved-words.js.map +1 -0
  38. package/vendor/bash-language-server/out/server.d.ts +56 -0
  39. package/vendor/bash-language-server/out/server.js +756 -0
  40. package/vendor/bash-language-server/out/server.js.map +1 -0
  41. package/vendor/bash-language-server/out/shellcheck/config.d.ts +5 -0
  42. package/vendor/bash-language-server/out/shellcheck/config.js +17 -0
  43. package/vendor/bash-language-server/out/shellcheck/config.js.map +1 -0
  44. package/vendor/bash-language-server/out/shellcheck/directive.d.ts +18 -0
  45. package/vendor/bash-language-server/out/shellcheck/directive.js +62 -0
  46. package/vendor/bash-language-server/out/shellcheck/directive.js.map +1 -0
  47. package/vendor/bash-language-server/out/shellcheck/index.d.ts +22 -0
  48. package/vendor/bash-language-server/out/shellcheck/index.js +229 -0
  49. package/vendor/bash-language-server/out/shellcheck/index.js.map +1 -0
  50. package/vendor/bash-language-server/out/shellcheck/types.d.ts +175 -0
  51. package/vendor/bash-language-server/out/shellcheck/types.js +35 -0
  52. package/vendor/bash-language-server/out/shellcheck/types.js.map +1 -0
  53. package/vendor/bash-language-server/out/shfmt/index.d.ts +18 -0
  54. package/vendor/bash-language-server/out/shfmt/index.js +151 -0
  55. package/vendor/bash-language-server/out/shfmt/index.js.map +1 -0
  56. package/vendor/bash-language-server/out/snippets.d.ts +2 -0
  57. package/vendor/bash-language-server/out/snippets.js +671 -0
  58. package/vendor/bash-language-server/out/snippets.js.map +1 -0
  59. package/vendor/bash-language-server/out/types.d.ts +13 -0
  60. package/vendor/bash-language-server/out/types.js +12 -0
  61. package/vendor/bash-language-server/out/types.js.map +1 -0
  62. package/vendor/bash-language-server/out/util/array.d.ts +14 -0
  63. package/vendor/bash-language-server/out/util/array.js +36 -0
  64. package/vendor/bash-language-server/out/util/array.js.map +1 -0
  65. package/vendor/bash-language-server/out/util/async.d.ts +13 -0
  66. package/vendor/bash-language-server/out/util/async.js +24 -0
  67. package/vendor/bash-language-server/out/util/async.js.map +1 -0
  68. package/vendor/bash-language-server/out/util/declarations.d.ts +88 -0
  69. package/vendor/bash-language-server/out/util/declarations.js +295 -0
  70. package/vendor/bash-language-server/out/util/declarations.js.map +1 -0
  71. package/vendor/bash-language-server/out/util/discriminate.d.ts +1 -0
  72. package/vendor/bash-language-server/out/util/discriminate.js +7 -0
  73. package/vendor/bash-language-server/out/util/discriminate.js.map +1 -0
  74. package/vendor/bash-language-server/out/util/fs.d.ts +6 -0
  75. package/vendor/bash-language-server/out/util/fs.js +73 -0
  76. package/vendor/bash-language-server/out/util/fs.js.map +1 -0
  77. package/vendor/bash-language-server/out/util/logger.d.ts +35 -0
  78. package/vendor/bash-language-server/out/util/logger.js +105 -0
  79. package/vendor/bash-language-server/out/util/logger.js.map +1 -0
  80. package/vendor/bash-language-server/out/util/lsp.d.ts +5 -0
  81. package/vendor/bash-language-server/out/util/lsp.js +13 -0
  82. package/vendor/bash-language-server/out/util/lsp.js.map +1 -0
  83. package/vendor/bash-language-server/out/util/platform.d.ts +1 -0
  84. package/vendor/bash-language-server/out/util/platform.js +7 -0
  85. package/vendor/bash-language-server/out/util/platform.js.map +1 -0
  86. package/vendor/bash-language-server/out/util/sh.d.ts +16 -0
  87. package/vendor/bash-language-server/out/util/sh.js +132 -0
  88. package/vendor/bash-language-server/out/util/sh.js.map +1 -0
  89. package/vendor/bash-language-server/out/util/shebang.d.ts +10 -0
  90. package/vendor/bash-language-server/out/util/shebang.js +53 -0
  91. package/vendor/bash-language-server/out/util/shebang.js.map +1 -0
  92. package/vendor/bash-language-server/out/util/sourcing.d.ts +15 -0
  93. package/vendor/bash-language-server/out/util/sourcing.js +182 -0
  94. package/vendor/bash-language-server/out/util/sourcing.js.map +1 -0
  95. package/vendor/bash-language-server/out/util/tree-sitter.d.ts +22 -0
  96. package/vendor/bash-language-server/out/util/tree-sitter.js +110 -0
  97. package/vendor/bash-language-server/out/util/tree-sitter.js.map +1 -0
  98. package/vendor/bash-language-server/package.json +52 -0
  99. package/vendor/bash-language-server/parser.info +2 -0
  100. package/vendor/bash-language-server/tree-sitter-bash.wasm +0 -0
  101. package/dist/_internal/platform/runtime/contracts/index.d.ts +0 -40
  102. package/dist/_internal/platform/runtime/contracts/index.d.ts.map +0 -1
  103. package/dist/_internal/platform/runtime/contracts/index.js +0 -133
  104. package/dist/_internal/platform/runtime/contracts/migrations/index.d.ts +0 -75
  105. package/dist/_internal/platform/runtime/contracts/migrations/index.d.ts.map +0 -1
  106. package/dist/_internal/platform/runtime/contracts/migrations/index.js +0 -158
  107. package/dist/_internal/platform/runtime/contracts/migrations/schemas.d.ts +0 -57
  108. package/dist/_internal/platform/runtime/contracts/migrations/schemas.d.ts.map +0 -1
  109. package/dist/_internal/platform/runtime/contracts/migrations/schemas.js +0 -157
  110. package/dist/_internal/platform/runtime/contracts/types.d.ts +0 -123
  111. package/dist/_internal/platform/runtime/contracts/types.d.ts.map +0 -1
  112. package/dist/_internal/platform/runtime/contracts/types.js +0 -41
  113. package/dist/_internal/platform/runtime/contracts/validators/event-envelope.d.ts +0 -24
  114. package/dist/_internal/platform/runtime/contracts/validators/event-envelope.d.ts.map +0 -1
  115. package/dist/_internal/platform/runtime/contracts/validators/event-envelope.js +0 -104
  116. package/dist/_internal/platform/runtime/contracts/validators/index.d.ts +0 -11
  117. package/dist/_internal/platform/runtime/contracts/validators/index.d.ts.map +0 -1
  118. package/dist/_internal/platform/runtime/contracts/validators/index.js +0 -10
  119. package/dist/_internal/platform/runtime/contracts/validators/runtime-state.d.ts +0 -23
  120. package/dist/_internal/platform/runtime/contracts/validators/runtime-state.d.ts.map +0 -1
  121. package/dist/_internal/platform/runtime/contracts/validators/runtime-state.js +0 -101
  122. package/dist/_internal/platform/runtime/contracts/validators/session.d.ts +0 -24
  123. package/dist/_internal/platform/runtime/contracts/validators/session.d.ts.map +0 -1
  124. package/dist/_internal/platform/runtime/contracts/validators/session.js +0 -103
  125. package/dist/_internal/platform/runtime/contracts/version.d.ts +0 -84
  126. package/dist/_internal/platform/runtime/contracts/version.d.ts.map +0 -1
  127. package/dist/_internal/platform/runtime/contracts/version.js +0 -41
  128. 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