Check completion item kind, non-property completion fast path (#1099)

This commit is contained in:
Jake Bailey 2020-10-14 15:32:07 -07:00 committed by GitHub
parent 97f8483392
commit b45ce5b492
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 150 additions and 14 deletions

View File

@ -3,7 +3,7 @@
"private": true,
"scripts": {
"postinstall": "npm run bootstrap",
"bootstrap": "node ./build/skipBootstrap.js || lerna bootstrap",
"bootstrap": "node ./build/skipBootstrap.js || lerna bootstrap --no-ci",
"clean": "lerna run --no-bail --stream clean",
"install:all": "npm install && lerna exec --no-bail npm install",
"update:all": "node ./build/updateDeps.js",

View File

@ -20,7 +20,7 @@ import {
import { ImportLookup } from '../analyzer/analyzerFileInfo';
import * as AnalyzerNodeInfo from '../analyzer/analyzerNodeInfo';
import { Declaration, DeclarationType } from '../analyzer/declaration';
import { Declaration, DeclarationType, FunctionDeclaration } from '../analyzer/declaration';
import { convertDocStringToMarkdown, convertDocStringToPlainText } from '../analyzer/docStringConversion';
import { ImportedModuleDescriptor, ImportResolver } from '../analyzer/importResolver';
import * as ParseTreeUtils from '../analyzer/parseTreeUtils';
@ -1567,9 +1567,11 @@ export class CompletionProvider {
: CompletionItemKind.Variable;
case DeclarationType.Function: {
const functionType = this._evaluator.getTypeOfFunction(resolvedDeclaration.node);
if (functionType && isProperty(functionType.decoratedType)) {
return CompletionItemKind.Property;
if (this._isPossiblePropertyDeclaration(resolvedDeclaration)) {
const functionType = this._evaluator.getTypeOfFunction(resolvedDeclaration.node);
if (functionType && isProperty(functionType.decoratedType)) {
return CompletionItemKind.Property;
}
}
return resolvedDeclaration.isMethod ? CompletionItemKind.Method : CompletionItemKind.Function;
}
@ -1625,4 +1627,10 @@ export class CompletionProvider {
return { completionList };
}
private _isPossiblePropertyDeclaration(decl: FunctionDeclaration) {
// Do cheap check using only nodes that will cover 99.9% cases
// before doing more expensive type evaluation.
return decl.isMethod && decl.node.decorators.length > 0;
}
}

View File

@ -17,6 +17,7 @@ await helper.verifyCompletion('included', 'markdown', {
completions: [
{
label: 'Test',
kind: Consts.CompletionItemKind.Class,
documentation: '```\nfrom testLib import Test\n```',
detail: 'Auto-import',
},

View File

@ -31,6 +31,7 @@ await helper.verifyCompletion('included', 'markdown', {
completions: [
{
label: 'test1',
kind: Consts.CompletionItemKind.Module,
documentation: '```\nfrom testLib import test1\n```',
detail: 'Auto-import',
},

View File

@ -18,6 +18,7 @@ await helper.verifyCompletion('excluded', 'markdown', {
completions: [
{
label: 'Test',
kind: Consts.CompletionItemKind.Class,
},
],
},

View File

@ -13,6 +13,7 @@ await helper.verifyCompletion('included', 'markdown', {
completions: [
{
label: 'Test',
kind: Consts.CompletionItemKind.Class,
documentation: '```\nfrom test2 import Test\n```',
detail: 'Auto-import',
},

View File

@ -13,6 +13,7 @@ await helper.verifyCompletion('included', 'plaintext', {
completions: [
{
label: 'Test',
kind: Consts.CompletionItemKind.Class,
documentation: 'from test2 import Test',
detail: 'Auto-import',
},

View File

@ -32,6 +32,7 @@ await helper.verifyCompletion('exact', 'markdown', {
completions: [
{
label: 'MyShadow',
kind: Consts.CompletionItemKind.Class,
documentation: '```\nfrom testLib import MyShadow\n```',
detail: 'Auto-import',
},

View File

@ -20,6 +20,7 @@ await helper.verifyCompletion('included', 'markdown', {
completions: [
{
label: 'os',
kind: Consts.CompletionItemKind.Module,
documentation: '```\nimport os\n```',
detail: 'Auto-import',
},
@ -35,6 +36,7 @@ await helper.verifyCompletion('included', 'markdown', {
completions: [
{
label: 'sys',
kind: Consts.CompletionItemKind.Module,
documentation: '```\nimport sys\n```',
detail: 'Auto-import',
},

View File

@ -7,6 +7,6 @@
// @ts-ignore
await helper.verifyCompletion('excluded', 'markdown', {
marker1: {
completions: [{ label: 'capitalize' }],
completions: [{ label: 'capitalize', kind: undefined }],
},
});

View File

@ -15,16 +15,18 @@
// @ts-ignore
await helper.verifyCompletion('exact', 'markdown', {
marker1: { completions: [{ label: 'localtime' }] },
marker2: { completions: [{ label: 'aaaaaa' }] },
marker1: { completions: [{ label: 'localtime', kind: Consts.CompletionItemKind.Function }] },
marker2: { completions: [{ label: 'aaaaaa', kind: Consts.CompletionItemKind.Variable }] },
marker3: {
completions: [
{
label: 'some_func1',
kind: Consts.CompletionItemKind.Function,
documentation: '```python\nsome_func1: (a: Unknown) -> None\n```\n---\nsome function docs',
},
{
label: 'some_func2',
kind: Consts.CompletionItemKind.Function,
documentation: '```python\nsome_func2: (a: Unknown) -> None\n```\n---\nanother function docs',
},
],

View File

@ -7,6 +7,6 @@
// @ts-ignore
await helper.verifyCompletion('included', 'markdown', {
marker1: {
completions: [{ label: 'numerator' }],
completions: [{ label: 'numerator', kind: Consts.CompletionItemKind.Property }],
},
});

View File

@ -47,6 +47,7 @@ await helper.verifyCompletion('included', 'markdown', {
completions: [
{
label: 'Validator',
kind: Consts.CompletionItemKind.Class,
documentation: '```python\nclass Validator()\n```\n---\nThe validator class',
},
],
@ -55,6 +56,7 @@ await helper.verifyCompletion('included', 'markdown', {
completions: [
{
label: 'is_valid',
kind: Consts.CompletionItemKind.Method,
documentation:
'```python\nis_valid: (text: str) -> bool\n```\n---\nChecks if the input string is valid.',
},
@ -64,10 +66,12 @@ await helper.verifyCompletion('included', 'markdown', {
completions: [
{
label: 'read_only_prop',
kind: Consts.CompletionItemKind.Property,
documentation: '```python\nread_only_prop: bool (property)\n```\n---\nThe read-only property.',
},
{
label: 'read_write_prop',
kind: Consts.CompletionItemKind.Property,
documentation: '```python\nread_write_prop: bool (property)\n```\n---\nThe read-write property.',
},
],

View File

@ -37,6 +37,7 @@ await helper.verifyCompletion('included', 'markdown', {
completions: [
{
label: 'Validator',
kind: Consts.CompletionItemKind.Class,
documentation: '```python\nclass Validator()\n```\n---\nThe validator class',
},
],
@ -45,6 +46,7 @@ await helper.verifyCompletion('included', 'markdown', {
completions: [
{
label: 'is_valid',
kind: Consts.CompletionItemKind.Method,
documentation:
'```python\nis_valid: (text: str) -> bool\n```\n---\nChecks if the input string is valid.',
},
@ -54,10 +56,12 @@ await helper.verifyCompletion('included', 'markdown', {
completions: [
{
label: 'read_only_prop',
kind: Consts.CompletionItemKind.Property,
documentation: '```python\nread_only_prop: bool (property)\n```\n---\nThe read-only property.',
},
{
label: 'read_write_prop',
kind: Consts.CompletionItemKind.Property,
documentation: '```python\nread_write_prop: bool (property)\n```\n---\nThe read-write property.',
},
],

View File

@ -37,6 +37,7 @@ await helper.verifyCompletion('included', 'markdown', {
completions: [
{
label: 'Validator',
kind: Consts.CompletionItemKind.Class,
documentation: '```python\nclass Validator()\n```\n---\nThe validator class',
},
],
@ -45,6 +46,7 @@ await helper.verifyCompletion('included', 'markdown', {
completions: [
{
label: 'is_valid',
kind: Consts.CompletionItemKind.Method,
documentation:
'```python\nis_valid: (text: str) -> bool\n```\n---\nChecks if the input string is valid.',
},
@ -54,10 +56,12 @@ await helper.verifyCompletion('included', 'markdown', {
completions: [
{
label: 'read_only_prop',
kind: Consts.CompletionItemKind.Property,
documentation: '```python\nread_only_prop: bool (property)\n```\n---\nThe read-only property.',
},
{
label: 'read_write_prop',
kind: Consts.CompletionItemKind.Property,
documentation: '```python\nread_write_prop: bool (property)\n```\n---\nThe read-write property.',
},
],

View File

@ -32,6 +32,7 @@ await helper.verifyCompletion('included', 'markdown', {
completions: [
{
label: 'Validator',
kind: Consts.CompletionItemKind.Class,
documentation: '```python\nclass Validator()\n```\n---\nThe validator class',
},
],
@ -40,6 +41,7 @@ await helper.verifyCompletion('included', 'markdown', {
completions: [
{
label: 'is_valid',
kind: Consts.CompletionItemKind.Method,
documentation:
'```python\nis_valid: (text: str) -> bool\n```\n---\nChecks if the input string is valid.',
},
@ -49,10 +51,12 @@ await helper.verifyCompletion('included', 'markdown', {
completions: [
{
label: 'read_only_prop',
kind: Consts.CompletionItemKind.Property,
documentation: '```python\nread_only_prop: bool (property)\n```\n---\nThe read-only property.',
},
{
label: 'read_write_prop',
kind: Consts.CompletionItemKind.Property,
documentation: '```python\nread_write_prop: bool (property)\n```\n---\nThe read-write property.',
},
],
@ -61,6 +65,7 @@ await helper.verifyCompletion('included', 'markdown', {
completions: [
{
label: 'is_valid',
kind: Consts.CompletionItemKind.Method,
documentation:
'```python\nis_valid: (self: Validator, text: str) -> bool\n```\n---\nChecks if the input string is valid.',
},

View File

@ -21,6 +21,7 @@ await helper.verifyCompletion('included', 'markdown', {
completions: [
{
label: 'func',
kind: Consts.CompletionItemKind.Function,
documentation: '```python\nfunc(x: str) -> str\nfunc(x: int) -> int\n```\n---\nfunc docs',
},
],

View File

@ -9,16 +9,16 @@
// @ts-ignore
await helper.verifyCompletion('excluded', 'markdown', {
marker1: {
completions: [{ label: 'param1' }],
completions: [{ label: 'param1', kind: undefined }],
},
marker2: {
completions: [{ label: 'param1' }],
completions: [{ label: 'param1', kind: undefined }],
},
});
// @ts-ignore
await helper.verifyCompletion('included', 'markdown', {
marker3: {
completions: [{ label: 'param1=' }],
completions: [{ label: 'param1=', kind: Consts.CompletionItemKind.Variable }],
},
});

View File

@ -15,16 +15,18 @@
// @ts-ignore
await helper.verifyCompletion('exact', 'plaintext', {
marker1: { completions: [{ label: 'localtime' }] },
marker2: { completions: [{ label: 'aaaaaa' }] },
marker1: { completions: [{ label: 'localtime', kind: Consts.CompletionItemKind.Function }] },
marker2: { completions: [{ label: 'aaaaaa', kind: Consts.CompletionItemKind.Variable }] },
marker3: {
completions: [
{
label: 'some_func1',
kind: Consts.CompletionItemKind.Function,
documentation: 'some_func1: (a: Unknown) -> None\n\nsome function docs',
},
{
label: 'some_func2',
kind: Consts.CompletionItemKind.Function,
documentation: 'some_func2: (a: Unknown) -> None\n\nanother function docs',
},
],

View File

@ -0,0 +1,28 @@
/// <reference path="fourslash.ts" />
// @filename: test.py
//// class C:
//// def __init__(self):
//// self._x = None
////
//// @property
//// def prop(self):
//// pass
////
//// @prop.setter
//// def prop(self, value):
//// pass
////
//// C()./*marker*/prop
// @ts-ignore
await helper.verifyCompletion('included', 'markdown', {
marker: {
completions: [
{
label: 'prop',
kind: Consts.CompletionItemKind.Property,
},
],
},
});

View File

@ -20,18 +20,22 @@ await helper.verifyCompletion('included', 'markdown', {
completions: [
{
label: 'method1',
kind: Consts.CompletionItemKind.Method,
documentation: '```python\nmethod1: () -> None\n```\n---\nMethod 1.',
},
{
label: 'new_method',
kind: Consts.CompletionItemKind.Method,
documentation: '```python\nnew_method: () -> None\n```\n',
},
{
label: 'prop1',
kind: Consts.CompletionItemKind.Property,
documentation: '```python\nprop1: Literal[2] (property)\n```\n---\nProperty 1.',
},
{
label: 'var1',
kind: Consts.CompletionItemKind.Variable,
documentation: '```python\nvar1: int\n```\n',
},
],

View File

@ -23,9 +23,37 @@
*/
declare namespace _ {
type CompletionItemKind =
| 1
| 2
| 3
| 4
| 5
| 6
| 7
| 8
| 9
| 10
| 11
| 12
| 13
| 14
| 15
| 16
| 17
| 18
| 19
| 20
| 21
| 22
| 23
| 24
| 25;
type FourSlashCompletionVerificationMode = 'exact' | 'included' | 'excluded';
interface FourSlashCompletionItem {
label: string;
kind: CompletionItemKind | undefined;
insertionText?: string;
documentation?: string;
detail?: string;
@ -251,4 +279,32 @@ declare namespace Consts {
*/
const Write: 3;
}
namespace CompletionItemKind {
const Text: 1;
const Method: 2;
const Function: 3;
const Constructor: 4;
const Field: 5;
const Variable: 6;
const Class: 7;
const Interface: 8;
const Module: 9;
const Property: 10;
const Unit: 11;
const Value: 12;
const Enum: 13;
const Keyword: 14;
const Snippet: 15;
const Color: 16;
const File: 17;
const Reference: 18;
const Folder: 19;
const EnumMember: 20;
const Constant: 21;
const Struct: 22;
const Event: 23;
const Operator: 24;
const TypeParameter: 25;
}
}

View File

@ -50,21 +50,27 @@ await helper.verifyCompletion('exact', 'markdown', {
completions: [
{
label: 'foofoofoo0',
kind: Consts.CompletionItemKind.Variable,
},
{
label: 'foofoofoo2',
kind: Consts.CompletionItemKind.Variable,
},
{
label: 'foofoofoo3',
kind: Consts.CompletionItemKind.Variable,
},
{
label: 'foofoofoo5',
kind: Consts.CompletionItemKind.Variable,
},
{
label: 'foofoofoo7',
kind: Consts.CompletionItemKind.Variable,
},
{
label: 'foofoofoo9',
kind: Consts.CompletionItemKind.Variable,
},
],
},

View File

@ -30,6 +30,7 @@ await helper.verifyCompletion('included', 'markdown', {
completions: [
{
label: 'method1',
kind: Consts.CompletionItemKind.Method,
documentation: '```python\nmethod1: () -> None\n```\n---\nMethod docs',
},
],

View File

@ -23,4 +23,6 @@ export namespace Consts {
orderImports = 'pyright.organizeimports',
addMissingOptionalToParam = 'pyright.addoptionalforparam',
}
export import CompletionItemKind = lsp.CompletionItemKind;
}

View File

@ -868,6 +868,7 @@ export class TestState {
const actual: CompletionItem = result.completionList.items[actualIndex];
assert.equal(actual.label, expected.label);
assert.equal(actual.detail, expected.detail);
assert.equal(actual.kind, expected.kind);
if (expectedCompletions[i].documentation !== undefined) {
if (actual.documentation === undefined) {
this.program.resolveCompletionItem(