mirror of
https://github.com/microsoft/pyright.git
synced 2024-10-05 20:38:25 +03:00
[PylanceBot] Pull Pylance with Pyright 1.1.295 (#4675)
Co-authored-by: Bill Schnurr <bschnurr@microsoft.com> Co-authored-by: HeeJae Chang <hechang@microsoft.com> Co-authored-by: Erik De Bonte <erikd@microsoft.com> Co-authored-by: Rich Chiodo <rchiodo@microsoft.com>
This commit is contained in:
parent
f5e43ca29f
commit
e411e53e85
4456
package-lock.json
generated
4456
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
16
package.json
16
package.json
@ -22,20 +22,20 @@
|
||||
"devDependencies": {
|
||||
"@types/glob": "^7.2.0",
|
||||
"@types/node": "^17.0.45",
|
||||
"@types/yargs": "^16.0.4",
|
||||
"@typescript-eslint/eslint-plugin": "^5.48.0",
|
||||
"@typescript-eslint/parser": "^5.48.0",
|
||||
"@types/yargs": "^16.0.5",
|
||||
"@typescript-eslint/eslint-plugin": "^5.52.0",
|
||||
"@typescript-eslint/parser": "^5.52.0",
|
||||
"detect-indent": "^6.1.0",
|
||||
"eslint": "^8.31.0",
|
||||
"eslint": "^8.34.0",
|
||||
"eslint-config-prettier": "^8.6.0",
|
||||
"eslint-plugin-simple-import-sort": "^7.0.0",
|
||||
"glob": "^7.2.3",
|
||||
"jsonc-parser": "^3.2.0",
|
||||
"lerna": "^5.6.2",
|
||||
"npm-check-updates": "^16.6.2",
|
||||
"lerna": "^6.5.1",
|
||||
"npm-check-updates": "^16.7.4",
|
||||
"p-queue": "^6.6.2",
|
||||
"prettier": "2.8.1",
|
||||
"syncpack": "^5.8.15",
|
||||
"prettier": "2.8.4",
|
||||
"syncpack": "^9.0.2",
|
||||
"typescript": "~4.4.4",
|
||||
"yargs": "^16.2.0"
|
||||
}
|
||||
|
28
packages/pyright-internal/package-lock.json
generated
28
packages/pyright-internal/package-lock.json
generated
@ -10,7 +10,7 @@
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@iarna/toml": "2.2.5",
|
||||
"@yarnpkg/fslib": "2.10.0",
|
||||
"@yarnpkg/fslib": "2.10.1",
|
||||
"@yarnpkg/libzip": "2.2.4",
|
||||
"chalk": "^4.1.2",
|
||||
"chokidar": "^3.5.3",
|
||||
@ -22,7 +22,7 @@
|
||||
"typescript-char": "^0.0.0",
|
||||
"vscode-jsonrpc": "8.1.0-next.6",
|
||||
"vscode-languageserver": "8.1.0-next.5",
|
||||
"vscode-languageserver-textdocument": "^1.0.8",
|
||||
"vscode-languageserver-textdocument": "^1.0.9",
|
||||
"vscode-languageserver-types": "3.17.2",
|
||||
"vscode-uri": "^3.0.7"
|
||||
},
|
||||
@ -1044,9 +1044,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@yarnpkg/fslib": {
|
||||
"version": "2.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@yarnpkg/fslib/-/fslib-2.10.0.tgz",
|
||||
"integrity": "sha512-eHqvrVlzlhd4owKoLsMRaL4wTGer+r9BXi95u1omHYcAcEQbKnHH3PqYf3j7nxsc8apa09WyA1XNCiiIniFXLg==",
|
||||
"version": "2.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@yarnpkg/fslib/-/fslib-2.10.1.tgz",
|
||||
"integrity": "sha512-pVMLtOYu87N5y5G2lyPNYTY2JbTco99v7nGFI34Blx01Ct9LmoKVOc91vnLOYIMMljKr1c8xs1O2UamRdMG5Pg==",
|
||||
"dependencies": {
|
||||
"@yarnpkg/libzip": "^2.2.4",
|
||||
"tslib": "^1.13.0"
|
||||
@ -4218,9 +4218,9 @@
|
||||
"integrity": "sha512-3kkNSCycNKUalSJIrjIptGeY9UTJr1Nk5HT/aT00jjIwiCvIUNbRdK90av2Y3j1Jityot68dBVc3YYdwwH3zOQ=="
|
||||
},
|
||||
"node_modules/vscode-languageserver-textdocument": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.8.tgz",
|
||||
"integrity": "sha512-1bonkGqQs5/fxGT5UchTgjGVnfysL0O8v1AYMBjqTbWQTFn721zaPGDYFkOKtfDgFiSgXM3KwaG3FMGfW4Ed9Q=="
|
||||
"version": "1.0.9",
|
||||
"resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.9.tgz",
|
||||
"integrity": "sha512-NPfHVGFW2/fQEWHspr8x3PXhRgtFbuDZdl7p6ifuN3M7nk2Yjf5POr/NfDBuAiQG88DehDyJ7nGOT+p+edEtbw=="
|
||||
},
|
||||
"node_modules/vscode-languageserver-types": {
|
||||
"version": "3.17.2",
|
||||
@ -5251,9 +5251,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"@yarnpkg/fslib": {
|
||||
"version": "2.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@yarnpkg/fslib/-/fslib-2.10.0.tgz",
|
||||
"integrity": "sha512-eHqvrVlzlhd4owKoLsMRaL4wTGer+r9BXi95u1omHYcAcEQbKnHH3PqYf3j7nxsc8apa09WyA1XNCiiIniFXLg==",
|
||||
"version": "2.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@yarnpkg/fslib/-/fslib-2.10.1.tgz",
|
||||
"integrity": "sha512-pVMLtOYu87N5y5G2lyPNYTY2JbTco99v7nGFI34Blx01Ct9LmoKVOc91vnLOYIMMljKr1c8xs1O2UamRdMG5Pg==",
|
||||
"requires": {
|
||||
"@yarnpkg/libzip": "^2.2.4",
|
||||
"tslib": "^1.13.0"
|
||||
@ -7631,9 +7631,9 @@
|
||||
}
|
||||
},
|
||||
"vscode-languageserver-textdocument": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.8.tgz",
|
||||
"integrity": "sha512-1bonkGqQs5/fxGT5UchTgjGVnfysL0O8v1AYMBjqTbWQTFn721zaPGDYFkOKtfDgFiSgXM3KwaG3FMGfW4Ed9Q=="
|
||||
"version": "1.0.9",
|
||||
"resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.9.tgz",
|
||||
"integrity": "sha512-NPfHVGFW2/fQEWHspr8x3PXhRgtFbuDZdl7p6ifuN3M7nk2Yjf5POr/NfDBuAiQG88DehDyJ7nGOT+p+edEtbw=="
|
||||
},
|
||||
"vscode-languageserver-types": {
|
||||
"version": "3.17.2",
|
||||
|
@ -16,7 +16,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@iarna/toml": "2.2.5",
|
||||
"@yarnpkg/fslib": "2.10.0",
|
||||
"@yarnpkg/fslib": "2.10.1",
|
||||
"@yarnpkg/libzip": "2.2.4",
|
||||
"chalk": "^4.1.2",
|
||||
"chokidar": "^3.5.3",
|
||||
@ -28,7 +28,7 @@
|
||||
"typescript-char": "^0.0.0",
|
||||
"vscode-jsonrpc": "8.1.0-next.6",
|
||||
"vscode-languageserver": "8.1.0-next.5",
|
||||
"vscode-languageserver-textdocument": "^1.0.8",
|
||||
"vscode-languageserver-textdocument": "^1.0.9",
|
||||
"vscode-languageserver-types": "3.17.2",
|
||||
"vscode-uri": "^3.0.7"
|
||||
},
|
||||
|
@ -125,9 +125,9 @@ export class BackgroundAnalysisProgram {
|
||||
this._reportDiagnosticsForRemovedFiles(diagnostics);
|
||||
}
|
||||
|
||||
addTrackedFile(filePath: string, isThirdPartyImport: boolean) {
|
||||
this._backgroundAnalysis?.addTrackedFile(filePath, isThirdPartyImport);
|
||||
this._program.addTrackedFile(filePath, isThirdPartyImport);
|
||||
addInterimFile(filePath: string) {
|
||||
this._backgroundAnalysis?.addInterimFile(filePath);
|
||||
this._program.addInterimFile(filePath);
|
||||
}
|
||||
|
||||
markAllFilesDirty(evenIfContentsAreSame: boolean, indexingNeeded = true) {
|
||||
|
@ -11,7 +11,7 @@
|
||||
import type { Dirent } from 'fs';
|
||||
|
||||
import { appendArray, flatten, getMapValues, getOrAdd } from '../common/collectionUtils';
|
||||
import { ConfigOptions, ExecutionEnvironment } from '../common/configOptions';
|
||||
import { ConfigOptions, ExecutionEnvironment, matchFileSpecs } from '../common/configOptions';
|
||||
import { FileSystem } from '../common/fileSystem';
|
||||
import { Host } from '../common/host';
|
||||
import { stubsSuffix } from '../common/pathConsts';
|
||||
@ -199,6 +199,7 @@ export class ImportResolver {
|
||||
let current = origin;
|
||||
while (this._shouldWalkUp(current, root, execEnv)) {
|
||||
const result = this.resolveAbsoluteImport(
|
||||
sourceFilePath,
|
||||
current,
|
||||
execEnv,
|
||||
moduleDescriptor,
|
||||
@ -243,6 +244,7 @@ export class ImportResolver {
|
||||
moduleDescriptor: ImportedModuleDescriptor,
|
||||
importFailureInfo: string[]
|
||||
) {
|
||||
const fromUserFile = matchFileSpecs(this._configOptions, sourceFilePath);
|
||||
const notFoundResult: ImportResult = {
|
||||
importName,
|
||||
isRelative: false,
|
||||
@ -279,7 +281,12 @@ export class ImportResolver {
|
||||
}
|
||||
} else {
|
||||
// Is it already cached?
|
||||
const cachedResults = this._lookUpResultsInCache(execEnv, importName, moduleDescriptor.importedSymbols);
|
||||
const cachedResults = this._lookUpResultsInCache(
|
||||
execEnv,
|
||||
importName,
|
||||
moduleDescriptor.importedSymbols,
|
||||
fromUserFile
|
||||
);
|
||||
|
||||
if (cachedResults) {
|
||||
// In most cases, we can simply return a cached entry. However, there are cases
|
||||
@ -315,11 +322,23 @@ export class ImportResolver {
|
||||
) || notFoundResult;
|
||||
}
|
||||
|
||||
return this.addResultsToCache(execEnv, importName, bestImport, moduleDescriptor.importedSymbols);
|
||||
return this.addResultsToCache(
|
||||
execEnv,
|
||||
importName,
|
||||
bestImport,
|
||||
moduleDescriptor.importedSymbols,
|
||||
fromUserFile
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return this.addResultsToCache(execEnv, importName, notFoundResult, /* importedSymbols */ undefined);
|
||||
return this.addResultsToCache(
|
||||
execEnv,
|
||||
importName,
|
||||
notFoundResult,
|
||||
/* importedSymbols */ undefined,
|
||||
fromUserFile
|
||||
);
|
||||
}
|
||||
|
||||
getCompletionSuggestions(
|
||||
@ -879,10 +898,11 @@ export class ImportResolver {
|
||||
execEnv: ExecutionEnvironment,
|
||||
importName: string,
|
||||
importResult: ImportResult,
|
||||
importedSymbols: string[] | undefined
|
||||
importedSymbols: string[] | undefined,
|
||||
fromUserFile: boolean
|
||||
) {
|
||||
getOrAdd(this._cachedImportResults, execEnv.root, () => new Map<string, ImportResult>()).set(
|
||||
importName,
|
||||
this._getCacheKey(importName, fromUserFile),
|
||||
importResult
|
||||
);
|
||||
|
||||
@ -892,6 +912,7 @@ export class ImportResolver {
|
||||
// Follows import resolution algorithm defined in PEP-420:
|
||||
// https://www.python.org/dev/peps/pep-0420/
|
||||
protected resolveAbsoluteImport(
|
||||
sourceFilePath: string | undefined,
|
||||
rootPath: string,
|
||||
execEnv: ExecutionEnvironment,
|
||||
moduleDescriptor: ImportedModuleDescriptor,
|
||||
@ -908,6 +929,7 @@ export class ImportResolver {
|
||||
// their stubs separately from their package implementation by appending the string
|
||||
// '-stubs' to its top - level directory name. We'll look there first.
|
||||
const importResult = this._resolveAbsoluteImport(
|
||||
sourceFilePath,
|
||||
rootPath,
|
||||
execEnv,
|
||||
moduleDescriptor,
|
||||
@ -931,6 +953,7 @@ export class ImportResolver {
|
||||
}
|
||||
|
||||
return this._resolveAbsoluteImport(
|
||||
sourceFilePath,
|
||||
rootPath,
|
||||
execEnv,
|
||||
moduleDescriptor,
|
||||
@ -963,6 +986,7 @@ export class ImportResolver {
|
||||
}
|
||||
|
||||
private _resolveAbsoluteImport(
|
||||
sourceFilePath: string | undefined,
|
||||
rootPath: string,
|
||||
execEnv: ExecutionEnvironment,
|
||||
moduleDescriptor: ImportedModuleDescriptor,
|
||||
@ -1137,6 +1161,18 @@ export class ImportResolver {
|
||||
importFound = resolvedPaths.length >= moduleDescriptor.nameParts.length;
|
||||
}
|
||||
|
||||
// Set import type based on if import is in the excluded list or not
|
||||
let importType = ImportType.Local;
|
||||
if (importFound && sourceFilePath && !isNamespacePackage && resolvedPaths.length > 0) {
|
||||
// Check the resolved path. If it's for an included file or the importing
|
||||
// file is also excluded, treat as a local import.
|
||||
importType =
|
||||
!matchFileSpecs(this._configOptions, sourceFilePath, true) ||
|
||||
matchFileSpecs(this._configOptions, resolvedPaths[resolvedPaths.length - 1], true)
|
||||
? ImportType.Local
|
||||
: ImportType.ThirdParty;
|
||||
}
|
||||
|
||||
return {
|
||||
importName,
|
||||
isRelative: false,
|
||||
@ -1146,7 +1182,7 @@ export class ImportResolver {
|
||||
isImportFound: importFound,
|
||||
isPartlyResolved,
|
||||
importFailureInfo,
|
||||
importType: ImportType.Local,
|
||||
importType: importType,
|
||||
resolvedPaths,
|
||||
searchPath: rootPath,
|
||||
isStubFile,
|
||||
@ -1197,17 +1233,22 @@ export class ImportResolver {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private _getCacheKey(importName: string, fromUserFile: boolean) {
|
||||
return `${importName}-${fromUserFile}`;
|
||||
}
|
||||
|
||||
private _lookUpResultsInCache(
|
||||
execEnv: ExecutionEnvironment,
|
||||
importName: string,
|
||||
importedSymbols: string[] | undefined
|
||||
importedSymbols: string[] | undefined,
|
||||
fromUserFile: boolean
|
||||
) {
|
||||
const cacheForExecEnv = this._cachedImportResults.get(execEnv.root);
|
||||
if (!cacheForExecEnv) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const cachedEntry = cacheForExecEnv.get(importName);
|
||||
const cachedEntry = cacheForExecEnv.get(this._getCacheKey(importName, fromUserFile));
|
||||
if (!cachedEntry) {
|
||||
return undefined;
|
||||
}
|
||||
@ -1315,6 +1356,7 @@ export class ImportResolver {
|
||||
if (allowPyi && this._configOptions.stubPath) {
|
||||
importFailureInfo.push(`Looking in stubPath '${this._configOptions.stubPath}'`);
|
||||
const typingsImport = this.resolveAbsoluteImport(
|
||||
sourceFilePath,
|
||||
this._configOptions.stubPath,
|
||||
execEnv,
|
||||
moduleDescriptor,
|
||||
@ -1356,6 +1398,7 @@ export class ImportResolver {
|
||||
importFailureInfo.push(`Looking in root directory of execution environment ` + `'${execEnv.root}'`);
|
||||
|
||||
localImport = this.resolveAbsoluteImport(
|
||||
sourceFilePath,
|
||||
execEnv.root,
|
||||
execEnv,
|
||||
moduleDescriptor,
|
||||
@ -1373,6 +1416,7 @@ export class ImportResolver {
|
||||
for (const extraPath of execEnv.extraPaths) {
|
||||
importFailureInfo.push(`Looking in extraPath '${extraPath}'`);
|
||||
localImport = this.resolveAbsoluteImport(
|
||||
sourceFilePath,
|
||||
extraPath,
|
||||
execEnv,
|
||||
moduleDescriptor,
|
||||
@ -1394,6 +1438,7 @@ export class ImportResolver {
|
||||
importFailureInfo.push(`Looking in python search path '${searchPath}'`);
|
||||
|
||||
const thirdPartyImport = this.resolveAbsoluteImport(
|
||||
sourceFilePath,
|
||||
searchPath,
|
||||
execEnv,
|
||||
moduleDescriptor,
|
||||
@ -1504,6 +1549,9 @@ export class ImportResolver {
|
||||
if (bestImportSoFar.importType === ImportType.Local && !bestImportSoFar.isNamespacePackage) {
|
||||
return bestImportSoFar;
|
||||
}
|
||||
if (newImport.importType === ImportType.Local && !newImport.isNamespacePackage) {
|
||||
return newImport;
|
||||
}
|
||||
|
||||
// If both are namespace imports, select the one that resolves the symbols.
|
||||
if (
|
||||
@ -1607,6 +1655,7 @@ export class ImportResolver {
|
||||
for (const typeshedPath of typeshedPaths) {
|
||||
if (this.dirExistsCached(typeshedPath)) {
|
||||
const importInfo = this.resolveAbsoluteImport(
|
||||
undefined,
|
||||
typeshedPath,
|
||||
execEnv,
|
||||
moduleDescriptor,
|
||||
@ -1975,6 +2024,7 @@ export class ImportResolver {
|
||||
|
||||
// Now try to match the module parts from the current directory location.
|
||||
const absImport = this.resolveAbsoluteImport(
|
||||
sourceFilePath,
|
||||
directory,
|
||||
execEnv,
|
||||
moduleDescriptor,
|
||||
@ -1989,6 +2039,7 @@ export class ImportResolver {
|
||||
// the same folder for the real module. Otherwise, it will
|
||||
// error out on runtime.
|
||||
absImport.nonStubImportResult = this.resolveAbsoluteImport(
|
||||
sourceFilePath,
|
||||
directory,
|
||||
execEnv,
|
||||
moduleDescriptor,
|
||||
|
@ -51,6 +51,7 @@ import {
|
||||
getEmptyRange,
|
||||
Position,
|
||||
Range,
|
||||
TextRange,
|
||||
} from '../common/textRange';
|
||||
import { Duration, timingStats } from '../common/timing';
|
||||
import {
|
||||
@ -73,7 +74,7 @@ import { DocumentSymbolCollector, DocumentSymbolCollectorUseCase } from '../lang
|
||||
import { IndexOptions, IndexResults, WorkspaceSymbolCallback } from '../languageService/documentSymbolProvider';
|
||||
import { HoverResults } from '../languageService/hoverProvider';
|
||||
import { ImportAdder } from '../languageService/importAdder';
|
||||
import { getNewlineIndentation, reindentSpan } from '../languageService/indentationUtils';
|
||||
import { getModuleStatementIndentation, reindentSpan } from '../languageService/indentationUtils';
|
||||
import { getInsertionPointForSymbolUnderModule } from '../languageService/insertionPointUtils';
|
||||
import { ReferenceCallback, ReferencesResult } from '../languageService/referencesProvider';
|
||||
import { RenameModuleProvider } from '../languageService/renameModuleProvider';
|
||||
@ -88,7 +89,7 @@ import { Declaration } from './declaration';
|
||||
import { getNameFromDeclaration } from './declarationUtils';
|
||||
import { ImportResolver } from './importResolver';
|
||||
import { ImportResult, ImportType } from './importResult';
|
||||
import { findNodeByOffset, getDocString, getFullStatementRange, isBlankLine } from './parseTreeUtils';
|
||||
import { findNodeByOffset, getDocString, isBlankLine } from './parseTreeUtils';
|
||||
import { Scope } from './scope';
|
||||
import { getScopeForNode } from './scopeUtils';
|
||||
import { IPythonMode, parseFile, SourceFile } from './sourceFile';
|
||||
@ -308,6 +309,16 @@ export class Program {
|
||||
});
|
||||
}
|
||||
|
||||
addInterimFile(filePath: string): SourceFileInfo {
|
||||
// Double check not already there.
|
||||
let fileInfo = this.getSourceFileInfo(filePath);
|
||||
if (!fileInfo) {
|
||||
fileInfo = this._createInterimFileInfo(filePath);
|
||||
this._addToSourceFileListAndMap(fileInfo);
|
||||
}
|
||||
return fileInfo;
|
||||
}
|
||||
|
||||
addTrackedFile(filePath: string, isThirdPartyImport = false, isInPyTypedPackage = false): SourceFile {
|
||||
let sourceFileInfo = this.getSourceFileInfo(filePath);
|
||||
const importName = this._getImportNameForFile(filePath);
|
||||
@ -394,11 +405,6 @@ export class Program {
|
||||
}
|
||||
|
||||
sourceFileInfo.sourceFile.setClientVersion(version, contents);
|
||||
|
||||
// Tell any extensions that this source file changed.
|
||||
Extensions.getProgramExtensions(filePath).forEach((e) =>
|
||||
e.sourceFileChanged ? e.sourceFileChanged(sourceFileInfo!) : undefined
|
||||
);
|
||||
}
|
||||
|
||||
getChainedFilePath(filePath: string): string | undefined {
|
||||
@ -505,8 +511,8 @@ export class Program {
|
||||
return this._sourceFileList.filter((s) => isUserCode(s)).length;
|
||||
}
|
||||
|
||||
getTracked(): SourceFileInfo[] {
|
||||
return this._sourceFileList.filter((s) => s.isTracked);
|
||||
getUserFiles(): SourceFileInfo[] {
|
||||
return this._sourceFileList.filter((s) => isUserCode(s));
|
||||
}
|
||||
|
||||
getOpened(): SourceFileInfo[] {
|
||||
@ -572,13 +578,13 @@ export class Program {
|
||||
return this._sourceFileMap.get(normalizePathCase(this._fs, filePath));
|
||||
}
|
||||
|
||||
getBoundSourceFileInfo(filePath: string): SourceFileInfo | undefined {
|
||||
getBoundSourceFileInfo(filePath: string, content?: string, force?: boolean): SourceFileInfo | undefined {
|
||||
const sourceFileInfo = this.getSourceFileInfo(filePath);
|
||||
if (!sourceFileInfo) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
this._bindFile(sourceFileInfo);
|
||||
this._bindFile(sourceFileInfo, content, force);
|
||||
return sourceFileInfo;
|
||||
}
|
||||
|
||||
@ -896,8 +902,7 @@ export class Program {
|
||||
let shadowFileInfo = this.getSourceFileInfo(shadowImplPath);
|
||||
|
||||
if (!shadowFileInfo) {
|
||||
shadowFileInfo = this._createInterimFileInfo(shadowImplPath);
|
||||
this._addToSourceFileListAndMap(shadowFileInfo);
|
||||
shadowFileInfo = this.addInterimFile(shadowImplPath);
|
||||
}
|
||||
|
||||
if (!shadowFileInfo.shadows.includes(stubFile)) {
|
||||
@ -969,8 +974,8 @@ export class Program {
|
||||
return this._evaluator;
|
||||
}
|
||||
|
||||
private _parseFile(fileToParse: SourceFileInfo, content?: string) {
|
||||
if (!this._isFileNeeded(fileToParse) || !fileToParse.sourceFile.isParseRequired()) {
|
||||
private _parseFile(fileToParse: SourceFileInfo, content?: string, force?: boolean) {
|
||||
if (!force && (!this._isFileNeeded(fileToParse) || !fileToParse.sourceFile.isParseRequired())) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -994,12 +999,12 @@ export class Program {
|
||||
|
||||
// Binds the specified file and all of its dependencies, recursively. If
|
||||
// it runs out of time, it returns true. If it completes, it returns false.
|
||||
private _bindFile(fileToAnalyze: SourceFileInfo, content?: string): void {
|
||||
if (!this._isFileNeeded(fileToAnalyze) || !fileToAnalyze.sourceFile.isBindingRequired()) {
|
||||
private _bindFile(fileToAnalyze: SourceFileInfo, content?: string, force?: boolean): void {
|
||||
if (!force && (!this._isFileNeeded(fileToAnalyze) || !fileToAnalyze.sourceFile.isBindingRequired())) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._parseFile(fileToAnalyze, content);
|
||||
this._parseFile(fileToAnalyze, content, force);
|
||||
|
||||
const getScopeIfAvailable = (fileInfo: SourceFileInfo | undefined) => {
|
||||
if (!fileInfo || fileInfo === fileToAnalyze) {
|
||||
@ -2021,26 +2026,6 @@ export class Program {
|
||||
}
|
||||
|
||||
moveSymbolAtPosition(
|
||||
filePath: string,
|
||||
newFilePath: string,
|
||||
position: Position,
|
||||
options: { importFormat: ImportFormat },
|
||||
token?: CancellationToken
|
||||
): FileEditActions | undefined {
|
||||
if (CancellationToken.is(options)) {
|
||||
return this._moveSymbolAtPosition(
|
||||
filePath,
|
||||
newFilePath,
|
||||
position,
|
||||
{ importFormat: ImportFormat.Absolute },
|
||||
options
|
||||
);
|
||||
}
|
||||
|
||||
return this._moveSymbolAtPosition(filePath, newFilePath, position, options, token!);
|
||||
}
|
||||
|
||||
private _moveSymbolAtPosition(
|
||||
filePath: string,
|
||||
newFilePath: string,
|
||||
position: Position,
|
||||
@ -2083,7 +2068,7 @@ export class Program {
|
||||
}
|
||||
|
||||
// If this isn't a name node, there are no references to be found.
|
||||
if (node.nodeType !== ParseNodeType.Name) {
|
||||
if (node.nodeType !== ParseNodeType.Name || !RenameModuleProvider.canMoveSymbol(this._evaluator!, node)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@ -2120,17 +2105,15 @@ export class Program {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const symbolRange = RenameModuleProvider.getSymbolTextRange(parseResults, sourceDecl);
|
||||
const importAdder = new ImportAdder(this._configOptions, this._importResolver, this._evaluator!);
|
||||
const collectedimports = importAdder.collectImportsForSymbolsUsed(parseResults, sourceDecl.node, token);
|
||||
const collectedimports = importAdder.collectImportsForSymbolsUsed(parseResults, symbolRange, token);
|
||||
|
||||
let insertionPoint: number | undefined = 0;
|
||||
let insertionIndentation = 0;
|
||||
|
||||
const newFileParseResults = newFileInfo?.sourceFile.getParseResults();
|
||||
if (newFileParseResults) {
|
||||
// TODO: Add "insertAfter" option to make sure we insert symbol after that point.
|
||||
// For example, if collectedImports has symbols from the destination file, we should
|
||||
// insert after those symbols are defined.
|
||||
const insertBefore = renameModuleProvider.tryGetFirstSymbolUsage(newFileParseResults);
|
||||
insertionPoint = getInsertionPointForSymbolUnderModule(
|
||||
this._evaluator!,
|
||||
@ -2146,17 +2129,22 @@ export class Program {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
insertionIndentation = getNewlineIndentation(newFileParseResults, insertionPoint);
|
||||
insertionIndentation = getModuleStatementIndentation(newFileParseResults);
|
||||
}
|
||||
|
||||
const fileOperations: FileOperations[] = [];
|
||||
const reindentResult = reindentSpan(parseResults, symbolRange, insertionIndentation);
|
||||
const fullRange = RenameModuleProvider.getSymbolFullStatementTextRange(parseResults, sourceDecl);
|
||||
renameModuleProvider.textEditTracker.addEdit(
|
||||
filePath,
|
||||
getFullStatementRange(sourceDecl.node, parseResults, { includeTrailingBlankLines: true }),
|
||||
convertTextRangeToRange(
|
||||
TextRange.combine([reindentResult.originalSpan, fullRange])!,
|
||||
parseResults.tokenizerOutput.lines
|
||||
),
|
||||
''
|
||||
);
|
||||
|
||||
let codeSnippetToInsert = reindentSpan(parseResults, sourceDecl.node, insertionIndentation);
|
||||
const fileOperations: FileOperations[] = [];
|
||||
let codeSnippetToInsert = reindentResult.text;
|
||||
if (newFileParseResults) {
|
||||
// TODO: We need to "add import" statement for symbols defined in the destination file
|
||||
// if we couldn't find insertion point where all constraints are met.
|
||||
@ -2193,6 +2181,7 @@ export class Program {
|
||||
|
||||
const insertAddEdits = importAdder.applyImports(
|
||||
collectedimports,
|
||||
newFilePath,
|
||||
tempParseResults,
|
||||
insertionPoint,
|
||||
options.importFormat,
|
||||
@ -2562,6 +2551,7 @@ export class Program {
|
||||
this._createNewEvaluator();
|
||||
this._discardCachedParseResults();
|
||||
this._parsedFileCount = 0;
|
||||
Extensions.getProgramExtensions(this.rootPath).forEach((e) => (e.clearCache ? e.clearCache() : null));
|
||||
}
|
||||
|
||||
test_createSourceMapper(execEnv: ExecutionEnvironment, from?: SourceFileInfo) {
|
||||
@ -2872,8 +2862,7 @@ export class Program {
|
||||
// Special case for import statement.
|
||||
// ex) import X.Y
|
||||
// SourceFile for X might not be in memory since import `X.Y` only brings in Y
|
||||
stubFileInfo = this._createInterimFileInfo(stubFilePath);
|
||||
this._addToSourceFileListAndMap(stubFileInfo);
|
||||
stubFileInfo = this.addInterimFile(stubFilePath);
|
||||
}
|
||||
|
||||
this._addShadowedFile(stubFileInfo, implFilePath);
|
||||
@ -2885,8 +2874,7 @@ export class Program {
|
||||
// Special case for import statement.
|
||||
// ex) import X.Y
|
||||
// SourceFile for X might not be in memory since import `X.Y` only brings in Y
|
||||
fileInfo = this._createInterimFileInfo(f);
|
||||
this._addToSourceFileListAndMap(fileInfo);
|
||||
fileInfo = this.addInterimFile(f);
|
||||
|
||||
// Even though this file is not referenced by anything, make sure
|
||||
// we have parse tree for doc string.
|
||||
|
@ -56,7 +56,7 @@ import {
|
||||
} from '../common/pathUtils';
|
||||
import { DocumentRange, Position, Range } from '../common/textRange';
|
||||
import { timingStats } from '../common/timing';
|
||||
import { AutoImportOptions } from '../languageService/autoImporter';
|
||||
import { AutoImportOptions, ImportFormat } from '../languageService/autoImporter';
|
||||
import { AbbreviationMap, CompletionOptions, CompletionResultsList } from '../languageService/completionProvider';
|
||||
import { DefinitionFilter } from '../languageService/definitionProvider';
|
||||
import { WorkspaceSymbolCallback } from '../languageService/documentSymbolProvider';
|
||||
@ -175,7 +175,7 @@ export class AnalyzerService {
|
||||
|
||||
// Create the extensions tied to this program. This is where the mutating 'addTrackedFile' will actually
|
||||
// mutate the local program and the BG thread one.
|
||||
Extensions.createProgramExtensions(this._program, { addTrackedFile: this.addTrackedFile.bind(this) });
|
||||
Extensions.createProgramExtensions(this._program, { addInterimFile: this.addInterimFile.bind(this) });
|
||||
}
|
||||
|
||||
clone(
|
||||
@ -192,7 +192,7 @@ export class AnalyzerService {
|
||||
});
|
||||
|
||||
// Cloned service will use whatever user files the service currently has.
|
||||
const userFiles = this.backgroundAnalysisProgram.program.getTracked().map((i) => i.sourceFile.getFilePath());
|
||||
const userFiles = this.getUserFiles();
|
||||
service.backgroundAnalysisProgram.setTrackedFiles(userFiles);
|
||||
service.backgroundAnalysisProgram.markAllFilesDirty(true);
|
||||
|
||||
@ -243,7 +243,7 @@ export class AnalyzerService {
|
||||
return this._options.importResolverFactory!;
|
||||
}
|
||||
|
||||
private get _cancellationProvider() {
|
||||
get cancellationProvider() {
|
||||
return this._options.cancellationProvider!;
|
||||
}
|
||||
|
||||
@ -293,6 +293,10 @@ export class AnalyzerService {
|
||||
return this._program.owns(filePath);
|
||||
}
|
||||
|
||||
getUserFiles() {
|
||||
return this._program.getUserFiles().map((i) => i.sourceFile.getFilePath());
|
||||
}
|
||||
|
||||
setFileOpened(
|
||||
path: string,
|
||||
version: number | null,
|
||||
@ -344,8 +348,8 @@ export class AnalyzerService {
|
||||
this._scheduleReanalysis(/* requireTrackedFileUpdate */ false);
|
||||
}
|
||||
|
||||
addTrackedFile(path: string, isThirdPartyImport: boolean) {
|
||||
this._backgroundAnalysisProgram.addTrackedFile(path, isThirdPartyImport);
|
||||
addInterimFile(path: string) {
|
||||
this._backgroundAnalysisProgram.addInterimFile(path);
|
||||
}
|
||||
|
||||
getParseResult(path: string) {
|
||||
@ -478,6 +482,16 @@ export class AnalyzerService {
|
||||
return this._program.performQuickAction(filePath, command, args, token);
|
||||
}
|
||||
|
||||
moveSymbolAtPosition(
|
||||
filePath: string,
|
||||
newFilePath: string,
|
||||
position: Position,
|
||||
options: { importFormat: ImportFormat },
|
||||
token: CancellationToken
|
||||
): FileEditActions | undefined {
|
||||
return this._program.moveSymbolAtPosition(filePath, newFilePath, position, options, token);
|
||||
}
|
||||
|
||||
renameModule(filePath: string, newFilePath: string, token: CancellationToken): FileEditActions | undefined {
|
||||
return this._program.renameModule(filePath, newFilePath, token);
|
||||
}
|
||||
@ -1208,6 +1222,8 @@ export class AnalyzerService {
|
||||
this._console.info(`Searching for source files`);
|
||||
fileList = this._getFileNamesFromFileSpecs();
|
||||
|
||||
// getFileNamesFromFileSpecs might have updated configOptions, resync options.
|
||||
this._backgroundAnalysisProgram.setConfigOptions(this._configOptions);
|
||||
this._backgroundAnalysisProgram.setTrackedFiles(fileList);
|
||||
this._backgroundAnalysisProgram.markAllFilesDirty(markFilesDirtyUnconditionally);
|
||||
|
||||
@ -1257,6 +1273,11 @@ export class AnalyzerService {
|
||||
|
||||
if (this._configOptions.autoExcludeVenv) {
|
||||
if (envMarkers.some((f) => this.fs.existsSync(combinePaths(absolutePath, ...f)))) {
|
||||
// Save auto exclude paths in the configOptions once we found them.
|
||||
if (!FileSpec.isInPath(absolutePath, exclude)) {
|
||||
exclude.push(getFileSpec(this.fs, this._configOptions.projectRoot, `${absolutePath}/**`));
|
||||
}
|
||||
|
||||
this._console.info(`Auto-excluding ${absolutePath}`);
|
||||
return;
|
||||
}
|
||||
@ -1766,7 +1787,7 @@ export class AnalyzerService {
|
||||
}
|
||||
|
||||
// This creates a cancellation source only if it actually gets used.
|
||||
this._backgroundAnalysisCancellationSource = this._cancellationProvider.createCancellationTokenSource();
|
||||
this._backgroundAnalysisCancellationSource = this.cancellationProvider.createCancellationTokenSource();
|
||||
const moreToAnalyze = this._backgroundAnalysisProgram.startAnalysis(
|
||||
this._backgroundAnalysisCancellationSource.token
|
||||
);
|
||||
|
@ -32,6 +32,7 @@ import { convertLevelToCategory, Diagnostic, DiagnosticCategory } from '../commo
|
||||
import { DiagnosticRule } from '../common/diagnosticRules';
|
||||
import { DiagnosticSink, TextRangeDiagnosticSink } from '../common/diagnosticSink';
|
||||
import { TextEditAction } from '../common/editAction';
|
||||
import { Extensions } from '../common/extensibility';
|
||||
import { FileSystem } from '../common/fileSystem';
|
||||
import { LogTracker } from '../common/logTracker';
|
||||
import { fromLSPAny } from '../common/lspUtils';
|
||||
@ -679,6 +680,8 @@ export class SourceFile {
|
||||
this._indexingNeeded = indexingNeeded;
|
||||
this._moduleSymbolTable = undefined;
|
||||
this._cachedIndexResults = undefined;
|
||||
const filePath = this.getFilePath();
|
||||
Extensions.getProgramExtensions(filePath).forEach((e) => (e.fileDirty ? e.fileDirty(filePath) : null));
|
||||
}
|
||||
|
||||
markReanalysisRequired(forceRebinding: boolean): void {
|
||||
|
@ -21,18 +21,18 @@ import {
|
||||
LogData,
|
||||
run,
|
||||
} from './backgroundThreadBase';
|
||||
import { OperationCanceledException, throwIfCancellationRequested } from './common/cancellationUtils';
|
||||
import {
|
||||
getCancellationTokenId,
|
||||
OperationCanceledException,
|
||||
throwIfCancellationRequested,
|
||||
} from './common/cancellationUtils';
|
||||
import { ConfigOptions } from './common/configOptions';
|
||||
import { ConsoleInterface, log, LogLevel } from './common/console';
|
||||
import * as debug from './common/debug';
|
||||
import { Diagnostic } from './common/diagnostic';
|
||||
import { FileDiagnostics } from './common/diagnosticSink';
|
||||
import { Extensions } from './common/extensibility';
|
||||
import {
|
||||
disposeCancellationToken,
|
||||
getCancellationTokenFromId,
|
||||
getCancellationTokenId,
|
||||
} from './common/fileBasedCancellationUtils';
|
||||
import { disposeCancellationToken, getCancellationTokenFromId } from './common/fileBasedCancellationUtils';
|
||||
import { FileSystem } from './common/fileSystem';
|
||||
import { Host, HostKind } from './common/host';
|
||||
import { LogTracker } from './common/logTracker';
|
||||
@ -127,8 +127,8 @@ export class BackgroundAnalysisBase {
|
||||
this.enqueueRequest({ requestType: 'setFileClosed', data: { filePath, isTracked } });
|
||||
}
|
||||
|
||||
addTrackedFile(filePath: string, isThirdPartyImport: boolean) {
|
||||
this.enqueueRequest({ requestType: 'addTrackedFile', data: { filePath, isThirdPartyImport } });
|
||||
addInterimFile(filePath: string) {
|
||||
this.enqueueRequest({ requestType: 'addInterimFile', data: { filePath } });
|
||||
}
|
||||
|
||||
markAllFilesDirty(evenIfContentsAreSame: boolean, indexingNeeded: boolean) {
|
||||
@ -313,8 +313,7 @@ export abstract class BackgroundAnalysisRunnerBase extends BackgroundThreadBase
|
||||
|
||||
// Create the extensions bound to the program for this background thread
|
||||
Extensions.createProgramExtensions(this._program, {
|
||||
addTrackedFile: (filePath: string, isThirdPartyImport: boolean) =>
|
||||
this._program.addTrackedFile(filePath, isThirdPartyImport),
|
||||
addInterimFile: (filePath: string) => this._program.addInterimFile(filePath),
|
||||
});
|
||||
}
|
||||
|
||||
@ -448,9 +447,9 @@ export abstract class BackgroundAnalysisRunnerBase extends BackgroundThreadBase
|
||||
break;
|
||||
}
|
||||
|
||||
case 'addTrackedFile': {
|
||||
const { filePath, isThirdPartyImport } = msg.data;
|
||||
this.program.addTrackedFile(filePath, isThirdPartyImport);
|
||||
case 'addInterimFile': {
|
||||
const { filePath } = msg.data;
|
||||
this.program.addInterimFile(filePath);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -640,7 +639,7 @@ export interface AnalysisRequest {
|
||||
| 'setImportResolver'
|
||||
| 'getInlayHints'
|
||||
| 'shutdown'
|
||||
| 'addTrackedFile';
|
||||
| 'addInterimFile';
|
||||
|
||||
data: any;
|
||||
port?: MessagePort | undefined;
|
||||
|
@ -60,6 +60,7 @@ export class CommandController implements ServerCommand {
|
||||
isLongRunningCommand(command: string): boolean {
|
||||
switch (command) {
|
||||
case Commands.createTypeStub:
|
||||
case Commands.restartServer:
|
||||
return true;
|
||||
|
||||
default:
|
||||
|
@ -6,7 +6,7 @@
|
||||
* Helper methods relating to cancellation.
|
||||
*/
|
||||
|
||||
import { AbstractCancellationTokenSource, CancellationTokenSource } from 'vscode-jsonrpc';
|
||||
import { AbstractCancellationTokenSource, CancellationTokenSource, Emitter, Event } from 'vscode-jsonrpc';
|
||||
import { CancellationToken, Disposable, LSPErrorCodes, ResponseError } from 'vscode-languageserver';
|
||||
|
||||
import { isDebugMode } from './core';
|
||||
@ -43,8 +43,8 @@ export function throwIfCancellationRequested(token: CancellationToken) {
|
||||
}
|
||||
}
|
||||
|
||||
export function CancelAfter(...tokens: CancellationToken[]) {
|
||||
const source = new CancellationTokenSource();
|
||||
export function CancelAfter(provider: CancellationProvider, ...tokens: CancellationToken[]) {
|
||||
const source = provider.createCancellationTokenSource();
|
||||
const disposables: Disposable[] = [];
|
||||
|
||||
for (const token of tokens) {
|
||||
@ -69,3 +69,91 @@ export class DefaultCancellationProvider implements CancellationProvider {
|
||||
return new CancellationTokenSource();
|
||||
}
|
||||
}
|
||||
|
||||
export function getCancellationTokenId(token: CancellationToken) {
|
||||
return token instanceof FileBasedToken ? token.cancellationFilePath : undefined;
|
||||
}
|
||||
|
||||
export class FileBasedToken implements CancellationToken {
|
||||
protected isCancelled = false;
|
||||
private _emitter: Emitter<any> | undefined;
|
||||
|
||||
constructor(readonly cancellationFilePath: string, private _fs: { statSync(filePath: string): void }) {
|
||||
// empty
|
||||
}
|
||||
|
||||
cancel() {
|
||||
if (!this.isCancelled) {
|
||||
this.isCancelled = true;
|
||||
if (this._emitter) {
|
||||
this._emitter.fire(undefined);
|
||||
this._disposeEmitter();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get isCancellationRequested(): boolean {
|
||||
if (this.isCancelled) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (CancellationThrottle.shouldCheck() && this._pipeExists()) {
|
||||
// The first time it encounters the cancellation file, it will
|
||||
// cancel itself and raise a cancellation event.
|
||||
// In this mode, cancel() might not be called explicitly by
|
||||
// jsonrpc layer.
|
||||
this.cancel();
|
||||
}
|
||||
|
||||
return this.isCancelled;
|
||||
}
|
||||
|
||||
get onCancellationRequested(): Event<any> {
|
||||
if (!this._emitter) {
|
||||
this._emitter = new Emitter<any>();
|
||||
}
|
||||
return this._emitter.event;
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._disposeEmitter();
|
||||
}
|
||||
|
||||
private _disposeEmitter() {
|
||||
if (this._emitter) {
|
||||
this._emitter.dispose();
|
||||
this._emitter = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
private _pipeExists(): boolean {
|
||||
try {
|
||||
this._fs.statSync(this.cancellationFilePath);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class CancellationThrottle {
|
||||
private static _lastCheckTimestamp = 0;
|
||||
|
||||
static shouldCheck() {
|
||||
// Throttle cancellation checks to one every 5ms. This value
|
||||
// was selected through empirical testing. If we call the
|
||||
// file system more often than this, type analysis performance
|
||||
// is affected. If we call it less often, performance doesn't
|
||||
// improve much, but responsiveness suffers.
|
||||
const minTimeBetweenChecksInMs = 5;
|
||||
const curTimestamp = Date.now().valueOf();
|
||||
const timeSinceLastCheck = curTimestamp - this._lastCheckTimestamp;
|
||||
|
||||
if (timeSinceLastCheck >= minTimeBetweenChecksInMs) {
|
||||
this._lastCheckTimestamp = curTimestamp;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -32,7 +32,8 @@ export interface ProgramExtension {
|
||||
readonly declarationProviderExtension?: DeclarationProviderExtension;
|
||||
readonly typeProviderExtension?: TypeProviderExtension;
|
||||
readonly codeActionExtension?: CodeActionExtension;
|
||||
sourceFileChanged?: (sourceFileInfo: SourceFileInfo) => void;
|
||||
fileDirty?: (filePath: string) => void;
|
||||
clearCache?: () => void;
|
||||
}
|
||||
|
||||
// Readonly wrapper around a Program. Makes sure it doesn't mutate the program.
|
||||
@ -43,12 +44,12 @@ export interface ProgramView {
|
||||
console: ConsoleInterface;
|
||||
getConfigOptions(): ConfigOptions;
|
||||
owns(file: string): boolean;
|
||||
getBoundSourceFileInfo(file: string): SourceFileInfo | undefined;
|
||||
getBoundSourceFileInfo(file: string, content?: string, force?: boolean): SourceFileInfo | undefined;
|
||||
}
|
||||
|
||||
// Mutable wrapper around a program. Allows the FG thread to forward this request to the BG thread
|
||||
export interface ProgramMutator {
|
||||
addTrackedFile(file: string, isThirdPartyImport: boolean): void;
|
||||
addInterimFile(file: string): void;
|
||||
}
|
||||
|
||||
export interface ExtensionFactory {
|
||||
|
@ -16,99 +16,20 @@ import {
|
||||
CancellationSenderStrategy,
|
||||
CancellationStrategy,
|
||||
CancellationToken,
|
||||
Emitter,
|
||||
Event,
|
||||
} from 'vscode-languageserver';
|
||||
|
||||
import { CancellationProvider, getCancellationFolderName, setCancellationFolderName } from './cancellationUtils';
|
||||
|
||||
class CancellationThrottle {
|
||||
private static _lastCheckTimestamp = 0;
|
||||
|
||||
static shouldCheck() {
|
||||
// Throttle cancellation checks to one every 5ms. This value
|
||||
// was selected through empirical testing. If we call the
|
||||
// file system more often than this, type analysis performance
|
||||
// is affected. If we call it less often, performance doesn't
|
||||
// improve much, but responsiveness suffers.
|
||||
const minTimeBetweenChecksInMs = 5;
|
||||
const curTimestamp = Date.now().valueOf();
|
||||
const timeSinceLastCheck = curTimestamp - this._lastCheckTimestamp;
|
||||
|
||||
if (timeSinceLastCheck >= minTimeBetweenChecksInMs) {
|
||||
this._lastCheckTimestamp = curTimestamp;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class FileBasedToken implements CancellationToken {
|
||||
protected isCancelled = false;
|
||||
private _emitter: Emitter<any> | undefined;
|
||||
|
||||
constructor(readonly cancellationFilePath: string) {}
|
||||
|
||||
cancel() {
|
||||
if (!this.isCancelled) {
|
||||
this.isCancelled = true;
|
||||
if (this._emitter) {
|
||||
this._emitter.fire(undefined);
|
||||
this._disposeEmitter();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get isCancellationRequested(): boolean {
|
||||
if (this.isCancelled) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (CancellationThrottle.shouldCheck() && this._pipeExists()) {
|
||||
// The first time it encounters the cancellation file, it will
|
||||
// cancel itself and raise a cancellation event.
|
||||
// In this mode, cancel() might not be called explicitly by
|
||||
// jsonrpc layer.
|
||||
this.cancel();
|
||||
}
|
||||
|
||||
return this.isCancelled;
|
||||
}
|
||||
|
||||
get onCancellationRequested(): Event<any> {
|
||||
if (!this._emitter) {
|
||||
this._emitter = new Emitter<any>();
|
||||
}
|
||||
return this._emitter.event;
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._disposeEmitter();
|
||||
}
|
||||
|
||||
private _disposeEmitter() {
|
||||
if (this._emitter) {
|
||||
this._emitter.dispose();
|
||||
this._emitter = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
private _pipeExists(): boolean {
|
||||
try {
|
||||
fs.statSync(this.cancellationFilePath);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
import {
|
||||
CancellationProvider,
|
||||
FileBasedToken,
|
||||
getCancellationFolderName,
|
||||
setCancellationFolderName,
|
||||
} from './cancellationUtils';
|
||||
|
||||
class OwningFileToken extends FileBasedToken {
|
||||
private _disposed = false;
|
||||
|
||||
constructor(cancellationFilePath: string) {
|
||||
super(cancellationFilePath);
|
||||
super(cancellationFilePath, fs);
|
||||
}
|
||||
|
||||
override cancel() {
|
||||
@ -157,7 +78,7 @@ class FileBasedCancellationTokenSource implements AbstractCancellationTokenSourc
|
||||
// Be lazy and create the token only when actually needed.
|
||||
this._token = this._ownFile
|
||||
? new OwningFileToken(this._cancellationFilePath)
|
||||
: new FileBasedToken(this._cancellationFilePath);
|
||||
: new FileBasedToken(this._cancellationFilePath, fs);
|
||||
}
|
||||
return this._token;
|
||||
}
|
||||
@ -245,11 +166,7 @@ export function getCancellationTokenFromId(cancellationId: string) {
|
||||
return CancellationToken.None;
|
||||
}
|
||||
|
||||
return new FileBasedToken(cancellationId);
|
||||
}
|
||||
|
||||
export function getCancellationTokenId(token: CancellationToken) {
|
||||
return token instanceof FileBasedToken ? token.cancellationFilePath : undefined;
|
||||
return new FileBasedToken(cancellationId, fs);
|
||||
}
|
||||
|
||||
let cancellationSourceId = 0;
|
||||
|
@ -56,6 +56,28 @@ export function convertWorkspaceDocumentEdits(
|
||||
changeAnnotations: changeAnnotations,
|
||||
};
|
||||
|
||||
// Ordering of documentChanges are important.
|
||||
// Make sure create operaiton happens before edits
|
||||
for (const operation of editActions.fileOperations) {
|
||||
switch (operation.kind) {
|
||||
case 'create':
|
||||
workspaceEdit.documentChanges!.push(
|
||||
CreateFile.create(
|
||||
convertPathToUri(fs, operation.filePath),
|
||||
/* options */ undefined,
|
||||
defaultAnnotationId
|
||||
)
|
||||
);
|
||||
break;
|
||||
case 'rename':
|
||||
case 'delete':
|
||||
break;
|
||||
default:
|
||||
assertNever(operation);
|
||||
}
|
||||
}
|
||||
|
||||
// Text edit's file path must refer to original file paths unless it is a new file just created.
|
||||
const mapPerFile = createMapFromItems(editActions.edits, (e) => e.filePath);
|
||||
for (const [key, value] of mapPerFile) {
|
||||
workspaceEdit.documentChanges!.push(
|
||||
@ -72,13 +94,6 @@ export function convertWorkspaceDocumentEdits(
|
||||
for (const operation of editActions.fileOperations) {
|
||||
switch (operation.kind) {
|
||||
case 'create':
|
||||
workspaceEdit.documentChanges!.push(
|
||||
CreateFile.create(
|
||||
convertPathToUri(fs, operation.filePath),
|
||||
/* options */ undefined,
|
||||
defaultAnnotationId
|
||||
)
|
||||
);
|
||||
break;
|
||||
case 'rename':
|
||||
workspaceEdit.documentChanges!.push(
|
||||
|
@ -12,13 +12,13 @@
|
||||
import './common/extensions';
|
||||
|
||||
import {
|
||||
AbstractCancellationTokenSource,
|
||||
CallHierarchyIncomingCallsParams,
|
||||
CallHierarchyItem,
|
||||
CallHierarchyOutgoingCall,
|
||||
CallHierarchyOutgoingCallsParams,
|
||||
CallHierarchyPrepareParams,
|
||||
CancellationToken,
|
||||
CancellationTokenSource,
|
||||
CodeAction,
|
||||
CodeActionParams,
|
||||
Command,
|
||||
@ -367,10 +367,10 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
|
||||
protected _cacheManager: CacheManager;
|
||||
|
||||
// We support running only one "find all reference" at a time.
|
||||
private _pendingFindAllRefsCancellationSource: CancellationTokenSource | undefined;
|
||||
private _pendingFindAllRefsCancellationSource: AbstractCancellationTokenSource | undefined;
|
||||
|
||||
// We support running only one command at a time.
|
||||
private _pendingCommandCancellationSource: CancellationTokenSource | undefined;
|
||||
private _pendingCommandCancellationSource: AbstractCancellationTokenSource | undefined;
|
||||
|
||||
private _progressReporter: ProgressReporter;
|
||||
|
||||
@ -1646,7 +1646,7 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
|
||||
// created by the LSP library. If it's the latter, we'll create a server-initiated
|
||||
// progress reporter.
|
||||
if (reporter.constructor !== nullProgressReporter.constructor) {
|
||||
return { reporter: reporter, source: CancelAfter(token) };
|
||||
return { reporter: reporter, source: CancelAfter(this._serverOptions.cancellationProvider, token) };
|
||||
}
|
||||
|
||||
const serverInitiatedReporter = await this._connection.window.createWorkDoneProgress();
|
||||
@ -1659,7 +1659,7 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
|
||||
|
||||
return {
|
||||
reporter: serverInitiatedReporter,
|
||||
source: CancelAfter(token, serverInitiatedReporter.token),
|
||||
source: CancelAfter(this._serverOptions.cancellationProvider, token, serverInitiatedReporter.token),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -82,6 +82,7 @@ export class ImportAdder {
|
||||
|
||||
applyImports(
|
||||
result: ImportData,
|
||||
filePath: string,
|
||||
parseResults: ParseResults,
|
||||
insertionPosition: number,
|
||||
importFormat: ImportFormat,
|
||||
@ -89,7 +90,6 @@ export class ImportAdder {
|
||||
): TextEditAction[] {
|
||||
throwIfCancellationRequested(token);
|
||||
|
||||
const filePath = getFileInfo(parseResults.parseTree).filePath;
|
||||
const importStatements = getTopLevelImports(parseResults.parseTree);
|
||||
const importNameInfo = this._getImportNameWithModuleInfo(filePath, result, importFormat);
|
||||
|
||||
|
@ -139,7 +139,18 @@ export function reindentSpan(
|
||||
previousInfo = info;
|
||||
}
|
||||
|
||||
return texts.join('');
|
||||
return {
|
||||
originalSpan: TextRange.combine(tokenInfo)!,
|
||||
text: texts.join(''),
|
||||
};
|
||||
}
|
||||
|
||||
export function getModuleStatementIndentation(parseResults: ParseResults) {
|
||||
if (parseResults.parseTree.statements.length === 0) {
|
||||
return getNewlineIndentation(parseResults, parseResults.parseTree.length, /* preferDedent */ true);
|
||||
}
|
||||
|
||||
return getNewlineIndentation(parseResults, parseResults.parseTree.statements[0].start, /* preferDedent */ true);
|
||||
}
|
||||
|
||||
function _getIndentation(
|
||||
|
@ -31,6 +31,7 @@ import {
|
||||
import {
|
||||
getDottedNameWithGivenNodeAsLastName,
|
||||
getFirstAncestorOrSelfOfKind,
|
||||
getFullStatementRange,
|
||||
isFromImportAlias,
|
||||
isFromImportModuleName,
|
||||
isFromImportName,
|
||||
@ -39,8 +40,11 @@ import {
|
||||
isLastNameOfModuleName,
|
||||
} from '../analyzer/parseTreeUtils';
|
||||
import { ParseTreeWalker } from '../analyzer/parseTreeWalker';
|
||||
import { ScopeType } from '../analyzer/scope';
|
||||
import { isStubFile } from '../analyzer/sourceMapper';
|
||||
import { isPrivateName } from '../analyzer/symbolNameUtils';
|
||||
import { TypeEvaluator } from '../analyzer/typeEvaluatorTypes';
|
||||
import { TypeCategory } from '../analyzer/types';
|
||||
import { getOrAdd } from '../common/collectionUtils';
|
||||
import { ConfigOptions } from '../common/configOptions';
|
||||
import { assert, assertNever } from '../common/debug';
|
||||
@ -57,6 +61,7 @@ import {
|
||||
resolvePaths,
|
||||
stripFileExtension,
|
||||
} from '../common/pathUtils';
|
||||
import { convertRangeToTextRange } from '../common/positionUtils';
|
||||
import { TextEditTracker } from '../common/textEditUtils';
|
||||
import { TextRange } from '../common/textRange';
|
||||
import {
|
||||
@ -158,6 +163,81 @@ export class RenameModuleProvider {
|
||||
);
|
||||
}
|
||||
|
||||
static canMoveSymbol(evaluator: TypeEvaluator, node: NameNode): boolean {
|
||||
if (isPrivateName(node.value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const lookUpResult = evaluator.lookUpSymbolRecursive(node, node.value, /* honorCodeFlow */ false);
|
||||
if (lookUpResult === undefined || lookUpResult.scope.type !== ScopeType.Module) {
|
||||
// We only allow moving a symbol at the module level.
|
||||
return false;
|
||||
}
|
||||
|
||||
// For now, we only supports module level variable, function and class.
|
||||
const declarations = lookUpResult.symbol.getDeclarations();
|
||||
if (declarations.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return declarations.every((d) => {
|
||||
if (!TextRange.containsRange(d.node, node)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isFunctionDeclaration(d) || isClassDeclaration(d)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isVariableDeclaration(d)) {
|
||||
// We only support simple variable assignment.
|
||||
// ex) a = 1
|
||||
if (d.typeAliasAnnotation) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (d.inferredTypeSource && isExpressionNode(d.inferredTypeSource)) {
|
||||
const type = evaluator.getType(d.inferredTypeSource);
|
||||
if (type?.category === TypeCategory.TypeVar) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// This make sure we are not one of these
|
||||
// ex) a = b = 1
|
||||
// a, b = 1, 2
|
||||
if (
|
||||
d.node.parent?.nodeType !== ParseNodeType.Assignment ||
|
||||
d.node.parent?.parent?.nodeType !== ParseNodeType.StatementList
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (d.node.start !== d.node.parent.start) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
static getSymbolTextRange(parseResults: ParseResults, decl: Declaration): TextRange {
|
||||
if (isVariableDeclaration(decl)) {
|
||||
const range = getFullStatementRange(decl.node, parseResults);
|
||||
return convertRangeToTextRange(range, parseResults.tokenizerOutput.lines) ?? decl.node;
|
||||
}
|
||||
|
||||
return decl.node;
|
||||
}
|
||||
|
||||
static getSymbolFullStatementTextRange(parseResults: ParseResults, decl: Declaration): TextRange {
|
||||
const range = getFullStatementRange(decl.node, parseResults, { includeTrailingBlankLines: true });
|
||||
return convertRangeToTextRange(range, parseResults.tokenizerOutput.lines) ?? decl.node;
|
||||
}
|
||||
|
||||
static getRenameModulePath(declarations: Declaration[]) {
|
||||
// If we have a decl with no node, we will prefer that decl over others.
|
||||
// The decl with no node is a synthesized alias decl created only for IDE case
|
||||
@ -307,10 +387,11 @@ export class RenameModuleProvider {
|
||||
}
|
||||
}
|
||||
|
||||
tryGetFirstSymbolUsage(parseResults: ParseResults) {
|
||||
tryGetFirstSymbolUsage(parseResults: ParseResults, symbol?: { name: string; decls: Declaration[] }) {
|
||||
const name = symbol?.name ?? getNameFromDeclaration(this.declarations[0]) ?? '';
|
||||
const collector = new DocumentSymbolCollector(
|
||||
[getNameFromDeclaration(this.declarations[0]) || ''],
|
||||
this.declarations,
|
||||
[name],
|
||||
symbol?.decls ?? this.declarations,
|
||||
this._evaluator!,
|
||||
this._token,
|
||||
parseResults.parseTree,
|
||||
@ -612,19 +693,11 @@ export class RenameModuleProvider {
|
||||
/* isLastPartImportName */ false
|
||||
);
|
||||
|
||||
const hasAlias = !!importFromAs.alias;
|
||||
if (isDestination && !hasAlias) {
|
||||
if (fromNode.imports.length === 1) {
|
||||
// If we have import statement for the symbol in the destination file,
|
||||
// we need to remove it.
|
||||
this._textEditTracker.deleteImportName(parseResults, importFromAs);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isDestination) {
|
||||
// If we have import statement for the symbol in the destination file,
|
||||
// we need to remove it.
|
||||
// ex) "from module import symbol, another_symbol" to
|
||||
// "from module import another_symbol" and "from module.changed import symbol"
|
||||
|
||||
// Delete the existing import name including alias.
|
||||
// "from module import another_symbol"
|
||||
this._textEditTracker.deleteImportName(parseResults, importFromAs);
|
||||
return;
|
||||
}
|
||||
|
@ -1242,13 +1242,25 @@ export class TestState {
|
||||
|
||||
const expected = map[name].definitions;
|
||||
|
||||
// If we're going to def from a file, act like it's open.
|
||||
if (!this.program.getSourceFileInfo(fileName)) {
|
||||
const file = this.testData.files.find((v) => v.fileName === fileName);
|
||||
if (file) {
|
||||
this.program.setFileOpened(fileName, file.version, [{ text: file.content }]);
|
||||
}
|
||||
}
|
||||
|
||||
const position = this.convertOffsetToPosition(fileName, marker.position);
|
||||
const actual = this.program.getDefinitionsForPosition(fileName, position, filter, CancellationToken.None);
|
||||
|
||||
assert.equal(actual?.length ?? 0, expected.length);
|
||||
assert.equal(actual?.length ?? 0, expected.length, `No definitions found for marker "${name}"`);
|
||||
|
||||
for (const r of expected) {
|
||||
assert.equal(actual?.filter((d) => this._deepEqual(d, r)).length, 1);
|
||||
assert.equal(
|
||||
actual?.filter((d) => this._deepEqual(d, r)).length,
|
||||
1,
|
||||
`No match found for ${JSON.stringify(r)} from marker ${name}`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1667,7 +1679,7 @@ export class TestState {
|
||||
}
|
||||
});
|
||||
|
||||
return new Map<string, typeof results[0][1]>(results);
|
||||
return new Map<string, (typeof results)[0][1]>(results);
|
||||
}
|
||||
|
||||
private _createAnalysisService(
|
||||
|
@ -1395,6 +1395,7 @@ function testImportMove(code: string, importFormat = ImportFormat.Absolute) {
|
||||
|
||||
const edits = importMover.applyImports(
|
||||
importData,
|
||||
dest.fileName,
|
||||
state.program.getBoundSourceFile(dest.fileName)!.getParseResults()!,
|
||||
dest.position,
|
||||
importFormat,
|
||||
|
@ -397,7 +397,7 @@ function testIndentation(code: string, indentation: number, expected: string, in
|
||||
TextRange.fromBounds(range.pos, range.end),
|
||||
indentation,
|
||||
indentFirstToken
|
||||
);
|
||||
).text;
|
||||
|
||||
assert.strictEqual(actual, expected);
|
||||
}
|
||||
|
@ -145,6 +145,66 @@ test('symbol from destination file used', () => {
|
||||
|
||||
testFromCode(code);
|
||||
});
|
||||
|
||||
test('insert after all symbols references', () => {
|
||||
const code = `
|
||||
// @filename: test.py
|
||||
//// from moved import MyType
|
||||
////
|
||||
//// [|{|"r":""|}def [|/*marker*/foo|](a: MyType) -> None:
|
||||
//// c: Mapping[str, MyType] = { 'hello', a }|]
|
||||
|
||||
// @filename: moved.py
|
||||
//// [|{|"r":""|}from test import foo
|
||||
//// |]
|
||||
//// class MyType:
|
||||
//// pass[|{|"r":"!n!!n!!n!def foo(a: MyType) -> None:!n! c: Mapping[str, MyType] = { 'hello', a }", "name": "dest"|}|]
|
||||
////
|
||||
//// foo()
|
||||
`;
|
||||
|
||||
testFromCode(code);
|
||||
});
|
||||
|
||||
test('insert after all symbols references 2', () => {
|
||||
const code = `
|
||||
// @filename: test.py
|
||||
//// from moved import MyType
|
||||
////
|
||||
//// [|{|"r":""|}def [|/*marker*/foo|](a: MyType) -> None:
|
||||
//// c: Mapping[str, MyType] = { 'hello', a }|]
|
||||
|
||||
// @filename: moved.py
|
||||
//// def __privateFoo():
|
||||
//// pass
|
||||
////
|
||||
//// class MyType:
|
||||
//// pass[|{|"r":"!n!!n!!n!def foo(a: MyType) -> None:!n! c: Mapping[str, MyType] = { 'hello', a }", "name": "dest"|}|]
|
||||
`;
|
||||
|
||||
testFromCode(code);
|
||||
});
|
||||
|
||||
test('symbol used before all symbol references', () => {
|
||||
const code = `
|
||||
// @filename: test.py
|
||||
//// from moved import MyType
|
||||
////
|
||||
//// [|{|"r":""|}def [|/*marker*/foo|](a: MyType) -> None:
|
||||
//// c: Mapping[str, MyType] = { 'hello', a }|]
|
||||
|
||||
// @filename: moved.py
|
||||
//// [|{|"r":""|}from test import foo[|{|"r":"!n!!n!!n!def foo(a: MyType) -> None:!n! c: Mapping[str, MyType] = { 'hello', a }", "name": "dest"|}|]
|
||||
//// |]
|
||||
//// foo()
|
||||
////
|
||||
//// class MyType:
|
||||
//// pass
|
||||
`;
|
||||
|
||||
testFromCode(code);
|
||||
});
|
||||
|
||||
function testFromCode(code: string) {
|
||||
const state = parseAndGetTestState(code).state;
|
||||
|
||||
|
@ -315,8 +315,8 @@ test('insert to a file with same symbol imported with alias', () => {
|
||||
//// pass|]
|
||||
|
||||
// @filename: moved.py
|
||||
//// from [|{|"r":"moved"|}test|] import foo as aliasFoo
|
||||
////
|
||||
//// [|{|"r":""|}from test import foo as aliasFoo
|
||||
//// |]
|
||||
//// aliasFoo()[|{|"r":"!n!!n!def foo():!n! pass", "name": "dest"|}|]
|
||||
`;
|
||||
|
||||
@ -406,6 +406,49 @@ test('original file has import for the symbol with alias', () => {
|
||||
testFromCode(code);
|
||||
});
|
||||
|
||||
test('move after class', () => {
|
||||
const code = `
|
||||
// @filename: test.py
|
||||
//// [|{|"r":""|}def [|/*marker*/foo|]():
|
||||
//// pass
|
||||
//// |]
|
||||
|
||||
// @filename: moved.py
|
||||
//// class A:
|
||||
//// def foo(self):
|
||||
//// pass[|{|"r":"!n!!n!!n!def foo():!n! pass", "name": "dest"|}|]
|
||||
`;
|
||||
|
||||
testFromCode(code);
|
||||
});
|
||||
|
||||
test('move variable', () => {
|
||||
const code = `
|
||||
// @filename: test.py
|
||||
//// [|{|"r":""|}[|/*marker*/A|] = 1|]
|
||||
|
||||
// @filename: moved.py
|
||||
//// [|{|"r":"A = 1", "name": "dest"|}|]
|
||||
`;
|
||||
|
||||
testFromCode(code);
|
||||
});
|
||||
|
||||
test('move variable with doc string', () => {
|
||||
const code = `
|
||||
// @filename: test.py
|
||||
//// [|{|"r":""|}[|/*marker*/A|] = 1
|
||||
//// '''
|
||||
//// doc string
|
||||
//// '''|]
|
||||
|
||||
// @filename: moved.py
|
||||
//// [|{|"r":"A = 1!n!'''!n! doc string!n!'''", "name": "dest"|}|]
|
||||
`;
|
||||
|
||||
testFromCode(code);
|
||||
});
|
||||
|
||||
function testFromCode(code: string) {
|
||||
const state = parseAndGetTestState(code).state;
|
||||
|
||||
|
@ -21,16 +21,7 @@ test('source and destnation file must have same ext', () => {
|
||||
//// [|/*dest*/|]
|
||||
`;
|
||||
|
||||
const state = parseAndGetTestState(code).state;
|
||||
|
||||
const actions = state.program.moveSymbolAtPosition(
|
||||
state.getMarkerByName('marker').fileName,
|
||||
state.getMarkerByName('dest').fileName,
|
||||
state.getPositionRange('marker').start,
|
||||
{ importFormat: ImportFormat.Absolute },
|
||||
CancellationToken.None
|
||||
);
|
||||
assert(!actions);
|
||||
testNoMoveFromCode(code);
|
||||
});
|
||||
|
||||
test('source and destnation file can not be same', () => {
|
||||
@ -39,6 +30,134 @@ test('source and destnation file can not be same', () => {
|
||||
//// [|/*dest*/|]def [|/*marker*/foo|](): pass
|
||||
`;
|
||||
|
||||
testNoMoveFromCode(code);
|
||||
});
|
||||
|
||||
test('Symbol must be module level symbol', () => {
|
||||
const code = `
|
||||
// @filename: test.py
|
||||
//// class A:
|
||||
//// def [|/*marker*/foo|](self): pass
|
||||
|
||||
// @filename: moved.py
|
||||
//// [|/*dest*/|]
|
||||
`;
|
||||
|
||||
testNoMoveFromCode(code);
|
||||
});
|
||||
|
||||
test('Import alias can not be moved', () => {
|
||||
const code = `
|
||||
// @filename: test.py
|
||||
//// import [|/*marker*/sys|]
|
||||
|
||||
// @filename: moved.py
|
||||
//// [|/*dest*/|]
|
||||
`;
|
||||
|
||||
testNoMoveFromCode(code);
|
||||
});
|
||||
|
||||
test('Type alias can not be moved', () => {
|
||||
const code = `
|
||||
// @filename: test.py
|
||||
//// from typing import TypeAlias
|
||||
//// [|/*marker*/TA|]: TypeAlias = int
|
||||
|
||||
// @filename: moved.py
|
||||
//// [|/*dest*/|]
|
||||
`;
|
||||
|
||||
testNoMoveFromCode(code);
|
||||
});
|
||||
|
||||
test('TypeVar can not be moved', () => {
|
||||
const code = `
|
||||
// @filename: test.py
|
||||
//// from typing import TypeVar
|
||||
//// [|/*marker*/T1|] = TypeVar("T1")
|
||||
|
||||
// @filename: moved.py
|
||||
//// [|/*dest*/|]
|
||||
`;
|
||||
|
||||
testNoMoveFromCode(code);
|
||||
});
|
||||
|
||||
test('tuple unpacking not supported', () => {
|
||||
const code = `
|
||||
// @filename: test.py
|
||||
//// [|/*marker*/a|], b = 1, 2
|
||||
|
||||
// @filename: moved.py
|
||||
//// [|/*dest*/|]
|
||||
`;
|
||||
|
||||
testNoMoveFromCode(code);
|
||||
});
|
||||
|
||||
test('tuple unpacking not supported 2', () => {
|
||||
const code = `
|
||||
// @filename: test.py
|
||||
//// a, [|/*marker*/b|] = 1, 2
|
||||
|
||||
// @filename: moved.py
|
||||
//// [|/*dest*/|]
|
||||
`;
|
||||
|
||||
testNoMoveFromCode(code);
|
||||
});
|
||||
|
||||
test('chained assignment not supported', () => {
|
||||
const code = `
|
||||
// @filename: test.py
|
||||
//// [|/*marker*/a|] = b = 1
|
||||
|
||||
// @filename: moved.py
|
||||
//// [|/*dest*/|]
|
||||
`;
|
||||
|
||||
testNoMoveFromCode(code);
|
||||
});
|
||||
|
||||
test('chained assignment not supported 2', () => {
|
||||
const code = `
|
||||
// @filename: test.py
|
||||
//// a = [|/*marker*/b|] = 1
|
||||
|
||||
// @filename: moved.py
|
||||
//// [|/*dest*/|]
|
||||
`;
|
||||
|
||||
testNoMoveFromCode(code);
|
||||
});
|
||||
|
||||
test('augmented assignment', () => {
|
||||
const code = `
|
||||
// @filename: test.py
|
||||
//// [|/*marker*/a|] += 1
|
||||
|
||||
// @filename: moved.py
|
||||
//// [|/*dest*/|]
|
||||
`;
|
||||
|
||||
testNoMoveFromCode(code);
|
||||
});
|
||||
|
||||
test('augmented assignment 2', () => {
|
||||
const code = `
|
||||
// @filename: test.py
|
||||
//// a = 1
|
||||
//// [|/*marker*/a|] += 1
|
||||
|
||||
// @filename: moved.py
|
||||
//// [|/*dest*/|]
|
||||
`;
|
||||
|
||||
testNoMoveFromCode(code);
|
||||
});
|
||||
|
||||
function testNoMoveFromCode(code: string) {
|
||||
const state = parseAndGetTestState(code).state;
|
||||
|
||||
const actions = state.program.moveSymbolAtPosition(
|
||||
@ -49,4 +168,4 @@ test('source and destnation file can not be same', () => {
|
||||
CancellationToken.None
|
||||
);
|
||||
assert(!actions);
|
||||
});
|
||||
}
|
||||
|
1142
packages/pyright/package-lock.json
generated
1142
packages/pyright/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -26,7 +26,7 @@
|
||||
"@types/copy-webpack-plugin": "^10.1.0",
|
||||
"@types/node": "^17.0.45",
|
||||
"copy-webpack-plugin": "^10.2.4",
|
||||
"esbuild-loader": "^2.20.0",
|
||||
"esbuild-loader": "^3.0.1",
|
||||
"shx": "^0.3.4",
|
||||
"ts-loader": "^9.4.2",
|
||||
"typescript": "~4.4.4",
|
||||
|
Loading…
Reference in New Issue
Block a user