Skip to content

Commit 0bb7cbb

Browse files
committed
Make completion based on imports configurable
1 parent f8c1850 commit 0bb7cbb

File tree

6 files changed

+186
-26
lines changed

6 files changed

+186
-26
lines changed

server/src/__tests__/analyzer.test.ts

Lines changed: 145 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -229,17 +229,65 @@ describe('commandNameAtPoint', () => {
229229
})
230230

231231
describe('findSymbolsMatchingWord', () => {
232-
it('return a list of symbols across the workspace', () => {
233-
analyzer.analyze('install.sh', FIXTURES.INSTALL)
234-
analyzer.analyze('sourcing-sh', FIXTURES.SOURCING)
232+
it('return a list of symbols across the workspace when isSourcingAware is false', async () => {
233+
const parser = await initializeParser()
234+
const connection = getMockConnection()
235+
236+
const analyzer = new Analyzer({
237+
console: connection.console,
238+
parser,
239+
isSourcingAware: false,
240+
})
241+
await analyzer.initiateBackgroundAnalysis({
242+
backgroundAnalysisMaxFiles: defaultConfig.backgroundAnalysisMaxFiles,
243+
globPattern: defaultConfig.globPattern,
244+
rootPath: FIXTURE_FOLDER,
245+
})
235246

236247
expect(
237248
analyzer.findSymbolsMatchingWord({
238249
word: 'npm_config_logl',
239250
uri: FIXTURE_URI.INSTALL,
240251
exactMatch: false,
241252
}),
242-
).toMatchInlineSnapshot(`Array []`)
253+
).toMatchInlineSnapshot(`
254+
Array [
255+
Object {
256+
"kind": 13,
257+
"location": Object {
258+
"range": Object {
259+
"end": Object {
260+
"character": 27,
261+
"line": 40,
262+
},
263+
"start": Object {
264+
"character": 0,
265+
"line": 40,
266+
},
267+
},
268+
"uri": "file://${FIXTURE_FOLDER}install.sh",
269+
},
270+
"name": "npm_config_loglevel",
271+
},
272+
Object {
273+
"kind": 13,
274+
"location": Object {
275+
"range": Object {
276+
"end": Object {
277+
"character": 31,
278+
"line": 48,
279+
},
280+
"start": Object {
281+
"character": 2,
282+
"line": 48,
283+
},
284+
},
285+
"uri": "file://${FIXTURE_FOLDER}install.sh",
286+
},
287+
"name": "npm_config_loglevel",
288+
},
289+
]
290+
`)
243291

244292
expect(
245293
analyzer.findSymbolsMatchingWord({
@@ -255,15 +303,107 @@ describe('findSymbolsMatchingWord', () => {
255303
uri: FIXTURE_URI.INSTALL,
256304
exactMatch: false,
257305
}),
258-
).toMatchInlineSnapshot(`Array []`)
306+
).toMatchInlineSnapshot(`
307+
Array [
308+
Object {
309+
"kind": 13,
310+
"location": Object {
311+
"range": Object {
312+
"end": Object {
313+
"character": 19,
314+
"line": 6,
315+
},
316+
"start": Object {
317+
"character": 0,
318+
"line": 6,
319+
},
320+
},
321+
"uri": "file://${FIXTURE_FOLDER}extension.inc",
322+
},
323+
"name": "BLUE",
324+
},
325+
]
326+
`)
259327

260328
expect(
261329
analyzer.findSymbolsMatchingWord({
262330
word: 'BLU',
263331
uri: FIXTURE_URI.SOURCING,
264332
exactMatch: false,
265333
}),
334+
).toMatchInlineSnapshot(`
335+
Array [
336+
Object {
337+
"kind": 13,
338+
"location": Object {
339+
"range": Object {
340+
"end": Object {
341+
"character": 19,
342+
"line": 6,
343+
},
344+
"start": Object {
345+
"character": 0,
346+
"line": 6,
347+
},
348+
},
349+
"uri": "file://${FIXTURE_FOLDER}extension.inc",
350+
},
351+
"name": "BLUE",
352+
},
353+
]
354+
`)
355+
})
356+
357+
it('return a list of symbols accessible to the uri when isSourcingAware is true', async () => {
358+
const parser = await initializeParser()
359+
const connection = getMockConnection()
360+
361+
const analyzer = new Analyzer({
362+
console: connection.console,
363+
parser,
364+
isSourcingAware: true,
365+
})
366+
await analyzer.initiateBackgroundAnalysis({
367+
backgroundAnalysisMaxFiles: defaultConfig.backgroundAnalysisMaxFiles,
368+
globPattern: defaultConfig.globPattern,
369+
rootPath: FIXTURE_FOLDER,
370+
})
371+
372+
expect(
373+
analyzer.findSymbolsMatchingWord({
374+
word: 'BLU',
375+
uri: FIXTURE_URI.INSTALL,
376+
exactMatch: false,
377+
}),
266378
).toMatchInlineSnapshot(`Array []`)
379+
380+
expect(
381+
analyzer.findSymbolsMatchingWord({
382+
word: 'BLU',
383+
uri: FIXTURE_URI.SOURCING,
384+
exactMatch: false,
385+
}),
386+
).toMatchInlineSnapshot(`
387+
Array [
388+
Object {
389+
"kind": 13,
390+
"location": Object {
391+
"range": Object {
392+
"end": Object {
393+
"character": 19,
394+
"line": 6,
395+
},
396+
"start": Object {
397+
"character": 0,
398+
"line": 6,
399+
},
400+
},
401+
"uri": "file://${FIXTURE_FOLDER}extension.inc",
402+
},
403+
"name": "BLUE",
404+
},
405+
]
406+
`)
267407
})
268408
})
269409

server/src/__tests__/config.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ describe('ConfigSchema', () => {
55
expect(ConfigSchema.parse({})).toMatchInlineSnapshot(`
66
Object {
77
"backgroundAnalysisMaxFiles": 500,
8+
"completionBasedOnImports": true,
89
"explainshellEndpoint": "",
910
"globPattern": "**/*@(.sh|.inc|.bash|.command)",
1011
"highlightParsingErrors": false,
@@ -24,6 +25,7 @@ describe('ConfigSchema', () => {
2425
).toMatchInlineSnapshot(`
2526
Object {
2627
"backgroundAnalysisMaxFiles": 1,
28+
"completionBasedOnImports": true,
2729
"explainshellEndpoint": "localhost:8080",
2830
"globPattern": "**/*@(.sh)",
2931
"highlightParsingErrors": true,

server/src/analyser.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,15 +47,20 @@ export default class Analyzer {
4747
variable_assignment: LSP.SymbolKind.Variable,
4848
}
4949

50+
private isSourcingAware: boolean
51+
5052
public constructor({
5153
console,
54+
isSourcingAware = true,
5255
parser,
5356
}: {
5457
console: LSP.RemoteConsole
58+
isSourcingAware?: boolean
5559
parser: Parser
5660
}) {
57-
this.parser = parser
5861
this.console = console
62+
this.isSourcingAware = isSourcingAware
63+
this.parser = parser
5964
}
6065

6166
/**
@@ -537,10 +542,15 @@ export default class Analyzer {
537542
)
538543
}
539544

545+
public setIsSourcingAware(isSourcingAware: boolean): void {
546+
this.isSourcingAware = isSourcingAware
547+
}
548+
540549
private getAllFileDeclarations({ uri }: { uri?: string } = {}): FileDeclarations {
541-
const uris = uri
542-
? [uri, ...Array.from(this.findAllSourcedUris({ uri }))]
543-
: Object.keys(this.uriToDeclarations)
550+
const uris =
551+
uri && this.isSourcingAware
552+
? [uri, ...Array.from(this.findAllSourcedUris({ uri }))]
553+
: Object.keys(this.uriToDeclarations)
544554

545555
return uris.reduce((fileDeclarations, uri) => {
546556
fileDeclarations[uri] = this.uriToDeclarations[uri] || {}

server/src/config.ts

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,22 @@ import { z } from 'zod'
22

33
export const ConfigSchema = z
44
.object({
5+
// Controls if completions are based only on analyzing the import/sourcing of files.
6+
// If false, completion will be based on all files in the workspace.
7+
completionBasedOnImports: z.boolean().default(true),
8+
9+
// Maximum number of files to analyze in the background. Set to 0 to disable background analysis.
10+
backgroundAnalysisMaxFiles: z.number().int().min(0).default(500),
11+
512
// Glob pattern for finding and parsing shell script files in the workspace. Used by the background analysis features across files.
613
globPattern: z.string().trim().default('**/*@(.sh|.inc|.bash|.command)'),
714

8-
// Controls if Treesitter parsing errors will be highlighted as problems.
9-
highlightParsingErrors: z.boolean().default(false),
10-
1115
// Configure explainshell server endpoint in order to get hover documentation on flags and options.
1216
// And empty string will disable the feature.
1317
explainshellEndpoint: z.string().trim().default(''),
1418

15-
// Controls the executable used for ShellCheck linting information. An empty string will disable linting.
16-
shellcheckPath: z.string().trim().default('shellcheck'),
19+
// Controls if Treesitter parsing errors will be highlighted as problems.
20+
highlightParsingErrors: z.boolean().default(false),
1721

1822
// Additional ShellCheck arguments. Note that we already add the following arguments: --shell, --format, --external-sources."
1923
shellcheckArguments: z
@@ -31,8 +35,8 @@ export const ConfigSchema = z
3135
}, z.array(z.string()))
3236
.default([]),
3337

34-
// Maximum number of files to analyze in the background. Set to 0 to disable background analysis.
35-
backgroundAnalysisMaxFiles: z.number().int().min(0).default(500),
38+
// Controls the executable used for ShellCheck linting information. An empty string will disable linting.
39+
shellcheckPath: z.string().trim().default('shellcheck'),
3640
})
3741
.strict()
3842

@@ -42,18 +46,14 @@ export function getConfigFromEnvironmentVariables(): {
4246
config: z.infer<typeof ConfigSchema>
4347
environmentVariablesUsed: string[]
4448
} {
45-
const { HIGHLIGHT_PARSING_ERRORS } = process.env
46-
4749
const rawConfig = {
48-
globPattern: process.env.GLOB_PATTERN,
49-
highlightParsingErrors:
50-
typeof HIGHLIGHT_PARSING_ERRORS !== 'undefined'
51-
? toBoolean(HIGHLIGHT_PARSING_ERRORS)
52-
: undefined,
50+
backgroundAnalysisMaxFiles: process.env.BACKGROUND_ANALYSIS_MAX_FILES,
51+
completionBasedOnImports: toBoolean(process.env.COMPLETION_BASED_ON_IMPORTS),
5352
explainshellEndpoint: process.env.EXPLAINSHELL_ENDPOINT,
54-
shellcheckPath: process.env.SHELLCHECK_PATH,
53+
globPattern: process.env.GLOB_PATTERN,
54+
highlightParsingErrors: toBoolean(process.env.HIGHLIGHT_PARSING_ERRORS),
5555
shellcheckArguments: process.env.SHELLCHECK_ARGUMENTS,
56-
backgroundAnalysisMaxFiles: process.env.BACKGROUND_ANALYSIS_MAX_FILES,
56+
shellcheckPath: process.env.SHELLCHECK_PATH,
5757
}
5858

5959
const environmentVariablesUsed = Object.entries(rawConfig)
@@ -69,4 +69,5 @@ export function getDefaultConfiguration(): z.infer<typeof ConfigSchema> {
6969
return ConfigSchema.parse({})
7070
}
7171

72-
const toBoolean = (s: string): boolean => s === 'true' || s === '1'
72+
const toBoolean = (s?: string): boolean | undefined =>
73+
typeof s !== 'undefined' ? s === 'true' || s === '1' : undefined

server/src/server.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,8 @@ export default class BashServer {
224224
})
225225
}
226226

227+
this.analyzer.setIsSourcingAware(this.config.completionBasedOnImports)
228+
227229
return true
228230
}
229231
} catch (err) {

vscode-client/package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@
3737
"description": "Maximum number of files to analyze in the background. Set to 0 to disable background analysis.",
3838
"minimum": 0
3939
},
40+
"bashIde.completionBasedOnImports": {
41+
"type": "boolean",
42+
"default": true,
43+
"description": "Controls if symbols completion across files is based on parsing sourcing statements (i.e. 'source file.sh' or '. file.sh'). If false then all symbols found in the workspace will be used."
44+
},
4045
"bashIde.explainshellEndpoint": {
4146
"type": "string",
4247
"default": "",

0 commit comments

Comments
 (0)