Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 2e02f9b

Browse files
authored
Merge pull request #234 from nikita-skobov/feature-comment-docs-onhover
Feature comment docs onhover
2 parents d03a676 + 1e35af1 commit 2e02f9b

File tree

6 files changed

+168
-3
lines changed

6 files changed

+168
-3
lines changed

‎server/src/__tests__/analyzer.test.ts

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,35 @@ describe('findSymbolCompletions', () => {
192192
})
193193
})
194194

195+
describe('commentsAbove', () => {
196+
it('returns a string of a comment block above a line', () => {
197+
analyzer.analyze(CURRENT_URI, FIXTURES.COMMENT_DOC)
198+
expect(analyzer.commentsAbove(CURRENT_URI, 22)).toEqual('doc for func_one')
199+
})
200+
201+
it('handles line breaks in comments', () => {
202+
analyzer.analyze(CURRENT_URI, FIXTURES.COMMENT_DOC)
203+
expect(analyzer.commentsAbove(CURRENT_URI, 28)).toEqual(
204+
'doc for func_two\nhas two lines',
205+
)
206+
})
207+
208+
it('only returns connected comments', () => {
209+
analyzer.analyze(CURRENT_URI, FIXTURES.COMMENT_DOC)
210+
expect(analyzer.commentsAbove(CURRENT_URI, 36)).toEqual('doc for func_three')
211+
})
212+
213+
it('returns null if no comment found', () => {
214+
analyzer.analyze(CURRENT_URI, FIXTURES.COMMENT_DOC)
215+
expect(analyzer.commentsAbove(CURRENT_URI, 45)).toEqual(null)
216+
})
217+
218+
it('works for variables', () => {
219+
analyzer.analyze(CURRENT_URI, FIXTURES.COMMENT_DOC)
220+
expect(analyzer.commentsAbove(CURRENT_URI, 42)).toEqual('works for variables')
221+
})
222+
})
223+
195224
describe('fromRoot', () => {
196225
it('initializes an analyzer from a root', async () => {
197226
const parser = await initializeParser()
@@ -210,7 +239,8 @@ describe('fromRoot', () => {
210239

211240
expect(connection.window.showWarningMessage).not.toHaveBeenCalled()
212241

213-
const FIXTURE_FILES_MATCHING_GLOB = 10
242+
// if you add a .sh file to testing/fixtures, update this value
243+
const FIXTURE_FILES_MATCHING_GLOB = 11
214244

215245
// Intro, stats on glob, one file skipped due to shebang, and outro
216246
const LOG_LINES = FIXTURE_FILES_MATCHING_GLOB + 4

‎server/src/__tests__/server.test.ts

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,33 @@ describe('server', () => {
7676
})
7777
})
7878

79+
it('responds to onHover with function documentation extracted from comments', async () => {
80+
const { connection, server } = await initializeServer()
81+
server.register(connection)
82+
83+
const onHover = connection.onHover.mock.calls[0][0]
84+
85+
const result = await onHover(
86+
{
87+
textDocument: {
88+
uri: FIXTURE_URI.COMMENT_DOC,
89+
},
90+
position: {
91+
line: 17,
92+
character: 0,
93+
},
94+
},
95+
{} as any,
96+
{} as any,
97+
)
98+
99+
expect(result).toBeDefined()
100+
expect(result).toEqual({
101+
contents:
102+
'Function defined on line 8\n\nthis is a comment\ndescribing the function\nhello_world\nthis function takes two arguments',
103+
})
104+
})
105+
79106
it('responds to onDocumentHighlight', async () => {
80107
const { connection, server } = await initializeServer()
81108
server.register(connection)
@@ -225,7 +252,7 @@ describe('server', () => {
225252
)
226253

227254
// Limited set (not using snapshot due to different executables on CI and locally)
228-
expect(result && 'length' in result && result.length < 5).toBe(true)
255+
expect(result && 'length' in result && result.length < 8).toBe(true)
229256
expect(result).toEqual(
230257
expect.arrayContaining([
231258
{

‎server/src/analyser.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,54 @@ export default class Analyzer {
399399
return name
400400
}
401401

402+
/**
403+
* Find a block of comments above a line position
404+
*/
405+
public commentsAbove(uri: string, line: number): string | null {
406+
const doc = this.uriToTextDocument[uri]
407+
408+
const commentBlock = []
409+
410+
// start from the line above
411+
let commentBlockIndex = line - 1
412+
413+
// will return the comment string without the comment '#'
414+
// and without leading whitespace, or null if the line 'l'
415+
// is not a comment line
416+
const getComment = (l: string): null | string => {
417+
// this regexp has to be defined within the function
418+
const commentRegExp = /^\s*#\s*(.*)/g
419+
const matches = commentRegExp.exec(l)
420+
return matches ? matches[1].trim() : null
421+
}
422+
423+
let currentLine = doc.getText({
424+
start: { line: commentBlockIndex, character: 0 },
425+
end: { line: commentBlockIndex + 1, character: 0 },
426+
})
427+
428+
// iterate on every line above and including
429+
// the current line until getComment returns null
430+
let currentComment: string | null = ''
431+
while ((currentComment = getComment(currentLine))) {
432+
commentBlock.push(currentComment)
433+
commentBlockIndex -= 1
434+
currentLine = doc.getText({
435+
start: { line: commentBlockIndex, character: 0 },
436+
end: { line: commentBlockIndex + 1, character: 0 },
437+
})
438+
}
439+
440+
if (commentBlock.length) {
441+
// since we searched from bottom up, we then reverse
442+
// the lines so that it reads top down.
443+
return commentBlock.reverse().join('\n')
444+
}
445+
446+
// no comments found above line:
447+
return null
448+
}
449+
402450
private getAllSymbols(): LSP.SymbolInformation[] {
403451
// NOTE: this could be cached, it takes < 1 ms to generate for a project with 250 bash files...
404452
const symbols: LSP.SymbolInformation[] = []

‎server/src/server.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,11 @@ export default class BashServer {
180180
return { contents: getMarkdownContent(shellDocumentation) }
181181
}
182182
} else {
183+
const getCommentsAbove = (uri: string, line: number): string => {
184+
const comment = this.analyzer.commentsAbove(uri, line)
185+
return comment ? `\n\n${comment}` : ''
186+
}
187+
183188
const symbolDocumentation = deduplicateSymbols({
184189
symbols: this.analyzer.findSymbolsMatchingWord({
185190
exactMatch: true,
@@ -194,9 +199,15 @@ export default class BashServer {
194199
? `${symbolKindToDescription(symbol.kind)} defined in ${path.relative(
195200
currentUri,
196201
symbol.location.uri,
202+
)}${getCommentsAbove(
203+
symbol.location.uri,
204+
symbol.location.range.start.line,
197205
)}`
198206
: `${symbolKindToDescription(symbol.kind)} defined on line ${symbol.location
199-
.range.start.line + 1}`,
207+
.range.start.line + 1}${getCommentsAbove(
208+
params.textDocument.uri,
209+
symbol.location.range.start.line,
210+
)}`,
200211
)
201212

202213
if (symbolDocumentation.length === 1) {

‎testing/fixtures.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export const FIXTURE_URI = {
2020
MISSING_NODE: `file://${path.join(FIXTURE_FOLDER, 'missing-node.sh')}`,
2121
PARSE_PROBLEMS: `file://${path.join(FIXTURE_FOLDER, 'parse-problems.sh')}`,
2222
SOURCING: `file://${path.join(FIXTURE_FOLDER, 'sourcing.sh')}`,
23+
COMMENT_DOC: `file://${path.join(FIXTURE_FOLDER, 'comment-doc-on-hover.sh')}`,
2324
}
2425

2526
export const FIXTURE_DOCUMENT = {
@@ -28,6 +29,7 @@ export const FIXTURE_DOCUMENT = {
2829
MISSING_NODE: getDocument(FIXTURE_URI.MISSING_NODE),
2930
PARSE_PROBLEMS: getDocument(FIXTURE_URI.PARSE_PROBLEMS),
3031
SOURCING: getDocument(FIXTURE_URI.SOURCING),
32+
COMMENT_DOC: getDocument(FIXTURE_URI.COMMENT_DOC),
3133
}
3234

3335
export default FIXTURE_DOCUMENT
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#!/usr/bin/env bash
2+
3+
4+
# this is a comment
5+
# describing the function
6+
# hello_world
7+
# this function takes two arguments
8+
hello_world() {
9+
echo "hello world to: 1ドル and 2ドル"
10+
}
11+
12+
13+
14+
# if the user hovers above the below hello_world invocation
15+
# they should see the comment doc string in a tooltip
16+
# containing the lines 4 - 7 above
17+
18+
hello_world "bob" "sally"
19+
20+
21+
22+
# doc for func_one
23+
func_one() {
24+
echo "func_one"
25+
}
26+
27+
# doc for func_two
28+
# has two lines
29+
func_two() {
30+
echo "func_two"
31+
}
32+
33+
34+
# this is not included
35+
36+
# doc for func_three
37+
func_three() {
38+
echo "func_three"
39+
}
40+
41+
42+
# works for variables
43+
my_var="pizza"
44+
45+
46+
my_other_var="no comments above me :("
47+

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /