Skip to content

Commit 41fc005

Browse files
committed
Support symbols in onHover result
1 parent 4107a32 commit 41fc005

File tree

3 files changed

+96
-54
lines changed

3 files changed

+96
-54
lines changed

server/src/__tests__/analyzer.test.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,9 @@ describe('findSymbolCompletions', () => {
107107
analyzer.analyze('install.sh', FIXTURES.INSTALL)
108108
analyzer.analyze('sourcing-sh', FIXTURES.SOURCING)
109109

110-
expect(analyzer.findSymbolsMatchingWord({ word: 'npm_config_logl' }))
111-
.toMatchInlineSnapshot(`
110+
expect(
111+
analyzer.findSymbolsMatchingWord({ word: 'npm_config_logl', exactMatch: false }),
112+
).toMatchInlineSnapshot(`
112113
Array [
113114
Object {
114115
"kind": 13,
@@ -181,13 +182,13 @@ describe('findSymbolCompletions', () => {
181182
]
182183
`)
183184

184-
expect(analyzer.findSymbolsMatchingWord({ word: 'xxxxxxxx' })).toMatchInlineSnapshot(
185-
`Array []`,
186-
)
185+
expect(
186+
analyzer.findSymbolsMatchingWord({ word: 'xxxxxxxx', exactMatch: false }),
187+
).toMatchInlineSnapshot(`Array []`)
187188

188-
expect(analyzer.findSymbolsMatchingWord({ word: 'BLU' })).toMatchInlineSnapshot(
189-
`Array []`,
190-
)
189+
expect(
190+
analyzer.findSymbolsMatchingWord({ word: 'BLU', exactMatch: false }),
191+
).toMatchInlineSnapshot(`Array []`)
191192
})
192193
})
193194

server/src/analyser.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -264,13 +264,20 @@ export default class Analyzer {
264264
/**
265265
* Find symbol completions for the given word.
266266
*/
267-
public findSymbolsMatchingWord({ word }: { word: string }): LSP.SymbolInformation[] {
267+
public findSymbolsMatchingWord({
268+
exactMatch,
269+
word,
270+
}: {
271+
exactMatch: boolean
272+
word: string
273+
}): LSP.SymbolInformation[] {
268274
const symbols: LSP.SymbolInformation[] = []
269275

270276
Object.keys(this.uriToDeclarations).forEach(uri => {
271277
const declarationsInFile = this.uriToDeclarations[uri] || {}
272278
Object.keys(declarationsInFile).map(name => {
273-
if (name.startsWith(word)) {
279+
const match = exactMatch ? name === word : name.startsWith(word)
280+
if (match) {
274281
declarationsInFile[name].forEach(symbol => symbols.push(symbol))
275282
}
276283
})

server/src/server.ts

Lines changed: 78 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -179,9 +179,30 @@ export default class BashServer {
179179
// eslint-disable-next-line no-console
180180
return { contents: getMarkdownContent(shellDocumentation) }
181181
}
182-
}
182+
} else {
183+
const symbolDocumentation = deduplicateSymbols({
184+
symbols: this.analyzer.findSymbolsMatchingWord({
185+
exactMatch: true,
186+
word,
187+
}),
188+
currentUri,
189+
})
190+
// do not return hover referencing for the current line
191+
.filter(symbol => symbol.location.range.start.line !== params.position.line)
192+
.map((symbol: LSP.SymbolInformation) =>
193+
symbol.location.uri !== currentUri
194+
? `${symbolKindToDescription(symbol.kind)} defined in ${path.relative(
195+
currentUri,
196+
symbol.location.uri,
197+
)}`
198+
: `${symbolKindToDescription(symbol.kind)} defined on line ${symbol.location
199+
.range.start.line + 1}`,
200+
)
183201

184-
// FIXME: could also be a symbol
202+
if (symbolDocumentation.length === 1) {
203+
return { contents: symbolDocumentation[0] }
204+
}
205+
}
185206

186207
return null
187208
}
@@ -238,6 +259,7 @@ export default class BashServer {
238259
? []
239260
: getCompletionItemsForSymbols({
240261
symbols: this.analyzer.findSymbolsMatchingWord({
262+
exactMatch: false,
241263
word,
242264
}),
243265
currentUri,
@@ -304,42 +326,39 @@ export default class BashServer {
304326

305327
this.connection.console.log(`onCompletionResolve name=${name} type=${type}`)
306328

307-
const getMarkdownCompletionItem = (doc: string) => ({
308-
...item,
309-
// LSP.MarkupContent
310-
documentation: {
311-
value: ['``` man', doc, '```'].join('\n'),
312-
// Passed as markdown for syntax highlighting
313-
kind: 'markdown' as const,
314-
},
315-
})
316-
317329
try {
318-
if (type === CompletionItemDataType.Executable) {
319-
const doc = await this.executables.documentation(name)
320-
return getMarkdownCompletionItem(doc)
321-
} else if (type === CompletionItemDataType.Builtin) {
322-
const doc = await Builtins.documentation(name)
323-
return getMarkdownCompletionItem(doc)
324-
} else if (type === CompletionItemDataType.ReservedWord) {
325-
const doc = await ReservedWords.documentation(name)
326-
return getMarkdownCompletionItem(doc)
327-
} else {
328-
return item
330+
let documentation = null
331+
332+
if (
333+
type === CompletionItemDataType.Executable ||
334+
type === CompletionItemDataType.Builtin ||
335+
type === CompletionItemDataType.ReservedWord
336+
) {
337+
documentation = await getShellDocumentation({ word: name })
329338
}
339+
340+
return documentation
341+
? {
342+
...item,
343+
documentation: getMarkdownContent(documentation),
344+
}
345+
: item
330346
} catch (error) {
331347
return item
332348
}
333349
}
334350
}
335351

336-
function getCompletionItemsForSymbols({
352+
/**
353+
* Deduplicate symbols by prioritizing the current file.
354+
*/
355+
function deduplicateSymbols({
337356
symbols,
338357
currentUri,
339358
}: {
340359
symbols: LSP.SymbolInformation[]
341360
currentUri: string
342-
}): BashCompletionItem[] {
361+
}) {
343362
const isCurrentFile = ({ location: { uri } }: LSP.SymbolInformation) =>
344363
uri === currentUri
345364

@@ -349,7 +368,7 @@ function getCompletionItemsForSymbols({
349368

350369
const symbolsOtherFiles = symbols
351370
.filter(s => !isCurrentFile(s))
352-
// Remove identical symbols
371+
// Remove identical symbols matching current file
353372
.filter(
354373
symbolOtherFiles =>
355374
!symbolsCurrentFile.some(
@@ -358,24 +377,33 @@ function getCompletionItemsForSymbols({
358377
),
359378
)
360379

361-
return uniqueBasedOnHash(
362-
[...symbolsCurrentFile, ...symbolsOtherFiles],
363-
getSymbolId,
364-
).map((symbol: LSP.SymbolInformation) => ({
365-
label: symbol.name,
366-
kind: symbolKindToCompletionKind(symbol.kind),
367-
data: {
368-
name: symbol.name,
369-
type: CompletionItemDataType.Symbol,
370-
},
371-
documentation:
372-
symbol.location.uri !== currentUri
373-
? `${symbolKindToDescription(symbol.kind)} defined in ${path.relative(
374-
currentUri,
375-
symbol.location.uri,
376-
)}`
377-
: undefined,
378-
}))
380+
return uniqueBasedOnHash([...symbolsCurrentFile, ...symbolsOtherFiles], getSymbolId)
381+
}
382+
383+
function getCompletionItemsForSymbols({
384+
symbols,
385+
currentUri,
386+
}: {
387+
symbols: LSP.SymbolInformation[]
388+
currentUri: string
389+
}): BashCompletionItem[] {
390+
return deduplicateSymbols({ symbols, currentUri }).map(
391+
(symbol: LSP.SymbolInformation) => ({
392+
label: symbol.name,
393+
kind: symbolKindToCompletionKind(symbol.kind),
394+
data: {
395+
name: symbol.name,
396+
type: CompletionItemDataType.Symbol,
397+
},
398+
documentation:
399+
symbol.location.uri !== currentUri
400+
? `${symbolKindToDescription(symbol.kind)} defined in ${path.relative(
401+
currentUri,
402+
symbol.location.uri,
403+
)}`
404+
: undefined,
405+
}),
406+
)
379407
}
380408

381409
function symbolKindToCompletionKind(s: LSP.SymbolKind): LSP.CompletionItemKind {
@@ -440,3 +468,9 @@ function symbolKindToDescription(s: LSP.SymbolKind): string {
440468
return 'Keyword'
441469
}
442470
}
471+
472+
const getMarkdownContent = (documentation: string): LSP.MarkupContent => ({
473+
value: ['``` man', documentation, '```'].join('\n'),
474+
// Passed as markdown for syntax highlighting
475+
kind: 'markdown' as const,
476+
})

0 commit comments

Comments
 (0)