Skip to content

Commit c3b43dd

Browse files
patrysDonJayamanne
authored andcommitted
Show symbol type on hover (DonJayamanne#657)
1 parent dfd1792 commit c3b43dd

File tree

10 files changed

+137
-91
lines changed

10 files changed

+137
-91
lines changed

pythonFiles/completion.py

Lines changed: 24 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -266,14 +266,14 @@ def _extract_range(self, definition):
266266
from jedi import common
267267
from jedi.parser.utils import load_parser
268268
# get the scope range
269-
if definition.type in ['class', 'function', 'method']:
270-
scope = definition._name.get_parent_scope()
269+
if definition.type in ['class', 'function']:
270+
scope = definition._definition
271271
start_line = scope.start_pos[0] - 1
272272
start_column = scope.start_pos[1]
273273
end_line = scope.end_pos[0] - 1
274274
end_column = scope.end_pos[1]
275275
# get the lines
276-
path = definition._name.get_parent_until().path
276+
path = definition._definition.get_parent_until().path
277277
parser = load_parser(path)
278278
lines = common.splitlines(parser.source)
279279
lines[end_line] = lines[end_line][:end_column]
@@ -334,27 +334,26 @@ def _serialize_definitions(self, definitions, identifier=None):
334334
def _serialize_tooltip(self, definitions, identifier=None):
335335
_definitions = []
336336
for definition in definitions:
337-
if definition.module_path:
338-
if definition.type == 'import':
339-
definition = self._top_definition(definition)
340-
if not definition.module_path:
341-
continue
342-
343-
description = definition.docstring()
344-
if description is not None:
345-
description = description.strip()
346-
if not description:
347-
description = self._additional_info(definition)
348-
_definition = {
349-
'text': definition.name,
350-
'type': self._get_definition_type(definition),
351-
'fileName': definition.module_path,
352-
'description': description,
353-
'line': definition.line - 1,
354-
'column': definition.column
355-
}
356-
_definitions.append(_definition)
357-
break
337+
signature = definition.name
338+
description = None
339+
if definition.type in ['class', 'function']:
340+
signature = self._generate_signature(definition)
341+
description = definition.docstring(raw=True).strip()
342+
if not description and not definition.get_line_code():
343+
# jedi returns an empty string for compiled objects
344+
description = definition.docstring().strip()
345+
if definition.type == 'module':
346+
signature = definition.full_name
347+
description = definition.docstring(raw=True).strip()
348+
if not description and not definition.get_line_code():
349+
# jedi returns an empty string for compiled objects
350+
description = definition.docstring().strip()
351+
_definition = {
352+
'type': self._get_definition_type(definition),
353+
'description': description,
354+
'signature': signature
355+
}
356+
_definitions.append(_definition)
358357
return json.dumps({'id': identifier, 'results': _definitions})
359358

360359
def _serialize_usages(self, usages, identifier=None):
@@ -429,7 +428,7 @@ def _process_request(self, request):
429428
script.goto_assignments(), request['id']))
430429
if lookup == 'tooltip':
431430
return self._write_response(self._serialize_tooltip(
432-
script.goto_assignments(), request['id']))
431+
script.goto_definitions(), request['id']))
433432
elif lookup == 'arguments':
434433
return self._write_response(self._serialize_arguments(
435434
script, request['id']))

src/client/providers/completionProvider.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { PythonSettings } from '../common/configSettings';
1010
const pythonSettings = PythonSettings.getInstance();
1111

1212
export class PythonCompletionItemProvider implements vscode.CompletionItemProvider {
13-
private jediProxyHandler: proxy.JediProxyHandler<proxy.ICompletionResult, vscode.CompletionItem[]>;
13+
private jediProxyHandler: proxy.JediProxyHandler<proxy.ICompletionResult>;
1414

1515
public constructor(context: vscode.ExtensionContext, jediProxy: proxy.JediProxy = null) {
1616
this.jediProxyHandler = new proxy.JediProxyHandler(context, jediProxy);

src/client/providers/definitionProvider.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import * as proxy from './jediProxy';
55
import * as telemetryContracts from "../common/telemetryContracts";
66

77
export class PythonDefinitionProvider implements vscode.DefinitionProvider {
8-
private jediProxyHandler: proxy.JediProxyHandler<proxy.IDefinitionResult, vscode.Definition>;
8+
private jediProxyHandler: proxy.JediProxyHandler<proxy.IDefinitionResult>;
99
public get JediProxy(): proxy.JediProxy {
1010
return this.jediProxyHandler.JediProxy;
1111
}

src/client/providers/hoverProvider.ts

Lines changed: 37 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3,58 +3,65 @@
33
import * as vscode from 'vscode';
44
import * as proxy from './jediProxy';
55
import * as telemetryContracts from "../common/telemetryContracts";
6-
import { extractHoverInfo} from './jediHelpers';
76

87
export class PythonHoverProvider implements vscode.HoverProvider {
9-
private jediProxyHandler: proxy.JediProxyHandler<proxy.ICompletionResult, vscode.Hover>;
8+
private jediProxyHandler: proxy.JediProxyHandler<proxy.IHoverResult>;
109

1110
public constructor(context: vscode.ExtensionContext, jediProxy: proxy.JediProxy = null) {
1211
this.jediProxyHandler = new proxy.JediProxyHandler(context, jediProxy);
1312
}
14-
public provideHover(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): Thenable<vscode.Hover> {
13+
private static parseData(data: proxy.IHoverResult): vscode.Hover {
14+
let results = [];
15+
data.items.forEach(item => {
16+
let { description, signature } = item;
17+
switch (item.kind) {
18+
case vscode.SymbolKind.Constructor:
19+
case vscode.SymbolKind.Function:
20+
case vscode.SymbolKind.Method: {
21+
signature = 'def ' + signature;
22+
break;
23+
}
24+
case vscode.SymbolKind.Class: {
25+
signature = 'class ' + signature;
26+
break;
27+
}
28+
}
29+
results.push({language: 'python', value: signature});
30+
if (item.description) {
31+
results.push(description);
32+
}
33+
});
34+
return new vscode.Hover(results);
35+
}
36+
public async provideHover(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): Promise<vscode.Hover> {
1537
var filename = document.fileName;
1638
if (document.lineAt(position.line).text.match(/^\s*\/\//)) {
17-
return Promise.resolve(null);
39+
return null;
1840
}
1941
if (position.character <= 0) {
20-
return Promise.resolve(null);
42+
return null;
2143
}
2244

2345
var range = document.getWordRangeAtPosition(position);
2446
if (!range || range.isEmpty) {
25-
return Promise.resolve(null);
47+
return null;
2648
}
27-
var columnIndex = range.start.character < range.end.character ? range.start.character + 2 : range.end.character;
28-
var cmd: proxy.ICommand<proxy.ICompletionResult> = {
29-
command: proxy.CommandType.Completions,
49+
50+
var cmd: proxy.ICommand<proxy.IDefinitionResult> = {
51+
command: proxy.CommandType.Hover,
3052
fileName: filename,
31-
columnIndex: columnIndex,
53+
columnIndex: range.end.character,
3254
lineIndex: position.line
3355
};
3456
if (document.isDirty) {
3557
cmd.source = document.getText();
3658
}
3759

38-
return this.jediProxyHandler.sendCommand(cmd, token).then(data => {
39-
if (!data || !Array.isArray(data.items) || data.items.length === 0) {
40-
return;
41-
}
42-
// Find the right items
43-
const wordUnderCursor = document.getText(range);
44-
const completionItem = data.items.filter(item => item.text === wordUnderCursor);
45-
if (completionItem.length === 0) {
46-
return;
47-
}
48-
var definition = completionItem[0];
49-
var txt = definition.description || definition.text;
50-
if (typeof txt !== 'string' || txt.length === 0) {
51-
return;
52-
}
53-
if (wordUnderCursor === txt) {
54-
return;
55-
}
60+
const data = await this.jediProxyHandler.sendCommand(cmd, token);
61+
if (!data || !data.items.length) {
62+
return;
63+
}
5664

57-
return extractHoverInfo(definition);
58-
});
65+
return PythonHoverProvider.parseData(data);
5966
}
6067
}

src/client/providers/jediHelpers.ts

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,3 @@ export function extractSignatureAndDocumentation(definition: proxy.IAutoComplete
4747
}
4848
return [signature, lines.join(EOL).trim().replace(/^\s+|\s+$/g, '').trim()];
4949
}
50-
51-
export function extractHoverInfo(definition: proxy.IAutoCompleteItem): vscode.Hover {
52-
const parts = extractSignatureAndDocumentation(definition, true);
53-
const hoverInfo: vscode.MarkedString[] = parts[0].length === 0 ? [] : [{ language: 'python', value: parts[0] }];
54-
if (parts[1].length > 0) {
55-
hoverInfo.push(parts[1]);
56-
}
57-
return new vscode.Hover(hoverInfo);
58-
}

src/client/providers/jediProxy.ts

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ function getMappedVSCodeSymbol(pythonType: string): vscode.SymbolKind {
106106
export enum CommandType {
107107
Arguments,
108108
Completions,
109+
Hover,
109110
Usages,
110111
Definitions,
111112
Symbols
@@ -115,6 +116,7 @@ var commandNames = new Map<CommandType, string>();
115116
commandNames.set(CommandType.Arguments, "arguments");
116117
commandNames.set(CommandType.Completions, "completions");
117118
commandNames.set(CommandType.Definitions, "definitions");
119+
commandNames.set(CommandType.Hover, "tooltip");
118120
commandNames.set(CommandType.Usages, "usages");
119121
commandNames.set(CommandType.Symbols, "names");
120122

@@ -291,7 +293,7 @@ function spawnProcess(dir: string) {
291293
const originalType = <string><any>item.type;
292294
item.type = getMappedVSCodeType(originalType);
293295
item.kind = getMappedVSCodeSymbol(originalType);
294-
item.raw_type = getMappedVSCodeType(originalType);
296+
item.rawType = getMappedVSCodeType(originalType);
295297
});
296298

297299
let completionResult: ICompletionResult = {
@@ -313,6 +315,7 @@ function spawnProcess(dir: string) {
313315
defResult.definition = {
314316
fileName: def.fileName,
315317
text: def.text,
318+
rawType: originalType,
316319
type: getMappedVSCodeType(originalType),
317320
kind: getMappedVSCodeSymbol(originalType),
318321
container: def.container,
@@ -328,6 +331,22 @@ function spawnProcess(dir: string) {
328331
cmd.deferred.resolve(defResult);
329332
break;
330333
}
334+
case CommandType.Hover: {
335+
var defs = <any[]>response['results'];
336+
var defResult: IHoverResult = {
337+
requestId: cmd.id,
338+
items: defs.map(def => {
339+
return {
340+
kind: getMappedVSCodeSymbol(def.type),
341+
description: def.description,
342+
signature: def.signature
343+
}
344+
})
345+
};
346+
347+
cmd.deferred.resolve(defResult);
348+
break;
349+
}
331350
case CommandType.Symbols: {
332351
var defs = <any[]>response['results'];
333352
defs = Array.isArray(defs) ? defs : [];
@@ -340,6 +359,7 @@ function spawnProcess(dir: string) {
340359
return {
341360
fileName: def.fileName,
342361
text: def.text,
362+
rawType: originalType,
343363
type: getMappedVSCodeType(originalType),
344364
kind: getMappedVSCodeSymbol(originalType),
345365
container: def.container,
@@ -563,6 +583,9 @@ export interface ICommandResult {
563583
export interface ICompletionResult extends ICommandResult {
564584
items: IAutoCompleteItem[];
565585
}
586+
export interface IHoverResult extends ICommandResult {
587+
items: IHoverItem[];
588+
}
566589
export interface IDefinitionResult extends ICommandResult {
567590
definition: IDefinition;
568591
}
@@ -600,7 +623,7 @@ export interface IReference {
600623

601624
export interface IAutoCompleteItem {
602625
type: vscode.CompletionItemKind;
603-
raw_type: vscode.CompletionItemKind;
626+
rawType: vscode.CompletionItemKind;
604627
kind: vscode.SymbolKind;
605628
text: string;
606629
description: string;
@@ -614,6 +637,7 @@ interface IDefinitionRange {
614637
endColumn: number;
615638
}
616639
export interface IDefinition {
640+
rawType: string;
617641
type: vscode.CompletionItemKind;
618642
kind: vscode.SymbolKind;
619643
text: string;
@@ -622,7 +646,13 @@ export interface IDefinition {
622646
range: IDefinitionRange;
623647
}
624648

625-
export class JediProxyHandler<R extends ICommandResult, T> {
649+
export interface IHoverItem {
650+
kind: vscode.SymbolKind;
651+
description: string;
652+
signature: string;
653+
}
654+
655+
export class JediProxyHandler<R extends ICommandResult> {
626656
private jediProxy: JediProxy;
627657
private lastToken: vscode.CancellationToken;
628658
private lastCommandId: number;

src/client/providers/referenceProvider.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import * as telemetryContracts from "../common/telemetryContracts";
66

77

88
export class PythonReferenceProvider implements vscode.ReferenceProvider {
9-
private jediProxyHandler: proxy.JediProxyHandler<proxy.IReferenceResult, vscode.Location[]>;
9+
private jediProxyHandler: proxy.JediProxyHandler<proxy.IReferenceResult>;
1010

1111
public constructor(context: vscode.ExtensionContext, jediProxy: proxy.JediProxy = null) {
1212
this.jediProxyHandler = new proxy.JediProxyHandler(context, jediProxy);

src/client/providers/signatureProvider.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ function extractParamDocString(paramName: string, docString: string): string {
4444
return paramDocString.trim();
4545
}
4646
export class PythonSignatureProvider implements vscode.SignatureHelpProvider {
47-
private jediProxyHandler: proxy.JediProxyHandler<proxy.IArgumentsResult, vscode.SignatureHelp>;
47+
private jediProxyHandler: proxy.JediProxyHandler<proxy.IArgumentsResult>;
4848

4949
public constructor(context: vscode.ExtensionContext, jediProxy: proxy.JediProxy = null) {
5050
this.jediProxyHandler = new proxy.JediProxyHandler(context, jediProxy);

src/client/providers/symbolProvider.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import * as proxy from './jediProxy';
55
import * as telemetryContracts from "../common/telemetryContracts";
66

77
export class PythonSymbolProvider implements vscode.DocumentSymbolProvider {
8-
private jediProxyHandler: proxy.JediProxyHandler<proxy.ISymbolResult, vscode.SymbolInformation[]>;
8+
private jediProxyHandler: proxy.JediProxyHandler<proxy.ISymbolResult>;
99

1010
public constructor(context: vscode.ExtensionContext, jediProxy: proxy.JediProxy = null) {
1111
this.jediProxyHandler = new proxy.JediProxyHandler(context, jediProxy);

0 commit comments

Comments
 (0)