mirror of
https://github.com/microsoft/pyright.git
synced 2024-07-14 19:10:39 +03:00
Push pylance changes to pyright (#4783)
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
cebfc312e5
commit
6de737544c
1194
package-lock.json
generated
1194
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
10
package.json
10
package.json
@ -23,19 +23,19 @@
|
||||
"@types/glob": "^7.2.0",
|
||||
"@types/node": "^17.0.45",
|
||||
"@types/yargs": "^16.0.5",
|
||||
"@typescript-eslint/eslint-plugin": "^5.52.0",
|
||||
"@typescript-eslint/parser": "^5.52.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.54.0",
|
||||
"@typescript-eslint/parser": "^5.54.0",
|
||||
"detect-indent": "^6.1.0",
|
||||
"eslint": "^8.34.0",
|
||||
"eslint": "^8.35.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": "^6.5.1",
|
||||
"npm-check-updates": "^16.7.4",
|
||||
"npm-check-updates": "^16.7.10",
|
||||
"p-queue": "^6.6.2",
|
||||
"prettier": "2.8.4",
|
||||
"syncpack": "^9.0.2",
|
||||
"syncpack": "^9.8.4",
|
||||
"typescript": "~4.4.4",
|
||||
"yargs": "^16.2.0"
|
||||
}
|
||||
|
78
packages/pyright-internal/package-lock.json
generated
78
packages/pyright-internal/package-lock.json
generated
@ -20,10 +20,10 @@
|
||||
"source-map-support": "^0.5.21",
|
||||
"tmp": "^0.2.1",
|
||||
"typescript-char": "^0.0.0",
|
||||
"vscode-jsonrpc": "8.1.0-next.6",
|
||||
"vscode-languageserver": "8.1.0-next.5",
|
||||
"vscode-jsonrpc": "8.1.0",
|
||||
"vscode-languageserver": "8.1.0",
|
||||
"vscode-languageserver-textdocument": "^1.0.9",
|
||||
"vscode-languageserver-types": "3.17.2",
|
||||
"vscode-languageserver-types": "3.17.3",
|
||||
"vscode-uri": "^3.0.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
@ -4185,47 +4185,42 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vscode-jsonrpc": {
|
||||
"version": "8.1.0-next.6",
|
||||
"resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.1.0-next.6.tgz",
|
||||
"integrity": "sha512-AahQokGczPwXKo1Qhnn3aqkZgwUJ0rjVwhWWKW5I5LEWRoqfnWkQp7haVIV6GJRX0oyGL2ezVy7IhwgP5u/3xw==",
|
||||
"version": "8.1.0",
|
||||
"resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.1.0.tgz",
|
||||
"integrity": "sha512-6TDy/abTQk+zDGYazgbIPc+4JoXdwC8NHU9Pbn4UJP1fehUyZmM4RHp5IthX7A6L5KS30PRui+j+tbbMMMafdw==",
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vscode-languageserver": {
|
||||
"version": "8.1.0-next.5",
|
||||
"resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-8.1.0-next.5.tgz",
|
||||
"integrity": "sha512-VivctbjOca/iPZKXqgqz03MUhnjAlVkf8/AwfndIEVzD8itD7zaoMlqNUynHJVbGcU5PEygC5deUzKHMU8cNhw==",
|
||||
"version": "8.1.0",
|
||||
"resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-8.1.0.tgz",
|
||||
"integrity": "sha512-eUt8f1z2N2IEUDBsKaNapkz7jl5QpskN2Y0G01T/ItMxBxw1fJwvtySGB9QMecatne8jFIWJGWI61dWjyTLQsw==",
|
||||
"dependencies": {
|
||||
"vscode-languageserver-protocol": "3.17.3-next.5"
|
||||
"vscode-languageserver-protocol": "3.17.3"
|
||||
},
|
||||
"bin": {
|
||||
"installServerIntoExtension": "bin/installServerIntoExtension"
|
||||
}
|
||||
},
|
||||
"node_modules/vscode-languageserver-protocol": {
|
||||
"version": "3.17.3-next.5",
|
||||
"resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.3-next.5.tgz",
|
||||
"integrity": "sha512-9HafkatRVhBVpWQrODes4JaoSu7ozHQUOzYiTmfMmxeFOUYgsSqyODp+j/c+SovcsuwABjuqnsQ9RiFkXCbeMA==",
|
||||
"version": "3.17.3",
|
||||
"resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.3.tgz",
|
||||
"integrity": "sha512-924/h0AqsMtA5yK22GgMtCYiMdCOtWTSGgUOkgEDX+wk2b0x4sAfLiO4NxBxqbiVtz7K7/1/RgVrVI0NClZwqA==",
|
||||
"dependencies": {
|
||||
"vscode-jsonrpc": "8.1.0-next.6",
|
||||
"vscode-languageserver-types": "3.17.3-next.2"
|
||||
"vscode-jsonrpc": "8.1.0",
|
||||
"vscode-languageserver-types": "3.17.3"
|
||||
}
|
||||
},
|
||||
"node_modules/vscode-languageserver-protocol/node_modules/vscode-languageserver-types": {
|
||||
"version": "3.17.3-next.2",
|
||||
"resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.3-next.2.tgz",
|
||||
"integrity": "sha512-3kkNSCycNKUalSJIrjIptGeY9UTJr1Nk5HT/aT00jjIwiCvIUNbRdK90av2Y3j1Jityot68dBVc3YYdwwH3zOQ=="
|
||||
},
|
||||
"node_modules/vscode-languageserver-textdocument": {
|
||||
"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",
|
||||
"resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.2.tgz",
|
||||
"integrity": "sha512-zHhCWatviizPIq9B7Vh9uvrH6x3sK8itC84HkamnBWoDFJtzBf7SWlpLCZUit72b3os45h6RWQNC9xHRDF8dRA=="
|
||||
"version": "3.17.3",
|
||||
"resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.3.tgz",
|
||||
"integrity": "sha512-SYU4z1dL0PyIMd4Vj8YOqFvHu7Hz/enbWtpfnVbJHU4Nd1YNYx8u0ennumc6h48GQNeOLxmwySmnADouT/AuZA=="
|
||||
},
|
||||
"node_modules/vscode-uri": {
|
||||
"version": "3.0.7",
|
||||
@ -7602,32 +7597,25 @@
|
||||
}
|
||||
},
|
||||
"vscode-jsonrpc": {
|
||||
"version": "8.1.0-next.6",
|
||||
"resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.1.0-next.6.tgz",
|
||||
"integrity": "sha512-AahQokGczPwXKo1Qhnn3aqkZgwUJ0rjVwhWWKW5I5LEWRoqfnWkQp7haVIV6GJRX0oyGL2ezVy7IhwgP5u/3xw=="
|
||||
"version": "8.1.0",
|
||||
"resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.1.0.tgz",
|
||||
"integrity": "sha512-6TDy/abTQk+zDGYazgbIPc+4JoXdwC8NHU9Pbn4UJP1fehUyZmM4RHp5IthX7A6L5KS30PRui+j+tbbMMMafdw=="
|
||||
},
|
||||
"vscode-languageserver": {
|
||||
"version": "8.1.0-next.5",
|
||||
"resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-8.1.0-next.5.tgz",
|
||||
"integrity": "sha512-VivctbjOca/iPZKXqgqz03MUhnjAlVkf8/AwfndIEVzD8itD7zaoMlqNUynHJVbGcU5PEygC5deUzKHMU8cNhw==",
|
||||
"version": "8.1.0",
|
||||
"resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-8.1.0.tgz",
|
||||
"integrity": "sha512-eUt8f1z2N2IEUDBsKaNapkz7jl5QpskN2Y0G01T/ItMxBxw1fJwvtySGB9QMecatne8jFIWJGWI61dWjyTLQsw==",
|
||||
"requires": {
|
||||
"vscode-languageserver-protocol": "3.17.3-next.5"
|
||||
"vscode-languageserver-protocol": "3.17.3"
|
||||
}
|
||||
},
|
||||
"vscode-languageserver-protocol": {
|
||||
"version": "3.17.3-next.5",
|
||||
"resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.3-next.5.tgz",
|
||||
"integrity": "sha512-9HafkatRVhBVpWQrODes4JaoSu7ozHQUOzYiTmfMmxeFOUYgsSqyODp+j/c+SovcsuwABjuqnsQ9RiFkXCbeMA==",
|
||||
"version": "3.17.3",
|
||||
"resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.3.tgz",
|
||||
"integrity": "sha512-924/h0AqsMtA5yK22GgMtCYiMdCOtWTSGgUOkgEDX+wk2b0x4sAfLiO4NxBxqbiVtz7K7/1/RgVrVI0NClZwqA==",
|
||||
"requires": {
|
||||
"vscode-jsonrpc": "8.1.0-next.6",
|
||||
"vscode-languageserver-types": "3.17.3-next.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"vscode-languageserver-types": {
|
||||
"version": "3.17.3-next.2",
|
||||
"resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.3-next.2.tgz",
|
||||
"integrity": "sha512-3kkNSCycNKUalSJIrjIptGeY9UTJr1Nk5HT/aT00jjIwiCvIUNbRdK90av2Y3j1Jityot68dBVc3YYdwwH3zOQ=="
|
||||
}
|
||||
"vscode-jsonrpc": "8.1.0",
|
||||
"vscode-languageserver-types": "3.17.3"
|
||||
}
|
||||
},
|
||||
"vscode-languageserver-textdocument": {
|
||||
@ -7636,9 +7624,9 @@
|
||||
"integrity": "sha512-NPfHVGFW2/fQEWHspr8x3PXhRgtFbuDZdl7p6ifuN3M7nk2Yjf5POr/NfDBuAiQG88DehDyJ7nGOT+p+edEtbw=="
|
||||
},
|
||||
"vscode-languageserver-types": {
|
||||
"version": "3.17.2",
|
||||
"resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.2.tgz",
|
||||
"integrity": "sha512-zHhCWatviizPIq9B7Vh9uvrH6x3sK8itC84HkamnBWoDFJtzBf7SWlpLCZUit72b3os45h6RWQNC9xHRDF8dRA=="
|
||||
"version": "3.17.3",
|
||||
"resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.3.tgz",
|
||||
"integrity": "sha512-SYU4z1dL0PyIMd4Vj8YOqFvHu7Hz/enbWtpfnVbJHU4Nd1YNYx8u0ennumc6h48GQNeOLxmwySmnADouT/AuZA=="
|
||||
},
|
||||
"vscode-uri": {
|
||||
"version": "3.0.7",
|
||||
|
@ -26,10 +26,10 @@
|
||||
"source-map-support": "^0.5.21",
|
||||
"tmp": "^0.2.1",
|
||||
"typescript-char": "^0.0.0",
|
||||
"vscode-jsonrpc": "8.1.0-next.6",
|
||||
"vscode-languageserver": "8.1.0-next.5",
|
||||
"vscode-jsonrpc": "8.1.0",
|
||||
"vscode-languageserver": "8.1.0",
|
||||
"vscode-languageserver-textdocument": "^1.0.9",
|
||||
"vscode-languageserver-types": "3.17.2",
|
||||
"vscode-languageserver-types": "3.17.3",
|
||||
"vscode-uri": "^3.0.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -22,6 +22,7 @@ export interface CacheOwner {
|
||||
}
|
||||
|
||||
export class CacheManager {
|
||||
private _pausedCount = 0;
|
||||
private readonly _cacheOwners: CacheOwner[] = [];
|
||||
|
||||
registerCacheOwner(provider: CacheOwner) {
|
||||
@ -37,7 +38,21 @@ export class CacheManager {
|
||||
}
|
||||
}
|
||||
|
||||
pauseTracking(): { dispose(): void } {
|
||||
const local = this;
|
||||
local._pausedCount++;
|
||||
return {
|
||||
dispose() {
|
||||
local._pausedCount--;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
getCacheUsage() {
|
||||
if (this._pausedCount > 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
let totalUsage = 0;
|
||||
|
||||
this._cacheOwners.forEach((p) => {
|
||||
|
@ -103,7 +103,7 @@ import {
|
||||
import { Scope } from './scope';
|
||||
import { getScopeForNode } from './scopeUtils';
|
||||
import { IPythonMode, SourceFile } from './sourceFile';
|
||||
import { isUserCode } from './sourceFileInfoUtils';
|
||||
import { collectImportedByFiles, isUserCode } from './sourceFileInfoUtils';
|
||||
import { isStubFile, SourceMapper } from './sourceMapper';
|
||||
import { Symbol } from './symbol';
|
||||
import { isPrivateOrProtectedName } from './symbolNameUtils';
|
||||
@ -1200,15 +1200,23 @@ export class Program {
|
||||
let dependentFiles: ParseResults[] | undefined = undefined;
|
||||
if (fileToCheck.sourceFile.getIPythonMode() === IPythonMode.CellDocs) {
|
||||
dependentFiles = [];
|
||||
const importedByFiles = new Set<SourceFileInfo>();
|
||||
this._collectImportedByFiles(fileToCheck, importedByFiles);
|
||||
const importedByFiles = collectImportedByFiles(fileToCheck);
|
||||
for (const file of importedByFiles) {
|
||||
if (!isUserCode(file)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the file is already analyzed, it will be no op.
|
||||
this._checkTypes(file, token);
|
||||
// And make sure we don't dump parse tree and etc while
|
||||
// recursively calling checker. Otherwise, inner check
|
||||
// can dump parse tree required by outer check.
|
||||
const handle = this._cacheManager.pauseTracking();
|
||||
try {
|
||||
this._checkTypes(file, token);
|
||||
} finally {
|
||||
handle.dispose();
|
||||
}
|
||||
|
||||
const parseResults = file.sourceFile.getParseResults();
|
||||
if (parseResults) {
|
||||
dependentFiles.push(parseResults);
|
||||
@ -1260,18 +1268,6 @@ export class Program {
|
||||
});
|
||||
}
|
||||
|
||||
private _collectImportedByFiles(file: SourceFileInfo, importedByFiles: Set<SourceFileInfo>) {
|
||||
file.importedBy.forEach((dep) => {
|
||||
if (importedByFiles.has(dep)) {
|
||||
// Already visited.
|
||||
return;
|
||||
}
|
||||
|
||||
importedByFiles.add(dep);
|
||||
this._collectImportedByFiles(dep, importedByFiles);
|
||||
});
|
||||
}
|
||||
|
||||
// Builds a map of files that includes the specified file and all of the files
|
||||
// it imports (recursively) and ensures that all such files. If any of these files
|
||||
// have already been checked (they and their recursive imports have completed the
|
||||
@ -1653,15 +1649,14 @@ export class Program {
|
||||
this._bindFile(sourceFileInfo);
|
||||
|
||||
const execEnv = this._configOptions.findExecEnvironment(filePath);
|
||||
const referencesResult = sourceFileInfo.sourceFile.getDeclarationForPosition(
|
||||
this._createSourceMapper(execEnv, token, sourceFileInfo),
|
||||
const referencesResult = this._getDeclarationForPosition(
|
||||
sourceFileInfo,
|
||||
position,
|
||||
this._evaluator!,
|
||||
reporter,
|
||||
DocumentSymbolCollectorUseCase.Reference,
|
||||
token
|
||||
this._createSourceMapper(execEnv, token, sourceFileInfo),
|
||||
token,
|
||||
reporter
|
||||
);
|
||||
|
||||
if (!referencesResult) {
|
||||
return;
|
||||
}
|
||||
@ -2844,12 +2839,11 @@ export class Program {
|
||||
token: CancellationToken
|
||||
) {
|
||||
const execEnv = this._configOptions.findExecEnvironment(filePath);
|
||||
const referencesResult = sourceFileInfo.sourceFile.getDeclarationForPosition(
|
||||
this._createSourceMapper(execEnv, token),
|
||||
const referencesResult = this._getDeclarationForPosition(
|
||||
sourceFileInfo,
|
||||
position,
|
||||
this._evaluator!,
|
||||
undefined,
|
||||
DocumentSymbolCollectorUseCase.Rename,
|
||||
this._createSourceMapper(execEnv, token),
|
||||
token
|
||||
);
|
||||
|
||||
@ -2876,6 +2870,25 @@ export class Program {
|
||||
);
|
||||
}
|
||||
|
||||
private _getDeclarationForPosition(
|
||||
sourceFileInfo: SourceFileInfo,
|
||||
position: Position,
|
||||
useCase: DocumentSymbolCollectorUseCase,
|
||||
sourceMapper: SourceMapper,
|
||||
token: CancellationToken,
|
||||
reporter?: ReferenceCallback
|
||||
) {
|
||||
return sourceFileInfo.sourceFile.getDeclarationForPosition(
|
||||
sourceMapper,
|
||||
position,
|
||||
this._evaluator!,
|
||||
reporter,
|
||||
useCase,
|
||||
token,
|
||||
Array.from(collectImportedByFiles(sourceFileInfo)).map((fileInfo) => fileInfo.sourceFile)
|
||||
);
|
||||
}
|
||||
|
||||
private _processModuleReferences(
|
||||
renameModuleProvider: RenameModuleProvider,
|
||||
filteringText: string,
|
||||
|
@ -297,6 +297,10 @@ export class AnalyzerService {
|
||||
return this._program.getUserFiles().map((i) => i.sourceFile.getFilePath());
|
||||
}
|
||||
|
||||
getOpenFiles() {
|
||||
return this._program.getOpened().map((i) => i.sourceFile.getFilePath());
|
||||
}
|
||||
|
||||
setFileOpened(
|
||||
path: string,
|
||||
version: number | null,
|
||||
@ -305,8 +309,11 @@ export class AnalyzerService {
|
||||
chainedFilePath?: string,
|
||||
realFilePath?: string
|
||||
) {
|
||||
// Open the file. Notebook cells are always tracked as they aren't 3rd party library files.
|
||||
// This is how it's worked in the past since each notebook used to have its own
|
||||
// workspace and the workspace include setting marked all cells as tracked.
|
||||
this._backgroundAnalysisProgram.setFileOpened(path, version, contents, {
|
||||
isTracked: this.isTracked(path),
|
||||
isTracked: this.isTracked(path) || ipythonMode !== IPythonMode.None,
|
||||
ipythonMode,
|
||||
chainedFilePath,
|
||||
realFilePath,
|
||||
|
@ -1064,7 +1064,8 @@ export class SourceFile {
|
||||
evaluator: TypeEvaluator,
|
||||
reporter: ReferenceCallback | undefined,
|
||||
useCase: DocumentSymbolCollectorUseCase,
|
||||
token: CancellationToken
|
||||
token: CancellationToken,
|
||||
implicitlyImportedBy?: SourceFile[]
|
||||
): ReferencesResult | undefined {
|
||||
// If we have no completed analysis job, there's nothing to do.
|
||||
if (!this._parseResults) {
|
||||
@ -1079,7 +1080,8 @@ export class SourceFile {
|
||||
evaluator,
|
||||
reporter,
|
||||
useCase,
|
||||
token
|
||||
token,
|
||||
implicitlyImportedBy
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -11,3 +11,21 @@ import { SourceFileInfo } from './program';
|
||||
export function isUserCode(fileInfo: SourceFileInfo | undefined) {
|
||||
return !!fileInfo && fileInfo.isTracked && !fileInfo.isThirdPartyImport && !fileInfo.isTypeshedFile;
|
||||
}
|
||||
|
||||
export function collectImportedByFiles(fileInfo: SourceFileInfo): Set<SourceFileInfo> {
|
||||
const importedByFiles = new Set<SourceFileInfo>();
|
||||
_collectImportedByFiles(fileInfo, importedByFiles);
|
||||
return importedByFiles;
|
||||
}
|
||||
|
||||
function _collectImportedByFiles(fileInfo: SourceFileInfo, importedByFiles: Set<SourceFileInfo>) {
|
||||
fileInfo.importedBy.forEach((dep) => {
|
||||
if (importedByFiles.has(dep)) {
|
||||
// Already visited.
|
||||
return;
|
||||
}
|
||||
|
||||
importedByFiles.add(dep);
|
||||
_collectImportedByFiles(dep, importedByFiles);
|
||||
});
|
||||
}
|
||||
|
@ -136,7 +136,7 @@ export class DumpFileDebugInfoCommand implements ServerCommand {
|
||||
const kind = params.arguments[1];
|
||||
|
||||
const workspace = await this._ls.getWorkspaceForFile(filePath);
|
||||
const parseResults = workspace.serviceInstance.getParseResult(filePath);
|
||||
const parseResults = workspace.service.getParseResult(filePath);
|
||||
if (!parseResults) {
|
||||
return [];
|
||||
}
|
||||
@ -181,7 +181,7 @@ export class DumpFileDebugInfoCommand implements ServerCommand {
|
||||
break;
|
||||
}
|
||||
case 'types': {
|
||||
const evaluator = workspace.serviceInstance.getEvaluator();
|
||||
const evaluator = workspace.service.getEvaluator();
|
||||
const start = params.arguments[2] as number;
|
||||
const end = params.arguments[3] as number;
|
||||
if (!evaluator || !start || !end) {
|
||||
@ -193,7 +193,7 @@ export class DumpFileDebugInfoCommand implements ServerCommand {
|
||||
break;
|
||||
}
|
||||
case 'cachedtypes': {
|
||||
const evaluator = workspace.serviceInstance.getEvaluator();
|
||||
const evaluator = workspace.service.getEvaluator();
|
||||
const start = params.arguments[2] as number;
|
||||
const end = params.arguments[3] as number;
|
||||
if (!evaluator || !start || !end) {
|
||||
@ -208,7 +208,7 @@ export class DumpFileDebugInfoCommand implements ServerCommand {
|
||||
}
|
||||
|
||||
case 'codeflowgraph': {
|
||||
const evaluator = workspace.serviceInstance.getEvaluator();
|
||||
const evaluator = workspace.service.getEvaluator();
|
||||
const offset = params.arguments[2] as number;
|
||||
if (!evaluator || offset === undefined) {
|
||||
return [];
|
||||
|
@ -27,17 +27,9 @@ export class QuickActionCommand implements ServerCommand {
|
||||
return [];
|
||||
}
|
||||
|
||||
const editActions = workspace.serviceInstance.performQuickAction(
|
||||
filePath,
|
||||
params.command,
|
||||
otherArgs,
|
||||
token
|
||||
);
|
||||
const editActions = workspace.service.performQuickAction(filePath, params.command, otherArgs, token);
|
||||
|
||||
return convertToWorkspaceEdit(
|
||||
workspace.serviceInstance.fs,
|
||||
convertToFileTextEdits(filePath, editActions ?? [])
|
||||
);
|
||||
return convertToWorkspaceEdit(workspace.service.fs, convertToFileTextEdits(filePath, editActions ?? []));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -199,3 +199,26 @@ export function log(console: ConsoleInterface, logType: LogLevel, msg: string) {
|
||||
debug.fail(`${logType} is not expected`);
|
||||
}
|
||||
}
|
||||
|
||||
export function convertLogLevel(logLevelValue?: string): LogLevel {
|
||||
if (!logLevelValue) {
|
||||
return LogLevel.Info;
|
||||
}
|
||||
|
||||
switch (logLevelValue.toLowerCase()) {
|
||||
case 'error':
|
||||
return LogLevel.Error;
|
||||
|
||||
case 'warning':
|
||||
return LogLevel.Warn;
|
||||
|
||||
case 'information':
|
||||
return LogLevel.Info;
|
||||
|
||||
case 'trace':
|
||||
return LogLevel.Log;
|
||||
|
||||
default:
|
||||
return LogLevel.Info;
|
||||
}
|
||||
}
|
||||
|
@ -87,7 +87,12 @@ export enum DeclarationUseCase {
|
||||
}
|
||||
|
||||
export interface DeclarationProviderExtension {
|
||||
tryGetDeclarations(node: ParseNode, useCase: DeclarationUseCase, token: CancellationToken): Declaration[];
|
||||
tryGetDeclarations(
|
||||
evaluator: TypeEvaluator,
|
||||
node: ParseNode,
|
||||
useCase: DeclarationUseCase,
|
||||
token: CancellationToken
|
||||
): Declaration[];
|
||||
}
|
||||
|
||||
export interface TypeProviderExtension {
|
||||
@ -102,6 +107,7 @@ export interface TypeProviderExtension {
|
||||
|
||||
export interface CodeActionExtension {
|
||||
addCodeActions(
|
||||
evaluator: TypeEvaluator,
|
||||
filePath: string,
|
||||
range: Range,
|
||||
parseResults: ParseResults,
|
||||
|
@ -17,5 +17,7 @@ declare interface Promise<T> {
|
||||
/* eslint-disable @typescript-eslint/no-empty-function */
|
||||
// Explicitly tells that promise should be run asynchronously.
|
||||
Promise.prototype.ignoreErrors = function <T>(this: Promise<T>) {
|
||||
this.catch(() => {});
|
||||
this.catch((e) => {
|
||||
console.log(e);
|
||||
});
|
||||
};
|
||||
|
@ -70,7 +70,6 @@ import {
|
||||
WatchKind,
|
||||
WorkDoneProgressReporter,
|
||||
WorkspaceEdit,
|
||||
WorkspaceFolder,
|
||||
WorkspaceSymbol,
|
||||
WorkspaceSymbolParams,
|
||||
} from 'vscode-languageserver';
|
||||
@ -94,7 +93,6 @@ import {
|
||||
} from './common/commandLineOptions';
|
||||
import { ConfigOptions, getDiagLevelDiagnosticRules, SignatureDisplayType } from './common/configOptions';
|
||||
import { ConsoleInterface, ConsoleWithLogLevel, LogLevel } from './common/console';
|
||||
import { createDeferred } from './common/deferred';
|
||||
import {
|
||||
Diagnostic as AnalyzerDiagnostic,
|
||||
DiagnosticCategory,
|
||||
@ -109,6 +107,7 @@ import { Host } from './common/host';
|
||||
import { fromLSPAny } from './common/lspUtils';
|
||||
import { convertPathToUri, deduplicateFolders, getDirectoryPath, getFileName, isFile } from './common/pathUtils';
|
||||
import { ProgressReporter, ProgressReportTracker } from './common/progressReporter';
|
||||
import { hashString } from './common/stringUtils';
|
||||
import { DocumentRange, Position, Range } from './common/textRange';
|
||||
import { UriParser } from './common/uriParser';
|
||||
import { convertToWorkspaceEdit } from './common/workspaceEditUtils';
|
||||
@ -121,7 +120,7 @@ import { convertHoverResults } from './languageService/hoverProvider';
|
||||
import { ReferenceCallback } from './languageService/referencesProvider';
|
||||
import { Localizer, setLocaleOverride } from './localization/localize';
|
||||
import { PyrightFileSystem } from './pyrightFileSystem';
|
||||
import { WorkspaceMap } from './workspaceMap';
|
||||
import { InitStatus, WellKnownWorkspaceKinds, Workspace, WorkspaceFactory } from './workspaceFactory';
|
||||
|
||||
export interface ServerSettings {
|
||||
venvPath?: string | undefined;
|
||||
@ -151,75 +150,6 @@ export interface ServerSettings {
|
||||
functionSignatureDisplay?: SignatureDisplayType | undefined;
|
||||
}
|
||||
|
||||
export enum WellKnownWorkspaceKinds {
|
||||
Default = 'default',
|
||||
Regular = 'regular',
|
||||
Limited = 'limited',
|
||||
Cloned = 'cloned',
|
||||
Test = 'test',
|
||||
}
|
||||
|
||||
export function createInitStatus(): InitStatus {
|
||||
// Due to the way we get `python path`, `include/exclude` from settings to initialize workspace,
|
||||
// we need to wait for getSettings to finish before letting IDE features to use workspace (`isInitialized` field).
|
||||
// So most of cases, whenever we create new workspace, we send request to workspace/configuration right way
|
||||
// except one place which is `initialize` LSP call.
|
||||
// In `initialize` method where we create `initial workspace`, we can't do that since LSP spec doesn't allow
|
||||
// LSP server from sending any request to client until `initialized` method is called.
|
||||
// This flag indicates whether we had our initial updateSetting call or not after `initialized` call.
|
||||
let called = false;
|
||||
|
||||
const deferred = createDeferred<void>();
|
||||
const self = {
|
||||
promise: deferred.promise,
|
||||
resolve: () => {
|
||||
called = true;
|
||||
deferred.resolve();
|
||||
},
|
||||
markCalled: () => {
|
||||
called = true;
|
||||
},
|
||||
reset: () => {
|
||||
if (!called) {
|
||||
return self;
|
||||
}
|
||||
|
||||
return createInitStatus();
|
||||
},
|
||||
resolved: () => {
|
||||
return deferred.resolved;
|
||||
},
|
||||
};
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
export interface InitStatus {
|
||||
resolve(): void;
|
||||
reset(): InitStatus;
|
||||
markCalled(): void;
|
||||
promise: Promise<void>;
|
||||
resolved(): boolean;
|
||||
}
|
||||
|
||||
// path and uri will point to a workspace itself. It could be a folder
|
||||
// if the workspace represents a folder. it could be '' if it is the default workspace.
|
||||
// But it also could be a file if it is a virtual workspace.
|
||||
// rootPath will always point to the folder that contains the workspace.
|
||||
export interface WorkspaceServiceInstance {
|
||||
workspaceName: string;
|
||||
rootPath: string;
|
||||
path: string;
|
||||
uri: string;
|
||||
kinds: string[];
|
||||
serviceInstance: AnalyzerService;
|
||||
disableLanguageServices: boolean;
|
||||
disableOrganizeImports: boolean;
|
||||
disableWorkspaceSymbol: boolean;
|
||||
isInitialized: InitStatus;
|
||||
searchPathsToWatch: string[];
|
||||
}
|
||||
|
||||
export interface MessageAction {
|
||||
title: string;
|
||||
id: string;
|
||||
@ -237,8 +167,8 @@ export interface WindowInterface {
|
||||
}
|
||||
|
||||
export interface LanguageServerInterface {
|
||||
getWorkspaceForFile(filePath: string): Promise<WorkspaceServiceInstance>;
|
||||
getSettings(workspace: WorkspaceServiceInstance): Promise<ServerSettings>;
|
||||
getWorkspaceForFile(filePath: string): Promise<Workspace>;
|
||||
getSettings(workspace: Workspace): Promise<ServerSettings>;
|
||||
createBackgroundAnalysis(serviceId: string): BackgroundAnalysisBase | undefined;
|
||||
reanalyze(): void;
|
||||
restart(): void;
|
||||
@ -254,7 +184,6 @@ export interface ServerOptions {
|
||||
productName: string;
|
||||
rootDirectory: string;
|
||||
version: string;
|
||||
workspaceMap: WorkspaceMap;
|
||||
cancellationProvider: CancellationProvider;
|
||||
fileSystem: FileSystem;
|
||||
fileWatcherHandler: FileWatcherHandler;
|
||||
@ -262,6 +191,7 @@ export interface ServerOptions {
|
||||
disableChecker?: boolean;
|
||||
supportedCommands?: string[];
|
||||
supportedCodeActions?: string[];
|
||||
supportsTelemetry?: boolean;
|
||||
}
|
||||
|
||||
export interface WorkspaceServices {
|
||||
@ -363,7 +293,7 @@ namespace VSDiagnosticRank {
|
||||
|
||||
export abstract class LanguageServerBase implements LanguageServerInterface {
|
||||
protected _defaultClientConfig: any;
|
||||
protected _workspaceMap: WorkspaceMap;
|
||||
protected _workspaceFactory: WorkspaceFactory;
|
||||
protected _cacheManager: CacheManager;
|
||||
|
||||
// We support running only one "find all reference" at a time.
|
||||
@ -378,6 +308,8 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
|
||||
|
||||
private _lastFileWatcherRegistration: Disposable | undefined;
|
||||
|
||||
private _initialized = false;
|
||||
|
||||
// Global root path - the basis for all global settings.
|
||||
rootPath = '';
|
||||
|
||||
@ -429,11 +361,18 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
|
||||
this.console.info(`Server root directory: ${_serverOptions.rootDirectory}`);
|
||||
|
||||
this._cacheManager = new CacheManager();
|
||||
this._workspaceMap = this._serverOptions.workspaceMap;
|
||||
|
||||
this._serviceFS = new PyrightFileSystem(this._serverOptions.fileSystem);
|
||||
this._uriParser = uriParserFactory(this._serviceFS);
|
||||
|
||||
this._workspaceFactory = new WorkspaceFactory(
|
||||
this.console,
|
||||
this._uriParser,
|
||||
this.createAnalyzerServiceForWorkspace.bind(this),
|
||||
this.isPythonPathImmutable.bind(this),
|
||||
this.onWorkspaceCreated.bind(this)
|
||||
);
|
||||
|
||||
// Set the working directory to a known location within
|
||||
// the extension directory. Otherwise the execution of
|
||||
// python can have unintended and surprising results.
|
||||
@ -470,7 +409,14 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
|
||||
token: CancellationToken
|
||||
): Promise<(Command | CodeAction)[] | undefined | null>;
|
||||
|
||||
abstract getSettings(workspace: WorkspaceServiceInstance): Promise<ServerSettings>;
|
||||
abstract getSettings(workspace: Workspace): Promise<ServerSettings>;
|
||||
|
||||
protected isPythonPathImmutable(filePath: string): boolean {
|
||||
// This function is called to determine if the file is using
|
||||
// a special pythonPath separate from a workspace or not.
|
||||
// The default is no.
|
||||
return false;
|
||||
}
|
||||
|
||||
protected async getConfiguration(scopeUri: string | undefined, section: string) {
|
||||
if (this.client.hasConfigurationCapability) {
|
||||
@ -577,7 +523,7 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
|
||||
}
|
||||
|
||||
async test_getWorkspaces() {
|
||||
const workspaces = [...this._workspaceMap.values()];
|
||||
const workspaces = [...this._workspaceFactory.items()];
|
||||
for (const workspace of workspaces) {
|
||||
await workspace.isInitialized.promise;
|
||||
}
|
||||
@ -585,19 +531,19 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
|
||||
return workspaces;
|
||||
}
|
||||
|
||||
async getWorkspaceForFile(filePath: string): Promise<WorkspaceServiceInstance> {
|
||||
return this._workspaceMap.getWorkspaceForFile(this, filePath);
|
||||
async getWorkspaceForFile(filePath: string, pythonPath?: string): Promise<Workspace> {
|
||||
return this._workspaceFactory.getWorkspaceForFile(filePath, pythonPath);
|
||||
}
|
||||
|
||||
reanalyze() {
|
||||
this._workspaceMap.forEach((workspace) => {
|
||||
workspace.serviceInstance.invalidateAndForceReanalysis();
|
||||
this._workspaceFactory.items().forEach((workspace) => {
|
||||
workspace.service.invalidateAndForceReanalysis();
|
||||
});
|
||||
}
|
||||
|
||||
restart() {
|
||||
this._workspaceMap.forEach((workspace) => {
|
||||
workspace.serviceInstance.restart();
|
||||
this._workspaceFactory.items().forEach((workspace) => {
|
||||
workspace.service.restart();
|
||||
});
|
||||
}
|
||||
|
||||
@ -711,17 +657,7 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
|
||||
);
|
||||
|
||||
// Create a service instance for each of the workspace folders.
|
||||
if (params.workspaceFolders) {
|
||||
params.workspaceFolders.forEach((folder) => {
|
||||
const path = this._uriParser.decodeTextDocumentUri(folder.uri);
|
||||
this._workspaceMap.set(path, this.createWorkspaceServiceInstance(folder, path, path));
|
||||
});
|
||||
} else if (params.rootPath) {
|
||||
this._workspaceMap.set(
|
||||
params.rootPath,
|
||||
this.createWorkspaceServiceInstance(undefined, params.rootPath, params.rootPath)
|
||||
);
|
||||
}
|
||||
this._workspaceFactory.handleInitialize(params);
|
||||
|
||||
const result: InitializeResult = {
|
||||
capabilities: {
|
||||
@ -771,6 +707,10 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
|
||||
}
|
||||
|
||||
protected onInitialized() {
|
||||
// Mark as initialized. We need this to make sure to
|
||||
// not send config updates before this point.
|
||||
this._initialized = true;
|
||||
|
||||
if (!this.client.hasWorkspaceFoldersCapability) {
|
||||
// If folder capability is not supported, initialize ones given by onInitialize.
|
||||
this.updateSettingsForAllWorkspaces();
|
||||
@ -778,18 +718,7 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
|
||||
}
|
||||
|
||||
this._connection.workspace.onDidChangeWorkspaceFolders((event) => {
|
||||
event.removed.forEach((workspaceInfo) => {
|
||||
const rootPath = this._uriParser.decodeTextDocumentUri(workspaceInfo.uri);
|
||||
this._workspaceMap.delete(rootPath);
|
||||
});
|
||||
|
||||
event.added.forEach((workspaceInfo) => {
|
||||
const rootPath = this._uriParser.decodeTextDocumentUri(workspaceInfo.uri);
|
||||
const newWorkspace = this.createWorkspaceServiceInstance(workspaceInfo, rootPath, rootPath);
|
||||
this._workspaceMap.set(rootPath, newWorkspace);
|
||||
this.updateSettingsForWorkspace(newWorkspace, newWorkspace.isInitialized).ignoreErrors();
|
||||
});
|
||||
|
||||
this._workspaceFactory.handleWorkspaceFoldersChanged(event);
|
||||
this._setupFileWatcher();
|
||||
});
|
||||
|
||||
@ -815,7 +744,7 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
|
||||
// Get rid of any search path under workspace root since it is already watched by
|
||||
// "**" above.
|
||||
const foldersToWatch = deduplicateFolders(
|
||||
this._workspaceMap
|
||||
this._workspaceFactory
|
||||
.getNonDefaultWorkspaces()
|
||||
.map((w) => w.searchPathsToWatch.filter((p) => !p.startsWith(w.rootPath)))
|
||||
);
|
||||
@ -856,7 +785,7 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
|
||||
token,
|
||||
this.client.hasGoToDeclarationCapability ? DefinitionFilter.PreferSource : DefinitionFilter.All,
|
||||
(workspace, filePath, position, filter, token) =>
|
||||
workspace.serviceInstance.getDefinitionForPosition(filePath, position, filter, token)
|
||||
workspace.service.getDefinitionForPosition(filePath, position, filter, token)
|
||||
);
|
||||
}
|
||||
|
||||
@ -869,7 +798,7 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
|
||||
token,
|
||||
this.client.hasGoToDeclarationCapability ? DefinitionFilter.PreferStubs : DefinitionFilter.All,
|
||||
(workspace, filePath, position, filter, token) =>
|
||||
workspace.serviceInstance.getDefinitionForPosition(filePath, position, filter, token)
|
||||
workspace.service.getDefinitionForPosition(filePath, position, filter, token)
|
||||
);
|
||||
}
|
||||
|
||||
@ -878,7 +807,7 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
|
||||
token: CancellationToken
|
||||
): Promise<Definition | DefinitionLink[] | undefined | null> {
|
||||
return this.getDefinitions(params, token, DefinitionFilter.All, (workspace, filePath, position, _, token) =>
|
||||
workspace.serviceInstance.getTypeDefinitionForPosition(filePath, position, token)
|
||||
workspace.service.getTypeDefinitionForPosition(filePath, position, token)
|
||||
);
|
||||
}
|
||||
|
||||
@ -887,7 +816,7 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
|
||||
token: CancellationToken,
|
||||
filter: DefinitionFilter,
|
||||
getDefinitionsFunc: (
|
||||
workspace: WorkspaceServiceInstance,
|
||||
workspace: Workspace,
|
||||
filePath: string,
|
||||
position: Position,
|
||||
filter: DefinitionFilter,
|
||||
@ -908,8 +837,8 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
|
||||
return undefined;
|
||||
}
|
||||
return locations
|
||||
.filter((loc) => this.canNavigateToFile(loc.path, workspace.serviceInstance.fs))
|
||||
.map((loc) => Location.create(convertPathToUri(workspace.serviceInstance.fs, loc.path), loc.range));
|
||||
.filter((loc) => this.canNavigateToFile(loc.path, workspace.service.fs))
|
||||
.map((loc) => Location.create(convertPathToUri(workspace.service.fs, loc.path), loc.range));
|
||||
}
|
||||
|
||||
protected async onReferences(
|
||||
@ -948,8 +877,8 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
|
||||
|
||||
const convert = (locs: DocumentRange[]): Location[] => {
|
||||
return locs
|
||||
.filter((loc) => this.canNavigateToFile(loc.path, workspace.serviceInstance.fs))
|
||||
.map((loc) => Location.create(convertPathToUri(workspace.serviceInstance.fs, loc.path), loc.range));
|
||||
.filter((loc) => this.canNavigateToFile(loc.path, workspace.service.fs))
|
||||
.map((loc) => Location.create(convertPathToUri(workspace.service.fs, loc.path), loc.range));
|
||||
};
|
||||
|
||||
const locations: Location[] = [];
|
||||
@ -957,7 +886,7 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
|
||||
? (locs) => resultReporter.report(convert(locs))
|
||||
: (locs) => appendArray(locations, convert(locs));
|
||||
|
||||
workspace.serviceInstance.reportReferencesForPosition(
|
||||
workspace.service.reportReferencesForPosition(
|
||||
filePath,
|
||||
position,
|
||||
params.context.includeDeclaration,
|
||||
@ -986,7 +915,7 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
|
||||
}
|
||||
|
||||
const symbolList: DocumentSymbol[] = [];
|
||||
workspace.serviceInstance.addSymbolsForDocument(filePath, symbolList, token);
|
||||
workspace.service.addSymbolsForDocument(filePath, symbolList, token);
|
||||
if (this.client.hasHierarchicalDocumentSymbolCapability) {
|
||||
return symbolList;
|
||||
}
|
||||
@ -1005,10 +934,10 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
|
||||
? (symbols) => resultReporter.report(symbols)
|
||||
: (symbols) => appendArray(symbolList, symbols);
|
||||
|
||||
for (const workspace of this._workspaceMap.values()) {
|
||||
for (const workspace of this._workspaceFactory.items()) {
|
||||
await workspace.isInitialized.promise;
|
||||
if (!workspace.disableLanguageServices && !workspace.disableWorkspaceSymbol) {
|
||||
workspace.serviceInstance.reportSymbolsForWorkspace(params.query, reporter, token);
|
||||
workspace.service.reportSymbolsForWorkspace(params.query, reporter, token);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1019,13 +948,17 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
|
||||
const { filePath, position } = this._uriParser.decodeTextDocumentPosition(params.textDocument, params.position);
|
||||
|
||||
const workspace = await this.getWorkspaceForFile(filePath);
|
||||
const hoverResults = workspace.serviceInstance.getHoverForPosition(
|
||||
const hoverResults = workspace.service.getHoverForPosition(
|
||||
filePath,
|
||||
position,
|
||||
this.client.hoverContentFormat,
|
||||
token
|
||||
);
|
||||
return convertHoverResults(this.client.hoverContentFormat, hoverResults);
|
||||
return convertHoverResults(
|
||||
this.client.hoverContentFormat,
|
||||
hoverResults,
|
||||
!!this._serverOptions.supportsTelemetry
|
||||
);
|
||||
}
|
||||
|
||||
protected async onDocumentHighlight(
|
||||
@ -1034,7 +967,7 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
|
||||
): Promise<DocumentHighlight[] | null | undefined> {
|
||||
const { filePath, position } = this._uriParser.decodeTextDocumentPosition(params.textDocument, params.position);
|
||||
const workspace = await this.getWorkspaceForFile(filePath);
|
||||
return workspace.serviceInstance.getDocumentHighlight(filePath, position, token);
|
||||
return workspace.service.getDocumentHighlight(filePath, position, token);
|
||||
}
|
||||
|
||||
protected async onSignatureHelp(
|
||||
@ -1047,7 +980,7 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
|
||||
if (workspace.disableLanguageServices) {
|
||||
return;
|
||||
}
|
||||
const signatureHelpResults = workspace.serviceInstance.getSignatureHelpForPosition(
|
||||
const signatureHelpResults = workspace.service.getSignatureHelpForPosition(
|
||||
filePath,
|
||||
position,
|
||||
this.client.signatureDocFormat,
|
||||
@ -1177,6 +1110,22 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
|
||||
completions.completionList.isIncomplete = completionIncomplete;
|
||||
}
|
||||
|
||||
// Add memberAccessInfo.lastKnownModule if we have it. The client side
|
||||
// will use this to send extra telemetry
|
||||
if (
|
||||
completions?.memberAccessInfo &&
|
||||
completions.completionList &&
|
||||
completions.completionList.items.length > 0 &&
|
||||
completions.memberAccessInfo.lastKnownModule &&
|
||||
this._serverOptions.supportsTelemetry
|
||||
) {
|
||||
// Just stick it on the first item. It only checks the first one
|
||||
completions.completionList.items[0].data = {
|
||||
...completions.completionList.items[0].data,
|
||||
moduleHash: hashString(completions.memberAccessInfo.lastKnownModule),
|
||||
};
|
||||
}
|
||||
|
||||
return completions?.completionList;
|
||||
}
|
||||
|
||||
@ -1206,10 +1155,10 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
|
||||
return null;
|
||||
}
|
||||
|
||||
const result = workspace.serviceInstance.canRenameSymbolAtPosition(
|
||||
const result = workspace.service.canRenameSymbolAtPosition(
|
||||
filePath,
|
||||
position,
|
||||
workspace.path === '',
|
||||
workspace.kinds.includes(WellKnownWorkspaceKinds.Default),
|
||||
this.allowModuleRename,
|
||||
token
|
||||
);
|
||||
@ -1228,11 +1177,11 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
|
||||
return;
|
||||
}
|
||||
|
||||
const editActions = workspace.serviceInstance.renameSymbolAtPosition(
|
||||
const editActions = workspace.service.renameSymbolAtPosition(
|
||||
filePath,
|
||||
position,
|
||||
params.newName,
|
||||
workspace.path === '',
|
||||
workspace.kinds.includes(WellKnownWorkspaceKinds.Default),
|
||||
this.allowModuleRename,
|
||||
token
|
||||
);
|
||||
@ -1241,7 +1190,7 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return convertToWorkspaceEdit(workspace.serviceInstance.fs, editActions);
|
||||
return convertToWorkspaceEdit(workspace.service.fs, editActions);
|
||||
}
|
||||
|
||||
protected async onPrepare(
|
||||
@ -1255,17 +1204,17 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
|
||||
return null;
|
||||
}
|
||||
|
||||
const callItem = workspace.serviceInstance.getCallForPosition(filePath, position, token) || null;
|
||||
const callItem = workspace.service.getCallForPosition(filePath, position, token) || null;
|
||||
if (!callItem) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!this.canNavigateToFile(callItem.uri, workspace.serviceInstance.fs)) {
|
||||
if (!this.canNavigateToFile(callItem.uri, workspace.service.fs)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Convert the file path in the item to proper URI.
|
||||
callItem.uri = convertPathToUri(workspace.serviceInstance.fs, callItem.uri);
|
||||
callItem.uri = convertPathToUri(workspace.service.fs, callItem.uri);
|
||||
|
||||
return [callItem];
|
||||
}
|
||||
@ -1278,16 +1227,16 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
|
||||
return null;
|
||||
}
|
||||
|
||||
let callItems = workspace.serviceInstance.getIncomingCallsForPosition(filePath, position, token) || null;
|
||||
let callItems = workspace.service.getIncomingCallsForPosition(filePath, position, token) || null;
|
||||
if (!callItems || callItems.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
callItems = callItems.filter((item) => this.canNavigateToFile(item.from.uri, workspace.serviceInstance.fs));
|
||||
callItems = callItems.filter((item) => this.canNavigateToFile(item.from.uri, workspace.service.fs));
|
||||
|
||||
// Convert the file paths in the items to proper URIs.
|
||||
callItems.forEach((item) => {
|
||||
item.from.uri = convertPathToUri(workspace.serviceInstance.fs, item.from.uri);
|
||||
item.from.uri = convertPathToUri(workspace.service.fs, item.from.uri);
|
||||
});
|
||||
|
||||
return callItems;
|
||||
@ -1304,16 +1253,16 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
|
||||
return null;
|
||||
}
|
||||
|
||||
let callItems = workspace.serviceInstance.getOutgoingCallsForPosition(filePath, position, token) || null;
|
||||
let callItems = workspace.service.getOutgoingCallsForPosition(filePath, position, token) || null;
|
||||
if (!callItems || callItems.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
callItems = callItems.filter((item) => this.canNavigateToFile(item.to.uri, workspace.serviceInstance.fs));
|
||||
callItems = callItems.filter((item) => this.canNavigateToFile(item.to.uri, workspace.service.fs));
|
||||
|
||||
// Convert the file paths in the items to proper URIs.
|
||||
callItems.forEach((item) => {
|
||||
item.to.uri = convertPathToUri(workspace.serviceInstance.fs, item.to.uri);
|
||||
item.to.uri = convertPathToUri(workspace.service.fs, item.to.uri);
|
||||
});
|
||||
|
||||
return callItems;
|
||||
@ -1328,12 +1277,7 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
|
||||
}
|
||||
|
||||
const workspace = await this.getWorkspaceForFile(filePath);
|
||||
workspace.serviceInstance.setFileOpened(
|
||||
filePath,
|
||||
params.textDocument.version,
|
||||
params.textDocument.text,
|
||||
ipythonMode
|
||||
);
|
||||
workspace.service.setFileOpened(filePath, params.textDocument.version, params.textDocument.text, ipythonMode);
|
||||
}
|
||||
|
||||
protected async onDidChangeTextDocument(params: DidChangeTextDocumentParams, ipythonMode = IPythonMode.None) {
|
||||
@ -1346,7 +1290,7 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
|
||||
}
|
||||
|
||||
const workspace = await this.getWorkspaceForFile(filePath);
|
||||
workspace.serviceInstance.updateOpenFileContents(
|
||||
workspace.service.updateOpenFileContents(
|
||||
filePath,
|
||||
params.textDocument.version,
|
||||
params.contentChanges,
|
||||
@ -1362,7 +1306,7 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
|
||||
}
|
||||
|
||||
const workspace = await this.getWorkspaceForFile(filePath);
|
||||
workspace.serviceInstance.setFileClosed(filePath);
|
||||
workspace.service.setFileClosed(filePath);
|
||||
}
|
||||
|
||||
protected onDidChangeWatchedFiles(params: DidChangeWatchedFilesParams) {
|
||||
@ -1423,17 +1367,17 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
|
||||
|
||||
protected onShutdown(token: CancellationToken) {
|
||||
// Shutdown remaining workspaces.
|
||||
this._workspaceMap.forEach((_, key) => this._workspaceMap.delete(key));
|
||||
this._workspaceFactory.clear();
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
protected resolveWorkspaceCompletionItem(
|
||||
workspace: WorkspaceServiceInstance,
|
||||
workspace: Workspace,
|
||||
filePath: string,
|
||||
item: CompletionItem,
|
||||
token: CancellationToken
|
||||
): void {
|
||||
workspace.serviceInstance.resolveCompletionItem(
|
||||
workspace.service.resolveCompletionItem(
|
||||
filePath,
|
||||
item,
|
||||
this.getCompletionOptions(workspace),
|
||||
@ -1443,16 +1387,16 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
|
||||
}
|
||||
|
||||
protected getWorkspaceCompletionsForPosition(
|
||||
workspace: WorkspaceServiceInstance,
|
||||
workspace: Workspace,
|
||||
filePath: string,
|
||||
position: Position,
|
||||
options: CompletionOptions,
|
||||
token: CancellationToken
|
||||
): Promise<CompletionResultsList | undefined> {
|
||||
return workspace.serviceInstance.getCompletionsForPosition(
|
||||
return workspace.service.getCompletionsForPosition(
|
||||
filePath,
|
||||
position,
|
||||
workspace.path,
|
||||
workspace.rootPath,
|
||||
options,
|
||||
undefined,
|
||||
token
|
||||
@ -1461,7 +1405,7 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
|
||||
|
||||
updateSettingsForAllWorkspaces(): void {
|
||||
const tasks: Promise<void>[] = [];
|
||||
this._workspaceMap.forEach((workspace) => {
|
||||
this._workspaceFactory.items().forEach((workspace) => {
|
||||
// Updating settings can change workspace's file ownership. Make workspace uninitialized so that
|
||||
// features can wait until workspace gets new settings.
|
||||
// the file's ownership can also changed by `pyrightconfig.json` changes, but those are synchronous
|
||||
@ -1475,7 +1419,7 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
|
||||
});
|
||||
}
|
||||
|
||||
protected getCompletionOptions(workspace: WorkspaceServiceInstance, params?: CompletionParams): CompletionOptions {
|
||||
protected getCompletionOptions(workspace: Workspace, params?: CompletionParams): CompletionOptions {
|
||||
return {
|
||||
format: this.client.completionDocFormat,
|
||||
snippet: this.client.completionSupportsSnippet,
|
||||
@ -1488,48 +1432,6 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
|
||||
};
|
||||
}
|
||||
|
||||
protected createWorkspaceServiceInstance(
|
||||
workspaceFolder: WorkspaceFolder | undefined,
|
||||
rootPath: string,
|
||||
path: string,
|
||||
kinds: string[] = [WellKnownWorkspaceKinds.Regular],
|
||||
services?: WorkspaceServices
|
||||
): WorkspaceServiceInstance {
|
||||
// 5 seconds default
|
||||
const defaultBackOffTime = 5 * 1000;
|
||||
|
||||
// 10 seconds back off for multi workspace.
|
||||
const multiWorkspaceBackOffTime = 10 * 1000;
|
||||
|
||||
const libraryReanalysisTimeProvider =
|
||||
kinds.length === 1 && kinds[0] === WellKnownWorkspaceKinds.Regular
|
||||
? () =>
|
||||
this._workspaceMap.hasMultipleWorkspaces(kinds[0])
|
||||
? multiWorkspaceBackOffTime
|
||||
: defaultBackOffTime
|
||||
: () => defaultBackOffTime;
|
||||
|
||||
const rootUri = workspaceFolder?.uri ?? '';
|
||||
|
||||
return {
|
||||
workspaceName: workspaceFolder?.name ?? '',
|
||||
rootPath,
|
||||
path,
|
||||
uri: rootUri,
|
||||
kinds,
|
||||
serviceInstance: this.createAnalyzerService(
|
||||
workspaceFolder?.name ?? path,
|
||||
services,
|
||||
libraryReanalysisTimeProvider
|
||||
),
|
||||
disableLanguageServices: false,
|
||||
disableOrganizeImports: false,
|
||||
disableWorkspaceSymbol: false,
|
||||
isInitialized: createInitStatus(),
|
||||
searchPathsToWatch: [],
|
||||
};
|
||||
}
|
||||
|
||||
protected convertDiagnostics(fs: FileSystem, fileDiagnostics: FileDiagnostics): PublishDiagnosticsParams[] {
|
||||
return [
|
||||
{
|
||||
@ -1577,7 +1479,7 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
|
||||
}
|
||||
|
||||
async updateSettingsForWorkspace(
|
||||
workspace: WorkspaceServiceInstance,
|
||||
workspace: Workspace,
|
||||
status: InitStatus | undefined,
|
||||
serverSettings?: ServerSettings
|
||||
): Promise<void> {
|
||||
@ -1588,7 +1490,12 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
|
||||
// Set logging level first.
|
||||
(this.console as ConsoleWithLogLevel).level = serverSettings.logLevel ?? LogLevel.Info;
|
||||
|
||||
// Apply the new path to the workspace (before restarting the service).
|
||||
serverSettings.pythonPath = this._workspaceFactory.applyPythonPath(workspace, serverSettings.pythonPath);
|
||||
|
||||
// Then use the updated settings to restart the service.
|
||||
this.updateOptionsAndRestartService(workspace, serverSettings);
|
||||
|
||||
workspace.disableLanguageServices = !!serverSettings.disableLanguageServices;
|
||||
workspace.disableOrganizeImports = !!serverSettings.disableOrganizeImports;
|
||||
|
||||
@ -1599,35 +1506,45 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
|
||||
}
|
||||
|
||||
updateOptionsAndRestartService(
|
||||
workspace: WorkspaceServiceInstance,
|
||||
workspace: Workspace,
|
||||
serverSettings: ServerSettings,
|
||||
typeStubTargetImportName?: string
|
||||
) {
|
||||
AnalyzerServiceExecutor.runWithOptions(this.rootPath, workspace, serverSettings, typeStubTargetImportName);
|
||||
workspace.searchPathsToWatch = workspace.serviceInstance.librarySearchPathsToWatch ?? [];
|
||||
workspace.searchPathsToWatch = workspace.service.librarySearchPathsToWatch ?? [];
|
||||
}
|
||||
|
||||
protected convertLogLevel(logLevelValue?: string): LogLevel {
|
||||
if (!logLevelValue) {
|
||||
return LogLevel.Info;
|
||||
protected onWorkspaceCreated(workspace: Workspace) {
|
||||
// Update settings on this workspace (but only if initialize has happened)
|
||||
if (this._initialized) {
|
||||
this.updateSettingsForWorkspace(workspace, workspace.isInitialized).ignoreErrors();
|
||||
}
|
||||
|
||||
switch (logLevelValue.toLowerCase()) {
|
||||
case 'error':
|
||||
return LogLevel.Error;
|
||||
// Otherwise the intiailize completion should cause settings to be updated on all workspaces.
|
||||
}
|
||||
|
||||
case 'warning':
|
||||
return LogLevel.Warn;
|
||||
protected createAnalyzerServiceForWorkspace(
|
||||
name: string,
|
||||
_rootPath: string,
|
||||
_uri: string,
|
||||
kinds: string[],
|
||||
services?: WorkspaceServices
|
||||
): AnalyzerService {
|
||||
// 5 seconds default
|
||||
const defaultBackOffTime = 5 * 1000;
|
||||
|
||||
case 'information':
|
||||
return LogLevel.Info;
|
||||
// 10 seconds back off for multi workspace.
|
||||
const multiWorkspaceBackOffTime = 10 * 1000;
|
||||
|
||||
case 'trace':
|
||||
return LogLevel.Log;
|
||||
const libraryReanalysisTimeProvider =
|
||||
kinds.length === 1 && kinds[0] === WellKnownWorkspaceKinds.Regular
|
||||
? () =>
|
||||
this._workspaceFactory.hasMultipleWorkspaces(kinds[0])
|
||||
? multiWorkspaceBackOffTime
|
||||
: defaultBackOffTime
|
||||
: () => defaultBackOffTime;
|
||||
|
||||
default:
|
||||
return LogLevel.Info;
|
||||
}
|
||||
return this.createAnalyzerService(name, services, libraryReanalysisTimeProvider);
|
||||
}
|
||||
|
||||
private _sendDiagnostics(params: PublishDiagnosticsParams[]) {
|
||||
@ -1775,8 +1692,8 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
|
||||
// Tell all of the services that the user is actively
|
||||
// interacting with one or more editors, so they should
|
||||
// back off from performing any work.
|
||||
this._workspaceMap.forEach((workspace: { serviceInstance: { recordUserInteractionTime: () => void } }) => {
|
||||
workspace.serviceInstance.recordUserInteractionTime();
|
||||
this._workspaceFactory.items().forEach((workspace: { service: { recordUserInteractionTime: () => void } }) => {
|
||||
workspace.service.recordUserInteractionTime();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -13,13 +13,8 @@ import { CommandLineOptions } from '../common/commandLineOptions';
|
||||
import { LogLevel } from '../common/console';
|
||||
import { FileSystem } from '../common/fileSystem';
|
||||
import { combinePaths } from '../common/pathUtils';
|
||||
import {
|
||||
createInitStatus,
|
||||
LanguageServerInterface,
|
||||
ServerSettings,
|
||||
WellKnownWorkspaceKinds,
|
||||
WorkspaceServiceInstance,
|
||||
} from '../languageServerBase';
|
||||
import { LanguageServerInterface, ServerSettings } from '../languageServerBase';
|
||||
import { createInitStatus, WellKnownWorkspaceKinds, Workspace } from '../workspaceFactory';
|
||||
|
||||
export interface CloneOptions {
|
||||
useBackgroundAnalysis?: boolean;
|
||||
@ -30,7 +25,7 @@ export interface CloneOptions {
|
||||
export class AnalyzerServiceExecutor {
|
||||
static runWithOptions(
|
||||
languageServiceRootPath: string,
|
||||
workspace: WorkspaceServiceInstance,
|
||||
workspace: Workspace,
|
||||
serverSettings: ServerSettings,
|
||||
typeStubTargetImportName?: string,
|
||||
trackFiles = true
|
||||
@ -44,12 +39,12 @@ export class AnalyzerServiceExecutor {
|
||||
);
|
||||
|
||||
// Setting options causes the analyzer service to re-analyze everything.
|
||||
workspace.serviceInstance.setOptions(commandLineOptions);
|
||||
workspace.service.setOptions(commandLineOptions);
|
||||
}
|
||||
|
||||
static async cloneService(
|
||||
ls: LanguageServerInterface,
|
||||
workspace: WorkspaceServiceInstance,
|
||||
workspace: Workspace,
|
||||
options?: CloneOptions
|
||||
): Promise<AnalyzerService> {
|
||||
// Allocate a temporary pseudo-workspace to perform this job.
|
||||
@ -58,13 +53,15 @@ export class AnalyzerServiceExecutor {
|
||||
|
||||
options = options ?? {};
|
||||
|
||||
const tempWorkspace: WorkspaceServiceInstance = {
|
||||
const tempWorkspace: Workspace = {
|
||||
...workspace,
|
||||
workspaceName: `temp workspace for cloned service`,
|
||||
rootPath: workspace.rootPath,
|
||||
path: workspace.path,
|
||||
uri: workspace.uri,
|
||||
pythonPath: workspace.pythonPath,
|
||||
pythonPathKind: workspace.pythonPathKind,
|
||||
kinds: [...workspace.kinds, WellKnownWorkspaceKinds.Cloned],
|
||||
serviceInstance: workspace.serviceInstance.clone(
|
||||
service: workspace.service.clone(
|
||||
instanceName,
|
||||
serviceId,
|
||||
options.useBackgroundAnalysis ? ls.createBackgroundAnalysis(serviceId) : undefined,
|
||||
@ -86,7 +83,7 @@ export class AnalyzerServiceExecutor {
|
||||
/* trackFiles */ false
|
||||
);
|
||||
|
||||
return tempWorkspace.serviceInstance;
|
||||
return tempWorkspace.service;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -20,12 +20,12 @@ import { FileEditActions } from '../common/editAction';
|
||||
import { convertPathToUri, getShortenedFileName } from '../common/pathUtils';
|
||||
import { Range } from '../common/textRange';
|
||||
import { convertToWorkspaceEdit } from '../common/workspaceEditUtils';
|
||||
import { WorkspaceServiceInstance } from '../languageServerBase';
|
||||
import { Localizer } from '../localization/localize';
|
||||
import { Workspace } from '../workspaceFactory';
|
||||
|
||||
export class CodeActionProvider {
|
||||
static async getCodeActionsForPosition(
|
||||
workspace: WorkspaceServiceInstance,
|
||||
workspace: Workspace,
|
||||
filePath: string,
|
||||
range: Range,
|
||||
kinds: CodeActionKind[] | undefined,
|
||||
@ -36,7 +36,7 @@ export class CodeActionProvider {
|
||||
const codeActions: CodeAction[] = [];
|
||||
|
||||
if (!workspace.disableLanguageServices) {
|
||||
const diags = await workspace.serviceInstance.getDiagnosticsForRange(filePath, range, token);
|
||||
const diags = await workspace.service.getDiagnosticsForRange(filePath, range, token);
|
||||
const typeStubDiag = diags.find((d) => {
|
||||
const actions = d.getActions();
|
||||
return actions && actions.find((a) => a.action === Commands.createTypeStub);
|
||||
@ -52,7 +52,7 @@ export class CodeActionProvider {
|
||||
Command.create(
|
||||
Localizer.CodeAction.createTypeStub(),
|
||||
Commands.createTypeStub,
|
||||
workspace.path,
|
||||
workspace.rootPath,
|
||||
action.moduleName,
|
||||
filePath
|
||||
),
|
||||
@ -72,7 +72,7 @@ export class CodeActionProvider {
|
||||
.getActions()!
|
||||
.find((a) => a.action === Commands.addMissingOptionalToParam) as AddMissingOptionalToParamAction;
|
||||
if (action) {
|
||||
const fs = workspace.serviceInstance.getImportResolver().fileSystem;
|
||||
const fs = workspace.service.getImportResolver().fileSystem;
|
||||
const addMissingOptionalAction = CodeAction.create(
|
||||
Localizer.CodeAction.addOptionalToAnnotation(),
|
||||
Command.create(
|
||||
@ -99,7 +99,7 @@ export class CodeActionProvider {
|
||||
oldFile: getShortenedFileName(action.oldFile),
|
||||
newFile: getShortenedFileName(action.newFile),
|
||||
});
|
||||
const fs = workspace.serviceInstance.getImportResolver().fileSystem;
|
||||
const fs = workspace.service.getImportResolver().fileSystem;
|
||||
const editActions: FileEditActions = {
|
||||
edits: [],
|
||||
fileOperations: [
|
||||
|
@ -1509,10 +1509,8 @@ export class CompletionProvider {
|
||||
});
|
||||
}
|
||||
|
||||
// If we don't know this type, look for a module we should stub.
|
||||
if (!leftType || isUnknown(leftType) || isUnbound(leftType)) {
|
||||
memberAccessInfo = this._getLastKnownModule(leftExprNode, leftType);
|
||||
}
|
||||
// Save member access info for every request
|
||||
memberAccessInfo = this._getLastKnownModule(leftExprNode, leftType);
|
||||
|
||||
return { completionMap, memberAccessInfo };
|
||||
}
|
||||
@ -1535,7 +1533,7 @@ export class CompletionProvider {
|
||||
curNode.nodeType === ParseNodeType.MemberAccess ? curNode?.memberName.value ?? '' : '';
|
||||
}
|
||||
} else {
|
||||
curNode = undefined;
|
||||
break;
|
||||
}
|
||||
|
||||
if (curNode) {
|
||||
|
@ -71,6 +71,7 @@ export class DefinitionProvider {
|
||||
Extensions.getProgramExtensions(node).forEach((e) => {
|
||||
if (e.declarationProviderExtension) {
|
||||
const declarations = e.declarationProviderExtension.tryGetDeclarations(
|
||||
evaluator,
|
||||
node,
|
||||
DeclarationUseCase.Definition,
|
||||
token
|
||||
|
@ -27,7 +27,9 @@ import {
|
||||
import { getModuleNode, getStringNodeValueRange } from '../analyzer/parseTreeUtils';
|
||||
import * as ParseTreeUtils from '../analyzer/parseTreeUtils';
|
||||
import { ParseTreeWalker } from '../analyzer/parseTreeWalker';
|
||||
import { ScopeType } from '../analyzer/scope';
|
||||
import * as ScopeUtils from '../analyzer/scopeUtils';
|
||||
import { SourceFile } from '../analyzer/sourceFile';
|
||||
import { isStubFile, SourceMapper } from '../analyzer/sourceMapper';
|
||||
import { TypeEvaluator } from '../analyzer/typeEvaluatorTypes';
|
||||
import { isInstantiableClass, TypeCategory } from '../analyzer/types';
|
||||
@ -103,7 +105,8 @@ export class DocumentSymbolCollector extends ParseTreeWalker {
|
||||
resolveLocalName: boolean,
|
||||
useCase: DocumentSymbolCollectorUseCase,
|
||||
token: CancellationToken,
|
||||
sourceMapper?: SourceMapper
|
||||
sourceMapper?: SourceMapper,
|
||||
implicitlyImportedBy?: SourceFile[]
|
||||
): Declaration[] {
|
||||
throwIfCancellationRequested(token);
|
||||
|
||||
@ -114,6 +117,30 @@ export class DocumentSymbolCollector extends ParseTreeWalker {
|
||||
token,
|
||||
/*skipUnreachableCode*/ false
|
||||
);
|
||||
|
||||
// Add declarations from chained source files
|
||||
let builtinsScope = AnalyzerNodeInfo.getFileInfo(node).builtinsScope;
|
||||
while (builtinsScope && builtinsScope.type === ScopeType.Module) {
|
||||
const symbol = builtinsScope?.lookUpSymbol(node.value);
|
||||
if (symbol) {
|
||||
declarations.push(...symbol.getDeclarations());
|
||||
}
|
||||
|
||||
builtinsScope = builtinsScope?.parent;
|
||||
}
|
||||
|
||||
// Add declarations from files that implicitly import the target file.
|
||||
implicitlyImportedBy?.forEach((implicitImport) => {
|
||||
const parseTree = implicitImport.getParseResults()?.parseTree;
|
||||
if (parseTree) {
|
||||
const scope = AnalyzerNodeInfo.getScope(parseTree);
|
||||
const symbol = scope?.lookUpSymbol(node.value);
|
||||
if (symbol) {
|
||||
declarations.push(...symbol.getDeclarations());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const resolvedDeclarations: Declaration[] = [];
|
||||
declarations.forEach((decl) => {
|
||||
const resolvedDecl = evaluator.resolveAliasDeclaration(decl, resolveLocalName);
|
||||
@ -427,7 +454,7 @@ export class DocumentSymbolCollector extends ParseTreeWalker {
|
||||
useCase === DocumentSymbolCollectorUseCase.Rename
|
||||
? DeclarationUseCase.Rename
|
||||
: DeclarationUseCase.References;
|
||||
const extras = e.declarationProviderExtension?.tryGetDeclarations(node, declUseCase, token);
|
||||
const extras = e.declarationProviderExtension?.tryGetDeclarations(evaluator, node, declUseCase, token);
|
||||
if (extras && extras.length > 0) {
|
||||
result.push(...extras);
|
||||
}
|
||||
|
@ -43,6 +43,7 @@ import { SignatureDisplayType } from '../common/configOptions';
|
||||
import { assertNever, fail } from '../common/debug';
|
||||
import { DeclarationUseCase, Extensions } from '../common/extensibility';
|
||||
import { convertOffsetToPosition, convertPositionToOffset } from '../common/positionUtils';
|
||||
import { hashString } from '../common/stringUtils';
|
||||
import { Position, Range } from '../common/textRange';
|
||||
import { TextRange } from '../common/textRange';
|
||||
import { ExpressionNode, isExpressionNode, NameNode, ParseNode, ParseNodeType, StringNode } from '../parser/parseNodes';
|
||||
@ -61,6 +62,7 @@ export interface HoverTextPart {
|
||||
|
||||
export interface HoverResults {
|
||||
parts: HoverTextPart[];
|
||||
lastKnownModule?: string;
|
||||
range: Range;
|
||||
}
|
||||
|
||||
@ -100,6 +102,7 @@ export class HoverProvider {
|
||||
.map(
|
||||
(e) =>
|
||||
e.declarationProviderExtension?.tryGetDeclarations(
|
||||
evaluator,
|
||||
node,
|
||||
DeclarationUseCase.Definition,
|
||||
token
|
||||
@ -140,6 +143,10 @@ export class HoverProvider {
|
||||
functionSignatureDisplay,
|
||||
token
|
||||
);
|
||||
|
||||
// Add the lastKnownModule for this declaration. We'll use this
|
||||
// in telemetry for hover.
|
||||
results.lastKnownModule = primaryDeclaration.moduleName;
|
||||
} else if (!node.parent || node.parent.nodeType !== ParseNodeType.ModuleName) {
|
||||
// If we had no declaration, see if we can provide a minimal tooltip. We'll skip
|
||||
// this if it's part of a module name, since a module name part with no declaration
|
||||
@ -631,12 +638,16 @@ export class HoverProvider {
|
||||
}
|
||||
}
|
||||
|
||||
export function convertHoverResults(format: MarkupKind, hoverResults: HoverResults | undefined): Hover | undefined {
|
||||
export function convertHoverResults(
|
||||
format: MarkupKind,
|
||||
hoverResults: HoverResults | undefined,
|
||||
includeHash?: boolean
|
||||
): Hover | undefined {
|
||||
if (!hoverResults) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const markupString = hoverResults.parts
|
||||
let markupString = hoverResults.parts
|
||||
.map((part) => {
|
||||
if (part.python) {
|
||||
if (format === MarkupKind.Markdown) {
|
||||
@ -652,6 +663,12 @@ export function convertHoverResults(format: MarkupKind, hoverResults: HoverResul
|
||||
.join('')
|
||||
.trimEnd();
|
||||
|
||||
// If we have a lastKnownModule in the hover results, stick in a comment with
|
||||
// the hashed module name. This is used by the other side to send telemetry.
|
||||
if (hoverResults.lastKnownModule && format === MarkupKind.Markdown && includeHash) {
|
||||
markupString += `<!--moduleHash:${hashString(hoverResults.lastKnownModule)}-->`;
|
||||
}
|
||||
|
||||
return {
|
||||
contents: {
|
||||
kind: format,
|
||||
|
@ -13,6 +13,7 @@ import { CancellationToken } from 'vscode-languageserver';
|
||||
import { Declaration, DeclarationType, isAliasDeclaration } from '../analyzer/declaration';
|
||||
import { getNameFromDeclaration } from '../analyzer/declarationUtils';
|
||||
import * as ParseTreeUtils from '../analyzer/parseTreeUtils';
|
||||
import { SourceFile } from '../analyzer/sourceFile';
|
||||
import { SourceMapper } from '../analyzer/sourceMapper';
|
||||
import { Symbol } from '../analyzer/symbol';
|
||||
import { isVisibleExternally } from '../analyzer/symbolUtils';
|
||||
@ -144,7 +145,8 @@ export class ReferencesProvider {
|
||||
evaluator: TypeEvaluator,
|
||||
reporter: ReferenceCallback | undefined,
|
||||
useCase: DocumentSymbolCollectorUseCase,
|
||||
token: CancellationToken
|
||||
token: CancellationToken,
|
||||
implicitlyImportedBy?: SourceFile[]
|
||||
) {
|
||||
throwIfCancellationRequested(token);
|
||||
|
||||
@ -154,7 +156,8 @@ export class ReferencesProvider {
|
||||
/* resolveLocalNames */ false,
|
||||
useCase,
|
||||
token,
|
||||
sourceMapper
|
||||
sourceMapper,
|
||||
implicitlyImportedBy
|
||||
);
|
||||
|
||||
if (declarations.length === 0) {
|
||||
@ -184,7 +187,8 @@ export class ReferencesProvider {
|
||||
evaluator: TypeEvaluator,
|
||||
reporter: ReferenceCallback | undefined,
|
||||
useCase: DocumentSymbolCollectorUseCase,
|
||||
token: CancellationToken
|
||||
token: CancellationToken,
|
||||
implicitlyImportedBy?: SourceFile[]
|
||||
): ReferencesResult | undefined {
|
||||
throwIfCancellationRequested(token);
|
||||
|
||||
@ -203,7 +207,16 @@ export class ReferencesProvider {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return this.getDeclarationForNode(sourceMapper, filePath, node, evaluator, reporter, useCase, token);
|
||||
return this.getDeclarationForNode(
|
||||
sourceMapper,
|
||||
filePath,
|
||||
node,
|
||||
evaluator,
|
||||
reporter,
|
||||
useCase,
|
||||
token,
|
||||
implicitlyImportedBy
|
||||
);
|
||||
}
|
||||
|
||||
static addReferences(
|
||||
|
@ -23,7 +23,7 @@ import { BackgroundAnalysisBase } from './backgroundAnalysisBase';
|
||||
import { CommandController } from './commands/commandController';
|
||||
import { getCancellationFolderName } from './common/cancellationUtils';
|
||||
import { ConfigOptions, SignatureDisplayType } from './common/configOptions';
|
||||
import { ConsoleWithLogLevel, LogLevel } from './common/console';
|
||||
import { ConsoleWithLogLevel, convertLogLevel, LogLevel } from './common/console';
|
||||
import { isDebugMode, isString } from './common/core';
|
||||
import { expandPathVariables } from './common/envVarUtils';
|
||||
import { FileBasedCancellationProvider } from './common/fileBasedCancellationUtils';
|
||||
@ -33,9 +33,9 @@ import { Host } from './common/host';
|
||||
import { resolvePaths } from './common/pathUtils';
|
||||
import { ProgressReporter } from './common/progressReporter';
|
||||
import { createFromRealFileSystem, WorkspaceFileWatcherProvider } from './common/realFileSystem';
|
||||
import { LanguageServerBase, ServerSettings, WorkspaceServiceInstance } from './languageServerBase';
|
||||
import { LanguageServerBase, ServerSettings } from './languageServerBase';
|
||||
import { CodeActionProvider } from './languageService/codeActionProvider';
|
||||
import { WorkspaceMap } from './workspaceMap';
|
||||
import { Workspace } from './workspaceFactory';
|
||||
|
||||
const maxAnalysisTimeInForeground = { openFilesTimeInMs: 50, noOpenFilesTimeInMs: 200 };
|
||||
|
||||
@ -52,7 +52,6 @@ export class PyrightServer extends LanguageServerBase {
|
||||
const rootDirectory = (global as any).__rootDirectory || __dirname;
|
||||
|
||||
const console = new ConsoleWithLogLevel(connection.console);
|
||||
const workspaceMap = new WorkspaceMap();
|
||||
const fileWatcherProvider = new WorkspaceFileWatcherProvider();
|
||||
const fileSystem = createFromRealFileSystem(console, fileWatcherProvider);
|
||||
|
||||
@ -61,7 +60,6 @@ export class PyrightServer extends LanguageServerBase {
|
||||
productName: 'Pyright',
|
||||
rootDirectory,
|
||||
version,
|
||||
workspaceMap,
|
||||
fileSystem,
|
||||
fileWatcherHandler: fileWatcherProvider,
|
||||
cancellationProvider: new FileBasedCancellationProvider('bg'),
|
||||
@ -75,7 +73,7 @@ export class PyrightServer extends LanguageServerBase {
|
||||
this._controller = new CommandController(this);
|
||||
}
|
||||
|
||||
async getSettings(workspace: WorkspaceServiceInstance): Promise<ServerSettings> {
|
||||
async getSettings(workspace: Workspace): Promise<ServerSettings> {
|
||||
const serverSettings: ServerSettings = {
|
||||
watchForSourceChanges: true,
|
||||
watchForLibraryChanges: true,
|
||||
@ -154,7 +152,7 @@ export class PyrightServer extends LanguageServerBase {
|
||||
serverSettings.useLibraryCodeForTypes = !!pythonAnalysisSection.useLibraryCodeForTypes;
|
||||
}
|
||||
|
||||
serverSettings.logLevel = this.convertLogLevel(pythonAnalysisSection.logLevel);
|
||||
serverSettings.logLevel = convertLogLevel(pythonAnalysisSection.logLevel);
|
||||
serverSettings.autoSearchPaths = !!pythonAnalysisSection.autoSearchPaths;
|
||||
|
||||
const extraPaths = pythonAnalysisSection.extraPaths;
|
||||
|
84
packages/pyright-internal/src/tests/cacheManager.test.ts
Normal file
84
packages/pyright-internal/src/tests/cacheManager.test.ts
Normal file
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* cacheManager.test.ts
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
* Licensed under the MIT license.
|
||||
*
|
||||
* Unit tests for cache manager
|
||||
*/
|
||||
|
||||
import assert from 'assert';
|
||||
|
||||
import { CacheManager, CacheOwner } from '../analyzer/cacheManager';
|
||||
|
||||
test('basic', () => {
|
||||
const manager = new CacheManager();
|
||||
const mock = new MockCacheOwner(10);
|
||||
|
||||
manager.registerCacheOwner(mock);
|
||||
assert.strictEqual(manager.getCacheUsage(), 10);
|
||||
|
||||
manager.unregisterCacheOwner(mock);
|
||||
assert.strictEqual(manager.getCacheUsage(), 0);
|
||||
});
|
||||
|
||||
test('nested stopTracking', () => {
|
||||
const manager = new CacheManager();
|
||||
const mock = new MockCacheOwner(10);
|
||||
|
||||
manager.registerCacheOwner(mock);
|
||||
assert.strictEqual(manager.getCacheUsage(), 10);
|
||||
|
||||
const handle1 = manager.pauseTracking();
|
||||
assert.strictEqual(manager.getCacheUsage(), -1);
|
||||
|
||||
// nested
|
||||
const handle2 = manager.pauseTracking();
|
||||
assert.strictEqual(manager.getCacheUsage(), -1);
|
||||
|
||||
handle2.dispose();
|
||||
assert.strictEqual(manager.getCacheUsage(), -1);
|
||||
|
||||
handle1.dispose();
|
||||
assert.strictEqual(manager.getCacheUsage(), 10);
|
||||
|
||||
manager.unregisterCacheOwner(mock);
|
||||
assert.strictEqual(manager.getCacheUsage(), 0);
|
||||
});
|
||||
|
||||
test('multiple owners', () => {
|
||||
const manager = new CacheManager();
|
||||
const mock1 = new MockCacheOwner(10);
|
||||
const mock2 = new MockCacheOwner(20);
|
||||
|
||||
manager.registerCacheOwner(mock1);
|
||||
assert.strictEqual(manager.getCacheUsage(), 10);
|
||||
|
||||
manager.registerCacheOwner(mock2);
|
||||
assert.strictEqual(manager.getCacheUsage(), 30);
|
||||
|
||||
const handle = manager.pauseTracking();
|
||||
assert.strictEqual(manager.getCacheUsage(), -1);
|
||||
|
||||
manager.unregisterCacheOwner(mock1);
|
||||
assert.strictEqual(manager.getCacheUsage(), -1);
|
||||
|
||||
handle.dispose();
|
||||
assert.strictEqual(manager.getCacheUsage(), 20);
|
||||
|
||||
manager.unregisterCacheOwner(mock2);
|
||||
assert.strictEqual(manager.getCacheUsage(), 0);
|
||||
});
|
||||
|
||||
class MockCacheOwner implements CacheOwner {
|
||||
constructor(private _used: number) {
|
||||
// empty
|
||||
}
|
||||
|
||||
getCacheUsage(): number {
|
||||
return this._used;
|
||||
}
|
||||
|
||||
emptyCache(): void {
|
||||
this._used = 0;
|
||||
}
|
||||
}
|
@ -812,10 +812,10 @@ test('completion quote trigger', async () => {
|
||||
triggerCharacter: '"',
|
||||
};
|
||||
|
||||
const result = await state.workspace.serviceInstance.getCompletionsForPosition(
|
||||
const result = await state.workspace.service.getCompletionsForPosition(
|
||||
filePath,
|
||||
position,
|
||||
state.workspace.path,
|
||||
state.workspace.rootPath,
|
||||
options,
|
||||
undefined,
|
||||
CancellationToken.None
|
||||
@ -854,10 +854,10 @@ test('completion quote trigger - middle', async () => {
|
||||
triggerCharacter: "'",
|
||||
};
|
||||
|
||||
const result = await state.workspace.serviceInstance.getCompletionsForPosition(
|
||||
const result = await state.workspace.service.getCompletionsForPosition(
|
||||
filePath,
|
||||
position,
|
||||
state.workspace.path,
|
||||
state.workspace.rootPath,
|
||||
options,
|
||||
undefined,
|
||||
CancellationToken.None
|
||||
|
@ -25,16 +25,14 @@ import * as debug from '../../../common/debug';
|
||||
import { FileSystem } from '../../../common/fileSystem';
|
||||
import { Range } from '../../../common/textRange';
|
||||
import { UriParser } from '../../../common/uriParser';
|
||||
import { LanguageServerInterface, MessageAction, ServerSettings, WindowInterface } from '../../../languageServerBase';
|
||||
import { CodeActionProvider } from '../../../languageService/codeActionProvider';
|
||||
import {
|
||||
createInitStatus,
|
||||
LanguageServerInterface,
|
||||
MessageAction,
|
||||
ServerSettings,
|
||||
WellKnownWorkspaceKinds,
|
||||
WindowInterface,
|
||||
WorkspaceServiceInstance,
|
||||
} from '../../../languageServerBase';
|
||||
import { CodeActionProvider } from '../../../languageService/codeActionProvider';
|
||||
Workspace,
|
||||
WorkspacePythonPathKind,
|
||||
} from '../../../workspaceFactory';
|
||||
import { TestAccessHost } from '../testAccessHost';
|
||||
import { HostSpecificFeatures } from './testState';
|
||||
|
||||
@ -59,12 +57,12 @@ export class TestFeatures implements HostSpecificFeatures {
|
||||
cacheManager
|
||||
);
|
||||
|
||||
runIndexer(workspace: WorkspaceServiceInstance, noStdLib: boolean, options?: string): void {
|
||||
runIndexer(workspace: Workspace, noStdLib: boolean, options?: string): void {
|
||||
/* empty */
|
||||
}
|
||||
|
||||
getCodeActionsForPosition(
|
||||
workspace: WorkspaceServiceInstance,
|
||||
workspace: Workspace,
|
||||
filePath: string,
|
||||
range: Range,
|
||||
token: CancellationToken
|
||||
@ -78,20 +76,21 @@ export class TestFeatures implements HostSpecificFeatures {
|
||||
}
|
||||
|
||||
export class TestLanguageService implements LanguageServerInterface {
|
||||
private readonly _workspace: WorkspaceServiceInstance;
|
||||
private readonly _defaultWorkspace: WorkspaceServiceInstance;
|
||||
private readonly _workspace: Workspace;
|
||||
private readonly _defaultWorkspace: Workspace;
|
||||
private readonly _uriParser: UriParser;
|
||||
|
||||
constructor(workspace: WorkspaceServiceInstance, readonly console: ConsoleInterface, readonly fs: FileSystem) {
|
||||
constructor(workspace: Workspace, readonly console: ConsoleInterface, readonly fs: FileSystem) {
|
||||
this._workspace = workspace;
|
||||
this._uriParser = new UriParser(this.fs);
|
||||
this._defaultWorkspace = {
|
||||
workspaceName: '',
|
||||
rootPath: '',
|
||||
path: '',
|
||||
uri: '',
|
||||
pythonPath: undefined,
|
||||
pythonPathKind: WorkspacePythonPathKind.Mutable,
|
||||
kinds: [WellKnownWorkspaceKinds.Test],
|
||||
serviceInstance: new AnalyzerService('test service', this.fs, {
|
||||
service: new AnalyzerService('test service', this.fs, {
|
||||
console: this.console,
|
||||
hostFactory: () => new TestAccessHost(),
|
||||
importResolverFactory: AnalyzerService.createImportResolver,
|
||||
@ -108,7 +107,7 @@ export class TestLanguageService implements LanguageServerInterface {
|
||||
return this._uriParser.decodeTextDocumentUri(uriString);
|
||||
}
|
||||
|
||||
getWorkspaceForFile(filePath: string): Promise<WorkspaceServiceInstance> {
|
||||
getWorkspaceForFile(filePath: string): Promise<Workspace> {
|
||||
if (filePath.startsWith(this._workspace.rootPath)) {
|
||||
return Promise.resolve(this._workspace);
|
||||
}
|
||||
@ -116,16 +115,16 @@ export class TestLanguageService implements LanguageServerInterface {
|
||||
return Promise.resolve(this._defaultWorkspace);
|
||||
}
|
||||
|
||||
getSettings(workspace: WorkspaceServiceInstance): Promise<ServerSettings> {
|
||||
getSettings(_workspace: Workspace): Promise<ServerSettings> {
|
||||
const settings: ServerSettings = {
|
||||
venvPath: this._workspace.serviceInstance.getConfigOptions().venvPath,
|
||||
pythonPath: this._workspace.serviceInstance.getConfigOptions().pythonPath,
|
||||
typeshedPath: this._workspace.serviceInstance.getConfigOptions().typeshedPath,
|
||||
openFilesOnly: this._workspace.serviceInstance.getConfigOptions().checkOnlyOpenFiles,
|
||||
useLibraryCodeForTypes: this._workspace.serviceInstance.getConfigOptions().useLibraryCodeForTypes,
|
||||
venvPath: this._workspace.service.getConfigOptions().venvPath,
|
||||
pythonPath: this._workspace.service.getConfigOptions().pythonPath,
|
||||
typeshedPath: this._workspace.service.getConfigOptions().typeshedPath,
|
||||
openFilesOnly: this._workspace.service.getConfigOptions().checkOnlyOpenFiles,
|
||||
useLibraryCodeForTypes: this._workspace.service.getConfigOptions().useLibraryCodeForTypes,
|
||||
disableLanguageServices: this._workspace.disableLanguageServices,
|
||||
autoImportCompletions: this._workspace.serviceInstance.getConfigOptions().autoImportCompletions,
|
||||
functionSignatureDisplay: this._workspace.serviceInstance.getConfigOptions().functionSignatureDisplay,
|
||||
autoImportCompletions: this._workspace.service.getConfigOptions().autoImportCompletions,
|
||||
functionSignatureDisplay: this._workspace.service.getConfigOptions().functionSignatureDisplay,
|
||||
};
|
||||
|
||||
return Promise.resolve(settings);
|
||||
|
@ -50,12 +50,7 @@ import {
|
||||
import { convertOffsetToPosition, convertPositionToOffset } from '../../../common/positionUtils';
|
||||
import { DocumentRange, Position, Range as PositionRange, rangesAreEqual, TextRange } from '../../../common/textRange';
|
||||
import { TextRangeCollection } from '../../../common/textRangeCollection';
|
||||
import {
|
||||
createInitStatus,
|
||||
LanguageServerInterface,
|
||||
WellKnownWorkspaceKinds,
|
||||
WorkspaceServiceInstance,
|
||||
} from '../../../languageServerBase';
|
||||
import { LanguageServerInterface } from '../../../languageServerBase';
|
||||
import { AbbreviationInfo, ImportFormat } from '../../../languageService/autoImporter';
|
||||
import { CompletionOptions } from '../../../languageService/completionProvider';
|
||||
import { DefinitionFilter } from '../../../languageService/definitionProvider';
|
||||
@ -64,6 +59,12 @@ import { ParseNode } from '../../../parser/parseNodes';
|
||||
import { ParseResults } from '../../../parser/parser';
|
||||
import { Tokenizer } from '../../../parser/tokenizer';
|
||||
import { PyrightFileSystem } from '../../../pyrightFileSystem';
|
||||
import {
|
||||
createInitStatus,
|
||||
WellKnownWorkspaceKinds,
|
||||
Workspace,
|
||||
WorkspacePythonPathKind,
|
||||
} from '../../../workspaceFactory';
|
||||
import { TestAccessHost } from '../testAccessHost';
|
||||
import * as host from '../testHost';
|
||||
import { stringify } from '../utils';
|
||||
@ -93,9 +94,9 @@ export interface HostSpecificFeatures {
|
||||
importResolverFactory: ImportResolverFactory;
|
||||
backgroundAnalysisProgramFactory: BackgroundAnalysisProgramFactory;
|
||||
|
||||
runIndexer(workspace: WorkspaceServiceInstance, noStdLib: boolean, options?: string): void;
|
||||
runIndexer(workspace: Workspace, noStdLib: boolean, options?: string): void;
|
||||
getCodeActionsForPosition(
|
||||
workspace: WorkspaceServiceInstance,
|
||||
workspace: Workspace,
|
||||
filePath: string,
|
||||
range: PositionRange,
|
||||
token: CancellationToken
|
||||
@ -113,7 +114,7 @@ export class TestState {
|
||||
|
||||
readonly testFS: vfs.TestFileSystem;
|
||||
readonly fs: PyrightFileSystem;
|
||||
readonly workspace: WorkspaceServiceInstance;
|
||||
readonly workspace: Workspace;
|
||||
readonly console: ConsoleInterface;
|
||||
readonly rawConfigJson: any | undefined;
|
||||
|
||||
@ -170,10 +171,11 @@ export class TestState {
|
||||
this.workspace = {
|
||||
workspaceName: 'test workspace',
|
||||
rootPath: vfsInfo.projectRoot,
|
||||
path: vfsInfo.projectRoot,
|
||||
pythonPath: undefined,
|
||||
pythonPathKind: WorkspacePythonPathKind.Mutable,
|
||||
uri: convertPathToUri(this.fs, vfsInfo.projectRoot),
|
||||
kinds: [WellKnownWorkspaceKinds.Test],
|
||||
serviceInstance: service,
|
||||
service: service,
|
||||
disableLanguageServices: false,
|
||||
disableOrganizeImports: false,
|
||||
disableWorkspaceSymbol: false,
|
||||
@ -203,19 +205,19 @@ export class TestState {
|
||||
}
|
||||
|
||||
get importResolver(): ImportResolver {
|
||||
return this.workspace.serviceInstance.getImportResolver();
|
||||
return this.workspace.service.getImportResolver();
|
||||
}
|
||||
|
||||
get configOptions(): ConfigOptions {
|
||||
return this.workspace.serviceInstance.getConfigOptions();
|
||||
return this.workspace.service.getConfigOptions();
|
||||
}
|
||||
|
||||
get program(): Program {
|
||||
return this.workspace.serviceInstance.test_program;
|
||||
return this.workspace.service.test_program;
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this.workspace.serviceInstance.dispose();
|
||||
this.workspace.service.dispose();
|
||||
}
|
||||
|
||||
cwd() {
|
||||
@ -650,7 +652,7 @@ export class TestState {
|
||||
verifyCodeActionCount?: boolean
|
||||
): Promise<any> {
|
||||
// make sure we don't use cache built from other tests
|
||||
this.workspace.serviceInstance.invalidateAndForceReanalysis();
|
||||
this.workspace.service.invalidateAndForceReanalysis();
|
||||
this.analyze();
|
||||
|
||||
for (const range of this.getRanges()) {
|
||||
@ -932,10 +934,10 @@ export class TestState {
|
||||
includeUserSymbolsInAutoImport: false,
|
||||
};
|
||||
const nameMap = abbrMap ? new Map<string, AbbreviationInfo>(Object.entries(abbrMap)) : undefined;
|
||||
const result = await this.workspace.serviceInstance.getCompletionsForPosition(
|
||||
const result = await this.workspace.service.getCompletionsForPosition(
|
||||
filePath,
|
||||
completionPosition,
|
||||
this.workspace.path,
|
||||
this.workspace.rootPath,
|
||||
options,
|
||||
nameMap,
|
||||
CancellationToken.None
|
||||
@ -979,7 +981,7 @@ export class TestState {
|
||||
|
||||
if (expected.additionalTextEdits !== undefined) {
|
||||
if (actual.additionalTextEdits === undefined) {
|
||||
this.workspace.serviceInstance.resolveCompletionItem(
|
||||
this.workspace.service.resolveCompletionItem(
|
||||
filePath,
|
||||
actual,
|
||||
options,
|
||||
@ -993,7 +995,7 @@ export class TestState {
|
||||
|
||||
if (expected.documentation !== undefined) {
|
||||
if (actual.documentation === undefined && actual.data) {
|
||||
this.workspace.serviceInstance.resolveCompletionItem(
|
||||
this.workspace.service.resolveCompletionItem(
|
||||
filePath,
|
||||
actual,
|
||||
options,
|
||||
|
@ -180,7 +180,7 @@ test('symbol must be from user files', () => {
|
||||
`;
|
||||
|
||||
const state = parseAndGetTestState(code).state;
|
||||
while (state.workspace.serviceInstance.test_program.analyze());
|
||||
while (state.workspace.service.test_program.analyze());
|
||||
|
||||
const actions = state.program.moveSymbolAtPosition(
|
||||
state.getMarkerByName('marker').fileName,
|
||||
|
@ -70,10 +70,10 @@ test('excluded but still part of program', () => {
|
||||
const state = parseAndGetTestState(code, '/projectRoot').state;
|
||||
const marker = state.getMarkerByName('marker');
|
||||
|
||||
while (state.workspace.serviceInstance.test_program.analyze());
|
||||
while (state.workspace.service.test_program.analyze());
|
||||
|
||||
assert.strictEqual(
|
||||
state.workspace.serviceInstance.test_shouldHandleSourceFileWatchChanges(marker.fileName, /* isFile */ true),
|
||||
state.workspace.service.test_shouldHandleSourceFileWatchChanges(marker.fileName, /* isFile */ true),
|
||||
true
|
||||
);
|
||||
});
|
||||
@ -87,7 +87,7 @@ test('random folder changed', () => {
|
||||
const state = parseAndGetTestState(code, '/projectRoot').state;
|
||||
|
||||
assert.strictEqual(
|
||||
state.workspace.serviceInstance.test_shouldHandleSourceFileWatchChanges('/randomFolder', /* isFile */ false),
|
||||
state.workspace.service.test_shouldHandleSourceFileWatchChanges('/randomFolder', /* isFile */ false),
|
||||
false
|
||||
);
|
||||
});
|
||||
@ -185,5 +185,5 @@ function testSourceFileWatchChange(code: string, expected = true, isFile = true)
|
||||
const marker = state.getMarkerByName('marker');
|
||||
const path = isFile ? marker.fileName : getDirectoryPath(marker.fileName);
|
||||
|
||||
assert.strictEqual(state.workspace.serviceInstance.test_shouldHandleSourceFileWatchChanges(path, isFile), expected);
|
||||
assert.strictEqual(state.workspace.service.test_shouldHandleSourceFileWatchChanges(path, isFile), expected);
|
||||
}
|
||||
|
@ -76,10 +76,10 @@ function checkSignatureHelp(code: string, expects: boolean) {
|
||||
const state = parseAndGetTestState(code).state;
|
||||
const marker = state.getMarkerByName('marker');
|
||||
|
||||
const parseResults = state.workspace.serviceInstance.getParseResult(marker.fileName)!;
|
||||
const parseResults = state.workspace.service.getParseResult(marker.fileName)!;
|
||||
const position = convertOffsetToPosition(marker.position, parseResults.tokenizerOutput.lines);
|
||||
|
||||
const actual = state.workspace.serviceInstance.getSignatureHelpForPosition(
|
||||
const actual = state.workspace.service.getSignatureHelpForPosition(
|
||||
marker.fileName,
|
||||
position,
|
||||
MarkupKind.Markdown,
|
||||
|
@ -36,13 +36,10 @@ test('Empty Open file', () => {
|
||||
const marker = state.getMarkerByName('marker');
|
||||
|
||||
assert.strictEqual(
|
||||
state.workspace.serviceInstance.test_program.getSourceFile(marker.fileName)?.getFileContent(),
|
||||
state.workspace.service.test_program.getSourceFile(marker.fileName)?.getFileContent(),
|
||||
'# Content'
|
||||
);
|
||||
|
||||
state.workspace.serviceInstance.updateOpenFileContents(marker.fileName, 1, [{ text: '' }]);
|
||||
assert.strictEqual(
|
||||
state.workspace.serviceInstance.test_program.getSourceFile(marker.fileName)?.getFileContent(),
|
||||
''
|
||||
);
|
||||
state.workspace.service.updateOpenFileContents(marker.fileName, 1, [{ text: '' }]);
|
||||
assert.strictEqual(state.workspace.service.test_program.getSourceFile(marker.fileName)?.getFileContent(), '');
|
||||
});
|
||||
|
@ -171,7 +171,7 @@ test('test generateWorkspaceEdits', async () => {
|
||||
|
||||
assert.strictEqual(fileChanged.size, 2);
|
||||
|
||||
const actualEdits = generateWorkspaceEdit(state.workspace.serviceInstance, cloned, fileChanged);
|
||||
const actualEdits = generateWorkspaceEdit(state.workspace.service, cloned, fileChanged);
|
||||
verifyWorkspaceEdit(
|
||||
{
|
||||
changes: {
|
||||
@ -195,7 +195,7 @@ test('test generateWorkspaceEdits', async () => {
|
||||
|
||||
async function getClonedService(state: TestState) {
|
||||
return await AnalyzerServiceExecutor.cloneService(
|
||||
new TestLanguageService(state.workspace, state.console, state.workspace.serviceInstance.fs),
|
||||
new TestLanguageService(state.workspace, state.console, state.workspace.service.fs),
|
||||
state.workspace,
|
||||
{ useBackgroundAnalysis: false }
|
||||
);
|
||||
|
542
packages/pyright-internal/src/workspaceFactory.ts
Normal file
542
packages/pyright-internal/src/workspaceFactory.ts
Normal file
@ -0,0 +1,542 @@
|
||||
/*
|
||||
* workspaceFactory.ts
|
||||
*
|
||||
* Workspace management related functionality.
|
||||
*/
|
||||
|
||||
import { InitializeParams, WorkspaceFoldersChangeEvent } from 'vscode-languageserver';
|
||||
|
||||
import { AnalyzerService } from './analyzer/service';
|
||||
import { ConsoleInterface } from './common/console';
|
||||
import { createDeferred } from './common/deferred';
|
||||
import { UriParser } from './common/uriParser';
|
||||
|
||||
let WorkspaceFactoryIdCounter = 0;
|
||||
|
||||
export enum WellKnownWorkspaceKinds {
|
||||
Default = 'default',
|
||||
Regular = 'regular',
|
||||
Limited = 'limited',
|
||||
Cloned = 'cloned',
|
||||
Test = 'test',
|
||||
}
|
||||
|
||||
export enum WorkspacePythonPathKind {
|
||||
Immutable = 'immutable',
|
||||
Mutable = 'mutable',
|
||||
}
|
||||
|
||||
export interface InitStatus {
|
||||
resolve(): void;
|
||||
reset(): InitStatus;
|
||||
markCalled(): void;
|
||||
promise: Promise<void>;
|
||||
resolved(): boolean;
|
||||
}
|
||||
|
||||
export function createInitStatus(): InitStatus {
|
||||
// Due to the way we get `python path`, `include/exclude` from settings to initialize workspace,
|
||||
// we need to wait for getSettings to finish before letting IDE features to use workspace (`isInitialized` field).
|
||||
// So most of cases, whenever we create new workspace, we send request to workspace/configuration right way
|
||||
// except one place which is `initialize` LSP call.
|
||||
// In `initialize` method where we create `initial workspace`, we can't do that since LSP spec doesn't allow
|
||||
// LSP server from sending any request to client until `initialized` method is called.
|
||||
// This flag indicates whether we had our initial updateSetting call or not after `initialized` call.
|
||||
let called = false;
|
||||
|
||||
const deferred = createDeferred<void>();
|
||||
const self = {
|
||||
promise: deferred.promise,
|
||||
resolve: () => {
|
||||
called = true;
|
||||
deferred.resolve();
|
||||
},
|
||||
markCalled: () => {
|
||||
called = true;
|
||||
},
|
||||
reset: () => {
|
||||
if (!called) {
|
||||
return self;
|
||||
}
|
||||
|
||||
return createInitStatus();
|
||||
},
|
||||
resolved: () => {
|
||||
return deferred.resolved;
|
||||
},
|
||||
};
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
// path and uri will point to a workspace itself. It could be a folder
|
||||
// if the workspace represents a folder. it could be '' if it is the default workspace.
|
||||
// But it also could be a file if it is a virtual workspace.
|
||||
// rootPath will always point to the folder that contains the workspace.
|
||||
export interface Workspace {
|
||||
workspaceName: string;
|
||||
rootPath: string;
|
||||
uri: string;
|
||||
kinds: string[];
|
||||
service: AnalyzerService;
|
||||
disableLanguageServices: boolean;
|
||||
disableOrganizeImports: boolean;
|
||||
disableWorkspaceSymbol: boolean;
|
||||
isInitialized: InitStatus;
|
||||
searchPathsToWatch: string[];
|
||||
pythonPath: string | undefined;
|
||||
pythonPathKind: WorkspacePythonPathKind;
|
||||
}
|
||||
|
||||
export class WorkspaceFactory {
|
||||
private _defaultWorkspacePath = '<default>';
|
||||
private _map = new Map<string, Workspace>();
|
||||
private _id = WorkspaceFactoryIdCounter++;
|
||||
|
||||
constructor(
|
||||
private readonly _console: ConsoleInterface,
|
||||
private readonly _uriParser: UriParser,
|
||||
private readonly _createService: (
|
||||
name: string,
|
||||
rootPath: string,
|
||||
uri: string,
|
||||
kinds: string[]
|
||||
) => AnalyzerService,
|
||||
private readonly _isPythonPathImmutable: (path: string) => boolean,
|
||||
private readonly _onWorkspaceCreated: (workspace: Workspace) => void
|
||||
) {
|
||||
this._console.log(`WorkspaceFactory ${this._id} created`);
|
||||
}
|
||||
|
||||
handleInitialize(params: InitializeParams) {
|
||||
// Create a service instance for each of the workspace folders.
|
||||
if (params.workspaceFolders) {
|
||||
params.workspaceFolders.forEach((folder) => {
|
||||
const path = this._uriParser.decodeTextDocumentUri(folder.uri);
|
||||
this._add(folder.uri, path, folder.name, undefined, WorkspacePythonPathKind.Mutable, [
|
||||
WellKnownWorkspaceKinds.Regular,
|
||||
]);
|
||||
});
|
||||
} else if (params.rootPath) {
|
||||
this._add(params.rootPath, params.rootPath, params.rootPath, undefined, WorkspacePythonPathKind.Mutable, [
|
||||
WellKnownWorkspaceKinds.Regular,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
handleWorkspaceFoldersChanged(params: WorkspaceFoldersChangeEvent) {
|
||||
params.removed.forEach((workspaceInfo) => {
|
||||
const rootPath = this._uriParser.decodeTextDocumentUri(workspaceInfo.uri);
|
||||
// Delete all workspaces for this folder. Even the ones generated for notebook kernels.
|
||||
const workspaces = this.getNonDefaultWorkspaces().filter((w) => w.rootPath === rootPath);
|
||||
workspaces.forEach((w) => {
|
||||
this._remove(w);
|
||||
});
|
||||
});
|
||||
|
||||
params.added.forEach((workspaceInfo) => {
|
||||
const rootPath = this._uriParser.decodeTextDocumentUri(workspaceInfo.uri);
|
||||
this._add(workspaceInfo.uri, rootPath, workspaceInfo.name, undefined, WorkspacePythonPathKind.Mutable, [
|
||||
WellKnownWorkspaceKinds.Regular,
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
items() {
|
||||
return [...this._map.values()];
|
||||
}
|
||||
|
||||
applyPythonPath(workspace: Workspace, newPythonPath: string | undefined): string | undefined {
|
||||
// See if were allowed to apply the new python path
|
||||
if (workspace.pythonPathKind === WorkspacePythonPathKind.Mutable && newPythonPath) {
|
||||
const originalPythonPath = workspace.pythonPath;
|
||||
workspace.pythonPath = newPythonPath;
|
||||
|
||||
// This may not be the workspace in our map. Update the workspace in the map too.
|
||||
// This can happen during startup were the Initialize creates a workspace and then
|
||||
// onDidChangeConfiguration is called right away.
|
||||
const key = this._getWorkspaceKey(workspace);
|
||||
const workspaceInMap = this._map.get(key);
|
||||
if (workspaceInMap) {
|
||||
workspaceInMap.pythonPath = newPythonPath;
|
||||
}
|
||||
|
||||
// If the python path has changed, we may need to move the immutable files to the correct workspace.
|
||||
if (originalPythonPath && originalPythonPath !== newPythonPath && workspaceInMap) {
|
||||
// Potentially move immutable files from one workspace to another.
|
||||
this._moveImmutableFilesToCorrectWorkspace(originalPythonPath, workspaceInMap);
|
||||
}
|
||||
}
|
||||
|
||||
// Return the python path that should be used (whether hardcoded or configured)
|
||||
return workspace.pythonPath;
|
||||
}
|
||||
|
||||
clear() {
|
||||
this._map.forEach((workspace) => {
|
||||
workspace.isInitialized.resolve();
|
||||
workspace.service.dispose();
|
||||
});
|
||||
this._map.clear();
|
||||
this._console.log(`WorkspaceFactory ${this._id} clear`);
|
||||
}
|
||||
|
||||
hasMultipleWorkspaces(kind?: string) {
|
||||
if (this._map.size === 0 || this._map.size === 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let count = 0;
|
||||
for (const kv of this._map) {
|
||||
if (!kind || kv[1].kinds.some((k) => k === kind)) {
|
||||
count++;
|
||||
}
|
||||
|
||||
if (count > 1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
getContainingWorkspace(filePath: string, pythonPath?: string) {
|
||||
return this._getBestRegularWorkspace(
|
||||
this.getNonDefaultWorkspaces(WellKnownWorkspaceKinds.Regular).filter((w) =>
|
||||
filePath.startsWith(w.rootPath)
|
||||
),
|
||||
pythonPath
|
||||
);
|
||||
}
|
||||
|
||||
moveFiles(filePaths: string[], fromWorkspace: Workspace, toWorkspace: Workspace) {
|
||||
if (fromWorkspace === toWorkspace) {
|
||||
return;
|
||||
}
|
||||
|
||||
filePaths.forEach((f) => {
|
||||
const fileInfo = fromWorkspace.service.backgroundAnalysisProgram.program.getSourceFileInfo(f);
|
||||
if (fileInfo) {
|
||||
toWorkspace.service.setFileOpened(
|
||||
f,
|
||||
fileInfo.sourceFile.getClientVersion() || null,
|
||||
fileInfo.sourceFile.getFileContent() || '',
|
||||
fileInfo.sourceFile.getIPythonMode(),
|
||||
fileInfo.chainedSourceFile ? fileInfo.chainedSourceFile.sourceFile.getFilePath() : undefined,
|
||||
fileInfo.sourceFile.getRealFilePath()
|
||||
);
|
||||
fromWorkspace.service.setFileClosed(f, fileInfo.isTracked);
|
||||
}
|
||||
});
|
||||
|
||||
// If the fromWorkspace has no more files in it (and it's an immutable pythonPath), then remove it.
|
||||
this.removeUnused(fromWorkspace);
|
||||
}
|
||||
|
||||
getNonDefaultWorkspaces(kind?: string): Workspace[] {
|
||||
const workspaces: Workspace[] = [];
|
||||
this._map.forEach((workspace) => {
|
||||
if (!workspace.rootPath) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (kind && !workspace.kinds.some((k) => k === kind)) {
|
||||
return;
|
||||
}
|
||||
|
||||
workspaces.push(workspace);
|
||||
});
|
||||
|
||||
return workspaces;
|
||||
}
|
||||
|
||||
// Returns the best workspace for a file. Waits for the workspace to be finished handling other events before
|
||||
// returning the appropriate workspace.
|
||||
async getWorkspaceForFile(filePath: string, pythonPath: string | undefined): Promise<Workspace> {
|
||||
// Wait for all workspaces to be initialized before attempting to find the best workspace. Otherwise
|
||||
// the list of files won't be complete and the `contains` check might fail.
|
||||
await Promise.all([...this._map.values()].map((w) => w.isInitialized.promise));
|
||||
|
||||
// Find or create best match.
|
||||
const workspace = await this._getOrCreateBestWorkspaceForFile(filePath, pythonPath);
|
||||
|
||||
// The workspace may have just been created. Wait for it to be initialized before returning it.
|
||||
await workspace.isInitialized.promise;
|
||||
|
||||
return workspace;
|
||||
}
|
||||
|
||||
removeUnused(workspace: Workspace) {
|
||||
// Only remove this workspace is it's not being used and it's a hardcoded path kind.
|
||||
if (
|
||||
workspace.service.getOpenFiles().length === 0 &&
|
||||
workspace.pythonPathKind === WorkspacePythonPathKind.Immutable
|
||||
) {
|
||||
// Destroy the workspace since it only had immutable files in it.
|
||||
this._remove(workspace);
|
||||
}
|
||||
}
|
||||
|
||||
private async _moveImmutableFilesToCorrectWorkspace(oldPythonPath: string, mutableWorkspace: Workspace) {
|
||||
// If the python path changes we may need to move some immutable files around.
|
||||
// For example, if a notebook had the old python path, we need to create a new workspace
|
||||
// for the notebook.
|
||||
// If a notebook has the new python path but is currently in a workspace with the path hardcoded, we need to move it to
|
||||
// this workspace.
|
||||
const oldPathFiles = new Set<string>(
|
||||
mutableWorkspace.service.getOpenFiles().filter((f) => this._isPythonPathImmutable(f))
|
||||
);
|
||||
const exitingWorkspaceWithSamePath = this.items().find(
|
||||
(w) => w.pythonPath === mutableWorkspace.pythonPath && w !== mutableWorkspace
|
||||
);
|
||||
const newPathFiles = new Set<string>(
|
||||
exitingWorkspaceWithSamePath?.service.getOpenFiles().filter((f) => this._isPythonPathImmutable(f))
|
||||
);
|
||||
|
||||
// Immutable files that were in this mutableWorkspace have to be moved
|
||||
// to a (potentially) new workspace (with the old path).
|
||||
for (const file of oldPathFiles) {
|
||||
const workspace = this._getOrCreateBestWorkspaceFileSync(file, oldPythonPath);
|
||||
if (workspace !== mutableWorkspace) {
|
||||
this.moveFiles([file], mutableWorkspace, workspace);
|
||||
}
|
||||
}
|
||||
|
||||
// Immutable files from a different workspace (with the same path as the new path)
|
||||
// have to be moved to the mutable workspace (which now has the new path)
|
||||
if (exitingWorkspaceWithSamePath) {
|
||||
this.moveFiles([...newPathFiles], exitingWorkspaceWithSamePath!, mutableWorkspace);
|
||||
this.removeUnused(exitingWorkspaceWithSamePath);
|
||||
}
|
||||
}
|
||||
|
||||
private _add(
|
||||
rootUri: string,
|
||||
rootPath: string,
|
||||
name: string,
|
||||
pythonPath: string | undefined,
|
||||
pythonPathKind: WorkspacePythonPathKind,
|
||||
kinds: string[]
|
||||
) {
|
||||
// Update the kind based of the uri is local or not
|
||||
if (!this._uriParser.isLocal(rootUri)) {
|
||||
// Web based workspace should be limited.
|
||||
kinds = [...kinds, WellKnownWorkspaceKinds.Limited];
|
||||
}
|
||||
|
||||
const result: Workspace = {
|
||||
workspaceName: name,
|
||||
rootPath,
|
||||
uri: rootUri,
|
||||
kinds,
|
||||
pythonPath,
|
||||
pythonPathKind,
|
||||
service: this._createService(name, rootPath, rootUri, kinds),
|
||||
disableLanguageServices: false,
|
||||
disableOrganizeImports: false,
|
||||
disableWorkspaceSymbol: false,
|
||||
isInitialized: createInitStatus(),
|
||||
searchPathsToWatch: [],
|
||||
};
|
||||
|
||||
// Tell our owner we added something
|
||||
this._onWorkspaceCreated(result);
|
||||
|
||||
// Stick in our map
|
||||
const key = this._getWorkspaceKey(result);
|
||||
|
||||
// Make sure to delete existing workspaces if there are any.
|
||||
this._remove(result);
|
||||
this._console.log(`WorkspaceFactory ${this._id} add ${key}`);
|
||||
this._map.set(key, result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private _remove(value: Workspace) {
|
||||
const key = this._getWorkspaceKey(value);
|
||||
const workspace = this._map.get(key);
|
||||
if (workspace) {
|
||||
workspace.isInitialized.resolve();
|
||||
workspace.service.dispose();
|
||||
this._console.log(`WorkspaceFactory ${this._id} remove ${key}`);
|
||||
this._map.delete(key);
|
||||
}
|
||||
}
|
||||
|
||||
private _getDefaultWorskpaceKey(pythonPath: string | undefined) {
|
||||
return `${this._defaultWorkspacePath}:${pythonPath ? pythonPath : WorkspacePythonPathKind.Mutable}`;
|
||||
}
|
||||
|
||||
private _getWorkspaceKey(value: Workspace) {
|
||||
// Special the root path for the default workspace. It will be created
|
||||
// without a root path
|
||||
const rootPath = value.kinds.includes(WellKnownWorkspaceKinds.Default)
|
||||
? this._defaultWorkspacePath
|
||||
: value.rootPath;
|
||||
|
||||
// Key is defined by the rootPath and the pythonPath. We might include platform in this, but for now
|
||||
// platform is only used by the import resolver.
|
||||
return `${rootPath}:${
|
||||
value.pythonPathKind === WorkspacePythonPathKind.Mutable ? value.pythonPathKind : value.pythonPath
|
||||
}`;
|
||||
}
|
||||
|
||||
private async _getOrCreateBestWorkspaceForFile(
|
||||
filePath: string,
|
||||
pythonPath: string | undefined
|
||||
): Promise<Workspace> {
|
||||
// Find the current best workspace (without creating a new one)
|
||||
let bestInstance = this._getBestWorkspaceForFile(filePath, pythonPath);
|
||||
|
||||
// Make sure the best instance is initialized so that it has its pythonPath.
|
||||
await bestInstance.isInitialized.promise;
|
||||
|
||||
// If this best instance doesn't match the pythonPath, then we need to create a new one.
|
||||
if (pythonPath && bestInstance.pythonPath !== pythonPath) {
|
||||
bestInstance = this._add(
|
||||
bestInstance.uri,
|
||||
bestInstance.rootPath,
|
||||
bestInstance.workspaceName,
|
||||
pythonPath,
|
||||
WorkspacePythonPathKind.Immutable, // This means the pythonPath should never change.
|
||||
bestInstance.kinds
|
||||
);
|
||||
}
|
||||
|
||||
return bestInstance;
|
||||
}
|
||||
|
||||
private _getOrCreateBestWorkspaceFileSync(filePath: string, pythonPath: string) {
|
||||
// Find the current best workspace (without creating a new one)
|
||||
let bestInstance = this._getBestWorkspaceForFile(filePath, pythonPath);
|
||||
|
||||
// If this best instance doesn't match the pythonPath, then we need to create a new one.
|
||||
if (bestInstance.pythonPath !== pythonPath) {
|
||||
bestInstance = this._add(
|
||||
bestInstance.uri,
|
||||
bestInstance.rootPath,
|
||||
bestInstance.workspaceName,
|
||||
pythonPath,
|
||||
WorkspacePythonPathKind.Immutable, // This means the pythonPath should never change.
|
||||
bestInstance.kinds
|
||||
);
|
||||
}
|
||||
|
||||
return bestInstance;
|
||||
}
|
||||
|
||||
private _getBestWorkspaceForFile(filePath: string, pythonPath: string | undefined): Workspace {
|
||||
let bestRootPath: string | undefined;
|
||||
let bestInstance: Workspace | undefined;
|
||||
|
||||
// The order of how we find the best matching workspace for the given file is
|
||||
// 1. The given file is the workspace itself (ex, a file being a virtual workspace itself).
|
||||
// 2. The given file matches the fileSpec of the service under the workspace
|
||||
// (the file is a user file the workspace provides LSP service for).
|
||||
// 3. The given file doesn't match anything but we have only 1 regular workspace
|
||||
// (ex, open a library file from the workspace).
|
||||
// 4. The given file doesn't match anything and there are multiple workspaces but one of workspaces
|
||||
// contains the file (ex, open a library file already imported by a workspace).
|
||||
// 5. If none of the above works, then it matches the default workspace.
|
||||
this._map.forEach((workspace) => {
|
||||
if (workspace.rootPath) {
|
||||
if (workspace.rootPath !== filePath && !workspace.service.isTracked(filePath)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Among workspaces that own the file, make sure we return the inner most one which
|
||||
// we consider as the best workspace.
|
||||
if (
|
||||
bestRootPath === undefined ||
|
||||
(workspace.rootPath.startsWith(bestRootPath) && workspace.rootPath !== bestRootPath)
|
||||
) {
|
||||
// Among workspaces with a python path, make sure we return the one that matches the python path
|
||||
if (pythonPath && workspace.pythonPath === pythonPath) {
|
||||
bestRootPath = workspace.rootPath;
|
||||
bestInstance = workspace;
|
||||
} else if (workspace.pythonPathKind === WorkspacePythonPathKind.Mutable && !pythonPath) {
|
||||
// If no python path passed, pick the workspace with the configured python path.
|
||||
bestRootPath = workspace.rootPath;
|
||||
bestInstance = workspace;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// If there were multiple workspaces or we couldn't find any,
|
||||
// use the default one.
|
||||
if (bestInstance === undefined) {
|
||||
const regularWorkspaces = this.getNonDefaultWorkspaces(WellKnownWorkspaceKinds.Regular);
|
||||
|
||||
// If we have only regular workspaces with the same path, then pick the one that best matches the python path.
|
||||
if (
|
||||
regularWorkspaces.length &&
|
||||
regularWorkspaces.every((w) => w.rootPath === regularWorkspaces[0].rootPath)
|
||||
) {
|
||||
bestInstance = pythonPath
|
||||
? regularWorkspaces.find((w) => w.pythonPath === pythonPath) || regularWorkspaces[0]
|
||||
: regularWorkspaces[0];
|
||||
} else {
|
||||
// If we have multiple workspaces, then pick the containing workspace that best matches the python path.
|
||||
const containingWorkspace = this._getBestRegularWorkspace(
|
||||
regularWorkspaces.filter((w) => w.service.contains(filePath)),
|
||||
pythonPath
|
||||
);
|
||||
if (containingWorkspace) {
|
||||
bestInstance = containingWorkspace;
|
||||
} else {
|
||||
// If no workspace contains it, then it belongs to the default workspace.
|
||||
bestInstance = this._getOrCreateDefaultWorkspace(pythonPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bestInstance;
|
||||
}
|
||||
|
||||
private _getOrCreateDefaultWorkspace(pythonPath: string | undefined): Workspace {
|
||||
// Default key depends upon the pythonPath
|
||||
let defaultWorkspace = this._map.get(this._getDefaultWorskpaceKey(pythonPath));
|
||||
if (!defaultWorkspace) {
|
||||
// Create a default workspace for files that are outside
|
||||
// of all workspaces.
|
||||
defaultWorkspace = this._add(
|
||||
'',
|
||||
'',
|
||||
this._defaultWorkspacePath,
|
||||
pythonPath,
|
||||
pythonPath ? WorkspacePythonPathKind.Immutable : WorkspacePythonPathKind.Mutable,
|
||||
[WellKnownWorkspaceKinds.Default]
|
||||
);
|
||||
}
|
||||
|
||||
return defaultWorkspace;
|
||||
}
|
||||
|
||||
private _getBestRegularWorkspace(workspaces: Workspace[], pythonPath?: string): Workspace | undefined {
|
||||
if (workspaces.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (workspaces.length === 1) {
|
||||
return workspaces[0];
|
||||
}
|
||||
|
||||
// Further filter by longest paths.
|
||||
const longestPath = workspaces.reduce((previousPath, currentWorkspace) => {
|
||||
if (!previousPath) {
|
||||
return currentWorkspace.rootPath;
|
||||
}
|
||||
if (currentWorkspace.rootPath.length > previousPath.length) {
|
||||
return currentWorkspace.rootPath;
|
||||
}
|
||||
|
||||
return previousPath;
|
||||
}, '');
|
||||
const longestWorkspaces = workspaces.filter((w) => w.rootPath === longestPath);
|
||||
|
||||
// Filter by any that match the current python path.
|
||||
return longestWorkspaces.find((w) => !pythonPath || w.pythonPath === pythonPath) || longestWorkspaces[0];
|
||||
}
|
||||
}
|
@ -1,211 +0,0 @@
|
||||
/*
|
||||
* workspaceMap.ts
|
||||
*
|
||||
* Workspace management related functionality.
|
||||
*/
|
||||
|
||||
import {
|
||||
createInitStatus,
|
||||
LanguageServerBase,
|
||||
WellKnownWorkspaceKinds,
|
||||
WorkspaceServiceInstance,
|
||||
} from './languageServerBase';
|
||||
|
||||
export class WorkspaceMap extends Map<string, WorkspaceServiceInstance> {
|
||||
private _defaultWorkspacePath = '<default>';
|
||||
|
||||
override set(key: string, value: WorkspaceServiceInstance): this {
|
||||
// Make sure to delete existing workspace if there is one.
|
||||
this.delete(key);
|
||||
return super.set(key, value);
|
||||
}
|
||||
|
||||
override delete(key: string): boolean {
|
||||
const workspace = this.get(key);
|
||||
if (!workspace) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Make sure to unblock if there is someone waiting this workspace.
|
||||
workspace.isInitialized.resolve();
|
||||
|
||||
// Properly dispose of the service instance.
|
||||
workspace.serviceInstance.dispose();
|
||||
|
||||
return super.delete(key);
|
||||
}
|
||||
|
||||
hasMultipleWorkspaces(kind?: string) {
|
||||
if (this.size === 0 || this.size === 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let count = 0;
|
||||
for (const kv of this) {
|
||||
if (!kind || kv[1].kinds.some((k) => k === kind)) {
|
||||
count++;
|
||||
}
|
||||
|
||||
if (count > 1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
getNonDefaultWorkspaces(kind?: string): WorkspaceServiceInstance[] {
|
||||
const workspaces: WorkspaceServiceInstance[] = [];
|
||||
this.forEach((workspace) => {
|
||||
if (!workspace.path) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (kind && !workspace.kinds.some((k) => k === kind)) {
|
||||
return;
|
||||
}
|
||||
|
||||
workspaces.push(workspace);
|
||||
});
|
||||
|
||||
return workspaces;
|
||||
}
|
||||
|
||||
// Returns the best workspace for a file. Waits for the workspace to be finished handling other events before
|
||||
// returning the appropriate workspace.
|
||||
async getWorkspaceForFile(ls: LanguageServerBase, filePath: string): Promise<WorkspaceServiceInstance> {
|
||||
// Make sure we always have a default workspace.
|
||||
const defaultWorkspace = this._createDefaultWorkspace(ls);
|
||||
|
||||
// Wait for all workspaces to be initialized before attempting to find the best workspace. Otherwise
|
||||
// the list of files won't be complete and the `contains` check might fail.
|
||||
await Promise.all([...this.values()].map((w) => w.isInitialized.promise));
|
||||
|
||||
// Find best match.
|
||||
const workspace = this._getBestWorkspaceForFile(ls, filePath, defaultWorkspace);
|
||||
|
||||
// During the previous await we might have reset to being uninitialized again, wait before returning
|
||||
await workspace.isInitialized.promise;
|
||||
|
||||
return workspace;
|
||||
}
|
||||
|
||||
getContainingWorkspace(filePath: string) {
|
||||
return this._getBestWorkspace(
|
||||
this.getNonDefaultWorkspaces(WellKnownWorkspaceKinds.Regular).filter((w) => filePath.startsWith(w.path))
|
||||
);
|
||||
}
|
||||
|
||||
getDefaultWorkspace(): WorkspaceServiceInstance | undefined {
|
||||
return this.get(this._defaultWorkspacePath);
|
||||
}
|
||||
|
||||
private _getBestWorkspaceForFile(
|
||||
ls: LanguageServerBase,
|
||||
filePath: string,
|
||||
defaultWorkspace: WorkspaceServiceInstance
|
||||
): WorkspaceServiceInstance {
|
||||
let bestRootPath: string | undefined;
|
||||
let bestInstance: WorkspaceServiceInstance | undefined;
|
||||
|
||||
// The order of how we find the best matching workspace for the given file is
|
||||
// 1. The given file is the workspace itself (ex, a file being a virtual workspace itself).
|
||||
// 2. The given file matches the fileSpec of the service under the workspace
|
||||
// (the file is a user file the workspace provides LSP service for).
|
||||
// 3. The given file doesn't match anything but we have only 1 regular workspace
|
||||
// (ex, open a library file from the workspace).
|
||||
// 4. The given file doesn't match anything and there are multiple workspaces but one of workspaces
|
||||
// contains the file (ex, open a library file already imported by a workspace).
|
||||
// 5. If none of the above works, then it matches the default workspace.
|
||||
this.forEach((workspace) => {
|
||||
if (workspace.path) {
|
||||
if (workspace.path !== filePath && !workspace.serviceInstance.isTracked(filePath)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Among workspaces that own the file, make sure we return the inner most one which
|
||||
// we consider as the best workspace.
|
||||
if (bestRootPath === undefined || workspace.path.startsWith(bestRootPath)) {
|
||||
bestRootPath = workspace.path;
|
||||
bestInstance = workspace;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// If there were multiple workspaces or we couldn't find any,
|
||||
// use the default one.
|
||||
if (bestInstance === undefined) {
|
||||
const regularWorkspaces = this.getNonDefaultWorkspaces(WellKnownWorkspaceKinds.Regular);
|
||||
|
||||
// If we have only 1 regular workspace, then use that.
|
||||
if (regularWorkspaces.length === 1) {
|
||||
bestInstance = regularWorkspaces[0];
|
||||
} else {
|
||||
// If we have multiple workspaces, see whether we can at least find one that contains the file.
|
||||
// the file might not be tracked (user file), but still belongs to a workspace as a library file or as an orphan file to the workspace.
|
||||
const containingWorkspace = this._getBestWorkspace(
|
||||
regularWorkspaces.filter((w) => w.serviceInstance.contains(filePath))
|
||||
);
|
||||
if (containingWorkspace) {
|
||||
bestInstance = containingWorkspace;
|
||||
} else {
|
||||
// If no workspace contains it, then it belongs to the default workspace.
|
||||
bestInstance = defaultWorkspace;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bestInstance;
|
||||
}
|
||||
|
||||
private _createDefaultWorkspace(ls: LanguageServerBase) {
|
||||
let defaultWorkspace = this.get(this._defaultWorkspacePath);
|
||||
if (!defaultWorkspace) {
|
||||
// Create a default workspace for files that are outside
|
||||
// of all workspaces.
|
||||
defaultWorkspace = {
|
||||
workspaceName: '',
|
||||
rootPath: '',
|
||||
path: '',
|
||||
uri: '',
|
||||
serviceInstance: ls.createAnalyzerService(this._defaultWorkspacePath),
|
||||
kinds: [WellKnownWorkspaceKinds.Default],
|
||||
disableLanguageServices: false,
|
||||
disableOrganizeImports: false,
|
||||
disableWorkspaceSymbol: false,
|
||||
isInitialized: createInitStatus(),
|
||||
searchPathsToWatch: [],
|
||||
};
|
||||
this.set(this._defaultWorkspacePath, defaultWorkspace);
|
||||
|
||||
// Do not await this. let isInitialized.promise to await. Otherwise, ordering
|
||||
// will get messed up. The very first call will run last.
|
||||
ls.updateSettingsForWorkspace(defaultWorkspace, defaultWorkspace.isInitialized).ignoreErrors();
|
||||
}
|
||||
|
||||
return defaultWorkspace;
|
||||
}
|
||||
|
||||
private _getBestWorkspace(workspaces: WorkspaceServiceInstance[]) {
|
||||
if (workspaces.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (workspaces.length === 1) {
|
||||
return workspaces[0];
|
||||
}
|
||||
|
||||
// Best workspace is the inner most workspace.
|
||||
return workspaces.reduce((previousWorkspace, currentWorkspace) => {
|
||||
if (!previousWorkspace) {
|
||||
return currentWorkspace;
|
||||
}
|
||||
|
||||
if (currentWorkspace.path.startsWith(previousWorkspace.path)) {
|
||||
return currentWorkspace;
|
||||
}
|
||||
|
||||
return previousWorkspace;
|
||||
}, workspaces[0]);
|
||||
}
|
||||
}
|
14
packages/pyright/package-lock.json
generated
14
packages/pyright/package-lock.json
generated
@ -20,7 +20,7 @@
|
||||
"shx": "^0.3.4",
|
||||
"ts-loader": "^9.4.2",
|
||||
"typescript": "~4.4.4",
|
||||
"webpack": "^5.75.0",
|
||||
"webpack": "^5.76.0",
|
||||
"webpack-cli": "^4.10.0"
|
||||
},
|
||||
"engines": {
|
||||
@ -2308,9 +2308,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/webpack": {
|
||||
"version": "5.76.0",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.76.0.tgz",
|
||||
"integrity": "sha512-l5sOdYBDunyf72HW8dF23rFtWq/7Zgvt/9ftMof71E/yUb1YLOBmTgA2K4vQthB3kotMrSj609txVE0dnr2fjA==",
|
||||
"version": "5.76.1",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.76.1.tgz",
|
||||
"integrity": "sha512-4+YIK4Abzv8172/SGqObnUjaIHjLEuUasz9EwQj/9xmPPkYJy2Mh03Q/lJfSD3YLzbxy5FeTq5Uw0323Oh6SJQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/eslint-scope": "^3.7.3",
|
||||
@ -4134,9 +4134,9 @@
|
||||
}
|
||||
},
|
||||
"webpack": {
|
||||
"version": "5.76.0",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.76.0.tgz",
|
||||
"integrity": "sha512-l5sOdYBDunyf72HW8dF23rFtWq/7Zgvt/9ftMof71E/yUb1YLOBmTgA2K4vQthB3kotMrSj609txVE0dnr2fjA==",
|
||||
"version": "5.76.1",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.76.1.tgz",
|
||||
"integrity": "sha512-4+YIK4Abzv8172/SGqObnUjaIHjLEuUasz9EwQj/9xmPPkYJy2Mh03Q/lJfSD3YLzbxy5FeTq5Uw0323Oh6SJQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/eslint-scope": "^3.7.3",
|
||||
|
@ -30,7 +30,7 @@
|
||||
"shx": "^0.3.4",
|
||||
"ts-loader": "^9.4.2",
|
||||
"typescript": "~4.4.4",
|
||||
"webpack": "^5.75.0",
|
||||
"webpack": "^5.76.0",
|
||||
"webpack-cli": "^4.10.0"
|
||||
},
|
||||
"files": [
|
||||
|
134
packages/vscode-pyright/package-lock.json
generated
134
packages/vscode-pyright/package-lock.json
generated
@ -9,10 +9,10 @@
|
||||
"version": "1.1.299",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"vscode-jsonrpc": "8.1.0-next.6",
|
||||
"vscode-languageclient": "8.1.0-next.5",
|
||||
"vscode-languageserver": "8.1.0-next.5",
|
||||
"vscode-languageserver-protocol": "3.17.3-next.5"
|
||||
"vscode-jsonrpc": "8.1.0",
|
||||
"vscode-languageclient": "8.1.0",
|
||||
"vscode-languageserver": "8.1.0",
|
||||
"vscode-languageserver-protocol": "3.17.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/copy-webpack-plugin": "^10.1.0",
|
||||
@ -24,7 +24,7 @@
|
||||
"ts-loader": "^9.4.2",
|
||||
"typescript": "~4.4.4",
|
||||
"vsce": "^2.7.0",
|
||||
"webpack": "^5.75.0",
|
||||
"webpack": "^5.76.0",
|
||||
"webpack-cli": "^4.10.0"
|
||||
},
|
||||
"engines": {
|
||||
@ -1142,9 +1142,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/fast-glob": {
|
||||
"version": "3.2.11",
|
||||
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz",
|
||||
"integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==",
|
||||
"version": "3.2.12",
|
||||
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz",
|
||||
"integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@nodelib/fs.stat": "^2.0.2",
|
||||
@ -1182,9 +1182,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/fastq": {
|
||||
"version": "1.13.0",
|
||||
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz",
|
||||
"integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==",
|
||||
"version": "1.15.0",
|
||||
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz",
|
||||
"integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"reusify": "^1.0.4"
|
||||
@ -1433,9 +1433,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/ignore": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz",
|
||||
"integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==",
|
||||
"version": "5.2.4",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
|
||||
"integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 4"
|
||||
@ -3027,21 +3027,21 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vscode-jsonrpc": {
|
||||
"version": "8.1.0-next.6",
|
||||
"resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.1.0-next.6.tgz",
|
||||
"integrity": "sha512-AahQokGczPwXKo1Qhnn3aqkZgwUJ0rjVwhWWKW5I5LEWRoqfnWkQp7haVIV6GJRX0oyGL2ezVy7IhwgP5u/3xw==",
|
||||
"version": "8.1.0",
|
||||
"resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.1.0.tgz",
|
||||
"integrity": "sha512-6TDy/abTQk+zDGYazgbIPc+4JoXdwC8NHU9Pbn4UJP1fehUyZmM4RHp5IthX7A6L5KS30PRui+j+tbbMMMafdw==",
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vscode-languageclient": {
|
||||
"version": "8.1.0-next.5",
|
||||
"resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-8.1.0-next.5.tgz",
|
||||
"integrity": "sha512-RbL68ENqp2uPDs1rsPiD8IfhPWzUP8e12ONdtpmSlR6kmj6FLOd8fEaC1pQMGDtfPfiFCpLav8YytH25QOYwRQ==",
|
||||
"version": "8.1.0",
|
||||
"resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-8.1.0.tgz",
|
||||
"integrity": "sha512-GL4QdbYUF/XxQlAsvYWZRV3V34kOkpRlvV60/72ghHfsYFnS/v2MANZ9P6sHmxFcZKOse8O+L9G7Czg0NUWing==",
|
||||
"dependencies": {
|
||||
"minimatch": "^5.1.0",
|
||||
"semver": "^7.3.7",
|
||||
"vscode-languageserver-protocol": "3.17.3-next.5"
|
||||
"vscode-languageserver-protocol": "3.17.3"
|
||||
},
|
||||
"engines": {
|
||||
"vscode": "^1.67.0"
|
||||
@ -3067,29 +3067,29 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vscode-languageserver": {
|
||||
"version": "8.1.0-next.5",
|
||||
"resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-8.1.0-next.5.tgz",
|
||||
"integrity": "sha512-VivctbjOca/iPZKXqgqz03MUhnjAlVkf8/AwfndIEVzD8itD7zaoMlqNUynHJVbGcU5PEygC5deUzKHMU8cNhw==",
|
||||
"version": "8.1.0",
|
||||
"resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-8.1.0.tgz",
|
||||
"integrity": "sha512-eUt8f1z2N2IEUDBsKaNapkz7jl5QpskN2Y0G01T/ItMxBxw1fJwvtySGB9QMecatne8jFIWJGWI61dWjyTLQsw==",
|
||||
"dependencies": {
|
||||
"vscode-languageserver-protocol": "3.17.3-next.5"
|
||||
"vscode-languageserver-protocol": "3.17.3"
|
||||
},
|
||||
"bin": {
|
||||
"installServerIntoExtension": "bin/installServerIntoExtension"
|
||||
}
|
||||
},
|
||||
"node_modules/vscode-languageserver-protocol": {
|
||||
"version": "3.17.3-next.5",
|
||||
"resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.3-next.5.tgz",
|
||||
"integrity": "sha512-9HafkatRVhBVpWQrODes4JaoSu7ozHQUOzYiTmfMmxeFOUYgsSqyODp+j/c+SovcsuwABjuqnsQ9RiFkXCbeMA==",
|
||||
"version": "3.17.3",
|
||||
"resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.3.tgz",
|
||||
"integrity": "sha512-924/h0AqsMtA5yK22GgMtCYiMdCOtWTSGgUOkgEDX+wk2b0x4sAfLiO4NxBxqbiVtz7K7/1/RgVrVI0NClZwqA==",
|
||||
"dependencies": {
|
||||
"vscode-jsonrpc": "8.1.0-next.6",
|
||||
"vscode-languageserver-types": "3.17.3-next.2"
|
||||
"vscode-jsonrpc": "8.1.0",
|
||||
"vscode-languageserver-types": "3.17.3"
|
||||
}
|
||||
},
|
||||
"node_modules/vscode-languageserver-types": {
|
||||
"version": "3.17.3-next.2",
|
||||
"resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.3-next.2.tgz",
|
||||
"integrity": "sha512-3kkNSCycNKUalSJIrjIptGeY9UTJr1Nk5HT/aT00jjIwiCvIUNbRdK90av2Y3j1Jityot68dBVc3YYdwwH3zOQ=="
|
||||
"version": "3.17.3",
|
||||
"resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.3.tgz",
|
||||
"integrity": "sha512-SYU4z1dL0PyIMd4Vj8YOqFvHu7Hz/enbWtpfnVbJHU4Nd1YNYx8u0ennumc6h48GQNeOLxmwySmnADouT/AuZA=="
|
||||
},
|
||||
"node_modules/watchpack": {
|
||||
"version": "2.4.0",
|
||||
@ -3105,9 +3105,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/webpack": {
|
||||
"version": "5.75.0",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.75.0.tgz",
|
||||
"integrity": "sha512-piaIaoVJlqMsPtX/+3KTTO6jfvrSYgauFVdt8cr9LTHKmcq/AMd4mhzsiP7ZF/PGRNPGA8336jldh9l2Kt2ogQ==",
|
||||
"version": "5.76.1",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.76.1.tgz",
|
||||
"integrity": "sha512-4+YIK4Abzv8172/SGqObnUjaIHjLEuUasz9EwQj/9xmPPkYJy2Mh03Q/lJfSD3YLzbxy5FeTq5Uw0323Oh6SJQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/eslint-scope": "^3.7.3",
|
||||
@ -4247,9 +4247,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"fast-glob": {
|
||||
"version": "3.2.11",
|
||||
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz",
|
||||
"integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==",
|
||||
"version": "3.2.12",
|
||||
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz",
|
||||
"integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@nodelib/fs.stat": "^2.0.2",
|
||||
@ -4283,9 +4283,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"fastq": {
|
||||
"version": "1.13.0",
|
||||
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz",
|
||||
"integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==",
|
||||
"version": "1.15.0",
|
||||
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz",
|
||||
"integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"reusify": "^1.0.4"
|
||||
@ -4474,9 +4474,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"ignore": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz",
|
||||
"integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==",
|
||||
"version": "5.2.4",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
|
||||
"integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==",
|
||||
"dev": true
|
||||
},
|
||||
"import-local": {
|
||||
@ -5659,18 +5659,18 @@
|
||||
}
|
||||
},
|
||||
"vscode-jsonrpc": {
|
||||
"version": "8.1.0-next.6",
|
||||
"resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.1.0-next.6.tgz",
|
||||
"integrity": "sha512-AahQokGczPwXKo1Qhnn3aqkZgwUJ0rjVwhWWKW5I5LEWRoqfnWkQp7haVIV6GJRX0oyGL2ezVy7IhwgP5u/3xw=="
|
||||
"version": "8.1.0",
|
||||
"resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.1.0.tgz",
|
||||
"integrity": "sha512-6TDy/abTQk+zDGYazgbIPc+4JoXdwC8NHU9Pbn4UJP1fehUyZmM4RHp5IthX7A6L5KS30PRui+j+tbbMMMafdw=="
|
||||
},
|
||||
"vscode-languageclient": {
|
||||
"version": "8.1.0-next.5",
|
||||
"resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-8.1.0-next.5.tgz",
|
||||
"integrity": "sha512-RbL68ENqp2uPDs1rsPiD8IfhPWzUP8e12ONdtpmSlR6kmj6FLOd8fEaC1pQMGDtfPfiFCpLav8YytH25QOYwRQ==",
|
||||
"version": "8.1.0",
|
||||
"resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-8.1.0.tgz",
|
||||
"integrity": "sha512-GL4QdbYUF/XxQlAsvYWZRV3V34kOkpRlvV60/72ghHfsYFnS/v2MANZ9P6sHmxFcZKOse8O+L9G7Czg0NUWing==",
|
||||
"requires": {
|
||||
"minimatch": "^5.1.0",
|
||||
"semver": "^7.3.7",
|
||||
"vscode-languageserver-protocol": "3.17.3-next.5"
|
||||
"vscode-languageserver-protocol": "3.17.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"brace-expansion": {
|
||||
@ -5692,26 +5692,26 @@
|
||||
}
|
||||
},
|
||||
"vscode-languageserver": {
|
||||
"version": "8.1.0-next.5",
|
||||
"resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-8.1.0-next.5.tgz",
|
||||
"integrity": "sha512-VivctbjOca/iPZKXqgqz03MUhnjAlVkf8/AwfndIEVzD8itD7zaoMlqNUynHJVbGcU5PEygC5deUzKHMU8cNhw==",
|
||||
"version": "8.1.0",
|
||||
"resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-8.1.0.tgz",
|
||||
"integrity": "sha512-eUt8f1z2N2IEUDBsKaNapkz7jl5QpskN2Y0G01T/ItMxBxw1fJwvtySGB9QMecatne8jFIWJGWI61dWjyTLQsw==",
|
||||
"requires": {
|
||||
"vscode-languageserver-protocol": "3.17.3-next.5"
|
||||
"vscode-languageserver-protocol": "3.17.3"
|
||||
}
|
||||
},
|
||||
"vscode-languageserver-protocol": {
|
||||
"version": "3.17.3-next.5",
|
||||
"resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.3-next.5.tgz",
|
||||
"integrity": "sha512-9HafkatRVhBVpWQrODes4JaoSu7ozHQUOzYiTmfMmxeFOUYgsSqyODp+j/c+SovcsuwABjuqnsQ9RiFkXCbeMA==",
|
||||
"version": "3.17.3",
|
||||
"resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.3.tgz",
|
||||
"integrity": "sha512-924/h0AqsMtA5yK22GgMtCYiMdCOtWTSGgUOkgEDX+wk2b0x4sAfLiO4NxBxqbiVtz7K7/1/RgVrVI0NClZwqA==",
|
||||
"requires": {
|
||||
"vscode-jsonrpc": "8.1.0-next.6",
|
||||
"vscode-languageserver-types": "3.17.3-next.2"
|
||||
"vscode-jsonrpc": "8.1.0",
|
||||
"vscode-languageserver-types": "3.17.3"
|
||||
}
|
||||
},
|
||||
"vscode-languageserver-types": {
|
||||
"version": "3.17.3-next.2",
|
||||
"resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.3-next.2.tgz",
|
||||
"integrity": "sha512-3kkNSCycNKUalSJIrjIptGeY9UTJr1Nk5HT/aT00jjIwiCvIUNbRdK90av2Y3j1Jityot68dBVc3YYdwwH3zOQ=="
|
||||
"version": "3.17.3",
|
||||
"resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.3.tgz",
|
||||
"integrity": "sha512-SYU4z1dL0PyIMd4Vj8YOqFvHu7Hz/enbWtpfnVbJHU4Nd1YNYx8u0ennumc6h48GQNeOLxmwySmnADouT/AuZA=="
|
||||
},
|
||||
"watchpack": {
|
||||
"version": "2.4.0",
|
||||
@ -5724,9 +5724,9 @@
|
||||
}
|
||||
},
|
||||
"webpack": {
|
||||
"version": "5.75.0",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.75.0.tgz",
|
||||
"integrity": "sha512-piaIaoVJlqMsPtX/+3KTTO6jfvrSYgauFVdt8cr9LTHKmcq/AMd4mhzsiP7ZF/PGRNPGA8336jldh9l2Kt2ogQ==",
|
||||
"version": "5.76.1",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.76.1.tgz",
|
||||
"integrity": "sha512-4+YIK4Abzv8172/SGqObnUjaIHjLEuUasz9EwQj/9xmPPkYJy2Mh03Q/lJfSD3YLzbxy5FeTq5Uw0323Oh6SJQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/eslint-scope": "^3.7.3",
|
||||
|
@ -161,7 +161,6 @@
|
||||
},
|
||||
"python.analysis.diagnosticSeverityOverrides": {
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"description": "Allows a user to override the severity levels for individual diagnostics.",
|
||||
"scope": "resource",
|
||||
"properties": {
|
||||
@ -940,10 +939,10 @@
|
||||
"webpack-dev": "npm run clean && webpack --mode development --watch --progress"
|
||||
},
|
||||
"dependencies": {
|
||||
"vscode-jsonrpc": "8.1.0-next.6",
|
||||
"vscode-languageclient": "8.1.0-next.5",
|
||||
"vscode-languageserver": "8.1.0-next.5",
|
||||
"vscode-languageserver-protocol": "3.17.3-next.5"
|
||||
"vscode-jsonrpc": "8.1.0",
|
||||
"vscode-languageclient": "8.1.0",
|
||||
"vscode-languageserver": "8.1.0",
|
||||
"vscode-languageserver-protocol": "3.17.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/copy-webpack-plugin": "^10.1.0",
|
||||
@ -955,7 +954,7 @@
|
||||
"ts-loader": "^9.4.2",
|
||||
"typescript": "~4.4.4",
|
||||
"vsce": "^2.7.0",
|
||||
"webpack": "^5.75.0",
|
||||
"webpack": "^5.76.0",
|
||||
"webpack-cli": "^4.10.0"
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user