Skip to content

Commit 8d79301

Browse files
author
Mikhail Arkhipov
authored
Fetch tooltip details on-demand for auto-completions (#368)
* Basic tokenizer * Fixed property names * Tests, round I * Tests, round II * tokenizer test * Remove temorary change * Fix merge issue * Merge conflict * Merge conflict * Completion test * Fix last line * Fix javascript math * Make test await for results * Add license headers * Rename definitions to types * Round I * License headers * Separate completion and doc fetching * Test fixes * Undo temp change * CR feedback * Restore signature, make info colorized * Provide details string * Fix tests
1 parent 8c40af3 commit 8d79301

14 files changed

+821
-1663
lines changed

package-lock.json

Lines changed: 102 additions & 1014 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pythonFiles/completion.py

Lines changed: 1 addition & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -208,16 +208,6 @@ def _serialize_completions(self, script, identifier=None, prefix=''):
208208
_completion['snippet'] = '%s=$1$0' % name
209209
_completion['text'] = name
210210
_completion['displayText'] = name
211-
if self.show_doc_strings:
212-
try:
213-
_completion['description'] = signature.docstring()
214-
_completion['raw_docstring'] = signature.docstring(raw=True)
215-
except Exception:
216-
_completion['description'] = ''
217-
_completion['raw_docstring'] = ''
218-
else:
219-
_completion['description'] = self._generate_signature(
220-
signature)
221211
_completions.append(_completion)
222212

223213
try:
@@ -227,22 +217,11 @@ def _serialize_completions(self, script, identifier=None, prefix=''):
227217
except :
228218
completions = []
229219
for completion in completions:
230-
if self.show_doc_strings:
231-
try:
232-
description = completion.docstring()
233-
except Exception:
234-
description = ''
235-
else:
236-
description = self._generate_signature(completion)
237-
238220
try:
239-
rawDocstring = completion.docstring(raw=True)
240221
_completion = {
241222
'text': completion.name,
242223
'type': self._get_definition_type(completion),
243224
'raw_type': completion.type,
244-
'description': description,
245-
'raw_docstring': rawDocstring,
246225
'rightLabel': self._additional_info(completion)
247226
}
248227
except Exception:
@@ -252,11 +231,7 @@ def _serialize_completions(self, script, identifier=None, prefix=''):
252231
if c['text'] == _completion['text']:
253232
c['type'] = _completion['type']
254233
c['raw_type'] = _completion['raw_type']
255-
if len(c['description']) == 0 and len(c['raw_docstring']) == 0:
256-
c['description'] = _completion['description']
257-
c['raw_docstring'] = _completion['description']
258-
259-
234+
260235
if any([c['text'].split('=')[0] == _completion['text']
261236
for c in _completions]):
262237
# ignore function arguments we already have

src/client/common/configSettings.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ export interface ITerminalSettings {
124124
launchArgs: string[];
125125
}
126126

127-
function isTestExecution(): boolean {
127+
export function isTestExecution(): boolean {
128128
// tslint:disable-next-line:interface-name no-string-literal
129129
return process.env['VSC_PYTHON_CI_TEST'] === '1';
130130
}

src/client/jedi/parsers/CompletionParser.ts

Lines changed: 0 additions & 27 deletions
This file was deleted.

src/client/jedi/parsers/HoverParser.ts

Lines changed: 0 additions & 71 deletions
This file was deleted.

src/client/languageServices/jediProxyFactory.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Disposable, Uri, workspace } from 'vscode';
2-
import { JediProxy, JediProxyHandler, ICommandResult } from '../providers/jediProxy';
2+
import { ICommandResult, JediProxy, JediProxyHandler } from '../providers/jediProxy';
33

44
export class JediFactory implements Disposable {
55
private disposables: Disposable[];
@@ -20,8 +20,7 @@ export class JediFactory implements Disposable {
2020
if (!workspacePath) {
2121
if (Array.isArray(workspace.workspaceFolders) && workspace.workspaceFolders.length > 0) {
2222
workspacePath = workspace.workspaceFolders[0].uri.fsPath;
23-
}
24-
else {
23+
} else {
2524
workspacePath = __dirname;
2625
}
2726
}
Lines changed: 23 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,76 +1,39 @@
11
'use strict';
22

33
import * as vscode from 'vscode';
4-
import { Position, ProviderResult, SnippetString, Uri } from 'vscode';
5-
import { PythonSettings } from '../common/configSettings';
6-
import { Tokenizer } from '../language/tokenizer';
7-
import { TokenType } from '../language/types';
4+
import { isTestExecution } from '../common/configSettings';
85
import { JediFactory } from '../languageServices/jediProxyFactory';
96
import { captureTelemetry } from '../telemetry';
107
import { COMPLETION } from '../telemetry/constants';
11-
import { extractSignatureAndDocumentation } from './jediHelpers';
12-
import * as proxy from './jediProxy';
8+
import { CompletionSource } from './completionSource';
139

1410
export class PythonCompletionItemProvider implements vscode.CompletionItemProvider {
11+
private completionSource: CompletionSource;
1512

16-
public constructor(private jediFactory: JediFactory) { }
17-
private static parseData(data: proxy.ICompletionResult, resource: Uri): vscode.CompletionItem[] {
18-
if (data && data.items.length > 0) {
19-
return data.items.map(item => {
20-
const sigAndDocs = extractSignatureAndDocumentation(item);
21-
const completionItem = new vscode.CompletionItem(item.text);
22-
completionItem.kind = item.type;
23-
completionItem.documentation = sigAndDocs[1].length === 0 ? item.description : sigAndDocs[1];
24-
completionItem.detail = sigAndDocs[0].split(/\r?\n/).join('');
25-
if (PythonSettings.getInstance(resource).autoComplete.addBrackets === true &&
26-
(item.kind === vscode.SymbolKind.Function || item.kind === vscode.SymbolKind.Method)) {
27-
completionItem.insertText = new SnippetString(item.text).appendText('(').appendTabstop().appendText(')');
28-
}
29-
30-
// ensure the built in memebers are at the bottom
31-
completionItem.sortText = (completionItem.label.startsWith('__') ? 'z' : (completionItem.label.startsWith('_') ? 'y' : '__')) + completionItem.label;
32-
return completionItem;
33-
});
34-
}
35-
return [];
13+
constructor(jediFactory: JediFactory) {
14+
this.completionSource = new CompletionSource(jediFactory);
3615
}
16+
3717
@captureTelemetry(COMPLETION)
38-
public provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): ProviderResult<vscode.CompletionItem[]> {
39-
if (position.character <= 0) {
40-
return Promise.resolve([]);
41-
}
42-
const filename = document.fileName;
43-
const lineText = document.lineAt(position.line).text;
44-
if (lineText.match(/^\s*\/\//)) {
45-
return Promise.resolve([]);
18+
public async provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken):
19+
Promise<vscode.CompletionItem[]> {
20+
const items = await this.completionSource.getVsCodeCompletionItems(document, position, token);
21+
if (isTestExecution()) {
22+
for (let i = 0; i < Math.min(3, items.length); i += 1) {
23+
items[i] = await this.resolveCompletionItem(items[i], token);
24+
}
4625
}
47-
// Suppress completion inside string and comments
48-
if (this.isPositionInsideStringOrComment(document, position)) {
49-
return Promise.resolve([]);
50-
}
51-
const type = proxy.CommandType.Completions;
52-
const columnIndex = position.character;
53-
54-
const source = document.getText();
55-
const cmd: proxy.ICommand<proxy.ICommandResult> = {
56-
command: type,
57-
fileName: filename,
58-
columnIndex: columnIndex,
59-
lineIndex: position.line,
60-
source: source
61-
};
62-
63-
return this.jediFactory.getJediProxyHandler<proxy.ICompletionResult>(document.uri).sendCommand(cmd, token).then(data => {
64-
return PythonCompletionItemProvider.parseData(data, document.uri);
65-
});
26+
return items;
6627
}
6728

68-
private isPositionInsideStringOrComment(document: vscode.TextDocument, position: vscode.Position): boolean {
69-
const tokenizeTo = position.translate(1, 0);
70-
const text = document.getText(new vscode.Range(new Position(0, 0), tokenizeTo));
71-
const t = new Tokenizer();
72-
const tokens = t.Tokenize(text);
73-
const index = tokens.getItemContaining(document.offsetAt(position));
74-
return index >= 0 && (tokens[index].TokenType === TokenType.String || tokens[index].TokenType === TokenType.Comment);
29+
public async resolveCompletionItem(item: vscode.CompletionItem, token: vscode.CancellationToken): Promise<vscode.CompletionItem> {
30+
if (!item.documentation) {
31+
const itemInfos = await this.completionSource.getDocumentation(item, token);
32+
if (itemInfos && itemInfos.length > 0) {
33+
item.detail = itemInfos[0].detail;
34+
item.documentation = itemInfos[0].documentation;
35+
}
36+
}
37+
return item;
7538
}
7639
}

0 commit comments

Comments
 (0)