diff --git a/.vscode/settings.json b/.vscode/settings.json index 024a84287c..7f46ede174 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -81,10 +81,6 @@ "ruff.nativeServer": true, "typescript.inlayHints.parameterNames.enabled": "literals", "typescript.inlayHints.enumMemberValues.enabled": true, - "typescript.inlayHints.functionLikeReturnTypes.enabled": true, - "typescript.inlayHints.parameterTypes.enabled": true, - "typescript.inlayHints.propertyDeclarationTypes.enabled": true, - "typescript.inlayHints.variableTypes.enabled": true, "jest.jestCommandLine": "npm run jest --", "files.readonlyInclude": { ".pdm-build/**/*": true, diff --git a/packages/pyright-internal/src/analyzer/semanticTokensWalker.ts b/packages/pyright-internal/src/analyzer/semanticTokensWalker.ts index 2ea3814340..883443b0f6 100644 --- a/packages/pyright-internal/src/analyzer/semanticTokensWalker.ts +++ b/packages/pyright-internal/src/analyzer/semanticTokensWalker.ts @@ -45,9 +45,7 @@ export class SemanticTokensWalker extends ParseTreeWalker { super(); } override visitClass(node: ClassNode): boolean { - this._addItem(node.d.name.start, node.d.name.length, SemanticTokenTypes.class, [ - SemanticTokenModifiers.definition, - ]); + this._addItemForNameNode(node.d.name, SemanticTokenTypes.class, [SemanticTokenModifiers.definition]); return super.visitClass(node); } @@ -58,9 +56,9 @@ export class SemanticTokensWalker extends ParseTreeWalker { } //TODO: whats the correct type here if ((node.a as any).declaration?.isMethod) { - this._addItem(node.d.name.start, node.d.name.length, SemanticTokenTypes.method, modifiers); + this._addItemForNameNode(node.d.name, SemanticTokenTypes.method, modifiers); } else { - this._addItem(node.d.name.start, node.d.name.length, SemanticTokenTypes.function, modifiers); + this._addItemForNameNode(node.d.name, SemanticTokenTypes.function, modifiers); } // parameters & return type are covered by visitName return super.visitFunction(node); @@ -68,9 +66,7 @@ export class SemanticTokensWalker extends ParseTreeWalker { override visitParameter(node: ParameterNode): boolean { if (node.d.name) { - this._addItem(node.d.name.start, node.d.name.length, SemanticTokenTypes.parameter, [ - SemanticTokenModifiers.definition, - ]); + this._addItemForNameNode(node.d.name, SemanticTokenTypes.parameter, [SemanticTokenModifiers.definition]); } return super.visitParameter(node); } @@ -82,17 +78,17 @@ export class SemanticTokensWalker extends ParseTreeWalker { // as-is and their individual symbols are highlighted with their normal token type. // see discussion in https://github.com/DetachHead/basedpyright/issues/278#issuecomment-2517502311 if (node.d.expr.nodeType === ParseNodeType.Name) { - this._addItem(node.d.expr.start, node.d.expr.length, SemanticTokenTypes.decorator, []); + this._addItemForNameNode(node.d.expr, SemanticTokenTypes.decorator, []); } return super.visitDecorator(node); } override visitImportAs(node: ImportAsNode): boolean { for (const part of node.d.module.d.nameParts) { - this._addItem(part.start, part.length, SemanticTokenTypes.namespace, []); + this._addItemForNameNode(part, SemanticTokenTypes.namespace, []); } if (node.d.alias) { - this._addItem(node.d.alias.start, node.d.alias.length, SemanticTokenTypes.namespace, []); + this._addItemForNameNode(node.d.alias, SemanticTokenTypes.namespace, []); } return super.visitImportAs(node); } @@ -104,7 +100,7 @@ export class SemanticTokensWalker extends ParseTreeWalker { override visitImportFrom(node: ImportFromNode): boolean { for (const part of node.d.module.d.nameParts) { - this._addItem(part.start, part.length, SemanticTokenTypes.namespace, []); + this._addItemForNameNode(part, SemanticTokenTypes.namespace, []); } return super.visitImportFrom(node); } @@ -133,9 +129,7 @@ export class SemanticTokensWalker extends ParseTreeWalker { method: 'set', }); if (declaredType && isClass(declaredType) && declaredType.shared.flags & ClassTypeFlags.PropertyClass) { - this._addItem(node.d.member.start, node.d.member.length, SemanticTokenTypes.variable, [ - SemanticTokenModifiers.readonly, - ]); + this._addItemForNameNode(node.d.member, SemanticTokenTypes.variable, [SemanticTokenModifiers.readonly]); } } return super.visitMemberAccess(node); @@ -146,41 +140,41 @@ export class SemanticTokensWalker extends ParseTreeWalker { case TypeCategory.Function: if (type.flags & TypeFlags.Instance) { if ((type as FunctionType).shared.declaration?.isMethod) { - this._addItem(node.start, node.length, SemanticTokenTypes.method, []); + this._addItemForNameNode(node, SemanticTokenTypes.method, []); } else { const modifiers = this.builtinModules.has(type.shared.moduleName) ? [SemanticTokenModifiers.defaultLibrary, CustomSemanticTokenModifiers.builtin] : []; - this._addItem(node.start, node.length, SemanticTokenTypes.function, modifiers); + this._addItemForNameNode(node, SemanticTokenTypes.function, modifiers); } } else { // type alias to Callable - this._addItem(node.start, node.length, SemanticTokenTypes.type, []); + this._addItemForNameNode(node, SemanticTokenTypes.type, []); } return; case TypeCategory.Overloaded: if (type.flags & TypeFlags.Instance) { const details = OverloadedType.getOverloads(type)[0].shared; if (details.declaration?.isMethod) { - this._addItem(node.start, node.length, SemanticTokenTypes.method, []); + this._addItemForNameNode(node, SemanticTokenTypes.method, []); } else { const modifiers = this.builtinModules.has(details.moduleName) ? [SemanticTokenModifiers.defaultLibrary, CustomSemanticTokenModifiers.builtin] : []; - this._addItem(node.start, node.length, SemanticTokenTypes.function, modifiers); + this._addItemForNameNode(node, SemanticTokenTypes.function, modifiers); } } else { // dunno if this is possible but better safe than sorry!!! - this._addItem(node.start, node.length, SemanticTokenTypes.type, []); + this._addItemForNameNode(node, SemanticTokenTypes.type, []); } return; case TypeCategory.Module: - this._addItem(node.start, node.length, SemanticTokenTypes.namespace, []); + this._addItemForNameNode(node, SemanticTokenTypes.namespace, []); return; case TypeCategory.Any: if (type.props?.specialForm) { - this._addItem(node.start, node.length, SemanticTokenTypes.type, []); + this._addItemForNameNode(node, SemanticTokenTypes.type, []); return; } // eslint-disable-next-line no-fallthrough -- intentional fallthrough. these are handled below @@ -192,7 +186,7 @@ export class SemanticTokensWalker extends ParseTreeWalker { return; case TypeCategory.Union: if (!(type.flags & TypeFlags.Instance)) { - this._addItem(node.start, node.length, SemanticTokenTypes.type, []); + this._addItemForNameNode(node, SemanticTokenTypes.type, []); return; } break; @@ -220,7 +214,7 @@ export class SemanticTokensWalker extends ParseTreeWalker { const modifiers = isBuiltIn ? [SemanticTokenModifiers.defaultLibrary, CustomSemanticTokenModifiers.builtin] : []; - this._addItem(node.start, node.length, SemanticTokenTypes.class, modifiers); + this._addItemForNameNode(node, SemanticTokenTypes.class, modifiers); return; } } @@ -238,7 +232,7 @@ export class SemanticTokensWalker extends ParseTreeWalker { (typeResult.type.category === TypeCategory.Never && !typeResult.includesVariableDecl) || (getTypeAliasInfo(type) && !typeResult.includesIllegalTypeAliasDecl) ) { - this._addItem(node.start, node.length, SemanticTokenTypes.type, []); + this._addItemForNameNode(node, SemanticTokenTypes.type, []); return; } } @@ -247,13 +241,13 @@ export class SemanticTokensWalker extends ParseTreeWalker { const parent = declarations[0].node.parent as FunctionNode | LambdaNode; // Avoid duplicates for parameters visited by `visitParameter` if (!parent.d.params.some((param) => param.d.name?.id === node.id)) { - this._addItem(node.start, node.length, SemanticTokenTypes.parameter, []); + this._addItemForNameNode(node, SemanticTokenTypes.parameter, []); } } else if (type?.category === TypeCategory.TypeVar && !(type.flags & TypeFlags.Instance)) { // `cls` method parameter is treated as a TypeVar in some special methods (methods // with @classmethod decorator, `__new__`, `__init_subclass__`, etc.) so we need to // check first if it's a parameter before checking that it's a TypeVar - this._addItem(node.start, node.length, SemanticTokenTypes.typeParameter, []); + this._addItemForNameNode(node, SemanticTokenTypes.typeParameter, []); return; } else if ( (type?.category === TypeCategory.Unknown || type?.category === TypeCategory.Any) && @@ -261,12 +255,15 @@ export class SemanticTokensWalker extends ParseTreeWalker { ) { return; } else if (isConstantName(node.d.value) || (symbol && this._evaluator.isFinalVariable(symbol))) { - this._addItem(node.start, node.length, SemanticTokenTypes.variable, [SemanticTokenModifiers.readonly]); + this._addItemForNameNode(node, SemanticTokenTypes.variable, [SemanticTokenModifiers.readonly]); } else { - this._addItem(node.start, node.length, SemanticTokenTypes.variable, []); + this._addItemForNameNode(node, SemanticTokenTypes.variable, []); } } + private _addItemForNameNode = (node: NameNode, type: string, modifiers: string[]) => + this._addItem(node.d.token.start, node.d.token.length, type, modifiers); + private _addItem(start: number, length: number, type: string, modifiers: string[]) { this.items.push({ type, modifiers, start, length }); } diff --git a/packages/pyright-internal/src/tests/samples/semantic_highlighting/variable.py b/packages/pyright-internal/src/tests/samples/semantic_highlighting/variable.py index d25d49e0ff..aa43df0798 100644 --- a/packages/pyright-internal/src/tests/samples/semantic_highlighting/variable.py +++ b/packages/pyright-internal/src/tests/samples/semantic_highlighting/variable.py @@ -1 +1,4 @@ -a = 1 \ No newline at end of file +foo = 1 +bar = ( + foo +) \ No newline at end of file diff --git a/packages/pyright-internal/src/tests/semanticTokensProvider.test.ts b/packages/pyright-internal/src/tests/semanticTokensProvider.test.ts index 2b75e0d6f6..0b9abe2697 100644 --- a/packages/pyright-internal/src/tests/semanticTokensProvider.test.ts +++ b/packages/pyright-internal/src/tests/semanticTokensProvider.test.ts @@ -4,7 +4,11 @@ import { semanticTokenizeSampleFile } from './testUtils'; if (process.platform !== 'win32' || !process.env['CI']) { test('variable', () => { const result = semanticTokenizeSampleFile('variable.py'); - expect(result).toStrictEqual([{ type: 'variable', length: 1, start: 0, modifiers: [] }]); + expect(result).toStrictEqual([ + { type: 'variable', start: 0, length: 3, modifiers: [] }, + { type: 'variable', start: 8, length: 3, modifiers: [] }, + { type: 'variable', start: 20, length: 3, modifiers: [] }, + ]); }); test('type annotation', () => {