-
-
Notifications
You must be signed in to change notification settings - Fork 45
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
0ca505e
commit 1f36838
Showing
5 changed files
with
454 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
import { | ||
CancellationToken, | ||
Emitter, | ||
Event, | ||
Hover, | ||
InlayHint, | ||
InlayHintLabelPart, | ||
InlayHintsProvider, | ||
LanguageClient, | ||
LinesTextDocument, | ||
MarkupContent, | ||
Position, | ||
Range, | ||
workspace, | ||
} from 'coc.nvim'; | ||
|
||
import * as typeInlayHintsParser from '../parsers/typeInlayHints'; | ||
|
||
export class TypeInlayHintsProvider implements InlayHintsProvider { | ||
private readonly _onDidChangeInlayHints = new Emitter<void>(); | ||
public readonly onDidChangeInlayHints: Event<void> = this._onDidChangeInlayHints.event; | ||
|
||
constructor(private client: LanguageClient) {} | ||
|
||
// eslint-disable-next-line @typescript-eslint/no-unused-vars | ||
async provideInlayHints(document: LinesTextDocument, _range: Range, _token: CancellationToken) { | ||
const inlayHints: InlayHint[] = []; | ||
|
||
const code = document.getText(); | ||
const parsed = typeInlayHintsParser.parse(code); | ||
if (!parsed) return []; | ||
|
||
const walker = new typeInlayHintsParser.TypeInlayHintsWalker(); | ||
walker.walk(parsed.parseTree); | ||
|
||
for (const item of walker.featureItems) { | ||
if (this.isDisableVariableTypes(item.inlayHintType)) continue; | ||
if (this.isDisableFunctionReturnTypes(item.inlayHintType)) continue; | ||
|
||
const startPosition = document.positionAt(item.startOffset); | ||
const endPosition = document.positionAt(item.endOffset); | ||
const hoverResponse = await this.getHoverAtOffset(document, startPosition); | ||
|
||
if (hoverResponse) { | ||
let inlayHintLabelValue: string | undefined = undefined; | ||
let inlayHintPosition: Position | undefined = undefined; | ||
|
||
if (item.inlayHintType === 'variable') { | ||
inlayHintLabelValue = this.getVariableHintAtHover(hoverResponse); | ||
} | ||
|
||
if (item.inlayHintType === 'functionReturn') { | ||
inlayHintLabelValue = this.getFunctionReturnHintAtHover(hoverResponse); | ||
} | ||
|
||
if (inlayHintLabelValue) { | ||
const inlayHintLabelPart: InlayHintLabelPart[] = [ | ||
{ | ||
value: inlayHintLabelValue, | ||
}, | ||
]; | ||
|
||
switch (item.inlayHintType) { | ||
case 'variable': | ||
inlayHintPosition = startPosition; | ||
break; | ||
case 'functionReturn': | ||
inlayHintPosition = endPosition; | ||
break; | ||
default: | ||
break; | ||
} | ||
|
||
if (inlayHintPosition) { | ||
const inlayHint: InlayHint = { | ||
label: inlayHintLabelPart, | ||
position: inlayHintPosition, | ||
}; | ||
|
||
inlayHints.push(inlayHint); | ||
} | ||
} | ||
} | ||
} | ||
|
||
return inlayHints; | ||
} | ||
|
||
private async getHoverAtOffset(document: LinesTextDocument, position: Position) { | ||
const params = { | ||
textDocument: { uri: document.uri }, | ||
position, | ||
}; | ||
|
||
return await this.client.sendRequest<Hover>('textDocument/hover', params); | ||
} | ||
|
||
private getVariableHintAtHover(hover: Hover): string | undefined { | ||
const contents = hover.contents as MarkupContent; | ||
if (contents) { | ||
if (contents.value.includes('(variable)')) { | ||
const text = contents.value.split(': ')[1].split('\n')[0].trim(); | ||
const hintText = ': ' + text; | ||
return hintText; | ||
} | ||
} | ||
} | ||
|
||
private getFunctionReturnHintAtHover(hover: Hover): string | undefined { | ||
const contents = hover.contents as MarkupContent; | ||
if (contents) { | ||
if (contents.value.includes('(function)') || contents.value.includes('(method)')) { | ||
const text = contents.value.split('->')[1].split('\n')[0].trim(); | ||
const hintText = '-> ' + text; | ||
return hintText; | ||
} | ||
} | ||
} | ||
|
||
private isDisableVariableTypes(inlayHintType: string) { | ||
if (!workspace.getConfiguration('pyright').get('inlayHints.variableTypes') && inlayHintType === 'variable') { | ||
return true; | ||
} | ||
return false; | ||
} | ||
|
||
private isDisableFunctionReturnTypes(inlayHintType: string) { | ||
if (!workspace.getConfiguration('pyright').get('inlayHints.functionReturnTypes') && inlayHintType === 'functionReturn') { | ||
return true; | ||
} | ||
return false; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
import { printParseNodeType } from '@zzzen/pyright-internal/dist/analyzer/parseTreeUtils'; | ||
import { ParseTreeWalker } from '@zzzen/pyright-internal/dist/analyzer/parseTreeWalker'; | ||
import { DiagnosticSink } from '@zzzen/pyright-internal/dist/common/diagnosticSink'; | ||
import { FunctionNode, MemberAccessNode, NameNode, ParseNode } from '@zzzen/pyright-internal/dist/parser/parseNodes'; | ||
import { ParseOptions, Parser, ParseResults } from '@zzzen/pyright-internal/dist/parser/parser'; | ||
|
||
export function parse(source: string) { | ||
let result: ParseResults | undefined = undefined; | ||
const parserOptions = new ParseOptions(); | ||
const diagSink = new DiagnosticSink(); | ||
const parser = new Parser(); | ||
try { | ||
result = parser.parseSourceFile(source, parserOptions, diagSink); | ||
} catch (e) {} | ||
return result; | ||
} | ||
|
||
export type TypeInlayHintsItemType = { | ||
inlayHintType: 'variable' | 'functionReturn'; | ||
startOffset: number; | ||
endOffset: number; | ||
value?: string; | ||
}; | ||
|
||
export class TypeInlayHintsWalker extends ParseTreeWalker { | ||
public featureItems: TypeInlayHintsItemType[] = []; | ||
|
||
override visitNode(node: ParseNode) { | ||
return super.visitNode(node); | ||
} | ||
|
||
override visitName(node: NameNode): boolean { | ||
if (node.parent) { | ||
const parentNodeType = printParseNodeType(node.parent.nodeType); | ||
// If the type already exists in the code, do not match. | ||
// The parent node is "TypeAnnotation" if the type exists. | ||
if (parentNodeType === 'Assignment') { | ||
this.featureItems.push({ | ||
inlayHintType: 'variable', | ||
startOffset: node.start, | ||
endOffset: node.start + node.length - 1, | ||
value: node.value, | ||
}); | ||
} | ||
} | ||
return super.visitName(node); | ||
} | ||
|
||
override visitMemberAccess(node: MemberAccessNode): boolean { | ||
if (node.parent) { | ||
const parentNodeType = printParseNodeType(node.parent.nodeType); | ||
// If the type already exists in the code, do not match. | ||
// The parent node is "TypeAnnotation" if the type exists. | ||
if (parentNodeType === 'Assignment') { | ||
this.featureItems.push({ | ||
inlayHintType: 'variable', | ||
startOffset: node.memberName.start, | ||
endOffset: node.memberName.start + node.memberName.length - 1, | ||
value: node.memberName.value, | ||
}); | ||
} | ||
} | ||
return super.visitMemberAccess(node); | ||
} | ||
|
||
override visitFunction(node: FunctionNode): boolean { | ||
// If the code describes a type, do not add the item. | ||
// Add item only if "node.returnTypeAnnotation" does not exist. | ||
if (!node.returnTypeAnnotation) { | ||
this.featureItems.push({ | ||
inlayHintType: 'functionReturn', | ||
startOffset: node.name.start, | ||
endOffset: node.suite.start, | ||
value: node.name.value, | ||
}); | ||
} | ||
return super.visitFunction(node); | ||
} | ||
} |
Oops, something went wrong.