GeezerVison is showing github.com — added signature provider (tooltips for arguments of functions/methods… · liming2013/pythonVSCode@1212945
File treeExpand file treeCollapse file tree Open diff view settings test/pythonFiles/docstrings
Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: pythonFiles/completion.py +40-17Lines changed: 40 additions & 17 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -95,6 +95,45 @@ def _get_call_signatures(self, script): | 95 | 95 | _signatures.append((signature, name, value)) | 96 | 96 | return _signatures | 97 | 97 |
| | 98 | + def _get_call_signatures_with_args(self, script): | | 99 | + """Extract call signatures from jedi.api.Script object in failsafe way. | | 100 | +
| | 101 | + Returns: | | 102 | + Array with dictionary | | 103 | + """ | | 104 | + _signatures = [] | | 105 | + try: | | 106 | + call_signatures = script.call_signatures() | | 107 | + except KeyError: | | 108 | + call_signatures = [] | | 109 | + for signature in call_signatures: | | 110 | + sig = {"name": "", "description": "", "docstring": "", | | 111 | + "paramindex": 0, "params": [], "bracketstart": []} | | 112 | + sig["description"] = signature.description | | 113 | + sig["docstring"] = signature.docstring() | | 114 | + sig["name"] = signature.name | | 115 | + sig["paramindex"] = signature.index | | 116 | + sig["bracketstart"].append(signature.index) | | 117 | + | | 118 | + _signatures.append(sig) | | 119 | + for pos, param in enumerate(signature.params): | | 120 | + if not param.name: | | 121 | + continue | | 122 | + if param.name == 'self' and pos == 0: | | 123 | + continue | | 124 | + if WORD_RE.match(param.name) is None: | | 125 | + continue | | 126 | + try: | | 127 | + name, value = param.description.split('=') | | 128 | + except ValueError: | | 129 | + name = param.description | | 130 | + value = None | | 131 | + #if name.startswith('*'): | | 132 | + # continue | | 133 | + #_signatures.append((signature, name, value)) | | 134 | + sig["params"].append({"name": name, "value":value, "docstring":param.docstring(), "description":param.description}) | | 135 | + return _signatures | | 136 | + | 98 | 137 | def _serialize_completions(self, script, identifier=None, prefix=''): | 99 | 138 | """Serialize response to be read from VSCode. | 100 | 139 |
| @@ -164,23 +203,7 @@ def _serialize_arguments(self, script, identifier=None): | 164 | 203 | Returns: | 165 | 204 | Serialized string to send to VSCode. | 166 | 205 | """ | 167 | | - seen = set() | 168 | | - arguments = [] | 169 | | - i = 1 | 170 | | - for _, name, value in self._get_call_signatures(script): | 171 | | - if not value: | 172 | | - arg = '${%s:%s}' % (i, name) | 173 | | - elif self.use_snippets == 'all': | 174 | | - arg = '%s=${%s:%s}' % (name, i, value) | 175 | | - else: | 176 | | - continue | 177 | | - if name not in seen: | 178 | | - seen.add(name) | 179 | | - arguments.append(arg) | 180 | | - i += 1 | 181 | | - snippet = '%s$0' % ', '.join(arguments) | 182 | | - return json.dumps({'id': identifier, 'results': [], | 183 | | - 'arguments': snippet}) | | 206 | + return json.dumps({"id": identifier, "results" : self._get_call_signatures_with_args(script) }) | 184 | 207 |
| 185 | 208 | def _serialize_definitions(self, definitions, identifier=None): | 186 | 209 | """Serialize response to be read from VSCode. | |
Collapse file Copy file name to clipboardExpand all lines: src/client/extension.ts +5-3Lines changed: 5 additions & 3 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -10,6 +10,7 @@ import {PythonFormattingEditProvider} from "./providers/formatProvider"; | 10 | 10 | import * as sortImports from "./sortImports"; | 11 | 11 | import {LintProvider} from "./providers/lintProvider"; | 12 | 12 | import {PythonSymbolProvider} from "./providers/symbolProvider"; | | 13 | +import {PythonSignatureProvider} from "./providers/signatureProvider"; | 13 | 14 | import {activateFormatOnSaveProvider} from "./providers/formatOnSaveProvider"; | 14 | 15 | import * as path from "path"; | 15 | 16 | import * as settings from "./common/configSettings"; | @@ -56,13 +57,14 @@ export function activate(context: vscode.ExtensionContext) { | 56 | 57 | ] | 57 | 58 | }); | 58 | 59 |
| 59 | | - context.subscriptions.push(vscode.languages.registerRenameProvider(PYTHON, new PythonRenameProvider(context))); | | 60 | + let renameProvider = new PythonRenameProvider(context); | | 61 | + context.subscriptions.push(vscode.languages.registerRenameProvider(PYTHON, renameProvider)); | 60 | 62 | context.subscriptions.push(vscode.languages.registerHoverProvider(PYTHON, new PythonHoverProvider(context))); | 61 | 63 | context.subscriptions.push(vscode.languages.registerDefinitionProvider(PYTHON, new PythonDefinitionProvider(context))); | 62 | 64 | context.subscriptions.push(vscode.languages.registerReferenceProvider(PYTHON, new PythonReferenceProvider(context))); | 63 | 65 | context.subscriptions.push(vscode.languages.registerCompletionItemProvider(PYTHON, new PythonCompletionItemProvider(context), ".")); | 64 | | - context.subscriptions.push(vscode.languages.registerDocumentSymbolProvider(PYTHON, new PythonSymbolProvider(context))); | 65 | | - | | 66 | + context.subscriptions.push(vscode.languages.registerDocumentSymbolProvider(PYTHON, new PythonSymbolProvider(context, renameProvider.JediProxy))); | | 67 | + context.subscriptions.push(vscode.languages.registerSignatureHelpProvider(PYTHON, new PythonSignatureProvider(context, renameProvider.JediProxy), "(", ",")); | 66 | 68 | context.subscriptions.push(vscode.languages.registerDocumentFormattingEditProvider(PYTHON, new PythonFormattingEditProvider(context, formatOutChannel, pythonSettings, vscode.workspace.rootPath))); | 67 | 69 | context.subscriptions.push(new LintProvider(context, lintingOutChannel, vscode.workspace.rootPath)); | 68 | 70 | } | |
Collapse file Copy file name to clipboardExpand all lines: src/client/providers/jediProxy.ts +44-8Lines changed: 44 additions & 8 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -309,6 +309,16 @@ function spawnProcess(dir: string) { | 309 | 309 | } | 310 | 310 | break; | 311 | 311 | } | | 312 | + case CommandType.Arguments: { | | 313 | + let defs = <any[]>response["results"]; | | 314 | + if (defs.length > 0) { | | 315 | + cmd.resolve(<IArgumentsResult>{ | | 316 | + requestId: cmd.id, | | 317 | + definitions: defs | | 318 | + }); | | 319 | + } | | 320 | + break; | | 321 | + } | 312 | 322 | } | 313 | 323 | } | 314 | 324 |
| @@ -472,7 +482,24 @@ export interface IReferenceResult extends ICommandResult { | 472 | 482 | references: IReference[]; | 473 | 483 | } | 474 | 484 | export interface ISymbolResult extends ICommandResult { | 475 | | - definitions: IDefinition[] | | 485 | + definitions: IDefinition[]; | | 486 | +} | | 487 | +export interface IArgumentsResult extends ICommandResult { | | 488 | + definitions: ISignature[]; | | 489 | +} | | 490 | + | | 491 | +export interface ISignature { | | 492 | + name: string; | | 493 | + docstring: string; | | 494 | + description: string; | | 495 | + paramindex: number; | | 496 | + params: IArgument[]; | | 497 | +} | | 498 | +export interface IArgument { | | 499 | + name: string; | | 500 | + value: string; | | 501 | + docstring: string; | | 502 | + description: string; | 476 | 503 | } | 477 | 504 |
| 478 | 505 | export interface IReference { | @@ -511,15 +538,24 @@ export class JediProxyHandler<R extends ICommandResult, T> { | 511 | 538 | private parseResponse: (data: R) => T; | 512 | 539 | private cancellationTokenSource: vscode.CancellationTokenSource; | 513 | 540 |
| 514 | | - public constructor(context: vscode.ExtensionContext, defaultCallbackData: T, parseResponse: (data: R) => T) { | 515 | | - if (pythonSettings.devOptions.indexOf("SINGLE_JEDI") >= 0) { | 516 | | - if (jediProxy_singleton === null) { | 517 | | - jediProxy_singleton = new JediProxy(context); | 518 | | - } | 519 | | - this.jediProxy = jediProxy_singleton; | | 541 | + public get JediProxy(): JediProxy { | | 542 | + return this.jediProxy; | | 543 | + } | | 544 | + | | 545 | + public constructor(context: vscode.ExtensionContext, defaultCallbackData: T, parseResponse: (data: R) => T, jediProxy: JediProxy = null) { | | 546 | + if (jediProxy) { | | 547 | + this.jediProxy = jediProxy; | 520 | 548 | } | 521 | 549 | else { | 522 | | - this.jediProxy = new JediProxy(context); | | 550 | + if (pythonSettings.devOptions.indexOf("SINGLE_JEDI") >= 0) { | | 551 | + if (jediProxy_singleton === null) { | | 552 | + jediProxy_singleton = new JediProxy(context); | | 553 | + } | | 554 | + this.jediProxy = jediProxy_singleton; | | 555 | + } | | 556 | + else { | | 557 | + this.jediProxy = new JediProxy(context); | | 558 | + } | 523 | 559 | } | 524 | 560 | this.defaultCallbackData = defaultCallbackData; | 525 | 561 | this.parseResponse = parseResponse; | |
Collapse file Copy file name to clipboardExpand all lines: src/client/providers/renameProvider.ts +3-1Lines changed: 3 additions & 1 deletion | Original file line number | Diff line number | Diff line change |
|---|
@@ -9,7 +9,9 @@ var _newName = ""; | 9 | 9 |
| 10 | 10 | export class PythonRenameProvider implements vscode.RenameProvider { | 11 | 11 | private jediProxyHandler: proxy.JediProxyHandler<proxy.IReferenceResult, vscode.WorkspaceEdit>; | 12 | | - | | 12 | + public get JediProxy(): proxy.JediProxy { | | 13 | + return this.jediProxyHandler.JediProxy; | | 14 | + } | 13 | 15 | public constructor(context: vscode.ExtensionContext) { | 14 | 16 | this.jediProxyHandler = new proxy.JediProxyHandler(context, null, PythonRenameProvider.parseData); | 15 | 17 | } | |
Collapse file Copy file name to clipboard +95Lines changed: 95 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| | 1 | +"use strict"; | | 2 | + | | 3 | +import * as vscode from "vscode"; | | 4 | +import {TextDocument, Position, CancellationToken, SignatureHelp, ExtensionContext} from "vscode"; | | 5 | +import * as proxy from "./jediProxy"; | | 6 | +import * as telemetryContracts from "../common/telemetryContracts"; | | 7 | + | | 8 | +const DOCSTRING_PARAM_PATTERNS = [ | | 9 | + "\\s*:type\\s*PARAMNAME:\\s*([^\\n, ]+)", // Sphinx | | 10 | + "\\s*:param\\s*(\\w?)\\s*PARAMNAME:[^\\n]+", // Sphinx param with type | | 11 | + "\\s*@type\\s*PARAMNAME:\\s*([^\\n, ]+)" // Epydoc | | 12 | +]; | | 13 | + | | 14 | +/** | | 15 | + * Extrct the documentation for parameters from a given docstring | | 16 | + * | | 17 | + * @param {string} paramName Name of the parameter | | 18 | + * @param {string} docString The docstring for the function | | 19 | + * @returns {string} Docstring for the parameter | | 20 | + */ | | 21 | +function extractParamDocString(paramName: string, docString: string): string { | | 22 | + let paramDocString = ""; | | 23 | + // In docstring the '*' is escaped with a backslash | | 24 | + paramName = paramName.replace(new RegExp("\\*", "g"), "\\\\\\*"); | | 25 | + | | 26 | + DOCSTRING_PARAM_PATTERNS.forEach(pattern => { | | 27 | + if (paramDocString.length > 0) { | | 28 | + return; | | 29 | + } | | 30 | + pattern = pattern.replace("PARAMNAME", paramName); | | 31 | + let regExp = new RegExp(pattern); | | 32 | + let matches = regExp.exec(docString); | | 33 | + if (matches && matches.length > 0) { | | 34 | + paramDocString = matches[0]; | | 35 | + if (paramDocString.indexOf(":") >= 0) { | | 36 | + paramDocString = paramDocString.substring(paramDocString.indexOf(":") + 1); | | 37 | + } | | 38 | + if (paramDocString.indexOf(":") >= 0) { | | 39 | + paramDocString = paramDocString.substring(paramDocString.indexOf(":") + 1); | | 40 | + } | | 41 | + } | | 42 | + }); | | 43 | + | | 44 | + return paramDocString.trim(); | | 45 | +} | | 46 | +export class PythonSignatureProvider implements vscode.SignatureHelpProvider { | | 47 | + private jediProxyHandler: proxy.JediProxyHandler<proxy.IArgumentsResult, vscode.SignatureHelp>; | | 48 | + | | 49 | + public constructor(context: vscode.ExtensionContext, jediProxy: proxy.JediProxy = null) { | | 50 | + this.jediProxyHandler = new proxy.JediProxyHandler(context, null, PythonSignatureProvider.parseData, jediProxy); | | 51 | + } | | 52 | + private static parseData(data: proxy.IArgumentsResult): vscode.SignatureHelp { | | 53 | + if (data && Array.isArray(data.definitions) && data.definitions.length > 0) { | | 54 | + let signature = new SignatureHelp(); | | 55 | + signature.activeSignature = 0; | | 56 | + | | 57 | + data.definitions.forEach(def => { | | 58 | + signature.activeParameter = def.paramindex; | | 59 | + // Don't display the documentation, as vs code doesn't format the docmentation | | 60 | + // i.e. line feeds are not respected, long content is stripped | | 61 | + let sig = <vscode.SignatureInformation>{ | | 62 | + // documentation: def.docstring, | | 63 | + label: def.description, | | 64 | + parameters: [] | | 65 | + }; | | 66 | + sig.parameters = def.params.map(arg => { | | 67 | + if (arg.docstring.length === 0) { | | 68 | + arg.docstring = extractParamDocString(arg.name, def.docstring); | | 69 | + } | | 70 | + return <vscode.ParameterInformation>{ | | 71 | + documentation: arg.docstring.length > 0 ? arg.docstring : arg.description, | | 72 | + label: arg.description.length > 0 ? arg.description : arg.name | | 73 | + }; | | 74 | + }); | | 75 | + signature.signatures.push(sig); | | 76 | + }); | | 77 | + return signature; | | 78 | + } | | 79 | + | | 80 | + return new SignatureHelp(); | | 81 | + } | | 82 | + provideSignatureHelp(document: TextDocument, position: Position, token: CancellationToken): Thenable<SignatureHelp> { | | 83 | + return new Promise<SignatureHelp>((resolve, reject) => { | | 84 | + let cmd: proxy.ICommand<proxy.IArgumentsResult> = { | | 85 | + telemetryEvent: telemetryContracts.IDE.Symbol, | | 86 | + command: proxy.CommandType.Arguments, | | 87 | + fileName: document.fileName, | | 88 | + columnIndex: position.character, | | 89 | + lineIndex: position.line, | | 90 | + source: document.getText() | | 91 | + }; | | 92 | + this.jediProxyHandler.sendCommand(cmd, resolve, token); | | 93 | + }); | | 94 | + } | | 95 | +} |
Collapse file Copy file name to clipboardExpand all lines: src/client/providers/symbolProvider.ts +2-2Lines changed: 2 additions & 2 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -7,8 +7,8 @@ import * as telemetryContracts from "../common/telemetryContracts"; | 7 | 7 | export class PythonSymbolProvider implements vscode.DocumentSymbolProvider { | 8 | 8 | private jediProxyHandler: proxy.JediProxyHandler<proxy.ISymbolResult, vscode.SymbolInformation[]>; | 9 | 9 |
| 10 | | - public constructor(context: vscode.ExtensionContext) { | 11 | | - this.jediProxyHandler = new proxy.JediProxyHandler(context, null, PythonSymbolProvider.parseData); | | 10 | + public constructor(context: vscode.ExtensionContext, jediProxy: proxy.JediProxy = null) { | | 11 | + this.jediProxyHandler = new proxy.JediProxyHandler(context, null, PythonSymbolProvider.parseData, jediProxy); | 12 | 12 | } | 13 | 13 | private static parseData(data: proxy.ISymbolResult): vscode.SymbolInformation[] { | 14 | 14 | if (data) { | |
Collapse file Copy file name to clipboardExpand all lines: src/test/pythonFiles/docstrings/one.py
|