Skip to content

Sourcing aware symbols (completion + jump to definition) #244

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Dec 9, 2022
Prev Previous commit
Next Next commit
Support sourcing files
- Only variables found in the sourced files are completed on
- Jump to definition works on the source file path (e.g. clicking "source my-file.sh")
  • Loading branch information
skovhus committed Dec 9, 2022
commit d4420c99d6fe584c5a7485965e7e62bd73325e98
18 changes: 0 additions & 18 deletions server/src/__tests__/__snapshots__/analyzer.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -38,24 +38,6 @@ Array [
]
`;

exports[`findDefinition returns a list of locations if parameter is found 1`] = `
Array [
Object {
"range": Object {
"end": Object {
"character": 37,
"line": 148,
},
"start": Object {
"character": 0,
"line": 148,
},
},
"uri": "dummy-uri.sh",
},
]
`;

exports[`findReferences returns a list of locations if parameter is found 1`] = `
Array [
Object {
Expand Down
200 changes: 120 additions & 80 deletions server/src/__tests__/analyzer.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import FIXTURES, { FIXTURE_FOLDER } from '../../../testing/fixtures'
import FIXTURES, { FIXTURE_FOLDER, FIXTURE_URI } from '../../../testing/fixtures'
import { getMockConnection } from '../../../testing/mocks'
import Analyzer from '../analyser'
import { getDefaultConfiguration } from '../config'
Expand Down Expand Up @@ -42,15 +42,60 @@ describe('analyze', () => {
describe('findDefinition', () => {
it('returns an empty list if word is not found', () => {
analyzer.analyze(CURRENT_URI, FIXTURES.INSTALL)
const result = analyzer.findDefinition({ word: 'foobar' })
const result = analyzer.findDefinition({ uri: CURRENT_URI, word: 'foobar' })
expect(result).toEqual([])
})

it('returns a location to a file if word is the path in a sourcing statement', () => {
analyzer.analyze(CURRENT_URI, FIXTURES.SOURCING)
const result = analyzer.findDefinition({
uri: CURRENT_URI,
word: './extension.inc',
position: { character: 10, line: 2 },
})
expect(result).toMatchInlineSnapshot(`
Array [
Object {
"range": Object {
"end": Object {
"character": 0,
"line": 0,
},
"start": Object {
"character": 0,
"line": 0,
},
},
"uri": "extension.inc",
},
]
`)
})

it('returns a list of locations if parameter is found', () => {
analyzer.analyze(CURRENT_URI, FIXTURES.INSTALL)
const result = analyzer.findDefinition({ word: 'node_version' })
const result = analyzer.findDefinition({
uri: CURRENT_URI,
word: 'node_version',
})
expect(result).not.toEqual([])
expect(result).toMatchSnapshot()
expect(result).toMatchInlineSnapshot(`
Array [
Object {
"range": Object {
"end": Object {
"character": 37,
"line": 148,
},
"start": Object {
"character": 0,
"line": 148,
},
},
"uri": "dummy-uri.sh",
},
]
`)
})
})

Expand Down Expand Up @@ -91,6 +136,52 @@ describe('findSymbolsForFile', () => {
})
})

describe('findAllSourcedUris', () => {
it('returns references to sourced files', async () => {
const parser = await initializeParser()
const connection = getMockConnection()

const newAnalyzer = new Analyzer({ console: connection.console, parser })
await newAnalyzer.initiateBackgroundAnalysis({
backgroundAnalysisMaxFiles: defaultConfig.backgroundAnalysisMaxFiles,
globPattern: defaultConfig.globPattern,
rootPath: FIXTURE_FOLDER,
})

const result = newAnalyzer.findAllSourcedUris({ uri: FIXTURE_URI.SOURCING })
expect(result).toEqual(
new Set([
`file://${FIXTURE_FOLDER}issue101.sh`,
`file://${FIXTURE_FOLDER}extension.inc`,
]),
)
})

it('returns references to sourced files without file extension', async () => {
const parser = await initializeParser()
const connection = getMockConnection()

const newAnalyzer = new Analyzer({ console: connection.console, parser })
await newAnalyzer.initiateBackgroundAnalysis({
backgroundAnalysisMaxFiles: defaultConfig.backgroundAnalysisMaxFiles,
globPattern: defaultConfig.globPattern,
rootPath: FIXTURE_FOLDER,
})

// Parse the file without extension
newAnalyzer.analyze(FIXTURE_URI.MISSING_EXTENSION, FIXTURES.MISSING_EXTENSION)

const result = newAnalyzer.findAllSourcedUris({ uri: FIXTURE_URI.MISSING_EXTENSION })
expect(result).toEqual(
new Set([
`file://${FIXTURE_FOLDER}extension.inc`,
`file://${FIXTURE_FOLDER}issue101.sh`,
`file://${FIXTURE_FOLDER}sourcing.sh`,
]),
)
})
})

describe('wordAtPoint', () => {
it('returns current word at a given point', () => {
analyzer.analyze(CURRENT_URI, FIXTURES.INSTALL)
Expand Down Expand Up @@ -137,92 +228,41 @@ describe('commandNameAtPoint', () => {
})
})

describe('findSymbolCompletions', () => {
describe('findSymbolsMatchingWord', () => {
it('return a list of symbols across the workspace', () => {
analyzer.analyze('install.sh', FIXTURES.INSTALL)
analyzer.analyze('sourcing-sh', FIXTURES.SOURCING)

expect(
analyzer.findSymbolsMatchingWord({ word: 'npm_config_logl', exactMatch: false }),
).toMatchInlineSnapshot(`
Array [
Object {
"kind": 13,
"location": Object {
"range": Object {
"end": Object {
"character": 27,
"line": 40,
},
"start": Object {
"character": 0,
"line": 40,
},
},
"uri": "dummy-uri.sh",
},
"name": "npm_config_loglevel",
},
Object {
"kind": 13,
"location": Object {
"range": Object {
"end": Object {
"character": 31,
"line": 48,
},
"start": Object {
"character": 2,
"line": 48,
},
},
"uri": "dummy-uri.sh",
},
"name": "npm_config_loglevel",
},
Object {
"kind": 13,
"location": Object {
"range": Object {
"end": Object {
"character": 27,
"line": 40,
},
"start": Object {
"character": 0,
"line": 40,
},
},
"uri": "install.sh",
},
"name": "npm_config_loglevel",
},
Object {
"kind": 13,
"location": Object {
"range": Object {
"end": Object {
"character": 31,
"line": 48,
},
"start": Object {
"character": 2,
"line": 48,
},
},
"uri": "install.sh",
},
"name": "npm_config_loglevel",
},
]
`)
analyzer.findSymbolsMatchingWord({
word: 'npm_config_logl',
uri: FIXTURE_URI.INSTALL,
exactMatch: false,
}),
).toMatchInlineSnapshot(`Array []`)

expect(
analyzer.findSymbolsMatchingWord({
word: 'xxxxxxxx',
uri: FIXTURE_URI.INSTALL,
exactMatch: false,
}),
).toMatchInlineSnapshot(`Array []`)

expect(
analyzer.findSymbolsMatchingWord({ word: 'xxxxxxxx', exactMatch: false }),
analyzer.findSymbolsMatchingWord({
word: 'BLU',
uri: FIXTURE_URI.INSTALL,
exactMatch: false,
}),
).toMatchInlineSnapshot(`Array []`)

expect(
analyzer.findSymbolsMatchingWord({ word: 'BLU', exactMatch: false }),
analyzer.findSymbolsMatchingWord({
word: 'BLU',
uri: FIXTURE_URI.SOURCING,
exactMatch: false,
}),
).toMatchInlineSnapshot(`Array []`)
})
})
Expand Down
80 changes: 80 additions & 0 deletions server/src/__tests__/server.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,86 @@ describe('server', () => {
expect(Array.from(new Set(result.map((item: any) => item.kind)))).toEqual([
LSP.CompletionItemKind.Variable,
])
expect(result).toMatchInlineSnapshot(`
Array [
Object {
"data": Object {
"name": "BOLD",
"type": 3,
},
"documentation": undefined,
"kind": 6,
"label": "BOLD",
},
Object {
"data": Object {
"name": "RED",
"type": 3,
},
"documentation": "### Variable: **RED** - *defined in ../extension.inc*",
"kind": 6,
"label": "RED",
},
Object {
"data": Object {
"name": "GREEN",
"type": 3,
},
"documentation": "### Variable: **GREEN** - *defined in ../extension.inc*",
"kind": 6,
"label": "GREEN",
},
Object {
"data": Object {
"name": "BLUE",
"type": 3,
},
"documentation": "### Variable: **BLUE** - *defined in ../extension.inc*",
"kind": 6,
"label": "BLUE",
},
Object {
"data": Object {
"name": "RESET",
"type": 3,
},
"documentation": "### Variable: **RESET** - *defined in ../extension.inc*",
"kind": 6,
"label": "RESET",
},
Object {
"data": Object {
"name": "USER",
"type": 3,
},
"documentation": "### Variable: **USER** - *defined in ../issue101.sh*",
"kind": 6,
"label": "USER",
},
Object {
"data": Object {
"name": "PASSWORD",
"type": 3,
},
"documentation": "### Variable: **PASSWORD** - *defined in ../issue101.sh*",
"kind": 6,
"label": "PASSWORD",
},
Object {
"data": Object {
"name": "COMMENTS",
"type": 3,
},
"documentation": "### Variable: **COMMENTS** - *defined in ../issue101.sh*

\`\`\`txt
Having shifted twice, the rest is now comments ...
\`\`\`",
"kind": 6,
"label": "COMMENTS",
},
]
`)
})

it('responds to onCodeAction', async () => {
Expand Down
Loading