[PylanceBot] Pull Pylance with Pyright 1.1.265 (#3783)

Co-authored-by: HeeJae Chang <hechang@microsoft.com>
This commit is contained in:
PylanceBot 2022-08-03 14:14:43 -07:00 committed by GitHub
parent cdba65d4c8
commit e4a0f2088c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 3098 additions and 3046 deletions

5850
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -23,16 +23,16 @@
"@types/glob": "^7.2.0", "@types/glob": "^7.2.0",
"@types/node": "^17.0.45", "@types/node": "^17.0.45",
"@types/yargs": "^16.0.4", "@types/yargs": "^16.0.4",
"@typescript-eslint/eslint-plugin": "^5.31.0", "@typescript-eslint/eslint-plugin": "^5.32.0",
"@typescript-eslint/parser": "^5.31.0", "@typescript-eslint/parser": "^5.32.0",
"detect-indent": "^6.1.0", "detect-indent": "^6.1.0",
"eslint": "^8.20.0", "eslint": "^8.21.0",
"eslint-config-prettier": "^8.5.0", "eslint-config-prettier": "^8.5.0",
"eslint-plugin-simple-import-sort": "^7.0.0", "eslint-plugin-simple-import-sort": "^7.0.0",
"glob": "^7.2.3", "glob": "^7.2.3",
"jsonc-parser": "^3.1.0", "jsonc-parser": "^3.1.0",
"lerna": "^5.2.0", "lerna": "^5.3.0",
"npm-check-updates": "^12.5.12", "npm-check-updates": "^16.0.5",
"p-queue": "^6.6.2", "p-queue": "^6.6.2",
"prettier": "2.7.1", "prettier": "2.7.1",
"syncpack": "^5.8.15", "syncpack": "^5.8.15",

View File

@ -38,7 +38,6 @@ import { ParseResults } from '../parser/parser';
import * as AnalyzerNodeInfo from './analyzerNodeInfo'; import * as AnalyzerNodeInfo from './analyzerNodeInfo';
import { ModuleNameAndType } from './importResolver'; import { ModuleNameAndType } from './importResolver';
import { ImportResult, ImportType } from './importResult'; import { ImportResult, ImportType } from './importResult';
import { ParseTreeWalker } from './parseTreeWalker';
import * as SymbolNameUtils from './symbolNameUtils'; import * as SymbolNameUtils from './symbolNameUtils';
export interface ImportStatement { export interface ImportStatement {
@ -72,6 +71,12 @@ export interface ImportNameInfo {
export interface ImportNameWithModuleInfo extends ImportNameInfo { export interface ImportNameWithModuleInfo extends ImportNameInfo {
module: ModuleNameAndType; module: ModuleNameAndType;
nameForImportFrom?: string;
}
export interface ModuleNameInfo {
name: string;
nameForImportFrom?: string;
} }
// Determines which import grouping should be used when sorting imports. // Determines which import grouping should be used when sorting imports.
@ -110,10 +115,6 @@ export function compareImportStatements(a: ImportStatement, b: ImportStatement)
return a.moduleName < b.moduleName ? -1 : 1; return a.moduleName < b.moduleName ? -1 : 1;
} }
export function getAllImports(root: ModuleNode, token: CancellationToken) {
return ImportCollector.collect(root, token);
}
// Looks for top-level 'import' and 'import from' statements and provides // Looks for top-level 'import' and 'import from' statements and provides
// an ordered list and a map (by file path). // an ordered list and a map (by file path).
export function getTopLevelImports(parseTree: ModuleNode, includeImplicitImports = false): ImportStatements { export function getTopLevelImports(parseTree: ModuleNode, includeImplicitImports = false): ImportStatements {
@ -338,13 +339,13 @@ export function getTextEditsForAutoImportInsertions(
return []; return [];
} }
const map = createMapFromItems(importNameInfo, (i) => i.module.moduleName); const map = createMapFromItems(importNameInfo, (i) => `${i.module.moduleName}-${i.nameForImportFrom ?? ''}`);
for (const importInfo of map.values()) { for (const importInfo of map.values()) {
insertionEdits.push( insertionEdits.push(
..._getInsertionEditsForAutoImportInsertion( ..._getInsertionEditsForAutoImportInsertion(
importInfo, importInfo,
{ name: importInfo[0].module.moduleName, nameForImportFrom: importInfo[0].nameForImportFrom },
importStatements, importStatements,
importInfo[0].module.moduleName,
getImportGroupFromModuleNameAndType(importInfo[0].module), getImportGroupFromModuleNameAndType(importInfo[0].module),
parseResults, parseResults,
invocationPosition invocationPosition
@ -357,16 +358,16 @@ export function getTextEditsForAutoImportInsertions(
export function getTextEditsForAutoImportInsertion( export function getTextEditsForAutoImportInsertion(
importNameInfo: ImportNameInfo[] | ImportNameInfo, importNameInfo: ImportNameInfo[] | ImportNameInfo,
moduleNameInfo: ModuleNameInfo,
importStatements: ImportStatements, importStatements: ImportStatements,
moduleName: string,
importGroup: ImportGroup, importGroup: ImportGroup,
parseResults: ParseResults, parseResults: ParseResults,
invocationPosition: Position invocationPosition: Position
): TextEditAction[] { ): TextEditAction[] {
const insertionEdits = _getInsertionEditsForAutoImportInsertion( const insertionEdits = _getInsertionEditsForAutoImportInsertion(
importNameInfo, importNameInfo,
moduleNameInfo,
importStatements, importStatements,
moduleName,
importGroup, importGroup,
parseResults, parseResults,
invocationPosition invocationPosition
@ -423,8 +424,8 @@ function _convertInsertionEditsToTextEdits(parseResults: ParseResults, insertion
function _getInsertionEditsForAutoImportInsertion( function _getInsertionEditsForAutoImportInsertion(
importNameInfo: ImportNameInfo[] | ImportNameInfo, importNameInfo: ImportNameInfo[] | ImportNameInfo,
moduleNameInfo: ModuleNameInfo,
importStatements: ImportStatements, importStatements: ImportStatements,
moduleName: string,
importGroup: ImportGroup, importGroup: ImportGroup,
parseResults: ParseResults, parseResults: ParseResults,
invocationPosition: Position invocationPosition: Position
@ -449,7 +450,10 @@ function _getInsertionEditsForAutoImportInsertion(
// Add from import statements next. // Add from import statements next.
const fromImports = map.get('from'); const fromImports = map.get('from');
if (fromImports) { if (fromImports) {
appendToEdits(fromImports, (names) => `from ${moduleName} import ${names.join(', ')}`); appendToEdits(
fromImports,
(names) => `from ${moduleNameInfo.nameForImportFrom ?? moduleNameInfo.name} import ${names.join(', ')}`
);
} }
return insertionEdits; return insertionEdits;
@ -464,7 +468,7 @@ function _getInsertionEditsForAutoImportInsertion(
function appendToEdits(importNameInfo: ImportNameInfo[], importStatementGetter: (n: string[]) => string) { function appendToEdits(importNameInfo: ImportNameInfo[], importStatementGetter: (n: string[]) => string) {
const importNames = importNameInfo const importNames = importNameInfo
.map((i) => getImportAsText(i, moduleName)) .map((i) => getImportAsText(i, moduleNameInfo.name))
.sort((a, b) => _compareImportNames(a.sortText, b.sortText)) .sort((a, b) => _compareImportNames(a.sortText, b.sortText))
.reduce((set, v) => addIfUnique(set, v.text), [] as string[]); .reduce((set, v) => addIfUnique(set, v.text), [] as string[]);
@ -472,7 +476,7 @@ function _getInsertionEditsForAutoImportInsertion(
_getInsertionEditForAutoImportInsertion( _getInsertionEditForAutoImportInsertion(
importStatementGetter(importNames), importStatementGetter(importNames),
importStatements, importStatements,
moduleName, moduleNameInfo.name,
importGroup, importGroup,
parseResults, parseResults,
invocationPosition invocationPosition
@ -897,34 +901,3 @@ export function getResolvedFilePath(importResult: ImportResult | undefined) {
// Regular case. // Regular case.
return importResult.resolvedPaths[importResult.resolvedPaths.length - 1]; return importResult.resolvedPaths[importResult.resolvedPaths.length - 1];
} }
class ImportCollector extends ParseTreeWalker {
private readonly _imports: (ImportNode | ImportFromNode)[] = [];
private constructor(private _token: CancellationToken) {
super();
}
public static collect(root: ModuleNode, token: CancellationToken) {
const collector = new ImportCollector(token);
collector.walk(root);
return collector._imports;
}
override walk(node: ParseNode): void {
throwIfCancellationRequested(this._token);
super.walk(node);
}
override visitImport(node: ImportNode) {
this._imports.push(node);
return true;
}
override visitImportFrom(node: ImportFromNode) {
this._imports.push(node);
return true;
}
}

View File

@ -47,6 +47,7 @@ import { DocumentRange, doesRangeContain, doRangesIntersect, Position, Range } f
import { Duration, timingStats } from '../common/timing'; import { Duration, timingStats } from '../common/timing';
import { import {
AutoImporter, AutoImporter,
AutoImportOptions,
AutoImportResult, AutoImportResult,
buildModuleSymbolsMap, buildModuleSymbolsMap,
ModuleSymbolMap, ModuleSymbolMap,
@ -1254,9 +1255,7 @@ export class Program {
range: Range, range: Range,
similarityLimit: number, similarityLimit: number,
nameMap: AbbreviationMap | undefined, nameMap: AbbreviationMap | undefined,
libraryMap: Map<string, IndexResults> | undefined, options: AutoImportOptions,
lazyEdit: boolean,
allowVariableInAll: boolean,
token: CancellationToken token: CancellationToken
): AutoImportResult[] { ): AutoImportResult[] {
const sourceFileInfo = this._getSourceFileInfoFromPath(filePath); const sourceFileInfo = this._getSourceFileInfoFromPath(filePath);
@ -1288,10 +1287,14 @@ export class Program {
const writtenWord = fileContents.substr(textRange.start, textRange.length); const writtenWord = fileContents.substr(textRange.start, textRange.length);
const map = this._buildModuleSymbolsMap( const map = this._buildModuleSymbolsMap(
sourceFileInfo, sourceFileInfo,
!!libraryMap, !!options.libraryMap,
/* includeIndexUserSymbols */ true, /* includeIndexUserSymbols */ true,
token token
); );
options.patternMatcher =
options.patternMatcher ?? ((p, t) => computeCompletionSimilarity(p, t) > similarityLimit);
const autoImporter = new AutoImporter( const autoImporter = new AutoImporter(
this._configOptions.findExecEnvironment(filePath), this._configOptions.findExecEnvironment(filePath),
this._importResolver, this._importResolver,
@ -1299,12 +1302,7 @@ export class Program {
range.start, range.start,
new CompletionMap(), new CompletionMap(),
map, map,
{ options
lazyEdit,
allowVariableInAll,
libraryMap,
patternMatcher: (p, t) => computeCompletionSimilarity(p, t) > similarityLimit,
}
); );
// Filter out any name that is already defined in the current scope. // Filter out any name that is already defined in the current scope.

View File

@ -55,6 +55,7 @@ import {
} from '../common/pathUtils'; } from '../common/pathUtils';
import { DocumentRange, Position, Range } from '../common/textRange'; import { DocumentRange, Position, Range } from '../common/textRange';
import { timingStats } from '../common/timing'; import { timingStats } from '../common/timing';
import { AutoImportOptions } from '../languageService/autoImporter';
import { AbbreviationMap, CompletionOptions, CompletionResultsList } from '../languageService/completionProvider'; import { AbbreviationMap, CompletionOptions, CompletionResultsList } from '../languageService/completionProvider';
import { DefinitionFilter } from '../languageService/definitionProvider'; import { DefinitionFilter } from '../languageService/definitionProvider';
import { IndexResults, WorkspaceSymbolCallback } from '../languageService/documentSymbolProvider'; import { IndexResults, WorkspaceSymbolCallback } from '../languageService/documentSymbolProvider';
@ -316,20 +317,11 @@ export class AnalyzerService {
range: Range, range: Range,
similarityLimit: number, similarityLimit: number,
nameMap: AbbreviationMap | undefined, nameMap: AbbreviationMap | undefined,
lazyEdit: boolean, options: AutoImportOptions,
allowVariableInAll: boolean,
token: CancellationToken token: CancellationToken
) { ) {
return this._program.getAutoImports( options.libraryMap = options.libraryMap ?? this._backgroundAnalysisProgram.getIndexing(filePath);
filePath, return this._program.getAutoImports(filePath, range, similarityLimit, nameMap, options, token);
range,
similarityLimit,
nameMap,
this._backgroundAnalysisProgram.getIndexing(filePath),
lazyEdit,
allowVariableInAll,
token
);
} }
getDefinitionForPosition( getDefinitionForPosition(

View File

@ -107,6 +107,7 @@ import { DocumentRange, Position, Range } from './common/textRange';
import { UriParser } from './common/uriParser'; import { UriParser } from './common/uriParser';
import { convertWorkspaceDocumentEdits } from './common/workspaceEditUtils'; import { convertWorkspaceDocumentEdits } from './common/workspaceEditUtils';
import { AnalyzerServiceExecutor } from './languageService/analyzerServiceExecutor'; import { AnalyzerServiceExecutor } from './languageService/analyzerServiceExecutor';
import { ImportFormat } from './languageService/autoImporter';
import { CompletionItemData, CompletionOptions, CompletionResultsList } from './languageService/completionProvider'; import { CompletionItemData, CompletionOptions, CompletionResultsList } from './languageService/completionProvider';
import { DefinitionFilter } from './languageService/definitionProvider'; import { DefinitionFilter } from './languageService/definitionProvider';
import { convertToFlatSymbols, WorkspaceSymbolCallback } from './languageService/documentSymbolProvider'; import { convertToFlatSymbols, WorkspaceSymbolCallback } from './languageService/documentSymbolProvider';
@ -1317,6 +1318,7 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
lazyEdit: this.client.completionItemResolveSupportsAdditionalTextEdits, lazyEdit: this.client.completionItemResolveSupportsAdditionalTextEdits,
autoImport: true, autoImport: true,
extraCommitChars: false, extraCommitChars: false,
importFormat: ImportFormat.Absolute,
}; };
} }

View File

@ -7,17 +7,21 @@
import { CancellationToken, CompletionItemKind, SymbolKind } from 'vscode-languageserver'; import { CancellationToken, CompletionItemKind, SymbolKind } from 'vscode-languageserver';
import { getFileInfo } from '../analyzer/analyzerNodeInfo';
import { DeclarationType } from '../analyzer/declaration'; import { DeclarationType } from '../analyzer/declaration';
import { ImportResolver, ModuleNameAndType } from '../analyzer/importResolver'; import { ImportResolver, ModuleNameAndType } from '../analyzer/importResolver';
import { ImportType } from '../analyzer/importResult'; import { ImportType } from '../analyzer/importResult';
import { import {
getImportGroup, getImportGroup,
getImportGroupFromModuleNameAndType, getImportGroupFromModuleNameAndType,
getRelativeModuleName,
getTextEditsForAutoImportInsertion, getTextEditsForAutoImportInsertion,
getTextEditsForAutoImportSymbolAddition, getTextEditsForAutoImportSymbolAddition,
getTopLevelImports, getTopLevelImports,
ImportGroup, ImportGroup,
ImportNameInfo,
ImportStatements, ImportStatements,
ModuleNameInfo,
} from '../analyzer/importStatementUtils'; } from '../analyzer/importStatementUtils';
import { SourceFileInfo } from '../analyzer/program'; import { SourceFileInfo } from '../analyzer/program';
import { isUserCode } from '../analyzer/sourceFileInfoUtils'; import { isUserCode } from '../analyzer/sourceFileInfoUtils';
@ -36,11 +40,16 @@ import { ParseResults } from '../parser/parser';
import { CompletionMap } from './completionProvider'; import { CompletionMap } from './completionProvider';
import { IndexAliasData, IndexResults } from './documentSymbolProvider'; import { IndexAliasData, IndexResults } from './documentSymbolProvider';
export const enum ImportFormat {
Absolute = 'absolute',
Relative = 'relative',
}
export interface AutoImportSymbol { export interface AutoImportSymbol {
readonly importAlias?: IndexAliasData | undefined; readonly importAlias?: IndexAliasData;
readonly symbol?: Symbol | undefined; readonly symbol?: Symbol;
readonly kind?: SymbolKind | undefined; readonly kind?: SymbolKind;
readonly itemKind?: CompletionItemKind | undefined; readonly itemKind?: CompletionItemKind;
} }
export interface ModuleSymbolTable { export interface ModuleSymbolTable {
@ -50,31 +59,32 @@ export interface ModuleSymbolTable {
export type ModuleSymbolMap = Map<string, ModuleSymbolTable>; export type ModuleSymbolMap = Map<string, ModuleSymbolTable>;
export interface AbbreviationInfo { export interface AbbreviationInfo {
importFrom?: string | undefined; importFrom?: string;
importName: string; importName: string;
} }
export interface AutoImportResult { export interface AutoImportResult {
name: string; name: string;
symbol?: Symbol | undefined; symbol?: Symbol;
source?: string | undefined; source?: string;
insertionText: string; insertionText: string;
edits?: TextEditAction[] | undefined; edits?: TextEditAction[];
alias?: string | undefined; alias?: string;
kind?: CompletionItemKind | undefined; kind?: CompletionItemKind;
} }
export interface AutoImportOptions { export interface AutoImportOptions {
libraryMap?: Map<string, IndexResults> | undefined; libraryMap?: Map<string, IndexResults>;
patternMatcher?: ((pattern: string, name: string) => boolean) | undefined; patternMatcher?: (pattern: string, name: string) => boolean;
allowVariableInAll?: boolean | undefined; allowVariableInAll?: boolean;
lazyEdit?: boolean | undefined; lazyEdit?: boolean;
importFormat?: ImportFormat;
} }
interface ImportParts { interface ImportParts {
importName: string; importName: string;
symbolName?: string | undefined; symbolName?: string;
importFrom?: string | undefined; importFrom?: string;
filePath: string; filePath: string;
dotCount: number; dotCount: number;
moduleNameAndType: ModuleNameAndType; moduleNameAndType: ModuleNameAndType;
@ -83,9 +93,9 @@ interface ImportParts {
interface ImportAliasData { interface ImportAliasData {
importParts: ImportParts; importParts: ImportParts;
importGroup: ImportGroup; importGroup: ImportGroup;
symbol?: Symbol | undefined; symbol?: Symbol;
kind?: SymbolKind | undefined; kind?: SymbolKind;
itemKind?: CompletionItemKind | undefined; itemKind?: CompletionItemKind;
} }
type AutoImportResultMap = Map<string, AutoImportResult[]>; type AutoImportResultMap = Map<string, AutoImportResult[]>;
@ -148,7 +158,7 @@ export function buildModuleSymbolsMap(
!declaration.isFinal !declaration.isFinal
? SymbolKind.Variable ? SymbolKind.Variable
: undefined; : undefined;
callbackfn({ symbol, kind: variableKind }, name, /* library */ false); callbackfn({ symbol, kind: variableKind }, name, /* library */ !isUserCode(file));
}); });
}, },
}); });
@ -167,11 +177,12 @@ export function buildModuleSymbolsMap(
} }
export class AutoImporter { export class AutoImporter {
private _importStatements: ImportStatements; private readonly _filePath: string;
private readonly _importStatements: ImportStatements;
// Track some auto import internal perf numbers. // Track some auto import internal perf numbers.
private _stopWatch = new Duration(); private readonly _stopWatch = new Duration();
private _perfInfo = { private readonly _perfInfo = {
indexUsed: false, indexUsed: false,
totalInMs: 0, totalInMs: 0,
@ -193,6 +204,7 @@ export class AutoImporter {
private _moduleSymbolMap: ModuleSymbolMap, private _moduleSymbolMap: ModuleSymbolMap,
private _options: AutoImportOptions private _options: AutoImportOptions
) { ) {
this._filePath = getFileInfo(_parseResults.parseTree).filePath;
this._importStatements = getTopLevelImports(this._parseResults.parseTree, /* includeImplicitImports */ true); this._importStatements = getTopLevelImports(this._parseResults.parseTree, /* includeImplicitImports */ true);
this._perfInfo.indexUsed = !!this._options.libraryMap; this._perfInfo.indexUsed = !!this._options.libraryMap;
@ -321,7 +333,7 @@ export class AutoImporter {
private _processModuleSymbolTable( private _processModuleSymbolTable(
topLevelSymbols: ModuleSymbolTable, topLevelSymbols: ModuleSymbolTable,
filePath: string, moduleFilePath: string,
word: string, word: string,
similarityLimit: number, similarityLimit: number,
isStubOrHasInit: { isStub: boolean; hasInit: boolean }, isStubOrHasInit: { isStub: boolean; hasInit: boolean },
@ -332,7 +344,7 @@ export class AutoImporter {
) { ) {
throwIfCancellationRequested(token); throwIfCancellationRequested(token);
const [importSource, importGroup, moduleNameAndType] = this._getImportPartsForSymbols(filePath); const [importSource, importGroup, moduleNameAndType] = this._getImportPartsForSymbols(moduleFilePath);
if (!importSource) { if (!importSource) {
return; return;
} }
@ -367,7 +379,7 @@ export class AutoImporter {
symbolName: name, symbolName: name,
importName: name, importName: name,
importFrom: importSource, importFrom: importSource,
filePath, filePath: moduleFilePath,
dotCount, dotCount,
moduleNameAndType, moduleNameAndType,
}, },
@ -381,13 +393,17 @@ export class AutoImporter {
return; return;
} }
const nameForImportFrom =
this._options.importFormat === ImportFormat.Relative && !library
? getRelativeModuleName(this._importResolver.fileSystem, this._filePath, moduleFilePath)
: undefined;
const autoImportTextEdits = this._getTextEditsForAutoImportByFilePath( const autoImportTextEdits = this._getTextEditsForAutoImportByFilePath(
importSource, { name, alias: abbrFromUsers },
name, { name: importSource, nameForImportFrom },
abbrFromUsers,
name, name,
importGroup, importGroup,
filePath moduleFilePath
); );
this._addResult(results, { this._addResult(results, {
@ -408,7 +424,7 @@ export class AutoImporter {
return; return;
} }
const importParts = this._getImportParts(filePath); const importParts = this._getImportParts(moduleFilePath);
if (!importParts) { if (!importParts) {
return; return;
} }
@ -425,7 +441,7 @@ export class AutoImporter {
this._addToImportAliasMap( this._addToImportAliasMap(
{ {
modulePath: filePath, modulePath: moduleFilePath,
originalName: importParts.importName, originalName: importParts.importName,
kind: SymbolKind.Module, kind: SymbolKind.Module,
itemKind: CompletionItemKind.Module, itemKind: CompletionItemKind.Module,
@ -510,9 +526,10 @@ export class AutoImporter {
} }
const autoImportTextEdits = this._getTextEditsForAutoImportByFilePath( const autoImportTextEdits = this._getTextEditsForAutoImportByFilePath(
importAliasData.importParts.importFrom ?? importAliasData.importParts.importName, { name: importAliasData.importParts.symbolName, alias: abbrFromUsers },
importAliasData.importParts.symbolName, {
abbrFromUsers, name: importAliasData.importParts.importFrom ?? importAliasData.importParts.importName,
},
importAliasData.importParts.importName, importAliasData.importParts.importName,
importAliasData.importGroup, importAliasData.importGroup,
importAliasData.importParts.filePath importAliasData.importParts.filePath
@ -685,9 +702,8 @@ export class AutoImporter {
} }
private _getTextEditsForAutoImportByFilePath( private _getTextEditsForAutoImportByFilePath(
moduleName: string, importNameInfo: ImportNameInfo,
importName: string | undefined, moduleNameInfo: ModuleNameInfo,
abbrFromUsers: string | undefined,
insertionText: string, insertionText: string,
importGroup: ImportGroup, importGroup: ImportGroup,
filePath: string filePath: string
@ -700,11 +716,11 @@ export class AutoImporter {
// For now, we don't check whether alias or moduleName got overwritten at // For now, we don't check whether alias or moduleName got overwritten at
// given position // given position
const importAlias = importStatement.subnode?.alias?.value; const importAlias = importStatement.subnode?.alias?.value;
if (importName) { if (importNameInfo.name) {
// ex) import module // ex) import module
// method | <= auto-import // method | <= auto-import
return { return {
insertionText: `${importAlias ?? importStatement.moduleName}.${importName}`, insertionText: `${importAlias ?? importStatement.moduleName}.${importNameInfo.name}`,
edits: [], edits: [],
}; };
} else if (importAlias) { } else if (importAlias) {
@ -719,18 +735,18 @@ export class AutoImporter {
// Does an 'import from' statement already exist? // Does an 'import from' statement already exist?
if ( if (
importName && importNameInfo.name &&
importStatement.node.nodeType === ParseNodeType.ImportFrom && importStatement.node.nodeType === ParseNodeType.ImportFrom &&
!importStatement.node.isWildcardImport !importStatement.node.isWildcardImport
) { ) {
// If so, see whether what we want already exist. // If so, see whether what we want already exist.
const importNode = importStatement.node.imports.find((i) => i.name.value === importName); const importNode = importStatement.node.imports.find((i) => i.name.value === importNameInfo.name);
if (importNode) { if (importNode) {
// For now, we don't check whether alias or moduleName got overwritten at // For now, we don't check whether alias or moduleName got overwritten at
// given position // given position
const importAlias = importNode.alias?.value; const importAlias = importNode.alias?.value;
return { return {
insertionText: `${importAlias ?? importName}`, insertionText: `${importAlias ?? importNameInfo.name}`,
edits: [], edits: [],
}; };
} }
@ -738,25 +754,25 @@ export class AutoImporter {
// If not, add what we want at the existing 'import from' statement as long as // If not, add what we want at the existing 'import from' statement as long as
// what is imported is not module itself. // what is imported is not module itself.
// ex) don't add "path" to existing "from os.path import dirname" statement. // ex) don't add "path" to existing "from os.path import dirname" statement.
if (moduleName === importStatement.moduleName) { if (moduleNameInfo.name === importStatement.moduleName) {
return { return {
insertionText: abbrFromUsers ?? insertionText, insertionText: importNameInfo.alias ?? insertionText,
edits: this._options.lazyEdit edits: this._options.lazyEdit
? undefined ? undefined
: getTextEditsForAutoImportSymbolAddition( : getTextEditsForAutoImportSymbolAddition(
{ name: importName, alias: abbrFromUsers }, importNameInfo,
importStatement, importStatement,
this._parseResults this._parseResults
), ),
}; };
} }
} }
} else if (importName) { } else if (importNameInfo.name) {
// If it is the module itself that got imported, make sure we don't import it again. // If it is the module itself that got imported, make sure we don't import it again.
// ex) from module import submodule // ex) from module import submodule
const imported = this._importStatements.orderedImports.find((i) => i.moduleName === moduleName); const imported = this._importStatements.orderedImports.find((i) => i.moduleName === moduleNameInfo.name);
if (imported && imported.node.nodeType === ParseNodeType.ImportFrom && !imported.node.isWildcardImport) { if (imported && imported.node.nodeType === ParseNodeType.ImportFrom && !imported.node.isWildcardImport) {
const importFrom = imported.node.imports.find((i) => i.name.value === importName); const importFrom = imported.node.imports.find((i) => i.name.value === importNameInfo.name);
if (importFrom) { if (importFrom) {
// For now, we don't check whether alias or moduleName got overwritten at // For now, we don't check whether alias or moduleName got overwritten at
// given position. only move to alias, but not the other way around // given position. only move to alias, but not the other way around
@ -770,14 +786,10 @@ export class AutoImporter {
} else { } else {
// If not, add what we want at the existing import from statement. // If not, add what we want at the existing import from statement.
return { return {
insertionText: abbrFromUsers ?? insertionText, insertionText: importNameInfo.alias ?? insertionText,
edits: this._options.lazyEdit edits: this._options.lazyEdit
? undefined ? undefined
: getTextEditsForAutoImportSymbolAddition( : getTextEditsForAutoImportSymbolAddition(importNameInfo, imported, this._parseResults),
{ name: importName, alias: abbrFromUsers },
imported,
this._parseResults
),
}; };
} }
} }
@ -789,20 +801,20 @@ export class AutoImporter {
// given position // given position
const importAlias = importFrom.alias?.value; const importAlias = importFrom.alias?.value;
return { return {
insertionText: `${importAlias ?? importFrom.name.value}.${importName}`, insertionText: `${importAlias ?? importFrom.name.value}.${importNameInfo.name}`,
edits: [], edits: [],
}; };
} }
} }
return { return {
insertionText: abbrFromUsers ?? insertionText, insertionText: importNameInfo.alias ?? insertionText,
edits: this._options.lazyEdit edits: this._options.lazyEdit
? undefined ? undefined
: getTextEditsForAutoImportInsertion( : getTextEditsForAutoImportInsertion(
{ name: importName, alias: abbrFromUsers }, importNameInfo,
moduleNameInfo,
this._importStatements, this._importStatements,
moduleName,
importGroup, importGroup,
this._parseResults, this._parseResults,
this._invocationPosition this._invocationPosition

View File

@ -112,7 +112,7 @@ import {
} from '../parser/parseNodes'; } from '../parser/parseNodes';
import { ParseResults } from '../parser/parser'; import { ParseResults } from '../parser/parser';
import { StringToken, StringTokenFlags, Token, TokenType } from '../parser/tokenizerTypes'; import { StringToken, StringTokenFlags, Token, TokenType } from '../parser/tokenizerTypes';
import { AbbreviationInfo, AutoImporter, AutoImportResult, ModuleSymbolMap } from './autoImporter'; import { AbbreviationInfo, AutoImporter, AutoImportResult, ImportFormat, ModuleSymbolMap } from './autoImporter';
import { DocumentSymbolCollector } from './documentSymbolCollector'; import { DocumentSymbolCollector } from './documentSymbolCollector';
import { IndexResults } from './documentSymbolProvider'; import { IndexResults } from './documentSymbolProvider';
import { import {
@ -282,6 +282,7 @@ export interface CompletionOptions {
lazyEdit: boolean; lazyEdit: boolean;
autoImport: boolean; autoImport: boolean;
extraCommitChars: boolean; extraCommitChars: boolean;
importFormat: ImportFormat;
} }
export type AbbreviationMap = Map<string, AbbreviationInfo>; export type AbbreviationMap = Map<string, AbbreviationInfo>;
@ -2241,7 +2242,11 @@ export class CompletionProvider {
this._position, this._position,
completionResults.completionMap, completionResults.completionMap,
moduleSymbolMap, moduleSymbolMap,
{ libraryMap: this._autoImportMaps.libraryMap, lazyEdit } {
libraryMap: this._autoImportMaps.libraryMap,
lazyEdit,
importFormat: this._options.importFormat,
}
); );
const results: AutoImportResult[] = []; const results: AutoImportResult[] = [];

View File

@ -52,6 +52,7 @@ import { convertOffsetToPosition } from '../common/positionUtils';
import { TextRange } from '../common/textRange'; import { TextRange } from '../common/textRange';
import { ModuleNameNode, NameNode, ParseNode, ParseNodeType } from '../parser/parseNodes'; import { ModuleNameNode, NameNode, ParseNode, ParseNodeType } from '../parser/parseNodes';
import { ParseResults } from '../parser/parser'; import { ParseResults } from '../parser/parser';
import { ImportFormat } from './autoImporter';
export interface ImportData { export interface ImportData {
containsUnreferenceableSymbols: boolean; containsUnreferenceableSymbols: boolean;
@ -79,6 +80,7 @@ export class ImportAdder {
result: ImportData, result: ImportData,
parseResults: ParseResults, parseResults: ParseResults,
insertionPosition: number, insertionPosition: number,
importFormat: ImportFormat,
token: CancellationToken token: CancellationToken
): TextEditAction[] { ): TextEditAction[] {
throwIfCancellationRequested(token); throwIfCancellationRequested(token);
@ -94,6 +96,7 @@ export class ImportAdder {
continue; continue;
} }
const relativePath = getRelativeModuleName(this._importResolver.fileSystem, filePath, importInfo.filePath);
const moduleAndType = this._importResolver.getModuleNameForImport(importInfo.filePath, execEnv); const moduleAndType = this._importResolver.getModuleNameForImport(importInfo.filePath, execEnv);
if (!moduleAndType.moduleName) { if (!moduleAndType.moduleName) {
if (!importInfo.nameInfo.name) { if (!importInfo.nameInfo.name) {
@ -103,16 +106,17 @@ export class ImportAdder {
// module can't be addressed by absolute path in "from import" statement. // module can't be addressed by absolute path in "from import" statement.
// ex) namespace package at [workspace root] or [workspace root]\__init__.py(i) // ex) namespace package at [workspace root] or [workspace root]\__init__.py(i)
// use relative path // use relative path
moduleAndType.moduleName = getRelativeModuleName( importFormat = ImportFormat.Relative;
this._importResolver.fileSystem,
filePath,
importInfo.filePath
);
} }
addIfUnique( addIfUnique(
importNameInfo, importNameInfo,
{ module: moduleAndType, name: importInfo.nameInfo.name, alias: importInfo.nameInfo.alias }, {
name: importInfo.nameInfo.name,
alias: importInfo.nameInfo.alias,
module: moduleAndType,
nameForImportFrom: importFormat === ImportFormat.Relative ? relativePath : undefined,
},
(a, b) => this._areSame(a, b) (a, b) => this._areSame(a, b)
); );
} }

View File

@ -100,8 +100,8 @@ function _addMissingOptionalToParam(
} else { } else {
const additionalEditActions = getTextEditsForAutoImportInsertion( const additionalEditActions = getTextEditsForAutoImportInsertion(
{ name: 'Optional' }, { name: 'Optional' },
{ name: 'typing' },
importStatements, importStatements,
'typing',
ImportGroup.BuiltIn, ImportGroup.BuiltIn,
parseResults, parseResults,
startPos startPos

View File

@ -486,8 +486,8 @@ export class RenameModuleProvider {
this._addResultEdits( this._addResultEdits(
getTextEditsForAutoImportInsertion( getTextEditsForAutoImportInsertion(
[], [],
{ name: this._newModuleName },
importStatements, importStatements,
this._newModuleName,
getImportGroupFromModuleNameAndType(this._newModuleNameAndType), getImportGroupFromModuleNameAndType(this._newModuleNameAndType),
parseResults, parseResults,
convertOffsetToPosition(parseResults.parseTree.length, parseResults.tokenizerOutput.lines) convertOffsetToPosition(parseResults.parseTree.length, parseResults.tokenizerOutput.lines)
@ -1324,8 +1324,8 @@ export class RenameModuleProvider {
return getTextEditsForAutoImportInsertion( return getTextEditsForAutoImportInsertion(
importNameInfo, importNameInfo,
{ name: moduleName },
importStatements, importStatements,
moduleName,
getImportGroupFromModuleNameAndType(this._newModuleNameAndType), getImportGroupFromModuleNameAndType(this._newModuleNameAndType),
parseResults, parseResults,
convertOffsetToPosition(parseResults.parseTree.length, parseResults.tokenizerOutput.lines) convertOffsetToPosition(parseResults.parseTree.length, parseResults.tokenizerOutput.lines)

View File

@ -17,6 +17,7 @@ import { ConfigOptions } from '../common/configOptions';
import { NullConsole } from '../common/console'; import { NullConsole } from '../common/console';
import { normalizeSlashes } from '../common/pathUtils'; import { normalizeSlashes } from '../common/pathUtils';
import { convertOffsetsToRange, convertOffsetToPosition } from '../common/positionUtils'; import { convertOffsetsToRange, convertOffsetToPosition } from '../common/positionUtils';
import { ImportFormat } from '../languageService/autoImporter';
import { parseTestData } from './harness/fourslash/fourSlashParser'; import { parseTestData } from './harness/fourslash/fourSlashParser';
import { TestAccessHost } from './harness/testAccessHost'; import { TestAccessHost } from './harness/testAccessHost';
import * as host from './harness/testHost'; import * as host from './harness/testHost';
@ -54,6 +55,7 @@ test('check chained files', async () => {
snippet: false, snippet: false,
autoImport: false, autoImport: false,
extraCommitChars: false, extraCommitChars: false,
importFormat: ImportFormat.Absolute,
}, },
undefined, undefined,
CancellationToken.None CancellationToken.None
@ -100,6 +102,7 @@ test('modify chained files', async () => {
snippet: false, snippet: false,
autoImport: false, autoImport: false,
extraCommitChars: false, extraCommitChars: false,
importFormat: ImportFormat.Absolute,
}, },
undefined, undefined,
CancellationToken.None CancellationToken.None

View File

@ -65,7 +65,7 @@ import {
WellKnownWorkspaceKinds, WellKnownWorkspaceKinds,
WorkspaceServiceInstance, WorkspaceServiceInstance,
} from '../../../languageServerBase'; } from '../../../languageServerBase';
import { AbbreviationInfo } from '../../../languageService/autoImporter'; import { AbbreviationInfo, ImportFormat } from '../../../languageService/autoImporter';
import { CompletionOptions } from '../../../languageService/completionProvider'; import { CompletionOptions } from '../../../languageService/completionProvider';
import { DefinitionFilter } from '../../../languageService/definitionProvider'; import { DefinitionFilter } from '../../../languageService/definitionProvider';
import { convertHoverResults } from '../../../languageService/hoverProvider'; import { convertHoverResults } from '../../../languageService/hoverProvider';
@ -1099,6 +1099,7 @@ export class TestState {
lazyEdit: true, lazyEdit: true,
autoImport: true, autoImport: true,
extraCommitChars: true, extraCommitChars: true,
importFormat: ImportFormat.Absolute,
}; };
const nameMap = abbrMap ? new Map<string, AbbreviationInfo>(Object.entries(abbrMap)) : undefined; const nameMap = abbrMap ? new Map<string, AbbreviationInfo>(Object.entries(abbrMap)) : undefined;
const result = await this.workspace.serviceInstance.getCompletionsForPosition( const result = await this.workspace.serviceInstance.getCompletionsForPosition(

View File

@ -10,6 +10,7 @@ import assert from 'assert';
import { CancellationToken } from 'vscode-languageserver'; import { CancellationToken } from 'vscode-languageserver';
import { rangesAreEqual, TextRange } from '../common/textRange'; import { rangesAreEqual, TextRange } from '../common/textRange';
import { ImportFormat } from '../languageService/autoImporter';
import { ImportAdder } from '../languageService/importAdder'; import { ImportAdder } from '../languageService/importAdder';
import { parseAndGetTestState } from './harness/fourslash/testState'; import { parseAndGetTestState } from './harness/fourslash/testState';
@ -1319,7 +1320,23 @@ test('move into the same file from import statement for submodule', () => {
testImportMove(code); testImportMove(code);
}); });
function testImportMove(code: string) { test('use relative import format', () => {
const code = `
// @filename: test1.py
//// from nested import module
////
//// [|/*src*/module.foo()|]
// @filename: nested/__init__.py
//// [|{|"r":"from . import module!n!!n!!n!"|}|][|/*dest*/|]
// @filename: nested/module.py
//// def foo(): pass
`;
testImportMove(code, ImportFormat.Relative);
});
function testImportMove(code: string, importFormat = ImportFormat.Absolute) {
const state = parseAndGetTestState(code).state; const state = parseAndGetTestState(code).state;
const src = state.getRangeByMarkerName('src')!; const src = state.getRangeByMarkerName('src')!;
@ -1336,6 +1353,7 @@ function testImportMove(code: string) {
importData, importData,
state.program.getBoundSourceFile(dest.fileName)!.getParseResults()!, state.program.getBoundSourceFile(dest.fileName)!.getParseResults()!,
dest.position, dest.position,
importFormat,
CancellationToken.None CancellationToken.None
); );