mirror of
https://github.com/microsoft/pyright.git
synced 2024-08-17 20:00:24 +03:00
[PylanceBot] Pull Pylance with Pyright 1.1.265 (#3783)
Co-authored-by: HeeJae Chang <hechang@microsoft.com>
This commit is contained in:
parent
cdba65d4c8
commit
e4a0f2088c
5850
package-lock.json
generated
5850
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
10
package.json
10
package.json
@ -23,16 +23,16 @@
|
||||
"@types/glob": "^7.2.0",
|
||||
"@types/node": "^17.0.45",
|
||||
"@types/yargs": "^16.0.4",
|
||||
"@typescript-eslint/eslint-plugin": "^5.31.0",
|
||||
"@typescript-eslint/parser": "^5.31.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.32.0",
|
||||
"@typescript-eslint/parser": "^5.32.0",
|
||||
"detect-indent": "^6.1.0",
|
||||
"eslint": "^8.20.0",
|
||||
"eslint": "^8.21.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-plugin-simple-import-sort": "^7.0.0",
|
||||
"glob": "^7.2.3",
|
||||
"jsonc-parser": "^3.1.0",
|
||||
"lerna": "^5.2.0",
|
||||
"npm-check-updates": "^12.5.12",
|
||||
"lerna": "^5.3.0",
|
||||
"npm-check-updates": "^16.0.5",
|
||||
"p-queue": "^6.6.2",
|
||||
"prettier": "2.7.1",
|
||||
"syncpack": "^5.8.15",
|
||||
|
@ -38,7 +38,6 @@ import { ParseResults } from '../parser/parser';
|
||||
import * as AnalyzerNodeInfo from './analyzerNodeInfo';
|
||||
import { ModuleNameAndType } from './importResolver';
|
||||
import { ImportResult, ImportType } from './importResult';
|
||||
import { ParseTreeWalker } from './parseTreeWalker';
|
||||
import * as SymbolNameUtils from './symbolNameUtils';
|
||||
|
||||
export interface ImportStatement {
|
||||
@ -72,6 +71,12 @@ export interface ImportNameInfo {
|
||||
|
||||
export interface ImportNameWithModuleInfo extends ImportNameInfo {
|
||||
module: ModuleNameAndType;
|
||||
nameForImportFrom?: string;
|
||||
}
|
||||
|
||||
export interface ModuleNameInfo {
|
||||
name: string;
|
||||
nameForImportFrom?: string;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
export function getAllImports(root: ModuleNode, token: CancellationToken) {
|
||||
return ImportCollector.collect(root, token);
|
||||
}
|
||||
|
||||
// Looks for top-level 'import' and 'import from' statements and provides
|
||||
// an ordered list and a map (by file path).
|
||||
export function getTopLevelImports(parseTree: ModuleNode, includeImplicitImports = false): ImportStatements {
|
||||
@ -338,13 +339,13 @@ export function getTextEditsForAutoImportInsertions(
|
||||
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()) {
|
||||
insertionEdits.push(
|
||||
..._getInsertionEditsForAutoImportInsertion(
|
||||
importInfo,
|
||||
{ name: importInfo[0].module.moduleName, nameForImportFrom: importInfo[0].nameForImportFrom },
|
||||
importStatements,
|
||||
importInfo[0].module.moduleName,
|
||||
getImportGroupFromModuleNameAndType(importInfo[0].module),
|
||||
parseResults,
|
||||
invocationPosition
|
||||
@ -357,16 +358,16 @@ export function getTextEditsForAutoImportInsertions(
|
||||
|
||||
export function getTextEditsForAutoImportInsertion(
|
||||
importNameInfo: ImportNameInfo[] | ImportNameInfo,
|
||||
moduleNameInfo: ModuleNameInfo,
|
||||
importStatements: ImportStatements,
|
||||
moduleName: string,
|
||||
importGroup: ImportGroup,
|
||||
parseResults: ParseResults,
|
||||
invocationPosition: Position
|
||||
): TextEditAction[] {
|
||||
const insertionEdits = _getInsertionEditsForAutoImportInsertion(
|
||||
importNameInfo,
|
||||
moduleNameInfo,
|
||||
importStatements,
|
||||
moduleName,
|
||||
importGroup,
|
||||
parseResults,
|
||||
invocationPosition
|
||||
@ -423,8 +424,8 @@ function _convertInsertionEditsToTextEdits(parseResults: ParseResults, insertion
|
||||
|
||||
function _getInsertionEditsForAutoImportInsertion(
|
||||
importNameInfo: ImportNameInfo[] | ImportNameInfo,
|
||||
moduleNameInfo: ModuleNameInfo,
|
||||
importStatements: ImportStatements,
|
||||
moduleName: string,
|
||||
importGroup: ImportGroup,
|
||||
parseResults: ParseResults,
|
||||
invocationPosition: Position
|
||||
@ -449,7 +450,10 @@ function _getInsertionEditsForAutoImportInsertion(
|
||||
// Add from import statements next.
|
||||
const fromImports = map.get('from');
|
||||
if (fromImports) {
|
||||
appendToEdits(fromImports, (names) => `from ${moduleName} import ${names.join(', ')}`);
|
||||
appendToEdits(
|
||||
fromImports,
|
||||
(names) => `from ${moduleNameInfo.nameForImportFrom ?? moduleNameInfo.name} import ${names.join(', ')}`
|
||||
);
|
||||
}
|
||||
|
||||
return insertionEdits;
|
||||
@ -464,7 +468,7 @@ function _getInsertionEditsForAutoImportInsertion(
|
||||
|
||||
function appendToEdits(importNameInfo: ImportNameInfo[], importStatementGetter: (n: string[]) => string) {
|
||||
const importNames = importNameInfo
|
||||
.map((i) => getImportAsText(i, moduleName))
|
||||
.map((i) => getImportAsText(i, moduleNameInfo.name))
|
||||
.sort((a, b) => _compareImportNames(a.sortText, b.sortText))
|
||||
.reduce((set, v) => addIfUnique(set, v.text), [] as string[]);
|
||||
|
||||
@ -472,7 +476,7 @@ function _getInsertionEditsForAutoImportInsertion(
|
||||
_getInsertionEditForAutoImportInsertion(
|
||||
importStatementGetter(importNames),
|
||||
importStatements,
|
||||
moduleName,
|
||||
moduleNameInfo.name,
|
||||
importGroup,
|
||||
parseResults,
|
||||
invocationPosition
|
||||
@ -897,34 +901,3 @@ export function getResolvedFilePath(importResult: ImportResult | undefined) {
|
||||
// Regular case.
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -47,6 +47,7 @@ import { DocumentRange, doesRangeContain, doRangesIntersect, Position, Range } f
|
||||
import { Duration, timingStats } from '../common/timing';
|
||||
import {
|
||||
AutoImporter,
|
||||
AutoImportOptions,
|
||||
AutoImportResult,
|
||||
buildModuleSymbolsMap,
|
||||
ModuleSymbolMap,
|
||||
@ -1254,9 +1255,7 @@ export class Program {
|
||||
range: Range,
|
||||
similarityLimit: number,
|
||||
nameMap: AbbreviationMap | undefined,
|
||||
libraryMap: Map<string, IndexResults> | undefined,
|
||||
lazyEdit: boolean,
|
||||
allowVariableInAll: boolean,
|
||||
options: AutoImportOptions,
|
||||
token: CancellationToken
|
||||
): AutoImportResult[] {
|
||||
const sourceFileInfo = this._getSourceFileInfoFromPath(filePath);
|
||||
@ -1288,10 +1287,14 @@ export class Program {
|
||||
const writtenWord = fileContents.substr(textRange.start, textRange.length);
|
||||
const map = this._buildModuleSymbolsMap(
|
||||
sourceFileInfo,
|
||||
!!libraryMap,
|
||||
!!options.libraryMap,
|
||||
/* includeIndexUserSymbols */ true,
|
||||
token
|
||||
);
|
||||
|
||||
options.patternMatcher =
|
||||
options.patternMatcher ?? ((p, t) => computeCompletionSimilarity(p, t) > similarityLimit);
|
||||
|
||||
const autoImporter = new AutoImporter(
|
||||
this._configOptions.findExecEnvironment(filePath),
|
||||
this._importResolver,
|
||||
@ -1299,12 +1302,7 @@ export class Program {
|
||||
range.start,
|
||||
new CompletionMap(),
|
||||
map,
|
||||
{
|
||||
lazyEdit,
|
||||
allowVariableInAll,
|
||||
libraryMap,
|
||||
patternMatcher: (p, t) => computeCompletionSimilarity(p, t) > similarityLimit,
|
||||
}
|
||||
options
|
||||
);
|
||||
|
||||
// Filter out any name that is already defined in the current scope.
|
||||
|
@ -55,6 +55,7 @@ import {
|
||||
} from '../common/pathUtils';
|
||||
import { DocumentRange, Position, Range } from '../common/textRange';
|
||||
import { timingStats } from '../common/timing';
|
||||
import { AutoImportOptions } from '../languageService/autoImporter';
|
||||
import { AbbreviationMap, CompletionOptions, CompletionResultsList } from '../languageService/completionProvider';
|
||||
import { DefinitionFilter } from '../languageService/definitionProvider';
|
||||
import { IndexResults, WorkspaceSymbolCallback } from '../languageService/documentSymbolProvider';
|
||||
@ -316,20 +317,11 @@ export class AnalyzerService {
|
||||
range: Range,
|
||||
similarityLimit: number,
|
||||
nameMap: AbbreviationMap | undefined,
|
||||
lazyEdit: boolean,
|
||||
allowVariableInAll: boolean,
|
||||
options: AutoImportOptions,
|
||||
token: CancellationToken
|
||||
) {
|
||||
return this._program.getAutoImports(
|
||||
filePath,
|
||||
range,
|
||||
similarityLimit,
|
||||
nameMap,
|
||||
this._backgroundAnalysisProgram.getIndexing(filePath),
|
||||
lazyEdit,
|
||||
allowVariableInAll,
|
||||
token
|
||||
);
|
||||
options.libraryMap = options.libraryMap ?? this._backgroundAnalysisProgram.getIndexing(filePath);
|
||||
return this._program.getAutoImports(filePath, range, similarityLimit, nameMap, options, token);
|
||||
}
|
||||
|
||||
getDefinitionForPosition(
|
||||
|
@ -107,6 +107,7 @@ import { DocumentRange, Position, Range } from './common/textRange';
|
||||
import { UriParser } from './common/uriParser';
|
||||
import { convertWorkspaceDocumentEdits } from './common/workspaceEditUtils';
|
||||
import { AnalyzerServiceExecutor } from './languageService/analyzerServiceExecutor';
|
||||
import { ImportFormat } from './languageService/autoImporter';
|
||||
import { CompletionItemData, CompletionOptions, CompletionResultsList } from './languageService/completionProvider';
|
||||
import { DefinitionFilter } from './languageService/definitionProvider';
|
||||
import { convertToFlatSymbols, WorkspaceSymbolCallback } from './languageService/documentSymbolProvider';
|
||||
@ -1317,6 +1318,7 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
|
||||
lazyEdit: this.client.completionItemResolveSupportsAdditionalTextEdits,
|
||||
autoImport: true,
|
||||
extraCommitChars: false,
|
||||
importFormat: ImportFormat.Absolute,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -7,17 +7,21 @@
|
||||
|
||||
import { CancellationToken, CompletionItemKind, SymbolKind } from 'vscode-languageserver';
|
||||
|
||||
import { getFileInfo } from '../analyzer/analyzerNodeInfo';
|
||||
import { DeclarationType } from '../analyzer/declaration';
|
||||
import { ImportResolver, ModuleNameAndType } from '../analyzer/importResolver';
|
||||
import { ImportType } from '../analyzer/importResult';
|
||||
import {
|
||||
getImportGroup,
|
||||
getImportGroupFromModuleNameAndType,
|
||||
getRelativeModuleName,
|
||||
getTextEditsForAutoImportInsertion,
|
||||
getTextEditsForAutoImportSymbolAddition,
|
||||
getTopLevelImports,
|
||||
ImportGroup,
|
||||
ImportNameInfo,
|
||||
ImportStatements,
|
||||
ModuleNameInfo,
|
||||
} from '../analyzer/importStatementUtils';
|
||||
import { SourceFileInfo } from '../analyzer/program';
|
||||
import { isUserCode } from '../analyzer/sourceFileInfoUtils';
|
||||
@ -36,11 +40,16 @@ import { ParseResults } from '../parser/parser';
|
||||
import { CompletionMap } from './completionProvider';
|
||||
import { IndexAliasData, IndexResults } from './documentSymbolProvider';
|
||||
|
||||
export const enum ImportFormat {
|
||||
Absolute = 'absolute',
|
||||
Relative = 'relative',
|
||||
}
|
||||
|
||||
export interface AutoImportSymbol {
|
||||
readonly importAlias?: IndexAliasData | undefined;
|
||||
readonly symbol?: Symbol | undefined;
|
||||
readonly kind?: SymbolKind | undefined;
|
||||
readonly itemKind?: CompletionItemKind | undefined;
|
||||
readonly importAlias?: IndexAliasData;
|
||||
readonly symbol?: Symbol;
|
||||
readonly kind?: SymbolKind;
|
||||
readonly itemKind?: CompletionItemKind;
|
||||
}
|
||||
|
||||
export interface ModuleSymbolTable {
|
||||
@ -50,31 +59,32 @@ export interface ModuleSymbolTable {
|
||||
export type ModuleSymbolMap = Map<string, ModuleSymbolTable>;
|
||||
|
||||
export interface AbbreviationInfo {
|
||||
importFrom?: string | undefined;
|
||||
importFrom?: string;
|
||||
importName: string;
|
||||
}
|
||||
|
||||
export interface AutoImportResult {
|
||||
name: string;
|
||||
symbol?: Symbol | undefined;
|
||||
source?: string | undefined;
|
||||
symbol?: Symbol;
|
||||
source?: string;
|
||||
insertionText: string;
|
||||
edits?: TextEditAction[] | undefined;
|
||||
alias?: string | undefined;
|
||||
kind?: CompletionItemKind | undefined;
|
||||
edits?: TextEditAction[];
|
||||
alias?: string;
|
||||
kind?: CompletionItemKind;
|
||||
}
|
||||
|
||||
export interface AutoImportOptions {
|
||||
libraryMap?: Map<string, IndexResults> | undefined;
|
||||
patternMatcher?: ((pattern: string, name: string) => boolean) | undefined;
|
||||
allowVariableInAll?: boolean | undefined;
|
||||
lazyEdit?: boolean | undefined;
|
||||
libraryMap?: Map<string, IndexResults>;
|
||||
patternMatcher?: (pattern: string, name: string) => boolean;
|
||||
allowVariableInAll?: boolean;
|
||||
lazyEdit?: boolean;
|
||||
importFormat?: ImportFormat;
|
||||
}
|
||||
|
||||
interface ImportParts {
|
||||
importName: string;
|
||||
symbolName?: string | undefined;
|
||||
importFrom?: string | undefined;
|
||||
symbolName?: string;
|
||||
importFrom?: string;
|
||||
filePath: string;
|
||||
dotCount: number;
|
||||
moduleNameAndType: ModuleNameAndType;
|
||||
@ -83,9 +93,9 @@ interface ImportParts {
|
||||
interface ImportAliasData {
|
||||
importParts: ImportParts;
|
||||
importGroup: ImportGroup;
|
||||
symbol?: Symbol | undefined;
|
||||
kind?: SymbolKind | undefined;
|
||||
itemKind?: CompletionItemKind | undefined;
|
||||
symbol?: Symbol;
|
||||
kind?: SymbolKind;
|
||||
itemKind?: CompletionItemKind;
|
||||
}
|
||||
|
||||
type AutoImportResultMap = Map<string, AutoImportResult[]>;
|
||||
@ -148,7 +158,7 @@ export function buildModuleSymbolsMap(
|
||||
!declaration.isFinal
|
||||
? SymbolKind.Variable
|
||||
: 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 {
|
||||
private _importStatements: ImportStatements;
|
||||
private readonly _filePath: string;
|
||||
private readonly _importStatements: ImportStatements;
|
||||
|
||||
// Track some auto import internal perf numbers.
|
||||
private _stopWatch = new Duration();
|
||||
private _perfInfo = {
|
||||
private readonly _stopWatch = new Duration();
|
||||
private readonly _perfInfo = {
|
||||
indexUsed: false,
|
||||
totalInMs: 0,
|
||||
|
||||
@ -193,6 +204,7 @@ export class AutoImporter {
|
||||
private _moduleSymbolMap: ModuleSymbolMap,
|
||||
private _options: AutoImportOptions
|
||||
) {
|
||||
this._filePath = getFileInfo(_parseResults.parseTree).filePath;
|
||||
this._importStatements = getTopLevelImports(this._parseResults.parseTree, /* includeImplicitImports */ true);
|
||||
|
||||
this._perfInfo.indexUsed = !!this._options.libraryMap;
|
||||
@ -321,7 +333,7 @@ export class AutoImporter {
|
||||
|
||||
private _processModuleSymbolTable(
|
||||
topLevelSymbols: ModuleSymbolTable,
|
||||
filePath: string,
|
||||
moduleFilePath: string,
|
||||
word: string,
|
||||
similarityLimit: number,
|
||||
isStubOrHasInit: { isStub: boolean; hasInit: boolean },
|
||||
@ -332,7 +344,7 @@ export class AutoImporter {
|
||||
) {
|
||||
throwIfCancellationRequested(token);
|
||||
|
||||
const [importSource, importGroup, moduleNameAndType] = this._getImportPartsForSymbols(filePath);
|
||||
const [importSource, importGroup, moduleNameAndType] = this._getImportPartsForSymbols(moduleFilePath);
|
||||
if (!importSource) {
|
||||
return;
|
||||
}
|
||||
@ -367,7 +379,7 @@ export class AutoImporter {
|
||||
symbolName: name,
|
||||
importName: name,
|
||||
importFrom: importSource,
|
||||
filePath,
|
||||
filePath: moduleFilePath,
|
||||
dotCount,
|
||||
moduleNameAndType,
|
||||
},
|
||||
@ -381,13 +393,17 @@ export class AutoImporter {
|
||||
return;
|
||||
}
|
||||
|
||||
const nameForImportFrom =
|
||||
this._options.importFormat === ImportFormat.Relative && !library
|
||||
? getRelativeModuleName(this._importResolver.fileSystem, this._filePath, moduleFilePath)
|
||||
: undefined;
|
||||
|
||||
const autoImportTextEdits = this._getTextEditsForAutoImportByFilePath(
|
||||
importSource,
|
||||
name,
|
||||
abbrFromUsers,
|
||||
{ name, alias: abbrFromUsers },
|
||||
{ name: importSource, nameForImportFrom },
|
||||
name,
|
||||
importGroup,
|
||||
filePath
|
||||
moduleFilePath
|
||||
);
|
||||
|
||||
this._addResult(results, {
|
||||
@ -408,7 +424,7 @@ export class AutoImporter {
|
||||
return;
|
||||
}
|
||||
|
||||
const importParts = this._getImportParts(filePath);
|
||||
const importParts = this._getImportParts(moduleFilePath);
|
||||
if (!importParts) {
|
||||
return;
|
||||
}
|
||||
@ -425,7 +441,7 @@ export class AutoImporter {
|
||||
|
||||
this._addToImportAliasMap(
|
||||
{
|
||||
modulePath: filePath,
|
||||
modulePath: moduleFilePath,
|
||||
originalName: importParts.importName,
|
||||
kind: SymbolKind.Module,
|
||||
itemKind: CompletionItemKind.Module,
|
||||
@ -510,9 +526,10 @@ export class AutoImporter {
|
||||
}
|
||||
|
||||
const autoImportTextEdits = this._getTextEditsForAutoImportByFilePath(
|
||||
importAliasData.importParts.importFrom ?? importAliasData.importParts.importName,
|
||||
importAliasData.importParts.symbolName,
|
||||
abbrFromUsers,
|
||||
{ name: importAliasData.importParts.symbolName, alias: abbrFromUsers },
|
||||
{
|
||||
name: importAliasData.importParts.importFrom ?? importAliasData.importParts.importName,
|
||||
},
|
||||
importAliasData.importParts.importName,
|
||||
importAliasData.importGroup,
|
||||
importAliasData.importParts.filePath
|
||||
@ -685,9 +702,8 @@ export class AutoImporter {
|
||||
}
|
||||
|
||||
private _getTextEditsForAutoImportByFilePath(
|
||||
moduleName: string,
|
||||
importName: string | undefined,
|
||||
abbrFromUsers: string | undefined,
|
||||
importNameInfo: ImportNameInfo,
|
||||
moduleNameInfo: ModuleNameInfo,
|
||||
insertionText: string,
|
||||
importGroup: ImportGroup,
|
||||
filePath: string
|
||||
@ -700,11 +716,11 @@ export class AutoImporter {
|
||||
// For now, we don't check whether alias or moduleName got overwritten at
|
||||
// given position
|
||||
const importAlias = importStatement.subnode?.alias?.value;
|
||||
if (importName) {
|
||||
if (importNameInfo.name) {
|
||||
// ex) import module
|
||||
// method | <= auto-import
|
||||
return {
|
||||
insertionText: `${importAlias ?? importStatement.moduleName}.${importName}`,
|
||||
insertionText: `${importAlias ?? importStatement.moduleName}.${importNameInfo.name}`,
|
||||
edits: [],
|
||||
};
|
||||
} else if (importAlias) {
|
||||
@ -719,18 +735,18 @@ export class AutoImporter {
|
||||
|
||||
// Does an 'import from' statement already exist?
|
||||
if (
|
||||
importName &&
|
||||
importNameInfo.name &&
|
||||
importStatement.node.nodeType === ParseNodeType.ImportFrom &&
|
||||
!importStatement.node.isWildcardImport
|
||||
) {
|
||||
// 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) {
|
||||
// For now, we don't check whether alias or moduleName got overwritten at
|
||||
// given position
|
||||
const importAlias = importNode.alias?.value;
|
||||
return {
|
||||
insertionText: `${importAlias ?? importName}`,
|
||||
insertionText: `${importAlias ?? importNameInfo.name}`,
|
||||
edits: [],
|
||||
};
|
||||
}
|
||||
@ -738,25 +754,25 @@ export class AutoImporter {
|
||||
// If not, add what we want at the existing 'import from' statement as long as
|
||||
// what is imported is not module itself.
|
||||
// ex) don't add "path" to existing "from os.path import dirname" statement.
|
||||
if (moduleName === importStatement.moduleName) {
|
||||
if (moduleNameInfo.name === importStatement.moduleName) {
|
||||
return {
|
||||
insertionText: abbrFromUsers ?? insertionText,
|
||||
insertionText: importNameInfo.alias ?? insertionText,
|
||||
edits: this._options.lazyEdit
|
||||
? undefined
|
||||
: getTextEditsForAutoImportSymbolAddition(
|
||||
{ name: importName, alias: abbrFromUsers },
|
||||
importNameInfo,
|
||||
importStatement,
|
||||
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.
|
||||
// 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) {
|
||||
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) {
|
||||
// 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
|
||||
@ -770,14 +786,10 @@ export class AutoImporter {
|
||||
} else {
|
||||
// If not, add what we want at the existing import from statement.
|
||||
return {
|
||||
insertionText: abbrFromUsers ?? insertionText,
|
||||
insertionText: importNameInfo.alias ?? insertionText,
|
||||
edits: this._options.lazyEdit
|
||||
? undefined
|
||||
: getTextEditsForAutoImportSymbolAddition(
|
||||
{ name: importName, alias: abbrFromUsers },
|
||||
imported,
|
||||
this._parseResults
|
||||
),
|
||||
: getTextEditsForAutoImportSymbolAddition(importNameInfo, imported, this._parseResults),
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -789,20 +801,20 @@ export class AutoImporter {
|
||||
// given position
|
||||
const importAlias = importFrom.alias?.value;
|
||||
return {
|
||||
insertionText: `${importAlias ?? importFrom.name.value}.${importName}`,
|
||||
insertionText: `${importAlias ?? importFrom.name.value}.${importNameInfo.name}`,
|
||||
edits: [],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
insertionText: abbrFromUsers ?? insertionText,
|
||||
insertionText: importNameInfo.alias ?? insertionText,
|
||||
edits: this._options.lazyEdit
|
||||
? undefined
|
||||
: getTextEditsForAutoImportInsertion(
|
||||
{ name: importName, alias: abbrFromUsers },
|
||||
importNameInfo,
|
||||
moduleNameInfo,
|
||||
this._importStatements,
|
||||
moduleName,
|
||||
importGroup,
|
||||
this._parseResults,
|
||||
this._invocationPosition
|
||||
|
@ -112,7 +112,7 @@ import {
|
||||
} from '../parser/parseNodes';
|
||||
import { ParseResults } from '../parser/parser';
|
||||
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 { IndexResults } from './documentSymbolProvider';
|
||||
import {
|
||||
@ -282,6 +282,7 @@ export interface CompletionOptions {
|
||||
lazyEdit: boolean;
|
||||
autoImport: boolean;
|
||||
extraCommitChars: boolean;
|
||||
importFormat: ImportFormat;
|
||||
}
|
||||
|
||||
export type AbbreviationMap = Map<string, AbbreviationInfo>;
|
||||
@ -2241,7 +2242,11 @@ export class CompletionProvider {
|
||||
this._position,
|
||||
completionResults.completionMap,
|
||||
moduleSymbolMap,
|
||||
{ libraryMap: this._autoImportMaps.libraryMap, lazyEdit }
|
||||
{
|
||||
libraryMap: this._autoImportMaps.libraryMap,
|
||||
lazyEdit,
|
||||
importFormat: this._options.importFormat,
|
||||
}
|
||||
);
|
||||
|
||||
const results: AutoImportResult[] = [];
|
||||
|
@ -52,6 +52,7 @@ import { convertOffsetToPosition } from '../common/positionUtils';
|
||||
import { TextRange } from '../common/textRange';
|
||||
import { ModuleNameNode, NameNode, ParseNode, ParseNodeType } from '../parser/parseNodes';
|
||||
import { ParseResults } from '../parser/parser';
|
||||
import { ImportFormat } from './autoImporter';
|
||||
|
||||
export interface ImportData {
|
||||
containsUnreferenceableSymbols: boolean;
|
||||
@ -79,6 +80,7 @@ export class ImportAdder {
|
||||
result: ImportData,
|
||||
parseResults: ParseResults,
|
||||
insertionPosition: number,
|
||||
importFormat: ImportFormat,
|
||||
token: CancellationToken
|
||||
): TextEditAction[] {
|
||||
throwIfCancellationRequested(token);
|
||||
@ -94,6 +96,7 @@ export class ImportAdder {
|
||||
continue;
|
||||
}
|
||||
|
||||
const relativePath = getRelativeModuleName(this._importResolver.fileSystem, filePath, importInfo.filePath);
|
||||
const moduleAndType = this._importResolver.getModuleNameForImport(importInfo.filePath, execEnv);
|
||||
if (!moduleAndType.moduleName) {
|
||||
if (!importInfo.nameInfo.name) {
|
||||
@ -103,16 +106,17 @@ export class ImportAdder {
|
||||
// module can't be addressed by absolute path in "from import" statement.
|
||||
// ex) namespace package at [workspace root] or [workspace root]\__init__.py(i)
|
||||
// use relative path
|
||||
moduleAndType.moduleName = getRelativeModuleName(
|
||||
this._importResolver.fileSystem,
|
||||
filePath,
|
||||
importInfo.filePath
|
||||
);
|
||||
importFormat = ImportFormat.Relative;
|
||||
}
|
||||
|
||||
addIfUnique(
|
||||
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)
|
||||
);
|
||||
}
|
||||
|
@ -100,8 +100,8 @@ function _addMissingOptionalToParam(
|
||||
} else {
|
||||
const additionalEditActions = getTextEditsForAutoImportInsertion(
|
||||
{ name: 'Optional' },
|
||||
{ name: 'typing' },
|
||||
importStatements,
|
||||
'typing',
|
||||
ImportGroup.BuiltIn,
|
||||
parseResults,
|
||||
startPos
|
||||
|
@ -486,8 +486,8 @@ export class RenameModuleProvider {
|
||||
this._addResultEdits(
|
||||
getTextEditsForAutoImportInsertion(
|
||||
[],
|
||||
{ name: this._newModuleName },
|
||||
importStatements,
|
||||
this._newModuleName,
|
||||
getImportGroupFromModuleNameAndType(this._newModuleNameAndType),
|
||||
parseResults,
|
||||
convertOffsetToPosition(parseResults.parseTree.length, parseResults.tokenizerOutput.lines)
|
||||
@ -1324,8 +1324,8 @@ export class RenameModuleProvider {
|
||||
|
||||
return getTextEditsForAutoImportInsertion(
|
||||
importNameInfo,
|
||||
{ name: moduleName },
|
||||
importStatements,
|
||||
moduleName,
|
||||
getImportGroupFromModuleNameAndType(this._newModuleNameAndType),
|
||||
parseResults,
|
||||
convertOffsetToPosition(parseResults.parseTree.length, parseResults.tokenizerOutput.lines)
|
||||
|
@ -17,6 +17,7 @@ import { ConfigOptions } from '../common/configOptions';
|
||||
import { NullConsole } from '../common/console';
|
||||
import { normalizeSlashes } from '../common/pathUtils';
|
||||
import { convertOffsetsToRange, convertOffsetToPosition } from '../common/positionUtils';
|
||||
import { ImportFormat } from '../languageService/autoImporter';
|
||||
import { parseTestData } from './harness/fourslash/fourSlashParser';
|
||||
import { TestAccessHost } from './harness/testAccessHost';
|
||||
import * as host from './harness/testHost';
|
||||
@ -54,6 +55,7 @@ test('check chained files', async () => {
|
||||
snippet: false,
|
||||
autoImport: false,
|
||||
extraCommitChars: false,
|
||||
importFormat: ImportFormat.Absolute,
|
||||
},
|
||||
undefined,
|
||||
CancellationToken.None
|
||||
@ -100,6 +102,7 @@ test('modify chained files', async () => {
|
||||
snippet: false,
|
||||
autoImport: false,
|
||||
extraCommitChars: false,
|
||||
importFormat: ImportFormat.Absolute,
|
||||
},
|
||||
undefined,
|
||||
CancellationToken.None
|
||||
|
@ -65,7 +65,7 @@ import {
|
||||
WellKnownWorkspaceKinds,
|
||||
WorkspaceServiceInstance,
|
||||
} from '../../../languageServerBase';
|
||||
import { AbbreviationInfo } from '../../../languageService/autoImporter';
|
||||
import { AbbreviationInfo, ImportFormat } from '../../../languageService/autoImporter';
|
||||
import { CompletionOptions } from '../../../languageService/completionProvider';
|
||||
import { DefinitionFilter } from '../../../languageService/definitionProvider';
|
||||
import { convertHoverResults } from '../../../languageService/hoverProvider';
|
||||
@ -1099,6 +1099,7 @@ export class TestState {
|
||||
lazyEdit: true,
|
||||
autoImport: true,
|
||||
extraCommitChars: true,
|
||||
importFormat: ImportFormat.Absolute,
|
||||
};
|
||||
const nameMap = abbrMap ? new Map<string, AbbreviationInfo>(Object.entries(abbrMap)) : undefined;
|
||||
const result = await this.workspace.serviceInstance.getCompletionsForPosition(
|
||||
|
@ -10,6 +10,7 @@ import assert from 'assert';
|
||||
import { CancellationToken } from 'vscode-languageserver';
|
||||
|
||||
import { rangesAreEqual, TextRange } from '../common/textRange';
|
||||
import { ImportFormat } from '../languageService/autoImporter';
|
||||
import { ImportAdder } from '../languageService/importAdder';
|
||||
import { parseAndGetTestState } from './harness/fourslash/testState';
|
||||
|
||||
@ -1319,7 +1320,23 @@ test('move into the same file from import statement for submodule', () => {
|
||||
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 src = state.getRangeByMarkerName('src')!;
|
||||
@ -1336,6 +1353,7 @@ function testImportMove(code: string) {
|
||||
importData,
|
||||
state.program.getBoundSourceFile(dest.fileName)!.getParseResults()!,
|
||||
dest.position,
|
||||
importFormat,
|
||||
CancellationToken.None
|
||||
);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user