mirror of
https://github.com/microsoft/pyright.git
synced 2024-10-03 19:37:39 +03:00
Change all file paths to use a new URI
class. (#6519)
* Preliminary idea * First commit * Refactor all uri based path functions to uriUtils * Most of real code building * More tests working * More progress on the tests * Rest of typecheck errors * Move URIs even further to prevent accidental issues * More tests * Relative path fixes * More test fixes and changes to 'startsWith' * Uri tests all passing * remove uri tests from path utils tests * Fix relative paths for partial stub remapping * Make isEmpty look empty and fix empty check for resolved paths * Fix module name retrieval issue * Use map instead of set for URI list * Another path is empty fix. * Fix importresolver finding source files for stubs * Fixup actual defs to match expected format * Relative path differences again * Fix some comparisons * More comparison fixes * Config of ('.') was using current directory to find module names * Shortcut empty URI for existence * Add back ignoring case * Fix regex matching * All tests passing * Fix build after merge * Fix failing tests * Fix rootDirectory and python path from interpreter * Some perf improvements * Don't bother reparsing in file system * More tests and some logging output * Extra data for analyzing slowness * Fix build problems * Refactor to not use vscode.URI except for initial parse * Try without vscode uri validation * Rework to new perf idea * Split out class and interface * All uri tests passing * Fix other tests * Cache results for faster perf * Add memoization decorator * combinePath remove one layer and make key gen faster * Move caches local * Generalize the memoization and optimize zero arg funcs * Cache more stuff * slight speedup for cache saving * slight improvement by just returning the cached item * Small optimization to skip loop if possible * Further speedup for similar calls of combinePath * Make it possible to switch between profiled and not * Another small optimization * Cache files in directory * Small opt for isNativeModuleFileName * Speed up getting pytyped info * Fix command line parsing to expand ~ Slight perf improvement for exists cached by not splitting * Split out instrumenting memoization * Remove instrumented memoizationin * Fix comment * Fix build on ubuntu/mac * Fix windows specific problems with tests Fix auto import to work with windows file paths * Fix command line output * Try fixing CI path for failing test * fix output paths in other spots * More file output locations * Output more data for failing test * Put validation back and fix test * Fix import cycles message to not show URI path * Remove printing of config options * More review feedback * Missed a spot in the review feedback * Fix build error * Update packages/pyright-internal/src/analyzer/importResolver.ts Co-authored-by: Erik De Bonte <erikd@microsoft.com> * Update packages/pyright-internal/src/analyzer/analyzerFileInfo.ts Co-authored-by: Erik De Bonte <erikd@microsoft.com> * Update packages/pyright-internal/src/analyzer/backgroundAnalysisProgram.ts Co-authored-by: Erik De Bonte <erikd@microsoft.com> * Review feedback from Erik * Revert changes to launch.json * Fixup config options to use same roots as before * Fix casing for cwd test on windows * Fix spack differences * Add back in the instrumentation until figure import resolve cache * Fixup after merge * Fix another equals comparison * Merge error * Fix after merge * Fixup after merge * Keep track of case sensitivity in the URIs * Fix test failures on windows/mac * Switch to the faster version * Remove memoization * Review feedback for some renames * Review feedback * Feedback from Erik, put back memoization for simple stuff * fix prettier * Fix typo and cache URI creations * Add a comment * Review feedback from Eric * Fix crash in config options * Missed a piece of feedback --------- Co-authored-by: Erik De Bonte <erikd@microsoft.com>
This commit is contained in:
parent
07568b047d
commit
a5b352d929
4
.vscode/settings.json
vendored
4
.vscode/settings.json
vendored
@ -8,8 +8,8 @@
|
|||||||
"editor.formatOnSave": true
|
"editor.formatOnSave": true
|
||||||
},
|
},
|
||||||
"editor.codeActionsOnSave": {
|
"editor.codeActionsOnSave": {
|
||||||
"source.fixAll.eslint": true,
|
"source.fixAll.eslint": "explicit",
|
||||||
"source.organizeImports": true
|
"source.organizeImports": "explicit"
|
||||||
},
|
},
|
||||||
"typescript.tsdk": "node_modules/typescript/lib",
|
"typescript.tsdk": "node_modules/typescript/lib",
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,8 @@
|
|||||||
"check:eslint": "eslint .",
|
"check:eslint": "eslint .",
|
||||||
"fix:eslint": "eslint --fix .",
|
"fix:eslint": "eslint --fix .",
|
||||||
"check:prettier": "prettier -c .",
|
"check:prettier": "prettier -c .",
|
||||||
"fix:prettier": "prettier --write ."
|
"fix:prettier": "prettier --write .",
|
||||||
|
"typecheck": "npx lerna exec --stream --no-bail --ignore=pyright -- tsc --noEmit"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/glob": "^7.2.0",
|
"@types/glob": "^7.2.0",
|
||||||
|
14
packages/pyright-internal/package-lock.json
generated
14
packages/pyright-internal/package-lock.json
generated
@ -23,7 +23,7 @@
|
|||||||
"vscode-languageserver": "8.1.0",
|
"vscode-languageserver": "8.1.0",
|
||||||
"vscode-languageserver-textdocument": "1.0.10",
|
"vscode-languageserver-textdocument": "1.0.10",
|
||||||
"vscode-languageserver-types": "3.17.3",
|
"vscode-languageserver-types": "3.17.3",
|
||||||
"vscode-uri": "^3.0.7"
|
"vscode-uri": "^3.0.8"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/command-line-args": "^5.2.0",
|
"@types/command-line-args": "^5.2.0",
|
||||||
@ -3887,9 +3887,9 @@
|
|||||||
"integrity": "sha512-SYU4z1dL0PyIMd4Vj8YOqFvHu7Hz/enbWtpfnVbJHU4Nd1YNYx8u0ennumc6h48GQNeOLxmwySmnADouT/AuZA=="
|
"integrity": "sha512-SYU4z1dL0PyIMd4Vj8YOqFvHu7Hz/enbWtpfnVbJHU4Nd1YNYx8u0ennumc6h48GQNeOLxmwySmnADouT/AuZA=="
|
||||||
},
|
},
|
||||||
"node_modules/vscode-uri": {
|
"node_modules/vscode-uri": {
|
||||||
"version": "3.0.7",
|
"version": "3.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz",
|
||||||
"integrity": "sha512-eOpPHogvorZRobNqJGhapa0JdwaxpjVvyBp0QIUMRMSf8ZAlqOdEquKuRmw9Qwu0qXtJIWqFtMkmvJjUZmMjVA=="
|
"integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw=="
|
||||||
},
|
},
|
||||||
"node_modules/walker": {
|
"node_modules/walker": {
|
||||||
"version": "1.0.8",
|
"version": "1.0.8",
|
||||||
@ -6943,9 +6943,9 @@
|
|||||||
"integrity": "sha512-SYU4z1dL0PyIMd4Vj8YOqFvHu7Hz/enbWtpfnVbJHU4Nd1YNYx8u0ennumc6h48GQNeOLxmwySmnADouT/AuZA=="
|
"integrity": "sha512-SYU4z1dL0PyIMd4Vj8YOqFvHu7Hz/enbWtpfnVbJHU4Nd1YNYx8u0ennumc6h48GQNeOLxmwySmnADouT/AuZA=="
|
||||||
},
|
},
|
||||||
"vscode-uri": {
|
"vscode-uri": {
|
||||||
"version": "3.0.7",
|
"version": "3.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz",
|
||||||
"integrity": "sha512-eOpPHogvorZRobNqJGhapa0JdwaxpjVvyBp0QIUMRMSf8ZAlqOdEquKuRmw9Qwu0qXtJIWqFtMkmvJjUZmMjVA=="
|
"integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw=="
|
||||||
},
|
},
|
||||||
"walker": {
|
"walker": {
|
||||||
"version": "1.0.8",
|
"version": "1.0.8",
|
||||||
|
@ -30,7 +30,7 @@
|
|||||||
"vscode-languageserver": "8.1.0",
|
"vscode-languageserver": "8.1.0",
|
||||||
"vscode-languageserver-textdocument": "1.0.10",
|
"vscode-languageserver-textdocument": "1.0.10",
|
||||||
"vscode-languageserver-types": "3.17.3",
|
"vscode-languageserver-types": "3.17.3",
|
||||||
"vscode-uri": "^3.0.7"
|
"vscode-uri": "^3.0.8"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/command-line-args": "^5.2.0",
|
"@types/command-line-args": "^5.2.0",
|
||||||
|
@ -13,13 +13,14 @@ import { TextRangeDiagnosticSink } from '../common/diagnosticSink';
|
|||||||
import { PythonVersion } from '../common/pythonVersion';
|
import { PythonVersion } from '../common/pythonVersion';
|
||||||
import { TextRange } from '../common/textRange';
|
import { TextRange } from '../common/textRange';
|
||||||
import { TextRangeCollection } from '../common/textRangeCollection';
|
import { TextRangeCollection } from '../common/textRangeCollection';
|
||||||
|
import { Uri } from '../common/uri/uri';
|
||||||
import { Scope } from './scope';
|
import { Scope } from './scope';
|
||||||
import { IPythonMode } from './sourceFile';
|
import { IPythonMode } from './sourceFile';
|
||||||
import { SymbolTable } from './symbol';
|
import { SymbolTable } from './symbol';
|
||||||
|
|
||||||
// Maps import paths to the symbol table for the imported module.
|
// Maps import paths to the symbol table for the imported module.
|
||||||
export interface AbsoluteModuleDescriptor {
|
export interface AbsoluteModuleDescriptor {
|
||||||
importingFilePath: string;
|
importingFileUri: Uri;
|
||||||
nameParts: string[];
|
nameParts: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,7 +30,7 @@ export interface LookupImportOptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type ImportLookup = (
|
export type ImportLookup = (
|
||||||
filePathOrModule: string | AbsoluteModuleDescriptor,
|
fileUriOrModule: Uri | AbsoluteModuleDescriptor,
|
||||||
options?: LookupImportOptions
|
options?: LookupImportOptions
|
||||||
) => ImportLookupResult | undefined;
|
) => ImportLookupResult | undefined;
|
||||||
|
|
||||||
@ -52,7 +53,7 @@ export interface AnalyzerFileInfo {
|
|||||||
lines: TextRangeCollection<TextRange>;
|
lines: TextRangeCollection<TextRange>;
|
||||||
typingSymbolAliases: Map<string, string>;
|
typingSymbolAliases: Map<string, string>;
|
||||||
definedConstants: Map<string, boolean | string>;
|
definedConstants: Map<string, boolean | string>;
|
||||||
filePath: string;
|
fileUri: Uri;
|
||||||
moduleName: string;
|
moduleName: string;
|
||||||
isStubFile: boolean;
|
isStubFile: boolean;
|
||||||
isTypingStubFile: boolean;
|
isTypingStubFile: boolean;
|
||||||
|
@ -13,12 +13,13 @@ import { BackgroundAnalysisBase } from '../backgroundAnalysisBase';
|
|||||||
import { ConfigOptions, ExecutionEnvironment } from '../common/configOptions';
|
import { ConfigOptions, ExecutionEnvironment } from '../common/configOptions';
|
||||||
import { Diagnostic } from '../common/diagnostic';
|
import { Diagnostic } from '../common/diagnostic';
|
||||||
import { FileDiagnostics } from '../common/diagnosticSink';
|
import { FileDiagnostics } from '../common/diagnosticSink';
|
||||||
|
import { ServiceProvider } from '../common/serviceProvider';
|
||||||
|
import '../common/serviceProviderExtensions';
|
||||||
import { Range } from '../common/textRange';
|
import { Range } from '../common/textRange';
|
||||||
|
import { Uri } from '../common/uri/uri';
|
||||||
import { AnalysisCompleteCallback, analyzeProgram } from './analysis';
|
import { AnalysisCompleteCallback, analyzeProgram } from './analysis';
|
||||||
import { ImportResolver } from './importResolver';
|
import { ImportResolver } from './importResolver';
|
||||||
import { MaxAnalysisTime, OpenFileOptions, Program } from './program';
|
import { MaxAnalysisTime, OpenFileOptions, Program } from './program';
|
||||||
import { ServiceProvider } from '../common/serviceProvider';
|
|
||||||
import '../common/serviceProviderExtensions';
|
|
||||||
|
|
||||||
export enum InvalidatedReason {
|
export enum InvalidatedReason {
|
||||||
Reanalyzed,
|
Reanalyzed,
|
||||||
@ -72,8 +73,8 @@ export class BackgroundAnalysisProgram {
|
|||||||
return this._backgroundAnalysis;
|
return this._backgroundAnalysis;
|
||||||
}
|
}
|
||||||
|
|
||||||
hasSourceFile(filePath: string): boolean {
|
hasSourceFile(fileUri: Uri): boolean {
|
||||||
return !!this._program.getSourceFile(filePath);
|
return !!this._program.getSourceFile(fileUri);
|
||||||
}
|
}
|
||||||
|
|
||||||
setConfigOptions(configOptions: ConfigOptions) {
|
setConfigOptions(configOptions: ConfigOptions) {
|
||||||
@ -90,9 +91,9 @@ export class BackgroundAnalysisProgram {
|
|||||||
this.configOptions.getExecutionEnvironments().forEach((e) => this._ensurePartialStubPackages(e));
|
this.configOptions.getExecutionEnvironments().forEach((e) => this._ensurePartialStubPackages(e));
|
||||||
}
|
}
|
||||||
|
|
||||||
setTrackedFiles(filePaths: string[]) {
|
setTrackedFiles(fileUris: Uri[]) {
|
||||||
this._backgroundAnalysis?.setTrackedFiles(filePaths);
|
this._backgroundAnalysis?.setTrackedFiles(fileUris);
|
||||||
const diagnostics = this._program.setTrackedFiles(filePaths);
|
const diagnostics = this._program.setTrackedFiles(fileUris);
|
||||||
this._reportDiagnosticsForRemovedFiles(diagnostics);
|
this._reportDiagnosticsForRemovedFiles(diagnostics);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,35 +102,35 @@ export class BackgroundAnalysisProgram {
|
|||||||
this._program.setAllowedThirdPartyImports(importNames);
|
this._program.setAllowedThirdPartyImports(importNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
setFileOpened(filePath: string, version: number | null, contents: string, options: OpenFileOptions) {
|
setFileOpened(fileUri: Uri, version: number | null, contents: string, options: OpenFileOptions) {
|
||||||
this._backgroundAnalysis?.setFileOpened(filePath, version, contents, options);
|
this._backgroundAnalysis?.setFileOpened(fileUri, version, contents, options);
|
||||||
this._program.setFileOpened(filePath, version, contents, options);
|
this._program.setFileOpened(fileUri, version, contents, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
getChainedFilePath(filePath: string): string | undefined {
|
getChainedUri(fileUri: Uri): Uri | undefined {
|
||||||
return this._program.getChainedFilePath(filePath);
|
return this._program.getChainedUri(fileUri);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateChainedFilePath(filePath: string, chainedFilePath: string | undefined) {
|
updateChainedUri(fileUri: Uri, chainedUri: Uri | undefined) {
|
||||||
this._backgroundAnalysis?.updateChainedFilePath(filePath, chainedFilePath);
|
this._backgroundAnalysis?.updateChainedUri(fileUri, chainedUri);
|
||||||
this._program.updateChainedFilePath(filePath, chainedFilePath);
|
this._program.updateChainedUri(fileUri, chainedUri);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateOpenFileContents(path: string, version: number | null, contents: string, options: OpenFileOptions) {
|
updateOpenFileContents(uri: Uri, version: number | null, contents: string, options: OpenFileOptions) {
|
||||||
this._backgroundAnalysis?.setFileOpened(path, version, contents, options);
|
this._backgroundAnalysis?.setFileOpened(uri, version, contents, options);
|
||||||
this._program.setFileOpened(path, version, contents, options);
|
this._program.setFileOpened(uri, version, contents, options);
|
||||||
this.markFilesDirty([path], /* evenIfContentsAreSame */ true);
|
this.markFilesDirty([uri], /* evenIfContentsAreSame */ true);
|
||||||
}
|
}
|
||||||
|
|
||||||
setFileClosed(filePath: string, isTracked?: boolean) {
|
setFileClosed(fileUri: Uri, isTracked?: boolean) {
|
||||||
this._backgroundAnalysis?.setFileClosed(filePath, isTracked);
|
this._backgroundAnalysis?.setFileClosed(fileUri, isTracked);
|
||||||
const diagnostics = this._program.setFileClosed(filePath, isTracked);
|
const diagnostics = this._program.setFileClosed(fileUri, isTracked);
|
||||||
this._reportDiagnosticsForRemovedFiles(diagnostics);
|
this._reportDiagnosticsForRemovedFiles(diagnostics);
|
||||||
}
|
}
|
||||||
|
|
||||||
addInterimFile(filePath: string) {
|
addInterimFile(fileUri: Uri) {
|
||||||
this._backgroundAnalysis?.addInterimFile(filePath);
|
this._backgroundAnalysis?.addInterimFile(fileUri);
|
||||||
this._program.addInterimFile(filePath);
|
this._program.addInterimFile(fileUri);
|
||||||
}
|
}
|
||||||
|
|
||||||
markAllFilesDirty(evenIfContentsAreSame: boolean) {
|
markAllFilesDirty(evenIfContentsAreSame: boolean) {
|
||||||
@ -137,9 +138,9 @@ export class BackgroundAnalysisProgram {
|
|||||||
this._program.markAllFilesDirty(evenIfContentsAreSame);
|
this._program.markAllFilesDirty(evenIfContentsAreSame);
|
||||||
}
|
}
|
||||||
|
|
||||||
markFilesDirty(filePaths: string[], evenIfContentsAreSame: boolean) {
|
markFilesDirty(fileUris: Uri[], evenIfContentsAreSame: boolean) {
|
||||||
this._backgroundAnalysis?.markFilesDirty(filePaths, evenIfContentsAreSame);
|
this._backgroundAnalysis?.markFilesDirty(fileUris, evenIfContentsAreSame);
|
||||||
this._program.markFilesDirty(filePaths, evenIfContentsAreSame);
|
this._program.markFilesDirty(fileUris, evenIfContentsAreSame);
|
||||||
}
|
}
|
||||||
|
|
||||||
setCompletionCallback(callback?: AnalysisCompleteCallback) {
|
setCompletionCallback(callback?: AnalysisCompleteCallback) {
|
||||||
@ -163,34 +164,34 @@ export class BackgroundAnalysisProgram {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async analyzeFile(filePath: string, token: CancellationToken): Promise<boolean> {
|
async analyzeFile(fileUri: Uri, token: CancellationToken): Promise<boolean> {
|
||||||
if (this._backgroundAnalysis) {
|
if (this._backgroundAnalysis) {
|
||||||
return this._backgroundAnalysis.analyzeFile(filePath, token);
|
return this._backgroundAnalysis.analyzeFile(fileUri, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this._program.analyzeFile(filePath, token);
|
return this._program.analyzeFile(fileUri, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
libraryUpdated() {
|
libraryUpdated() {
|
||||||
// empty
|
// empty
|
||||||
}
|
}
|
||||||
|
|
||||||
async getDiagnosticsForRange(filePath: string, range: Range, token: CancellationToken): Promise<Diagnostic[]> {
|
async getDiagnosticsForRange(fileUri: Uri, range: Range, token: CancellationToken): Promise<Diagnostic[]> {
|
||||||
if (this._backgroundAnalysis) {
|
if (this._backgroundAnalysis) {
|
||||||
return this._backgroundAnalysis.getDiagnosticsForRange(filePath, range, token);
|
return this._backgroundAnalysis.getDiagnosticsForRange(fileUri, range, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this._program.getDiagnosticsForRange(filePath, range);
|
return this._program.getDiagnosticsForRange(fileUri, range);
|
||||||
}
|
}
|
||||||
|
|
||||||
async writeTypeStub(
|
async writeTypeStub(
|
||||||
targetImportPath: string,
|
targetImportUri: Uri,
|
||||||
targetIsSingleFile: boolean,
|
targetIsSingleFile: boolean,
|
||||||
stubPath: string,
|
stubUri: Uri,
|
||||||
token: CancellationToken
|
token: CancellationToken
|
||||||
): Promise<any> {
|
): Promise<any> {
|
||||||
if (this._backgroundAnalysis) {
|
if (this._backgroundAnalysis) {
|
||||||
return this._backgroundAnalysis.writeTypeStub(targetImportPath, targetIsSingleFile, stubPath, token);
|
return this._backgroundAnalysis.writeTypeStub(targetImportUri, targetIsSingleFile, stubUri, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
analyzeProgram(
|
analyzeProgram(
|
||||||
@ -201,7 +202,7 @@ export class BackgroundAnalysisProgram {
|
|||||||
this._serviceProvider.console(),
|
this._serviceProvider.console(),
|
||||||
token
|
token
|
||||||
);
|
);
|
||||||
return this._program.writeTypeStub(targetImportPath, targetIsSingleFile, stubPath, token);
|
return this._program.writeTypeStub(targetImportUri, targetIsSingleFile, stubUri, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
invalidateAndForceReanalysis(reason: InvalidatedReason) {
|
invalidateAndForceReanalysis(reason: InvalidatedReason) {
|
||||||
@ -245,7 +246,7 @@ export class BackgroundAnalysisProgram {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _ensurePartialStubPackages(execEnv: ExecutionEnvironment) {
|
private _ensurePartialStubPackages(execEnv: ExecutionEnvironment) {
|
||||||
this._backgroundAnalysis?.ensurePartialStubPackages(execEnv.root);
|
this._backgroundAnalysis?.ensurePartialStubPackages(execEnv.root?.toString());
|
||||||
return this._importResolver.ensurePartialStubPackages(execEnv);
|
return this._importResolver.ensurePartialStubPackages(execEnv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,9 +22,10 @@ import { DiagnosticLevel } from '../common/configOptions';
|
|||||||
import { assert, assertNever, fail } from '../common/debug';
|
import { assert, assertNever, fail } from '../common/debug';
|
||||||
import { CreateTypeStubFileAction, Diagnostic } from '../common/diagnostic';
|
import { CreateTypeStubFileAction, Diagnostic } from '../common/diagnostic';
|
||||||
import { DiagnosticRule } from '../common/diagnosticRules';
|
import { DiagnosticRule } from '../common/diagnosticRules';
|
||||||
import { getFileName, stripFileExtension } from '../common/pathUtils';
|
import { stripFileExtension } from '../common/pathUtils';
|
||||||
import { convertTextRangeToRange } from '../common/positionUtils';
|
import { convertTextRangeToRange } from '../common/positionUtils';
|
||||||
import { TextRange, getEmptyRange } from '../common/textRange';
|
import { TextRange, getEmptyRange } from '../common/textRange';
|
||||||
|
import { Uri } from '../common/uri/uri';
|
||||||
import { Localizer } from '../localization/localize';
|
import { Localizer } from '../localization/localize';
|
||||||
import {
|
import {
|
||||||
ArgumentCategory,
|
ArgumentCategory,
|
||||||
@ -407,7 +408,7 @@ export class Binder extends ParseTreeWalker {
|
|||||||
const classDeclaration: ClassDeclaration = {
|
const classDeclaration: ClassDeclaration = {
|
||||||
type: DeclarationType.Class,
|
type: DeclarationType.Class,
|
||||||
node,
|
node,
|
||||||
path: this._fileInfo.filePath,
|
uri: this._fileInfo.fileUri,
|
||||||
range: convertTextRangeToRange(node.name, this._fileInfo.lines),
|
range: convertTextRangeToRange(node.name, this._fileInfo.lines),
|
||||||
moduleName: this._fileInfo.moduleName,
|
moduleName: this._fileInfo.moduleName,
|
||||||
isInExceptSuite: this._isInExceptSuite,
|
isInExceptSuite: this._isInExceptSuite,
|
||||||
@ -465,7 +466,7 @@ export class Binder extends ParseTreeWalker {
|
|||||||
node,
|
node,
|
||||||
isMethod: !!containingClassNode,
|
isMethod: !!containingClassNode,
|
||||||
isGenerator: false,
|
isGenerator: false,
|
||||||
path: this._fileInfo.filePath,
|
uri: this._fileInfo.fileUri,
|
||||||
range: convertTextRangeToRange(node.name, this._fileInfo.lines),
|
range: convertTextRangeToRange(node.name, this._fileInfo.lines),
|
||||||
moduleName: this._fileInfo.moduleName,
|
moduleName: this._fileInfo.moduleName,
|
||||||
isInExceptSuite: this._isInExceptSuite,
|
isInExceptSuite: this._isInExceptSuite,
|
||||||
@ -539,7 +540,7 @@ export class Binder extends ParseTreeWalker {
|
|||||||
const paramDeclaration: ParameterDeclaration = {
|
const paramDeclaration: ParameterDeclaration = {
|
||||||
type: DeclarationType.Parameter,
|
type: DeclarationType.Parameter,
|
||||||
node: paramNode,
|
node: paramNode,
|
||||||
path: this._fileInfo.filePath,
|
uri: this._fileInfo.fileUri,
|
||||||
range: convertTextRangeToRange(paramNode, this._fileInfo.lines),
|
range: convertTextRangeToRange(paramNode, this._fileInfo.lines),
|
||||||
moduleName: this._fileInfo.moduleName,
|
moduleName: this._fileInfo.moduleName,
|
||||||
isInExceptSuite: this._isInExceptSuite,
|
isInExceptSuite: this._isInExceptSuite,
|
||||||
@ -613,7 +614,7 @@ export class Binder extends ParseTreeWalker {
|
|||||||
const paramDeclaration: ParameterDeclaration = {
|
const paramDeclaration: ParameterDeclaration = {
|
||||||
type: DeclarationType.Parameter,
|
type: DeclarationType.Parameter,
|
||||||
node: paramNode,
|
node: paramNode,
|
||||||
path: this._fileInfo.filePath,
|
uri: this._fileInfo.fileUri,
|
||||||
range: convertTextRangeToRange(paramNode, this._fileInfo.lines),
|
range: convertTextRangeToRange(paramNode, this._fileInfo.lines),
|
||||||
moduleName: this._fileInfo.moduleName,
|
moduleName: this._fileInfo.moduleName,
|
||||||
isInExceptSuite: this._isInExceptSuite,
|
isInExceptSuite: this._isInExceptSuite,
|
||||||
@ -765,7 +766,7 @@ export class Binder extends ParseTreeWalker {
|
|||||||
const paramDeclaration: TypeParameterDeclaration = {
|
const paramDeclaration: TypeParameterDeclaration = {
|
||||||
type: DeclarationType.TypeParameter,
|
type: DeclarationType.TypeParameter,
|
||||||
node: param,
|
node: param,
|
||||||
path: this._fileInfo.filePath,
|
uri: this._fileInfo.fileUri,
|
||||||
range: convertTextRangeToRange(node, this._fileInfo.lines),
|
range: convertTextRangeToRange(node, this._fileInfo.lines),
|
||||||
moduleName: this._fileInfo.moduleName,
|
moduleName: this._fileInfo.moduleName,
|
||||||
isInExceptSuite: this._isInExceptSuite,
|
isInExceptSuite: this._isInExceptSuite,
|
||||||
@ -806,7 +807,7 @@ export class Binder extends ParseTreeWalker {
|
|||||||
const typeAliasDeclaration: TypeAliasDeclaration = {
|
const typeAliasDeclaration: TypeAliasDeclaration = {
|
||||||
type: DeclarationType.TypeAlias,
|
type: DeclarationType.TypeAlias,
|
||||||
node,
|
node,
|
||||||
path: this._fileInfo.filePath,
|
uri: this._fileInfo.fileUri,
|
||||||
range: convertTextRangeToRange(node.name, this._fileInfo.lines),
|
range: convertTextRangeToRange(node.name, this._fileInfo.lines),
|
||||||
moduleName: this._fileInfo.moduleName,
|
moduleName: this._fileInfo.moduleName,
|
||||||
isInExceptSuite: this._isInExceptSuite,
|
isInExceptSuite: this._isInExceptSuite,
|
||||||
@ -1414,7 +1415,7 @@ export class Binder extends ParseTreeWalker {
|
|||||||
node: node.name,
|
node: node.name,
|
||||||
isConstant: isConstantName(node.name.value),
|
isConstant: isConstantName(node.name.value),
|
||||||
inferredTypeSource: node,
|
inferredTypeSource: node,
|
||||||
path: this._fileInfo.filePath,
|
uri: this._fileInfo.fileUri,
|
||||||
range: convertTextRangeToRange(node.name, this._fileInfo.lines),
|
range: convertTextRangeToRange(node.name, this._fileInfo.lines),
|
||||||
moduleName: this._fileInfo.moduleName,
|
moduleName: this._fileInfo.moduleName,
|
||||||
isInExceptSuite: this._isInExceptSuite,
|
isInExceptSuite: this._isInExceptSuite,
|
||||||
@ -1745,9 +1746,9 @@ export class Binder extends ParseTreeWalker {
|
|||||||
|
|
||||||
AnalyzerNodeInfo.setFlowNode(node, this._currentFlowNode!);
|
AnalyzerNodeInfo.setFlowNode(node, this._currentFlowNode!);
|
||||||
|
|
||||||
let resolvedPath = '';
|
let resolvedPath = Uri.empty();
|
||||||
if (importInfo && importInfo.isImportFound && !importInfo.isNativeLib) {
|
if (importInfo && importInfo.isImportFound && !importInfo.isNativeLib) {
|
||||||
resolvedPath = importInfo.resolvedPaths[importInfo.resolvedPaths.length - 1];
|
resolvedPath = importInfo.resolvedUris[importInfo.resolvedUris.length - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
// If this file is a module __init__.py(i), relative imports of submodules
|
// If this file is a module __init__.py(i), relative imports of submodules
|
||||||
@ -1756,7 +1757,7 @@ export class Binder extends ParseTreeWalker {
|
|||||||
// symbols below) in case one of the imported symbols is the same name as the
|
// symbols below) in case one of the imported symbols is the same name as the
|
||||||
// submodule. In that case, we want to the symbol to appear later in the
|
// submodule. In that case, we want to the symbol to appear later in the
|
||||||
// declaration list because it should "win" when resolving the alias.
|
// declaration list because it should "win" when resolving the alias.
|
||||||
const fileName = stripFileExtension(getFileName(this._fileInfo.filePath));
|
const fileName = stripFileExtension(this._fileInfo.fileUri.fileName);
|
||||||
const isModuleInitFile =
|
const isModuleInitFile =
|
||||||
fileName === '__init__' && node.module.leadingDots === 1 && node.module.nameParts.length === 1;
|
fileName === '__init__' && node.module.leadingDots === 1 && node.module.nameParts.length === 1;
|
||||||
|
|
||||||
@ -1814,7 +1815,7 @@ export class Binder extends ParseTreeWalker {
|
|||||||
const aliasDecl: AliasDeclaration = {
|
const aliasDecl: AliasDeclaration = {
|
||||||
type: DeclarationType.Alias,
|
type: DeclarationType.Alias,
|
||||||
node,
|
node,
|
||||||
path: resolvedPath,
|
uri: resolvedPath,
|
||||||
loadSymbolsFromPath: true,
|
loadSymbolsFromPath: true,
|
||||||
range: getEmptyRange(), // Range is unknown for wildcard name import.
|
range: getEmptyRange(), // Range is unknown for wildcard name import.
|
||||||
usesLocalName: false,
|
usesLocalName: false,
|
||||||
@ -1834,7 +1835,7 @@ export class Binder extends ParseTreeWalker {
|
|||||||
const submoduleFallback: AliasDeclaration = {
|
const submoduleFallback: AliasDeclaration = {
|
||||||
type: DeclarationType.Alias,
|
type: DeclarationType.Alias,
|
||||||
node,
|
node,
|
||||||
path: implicitImport.path,
|
uri: implicitImport.uri,
|
||||||
loadSymbolsFromPath: true,
|
loadSymbolsFromPath: true,
|
||||||
range: getEmptyRange(),
|
range: getEmptyRange(),
|
||||||
usesLocalName: false,
|
usesLocalName: false,
|
||||||
@ -1845,7 +1846,7 @@ export class Binder extends ParseTreeWalker {
|
|||||||
const aliasDecl: AliasDeclaration = {
|
const aliasDecl: AliasDeclaration = {
|
||||||
type: DeclarationType.Alias,
|
type: DeclarationType.Alias,
|
||||||
node,
|
node,
|
||||||
path: resolvedPath,
|
uri: resolvedPath,
|
||||||
loadSymbolsFromPath: true,
|
loadSymbolsFromPath: true,
|
||||||
usesLocalName: false,
|
usesLocalName: false,
|
||||||
symbolName: name,
|
symbolName: name,
|
||||||
@ -1926,7 +1927,7 @@ export class Binder extends ParseTreeWalker {
|
|||||||
submoduleFallback = {
|
submoduleFallback = {
|
||||||
type: DeclarationType.Alias,
|
type: DeclarationType.Alias,
|
||||||
node: importSymbolNode,
|
node: importSymbolNode,
|
||||||
path: implicitImport.path,
|
uri: implicitImport.uri,
|
||||||
loadSymbolsFromPath: true,
|
loadSymbolsFromPath: true,
|
||||||
range: getEmptyRange(),
|
range: getEmptyRange(),
|
||||||
usesLocalName: false,
|
usesLocalName: false,
|
||||||
@ -1942,7 +1943,7 @@ export class Binder extends ParseTreeWalker {
|
|||||||
if (fileName === '__init__') {
|
if (fileName === '__init__') {
|
||||||
if (node.module.leadingDots === 1 && node.module.nameParts.length === 0) {
|
if (node.module.leadingDots === 1 && node.module.nameParts.length === 0) {
|
||||||
loadSymbolsFromPath = false;
|
loadSymbolsFromPath = false;
|
||||||
} else if (resolvedPath === this._fileInfo.filePath) {
|
} else if (resolvedPath.equals(this._fileInfo.fileUri)) {
|
||||||
loadSymbolsFromPath = false;
|
loadSymbolsFromPath = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1951,7 +1952,7 @@ export class Binder extends ParseTreeWalker {
|
|||||||
const aliasDecl: AliasDeclaration = {
|
const aliasDecl: AliasDeclaration = {
|
||||||
type: DeclarationType.Alias,
|
type: DeclarationType.Alias,
|
||||||
node: importSymbolNode,
|
node: importSymbolNode,
|
||||||
path: resolvedPath,
|
uri: resolvedPath,
|
||||||
loadSymbolsFromPath,
|
loadSymbolsFromPath,
|
||||||
usesLocalName: !!importSymbolNode.alias,
|
usesLocalName: !!importSymbolNode.alias,
|
||||||
symbolName: importedName,
|
symbolName: importedName,
|
||||||
@ -2304,7 +2305,7 @@ export class Binder extends ParseTreeWalker {
|
|||||||
node: node.target,
|
node: node.target,
|
||||||
isConstant: isConstantName(node.target.value),
|
isConstant: isConstantName(node.target.value),
|
||||||
inferredTypeSource: node,
|
inferredTypeSource: node,
|
||||||
path: this._fileInfo.filePath,
|
uri: this._fileInfo.fileUri,
|
||||||
range: convertTextRangeToRange(node.target, this._fileInfo.lines),
|
range: convertTextRangeToRange(node.target, this._fileInfo.lines),
|
||||||
moduleName: this._fileInfo.moduleName,
|
moduleName: this._fileInfo.moduleName,
|
||||||
isInExceptSuite: this._isInExceptSuite,
|
isInExceptSuite: this._isInExceptSuite,
|
||||||
@ -2389,7 +2390,7 @@ export class Binder extends ParseTreeWalker {
|
|||||||
node: slotNameNode,
|
node: slotNameNode,
|
||||||
isConstant: isConstantName(slotName),
|
isConstant: isConstantName(slotName),
|
||||||
isDefinedBySlots: true,
|
isDefinedBySlots: true,
|
||||||
path: this._fileInfo.filePath,
|
uri: this._fileInfo.fileUri,
|
||||||
range: convertTextRangeToRange(slotNameNode, this._fileInfo.lines),
|
range: convertTextRangeToRange(slotNameNode, this._fileInfo.lines),
|
||||||
moduleName: this._fileInfo.moduleName,
|
moduleName: this._fileInfo.moduleName,
|
||||||
isInExceptSuite: this._isInExceptSuite,
|
isInExceptSuite: this._isInExceptSuite,
|
||||||
@ -2438,7 +2439,7 @@ export class Binder extends ParseTreeWalker {
|
|||||||
node: target,
|
node: target,
|
||||||
isConstant: isConstantName(target.value),
|
isConstant: isConstantName(target.value),
|
||||||
inferredTypeSource: target.parent,
|
inferredTypeSource: target.parent,
|
||||||
path: this._fileInfo.filePath,
|
uri: this._fileInfo.fileUri,
|
||||||
range: convertTextRangeToRange(target, this._fileInfo.lines),
|
range: convertTextRangeToRange(target, this._fileInfo.lines),
|
||||||
moduleName: this._fileInfo.moduleName,
|
moduleName: this._fileInfo.moduleName,
|
||||||
isInExceptSuite: this._isInExceptSuite,
|
isInExceptSuite: this._isInExceptSuite,
|
||||||
@ -2466,23 +2467,25 @@ export class Binder extends ParseTreeWalker {
|
|||||||
const aliasDecl = varSymbol.getDeclarations().find((decl) => decl.type === DeclarationType.Alias) as
|
const aliasDecl = varSymbol.getDeclarations().find((decl) => decl.type === DeclarationType.Alias) as
|
||||||
| AliasDeclaration
|
| AliasDeclaration
|
||||||
| undefined;
|
| undefined;
|
||||||
const resolvedPath =
|
const resolvedUri =
|
||||||
aliasDecl?.path && aliasDecl.loadSymbolsFromPath
|
aliasDecl?.uri && !aliasDecl.uri.isEmpty() && aliasDecl.loadSymbolsFromPath
|
||||||
? aliasDecl.path
|
? aliasDecl.uri
|
||||||
: aliasDecl?.submoduleFallback?.path && aliasDecl.submoduleFallback.loadSymbolsFromPath
|
: aliasDecl?.submoduleFallback?.uri &&
|
||||||
? aliasDecl.submoduleFallback.path
|
!aliasDecl.submoduleFallback.uri.isEmpty() &&
|
||||||
|
aliasDecl.submoduleFallback.loadSymbolsFromPath
|
||||||
|
? aliasDecl.submoduleFallback.uri
|
||||||
: undefined;
|
: undefined;
|
||||||
if (!resolvedPath) {
|
if (!resolvedUri) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
let lookupInfo = this._fileInfo.importLookup(resolvedPath);
|
let lookupInfo = this._fileInfo.importLookup(resolvedUri);
|
||||||
if (lookupInfo?.dunderAllNames) {
|
if (lookupInfo?.dunderAllNames) {
|
||||||
return lookupInfo.dunderAllNames;
|
return lookupInfo.dunderAllNames;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (aliasDecl?.submoduleFallback?.path) {
|
if (aliasDecl?.submoduleFallback?.uri && !aliasDecl.submoduleFallback.uri.isEmpty()) {
|
||||||
lookupInfo = this._fileInfo.importLookup(aliasDecl.submoduleFallback.path);
|
lookupInfo = this._fileInfo.importLookup(aliasDecl.submoduleFallback.uri);
|
||||||
return lookupInfo?.dunderAllNames;
|
return lookupInfo?.dunderAllNames;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2520,15 +2523,15 @@ export class Binder extends ParseTreeWalker {
|
|||||||
.getDeclarations()
|
.getDeclarations()
|
||||||
.find((decl) => decl.type === DeclarationType.Alias && decl.firstNamePart === firstNamePartValue);
|
.find((decl) => decl.type === DeclarationType.Alias && decl.firstNamePart === firstNamePartValue);
|
||||||
let newDecl: AliasDeclaration;
|
let newDecl: AliasDeclaration;
|
||||||
let pathOfLastSubmodule: string;
|
let uriOfLastSubmodule: Uri;
|
||||||
if (importInfo && importInfo.isImportFound && !importInfo.isNativeLib && importInfo.resolvedPaths.length > 0) {
|
if (importInfo && importInfo.isImportFound && !importInfo.isNativeLib && importInfo.resolvedUris.length > 0) {
|
||||||
pathOfLastSubmodule = importInfo.resolvedPaths[importInfo.resolvedPaths.length - 1];
|
uriOfLastSubmodule = importInfo.resolvedUris[importInfo.resolvedUris.length - 1];
|
||||||
} else {
|
} else {
|
||||||
pathOfLastSubmodule = UnresolvedModuleMarker;
|
uriOfLastSubmodule = UnresolvedModuleMarker;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isResolved =
|
const isResolved =
|
||||||
importInfo && importInfo.isImportFound && !importInfo.isNativeLib && importInfo.resolvedPaths.length > 0;
|
importInfo && importInfo.isImportFound && !importInfo.isNativeLib && importInfo.resolvedUris.length > 0;
|
||||||
|
|
||||||
if (existingDecl) {
|
if (existingDecl) {
|
||||||
newDecl = existingDecl as AliasDeclaration;
|
newDecl = existingDecl as AliasDeclaration;
|
||||||
@ -2536,7 +2539,7 @@ export class Binder extends ParseTreeWalker {
|
|||||||
newDecl = {
|
newDecl = {
|
||||||
type: DeclarationType.Alias,
|
type: DeclarationType.Alias,
|
||||||
node,
|
node,
|
||||||
path: pathOfLastSubmodule,
|
uri: uriOfLastSubmodule,
|
||||||
loadSymbolsFromPath: false,
|
loadSymbolsFromPath: false,
|
||||||
range: getEmptyRange(),
|
range: getEmptyRange(),
|
||||||
usesLocalName: !!importAlias,
|
usesLocalName: !!importAlias,
|
||||||
@ -2551,7 +2554,7 @@ export class Binder extends ParseTreeWalker {
|
|||||||
newDecl = {
|
newDecl = {
|
||||||
type: DeclarationType.Alias,
|
type: DeclarationType.Alias,
|
||||||
node,
|
node,
|
||||||
path: pathOfLastSubmodule,
|
uri: uriOfLastSubmodule,
|
||||||
loadSymbolsFromPath: true,
|
loadSymbolsFromPath: true,
|
||||||
range: getEmptyRange(),
|
range: getEmptyRange(),
|
||||||
usesLocalName: !!importAlias,
|
usesLocalName: !!importAlias,
|
||||||
@ -2565,8 +2568,8 @@ export class Binder extends ParseTreeWalker {
|
|||||||
// See if there is import info for this part of the path. This allows us
|
// See if there is import info for this part of the path. This allows us
|
||||||
// to implicitly import all of the modules in a multi-part module name.
|
// to implicitly import all of the modules in a multi-part module name.
|
||||||
const implicitImportInfo = AnalyzerNodeInfo.getImportInfo(node.module.nameParts[0]);
|
const implicitImportInfo = AnalyzerNodeInfo.getImportInfo(node.module.nameParts[0]);
|
||||||
if (implicitImportInfo && implicitImportInfo.resolvedPaths.length) {
|
if (implicitImportInfo && implicitImportInfo.resolvedUris.length) {
|
||||||
newDecl.path = implicitImportInfo.resolvedPaths[0];
|
newDecl.uri = implicitImportInfo.resolvedUris[0];
|
||||||
newDecl.loadSymbolsFromPath = true;
|
newDecl.loadSymbolsFromPath = true;
|
||||||
this._addImplicitImportsToLoaderActions(implicitImportInfo, newDecl);
|
this._addImplicitImportsToLoaderActions(implicitImportInfo, newDecl);
|
||||||
}
|
}
|
||||||
@ -2574,7 +2577,7 @@ export class Binder extends ParseTreeWalker {
|
|||||||
// Add the implicit imports for this module if it's the last
|
// Add the implicit imports for this module if it's the last
|
||||||
// name part we're resolving.
|
// name part we're resolving.
|
||||||
if (importAlias || node.module.nameParts.length === 1) {
|
if (importAlias || node.module.nameParts.length === 1) {
|
||||||
newDecl.path = pathOfLastSubmodule;
|
newDecl.uri = uriOfLastSubmodule;
|
||||||
newDecl.loadSymbolsFromPath = true;
|
newDecl.loadSymbolsFromPath = true;
|
||||||
newDecl.isUnresolved = false;
|
newDecl.isUnresolved = false;
|
||||||
|
|
||||||
@ -2594,13 +2597,13 @@ export class Binder extends ParseTreeWalker {
|
|||||||
: undefined;
|
: undefined;
|
||||||
if (!loaderActions) {
|
if (!loaderActions) {
|
||||||
const loaderActionPath =
|
const loaderActionPath =
|
||||||
importInfo && i < importInfo.resolvedPaths.length
|
importInfo && i < importInfo.resolvedUris.length
|
||||||
? importInfo.resolvedPaths[i]
|
? importInfo.resolvedUris[i]
|
||||||
: UnresolvedModuleMarker;
|
: UnresolvedModuleMarker;
|
||||||
|
|
||||||
// Allocate a new loader action.
|
// Allocate a new loader action.
|
||||||
loaderActions = {
|
loaderActions = {
|
||||||
path: loaderActionPath,
|
uri: loaderActionPath,
|
||||||
loadSymbolsFromPath: false,
|
loadSymbolsFromPath: false,
|
||||||
implicitImports: new Map<string, ModuleLoaderActions>(),
|
implicitImports: new Map<string, ModuleLoaderActions>(),
|
||||||
isUnresolved: !isResolved,
|
isUnresolved: !isResolved,
|
||||||
@ -2614,8 +2617,8 @@ export class Binder extends ParseTreeWalker {
|
|||||||
if (i === node.module.nameParts.length - 1) {
|
if (i === node.module.nameParts.length - 1) {
|
||||||
// If this is the last name part we're resolving, add in the
|
// If this is the last name part we're resolving, add in the
|
||||||
// implicit imports as well.
|
// implicit imports as well.
|
||||||
if (importInfo && i < importInfo.resolvedPaths.length) {
|
if (importInfo && i < importInfo.resolvedUris.length) {
|
||||||
loaderActions.path = importInfo.resolvedPaths[i];
|
loaderActions.uri = importInfo.resolvedUris[i];
|
||||||
loaderActions.loadSymbolsFromPath = true;
|
loaderActions.loadSymbolsFromPath = true;
|
||||||
this._addImplicitImportsToLoaderActions(importInfo, loaderActions);
|
this._addImplicitImportsToLoaderActions(importInfo, loaderActions);
|
||||||
}
|
}
|
||||||
@ -2625,8 +2628,8 @@ export class Binder extends ParseTreeWalker {
|
|||||||
// import all of the modules in a multi-part module name (e.g. "import a.b.c"
|
// import all of the modules in a multi-part module name (e.g. "import a.b.c"
|
||||||
// imports "a" and "a.b" and "a.b.c").
|
// imports "a" and "a.b" and "a.b.c").
|
||||||
const implicitImportInfo = AnalyzerNodeInfo.getImportInfo(node.module.nameParts[i]);
|
const implicitImportInfo = AnalyzerNodeInfo.getImportInfo(node.module.nameParts[i]);
|
||||||
if (implicitImportInfo && implicitImportInfo.resolvedPaths.length) {
|
if (implicitImportInfo && implicitImportInfo.resolvedUris.length) {
|
||||||
loaderActions.path = implicitImportInfo.resolvedPaths[i];
|
loaderActions.uri = implicitImportInfo.resolvedUris[i];
|
||||||
loaderActions.loadSymbolsFromPath = true;
|
loaderActions.loadSymbolsFromPath = true;
|
||||||
this._addImplicitImportsToLoaderActions(implicitImportInfo, loaderActions);
|
this._addImplicitImportsToLoaderActions(implicitImportInfo, loaderActions);
|
||||||
}
|
}
|
||||||
@ -3486,7 +3489,7 @@ export class Binder extends ParseTreeWalker {
|
|||||||
type: DeclarationType.Intrinsic,
|
type: DeclarationType.Intrinsic,
|
||||||
node,
|
node,
|
||||||
intrinsicType: type,
|
intrinsicType: type,
|
||||||
path: this._fileInfo.filePath,
|
uri: this._fileInfo.fileUri,
|
||||||
range: getEmptyRange(),
|
range: getEmptyRange(),
|
||||||
moduleName: this._fileInfo.moduleName,
|
moduleName: this._fileInfo.moduleName,
|
||||||
isInExceptSuite: this._isInExceptSuite,
|
isInExceptSuite: this._isInExceptSuite,
|
||||||
@ -3561,7 +3564,7 @@ export class Binder extends ParseTreeWalker {
|
|||||||
inferredTypeSource: source,
|
inferredTypeSource: source,
|
||||||
isInferenceAllowedInPyTyped: this._isInferenceAllowedInPyTyped(name.value),
|
isInferenceAllowedInPyTyped: this._isInferenceAllowedInPyTyped(name.value),
|
||||||
typeAliasName: isPossibleTypeAlias ? target : undefined,
|
typeAliasName: isPossibleTypeAlias ? target : undefined,
|
||||||
path: this._fileInfo.filePath,
|
uri: this._fileInfo.fileUri,
|
||||||
range: convertTextRangeToRange(name, this._fileInfo.lines),
|
range: convertTextRangeToRange(name, this._fileInfo.lines),
|
||||||
moduleName: this._fileInfo.moduleName,
|
moduleName: this._fileInfo.moduleName,
|
||||||
isInExceptSuite: this._isInExceptSuite,
|
isInExceptSuite: this._isInExceptSuite,
|
||||||
@ -3609,7 +3612,7 @@ export class Binder extends ParseTreeWalker {
|
|||||||
isConstant: isConstantName(name.value),
|
isConstant: isConstantName(name.value),
|
||||||
inferredTypeSource: source,
|
inferredTypeSource: source,
|
||||||
isDefinedByMemberAccess: true,
|
isDefinedByMemberAccess: true,
|
||||||
path: this._fileInfo.filePath,
|
uri: this._fileInfo.fileUri,
|
||||||
range: convertTextRangeToRange(target.memberName, this._fileInfo.lines),
|
range: convertTextRangeToRange(target.memberName, this._fileInfo.lines),
|
||||||
moduleName: this._fileInfo.moduleName,
|
moduleName: this._fileInfo.moduleName,
|
||||||
isInExceptSuite: this._isInExceptSuite,
|
isInExceptSuite: this._isInExceptSuite,
|
||||||
@ -3701,7 +3704,7 @@ export class Binder extends ParseTreeWalker {
|
|||||||
isConstant: isConstantName(name.value),
|
isConstant: isConstantName(name.value),
|
||||||
isFinal: finalInfo.isFinal,
|
isFinal: finalInfo.isFinal,
|
||||||
typeAliasName: target,
|
typeAliasName: target,
|
||||||
path: this._fileInfo.filePath,
|
uri: this._fileInfo.fileUri,
|
||||||
typeAnnotationNode,
|
typeAnnotationNode,
|
||||||
range: convertTextRangeToRange(name, this._fileInfo.lines),
|
range: convertTextRangeToRange(name, this._fileInfo.lines),
|
||||||
moduleName: this._fileInfo.moduleName,
|
moduleName: this._fileInfo.moduleName,
|
||||||
@ -3774,7 +3777,7 @@ export class Binder extends ParseTreeWalker {
|
|||||||
isConstant: isConstantName(name.value),
|
isConstant: isConstantName(name.value),
|
||||||
isDefinedByMemberAccess: true,
|
isDefinedByMemberAccess: true,
|
||||||
isFinal: finalInfo.isFinal,
|
isFinal: finalInfo.isFinal,
|
||||||
path: this._fileInfo.filePath,
|
uri: this._fileInfo.fileUri,
|
||||||
typeAnnotationNode: finalInfo.isFinal && !finalInfo.finalTypeNode ? undefined : typeAnnotation,
|
typeAnnotationNode: finalInfo.isFinal && !finalInfo.finalTypeNode ? undefined : typeAnnotation,
|
||||||
range: convertTextRangeToRange(target.memberName, this._fileInfo.lines),
|
range: convertTextRangeToRange(target.memberName, this._fileInfo.lines),
|
||||||
moduleName: this._fileInfo.moduleName,
|
moduleName: this._fileInfo.moduleName,
|
||||||
@ -4021,14 +4024,14 @@ export class Binder extends ParseTreeWalker {
|
|||||||
? loaderActions.implicitImports.get(implicitImport.name)
|
? loaderActions.implicitImports.get(implicitImport.name)
|
||||||
: undefined;
|
: undefined;
|
||||||
if (existingLoaderAction) {
|
if (existingLoaderAction) {
|
||||||
existingLoaderAction.path = implicitImport.path;
|
existingLoaderAction.uri = implicitImport.uri;
|
||||||
existingLoaderAction.loadSymbolsFromPath = true;
|
existingLoaderAction.loadSymbolsFromPath = true;
|
||||||
} else {
|
} else {
|
||||||
if (!loaderActions.implicitImports) {
|
if (!loaderActions.implicitImports) {
|
||||||
loaderActions.implicitImports = new Map<string, ModuleLoaderActions>();
|
loaderActions.implicitImports = new Map<string, ModuleLoaderActions>();
|
||||||
}
|
}
|
||||||
loaderActions.implicitImports.set(implicitImport.name, {
|
loaderActions.implicitImports.set(implicitImport.name, {
|
||||||
path: implicitImport.path,
|
uri: implicitImport.uri,
|
||||||
loadSymbolsFromPath: true,
|
loadSymbolsFromPath: true,
|
||||||
implicitImports: new Map<string, ModuleLoaderActions>(),
|
implicitImports: new Map<string, ModuleLoaderActions>(),
|
||||||
});
|
});
|
||||||
@ -4095,7 +4098,7 @@ export class Binder extends ParseTreeWalker {
|
|||||||
symbol.addDeclaration({
|
symbol.addDeclaration({
|
||||||
type: DeclarationType.SpecialBuiltInClass,
|
type: DeclarationType.SpecialBuiltInClass,
|
||||||
node: annotationNode,
|
node: annotationNode,
|
||||||
path: this._fileInfo.filePath,
|
uri: this._fileInfo.fileUri,
|
||||||
range: convertTextRangeToRange(annotationNode, this._fileInfo.lines),
|
range: convertTextRangeToRange(annotationNode, this._fileInfo.lines),
|
||||||
moduleName: this._fileInfo.moduleName,
|
moduleName: this._fileInfo.moduleName,
|
||||||
isInExceptSuite: this._isInExceptSuite,
|
isInExceptSuite: this._isInExceptSuite,
|
||||||
|
@ -20,9 +20,9 @@ import { DiagnosticLevel } from '../common/configOptions';
|
|||||||
import { assert, assertNever } from '../common/debug';
|
import { assert, assertNever } from '../common/debug';
|
||||||
import { ActionKind, Diagnostic, DiagnosticAddendum, RenameShadowedFileAction } from '../common/diagnostic';
|
import { ActionKind, Diagnostic, DiagnosticAddendum, RenameShadowedFileAction } from '../common/diagnostic';
|
||||||
import { DiagnosticRule } from '../common/diagnosticRules';
|
import { DiagnosticRule } from '../common/diagnosticRules';
|
||||||
import { getFileExtension } from '../common/pathUtils';
|
|
||||||
import { PythonVersion, versionToString } from '../common/pythonVersion';
|
import { PythonVersion, versionToString } from '../common/pythonVersion';
|
||||||
import { TextRange } from '../common/textRange';
|
import { TextRange } from '../common/textRange';
|
||||||
|
import { Uri } from '../common/uri/uri';
|
||||||
import { DefinitionProvider } from '../languageService/definitionProvider';
|
import { DefinitionProvider } from '../languageService/definitionProvider';
|
||||||
import { Localizer } from '../localization/localize';
|
import { Localizer } from '../localization/localize';
|
||||||
import {
|
import {
|
||||||
@ -233,7 +233,9 @@ export class Checker extends ParseTreeWalker {
|
|||||||
const codeComplexity = AnalyzerNodeInfo.getCodeFlowComplexity(this._moduleNode);
|
const codeComplexity = AnalyzerNodeInfo.getCodeFlowComplexity(this._moduleNode);
|
||||||
|
|
||||||
if (isPrintCodeComplexityEnabled) {
|
if (isPrintCodeComplexityEnabled) {
|
||||||
console.log(`Code complexity of module ${this._fileInfo.filePath} is ${codeComplexity.toString()}`);
|
console.log(
|
||||||
|
`Code complexity of module ${this._fileInfo.fileUri.toUserVisibleString()} is ${codeComplexity.toString()}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (codeComplexity > maxCodeComplexity) {
|
if (codeComplexity > maxCodeComplexity) {
|
||||||
@ -1566,11 +1568,12 @@ export class Checker extends ParseTreeWalker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const resolvedAlias = this._evaluator.resolveAliasDeclaration(decl, /* resolveLocalNames */ true);
|
const resolvedAlias = this._evaluator.resolveAliasDeclaration(decl, /* resolveLocalNames */ true);
|
||||||
if (!resolvedAlias?.path || !isStubFile(resolvedAlias.path)) {
|
const resolvedAliasUri = resolvedAlias?.uri;
|
||||||
|
if (!resolvedAliasUri || !isStubFile(resolvedAliasUri)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const importResult = this._getImportResult(node, resolvedAlias.path);
|
const importResult = this._getImportResult(node, resolvedAliasUri);
|
||||||
if (!importResult) {
|
if (!importResult) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -1667,22 +1670,22 @@ export class Checker extends ParseTreeWalker {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getImportResult(node: ImportFromAsNode, filePath: string) {
|
private _getImportResult(node: ImportFromAsNode, uri: Uri) {
|
||||||
const execEnv = this._importResolver.getConfigOptions().findExecEnvironment(filePath);
|
const execEnv = this._importResolver.getConfigOptions().findExecEnvironment(uri);
|
||||||
const moduleNameNode = (node.parent as ImportFromNode).module;
|
const moduleNameNode = (node.parent as ImportFromNode).module;
|
||||||
|
|
||||||
// Handle both absolute and relative imports.
|
// Handle both absolute and relative imports.
|
||||||
const moduleName =
|
const moduleName =
|
||||||
moduleNameNode.leadingDots === 0
|
moduleNameNode.leadingDots === 0
|
||||||
? this._importResolver.getModuleNameForImport(filePath, execEnv).moduleName
|
? this._importResolver.getModuleNameForImport(uri, execEnv).moduleName
|
||||||
: getRelativeModuleName(this._importResolver.fileSystem, this._fileInfo.filePath, filePath);
|
: getRelativeModuleName(this._importResolver.fileSystem, this._fileInfo.fileUri, uri);
|
||||||
|
|
||||||
if (!moduleName) {
|
if (!moduleName) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this._importResolver.resolveImport(
|
return this._importResolver.resolveImport(
|
||||||
this._fileInfo.filePath,
|
this._fileInfo.fileUri,
|
||||||
execEnv,
|
execEnv,
|
||||||
createImportedModuleDescriptor(moduleName)
|
createImportedModuleDescriptor(moduleName)
|
||||||
);
|
);
|
||||||
@ -3087,7 +3090,7 @@ export class Checker extends ParseTreeWalker {
|
|||||||
if (diagnostic && overload.details.declaration) {
|
if (diagnostic && overload.details.declaration) {
|
||||||
diagnostic.addRelatedInfo(
|
diagnostic.addRelatedInfo(
|
||||||
Localizer.DiagnosticAddendum.overloadSignature(),
|
Localizer.DiagnosticAddendum.overloadSignature(),
|
||||||
overload.details.declaration?.path ?? primaryDecl.path,
|
overload.details.declaration?.uri ?? primaryDecl.uri,
|
||||||
overload.details.declaration?.range ?? primaryDecl.range
|
overload.details.declaration?.range ?? primaryDecl.range
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -3280,7 +3283,7 @@ export class Checker extends ParseTreeWalker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (primaryDeclNode) {
|
if (primaryDeclNode) {
|
||||||
diag.addRelatedInfo(primaryDeclInfo, primaryDecl.path, primaryDecl.range);
|
diag.addRelatedInfo(primaryDeclInfo, primaryDecl.uri, primaryDecl.range);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -4177,7 +4180,7 @@ export class Checker extends ParseTreeWalker {
|
|||||||
if (
|
if (
|
||||||
stdlibPath &&
|
stdlibPath &&
|
||||||
this._importResolver.isStdlibModule(desc, this._fileInfo.executionEnvironment) &&
|
this._importResolver.isStdlibModule(desc, this._fileInfo.executionEnvironment) &&
|
||||||
this._sourceMapper.isUserCode(this._fileInfo.filePath)
|
this._sourceMapper.isUserCode(this._fileInfo.fileUri)
|
||||||
) {
|
) {
|
||||||
// This means the user has a module that is overwriting the stdlib module.
|
// This means the user has a module that is overwriting the stdlib module.
|
||||||
const diag = this._evaluator.addDiagnosticForTextRange(
|
const diag = this._evaluator.addDiagnosticForTextRange(
|
||||||
@ -4186,7 +4189,7 @@ export class Checker extends ParseTreeWalker {
|
|||||||
DiagnosticRule.reportShadowedImports,
|
DiagnosticRule.reportShadowedImports,
|
||||||
Localizer.Diagnostic.stdlibModuleOverridden().format({
|
Localizer.Diagnostic.stdlibModuleOverridden().format({
|
||||||
name: moduleName,
|
name: moduleName,
|
||||||
path: this._fileInfo.filePath,
|
path: this._fileInfo.fileUri.toUserVisibleString(),
|
||||||
}),
|
}),
|
||||||
this._moduleNode
|
this._moduleNode
|
||||||
);
|
);
|
||||||
@ -4195,8 +4198,8 @@ export class Checker extends ParseTreeWalker {
|
|||||||
if (diag) {
|
if (diag) {
|
||||||
const renameAction: RenameShadowedFileAction = {
|
const renameAction: RenameShadowedFileAction = {
|
||||||
action: ActionKind.RenameShadowedFileAction,
|
action: ActionKind.RenameShadowedFileAction,
|
||||||
oldFile: this._fileInfo.filePath,
|
oldUri: this._fileInfo.fileUri,
|
||||||
newFile: this._sourceMapper.getNextFileName(this._fileInfo.filePath),
|
newUri: this._sourceMapper.getNextFileName(this._fileInfo.fileUri),
|
||||||
};
|
};
|
||||||
diag.addAction(renameAction);
|
diag.addAction(renameAction);
|
||||||
}
|
}
|
||||||
@ -4245,7 +4248,7 @@ export class Checker extends ParseTreeWalker {
|
|||||||
namePartNodes[namePartNodes.length - 1].start,
|
namePartNodes[namePartNodes.length - 1].start,
|
||||||
CancellationToken.None
|
CancellationToken.None
|
||||||
);
|
);
|
||||||
const paths = definitions ? definitions.map((d) => d.path) : [];
|
const paths = definitions ? definitions.map((d) => d.uri) : [];
|
||||||
paths.forEach((p) => {
|
paths.forEach((p) => {
|
||||||
if (!p.startsWith(stdlibPath) && !isStubFile(p) && this._sourceMapper.isUserCode(p)) {
|
if (!p.startsWith(stdlibPath) && !isStubFile(p) && this._sourceMapper.isUserCode(p)) {
|
||||||
// This means the user has a module that is overwriting the stdlib module.
|
// This means the user has a module that is overwriting the stdlib module.
|
||||||
@ -4254,7 +4257,7 @@ export class Checker extends ParseTreeWalker {
|
|||||||
DiagnosticRule.reportShadowedImports,
|
DiagnosticRule.reportShadowedImports,
|
||||||
Localizer.Diagnostic.stdlibModuleOverridden().format({
|
Localizer.Diagnostic.stdlibModuleOverridden().format({
|
||||||
name: nameParts.join('.'),
|
name: nameParts.join('.'),
|
||||||
path: p,
|
path: p.toUserVisibleString(),
|
||||||
}),
|
}),
|
||||||
node
|
node
|
||||||
);
|
);
|
||||||
@ -4262,8 +4265,8 @@ export class Checker extends ParseTreeWalker {
|
|||||||
if (diag) {
|
if (diag) {
|
||||||
const renameAction: RenameShadowedFileAction = {
|
const renameAction: RenameShadowedFileAction = {
|
||||||
action: ActionKind.RenameShadowedFileAction,
|
action: ActionKind.RenameShadowedFileAction,
|
||||||
oldFile: p,
|
oldUri: p,
|
||||||
newFile: this._sourceMapper.getNextFileName(p),
|
newUri: this._sourceMapper.getNextFileName(p),
|
||||||
};
|
};
|
||||||
diag.addAction(renameAction);
|
diag.addAction(renameAction);
|
||||||
}
|
}
|
||||||
@ -4755,7 +4758,7 @@ export class Checker extends ParseTreeWalker {
|
|||||||
decl.type !== DeclarationType.Function || ParseTreeUtils.isSuiteEmpty(decl.node.suite)
|
decl.type !== DeclarationType.Function || ParseTreeUtils.isSuiteEmpty(decl.node.suite)
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
if (getFileExtension(decls[0].path).toLowerCase() !== '.pyi') {
|
if (!decls[0].uri.hasExtension('.pyi')) {
|
||||||
if (!isSymbolImplemented(name)) {
|
if (!isSymbolImplemented(name)) {
|
||||||
diagAddendum.addMessage(
|
diagAddendum.addMessage(
|
||||||
Localizer.DiagnosticAddendum.missingProtocolMember().format({
|
Localizer.DiagnosticAddendum.missingProtocolMember().format({
|
||||||
@ -4881,7 +4884,7 @@ export class Checker extends ParseTreeWalker {
|
|||||||
if (fieldDecls.length > 0) {
|
if (fieldDecls.length > 0) {
|
||||||
diagnostic.addRelatedInfo(
|
diagnostic.addRelatedInfo(
|
||||||
Localizer.DiagnosticAddendum.dataClassFieldLocation(),
|
Localizer.DiagnosticAddendum.dataClassFieldLocation(),
|
||||||
fieldDecls[0].path,
|
fieldDecls[0].uri,
|
||||||
fieldDecls[0].range
|
fieldDecls[0].range
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -5103,7 +5106,16 @@ export class Checker extends ParseTreeWalker {
|
|||||||
const updatedClassType = ClassType.cloneWithNewTypeParameters(classType, updatedTypeParams);
|
const updatedClassType = ClassType.cloneWithNewTypeParameters(classType, updatedTypeParams);
|
||||||
|
|
||||||
const objectObject = ClassType.cloneAsInstance(objectType);
|
const objectObject = ClassType.cloneAsInstance(objectType);
|
||||||
const dummyTypeObject = ClassType.createInstantiable('__varianceDummy', '', '', '', 0, 0, undefined, undefined);
|
const dummyTypeObject = ClassType.createInstantiable(
|
||||||
|
'__varianceDummy',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
Uri.empty(),
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
undefined,
|
||||||
|
undefined
|
||||||
|
);
|
||||||
|
|
||||||
updatedTypeParams.forEach((param, paramIndex) => {
|
updatedTypeParams.forEach((param, paramIndex) => {
|
||||||
// Skip variadics and ParamSpecs.
|
// Skip variadics and ParamSpecs.
|
||||||
@ -5382,7 +5394,7 @@ export class Checker extends ParseTreeWalker {
|
|||||||
)
|
)
|
||||||
),
|
),
|
||||||
}),
|
}),
|
||||||
secondaryDecl.path,
|
secondaryDecl.uri,
|
||||||
secondaryDecl.range
|
secondaryDecl.range
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -5760,7 +5772,7 @@ export class Checker extends ParseTreeWalker {
|
|||||||
baseClass: this._evaluator.printType(convertToInstance(overriddenClassAndSymbol.classType)),
|
baseClass: this._evaluator.printType(convertToInstance(overriddenClassAndSymbol.classType)),
|
||||||
type: this._evaluator.printType(overriddenType),
|
type: this._evaluator.printType(overriddenType),
|
||||||
}),
|
}),
|
||||||
overriddenDecl.path,
|
overriddenDecl.uri,
|
||||||
overriddenDecl.range
|
overriddenDecl.range
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -5769,7 +5781,7 @@ export class Checker extends ParseTreeWalker {
|
|||||||
baseClass: this._evaluator.printType(convertToInstance(overrideClassAndSymbol.classType)),
|
baseClass: this._evaluator.printType(convertToInstance(overrideClassAndSymbol.classType)),
|
||||||
type: this._evaluator.printType(overrideType),
|
type: this._evaluator.printType(overrideType),
|
||||||
}),
|
}),
|
||||||
overrideDecl.path,
|
overrideDecl.uri,
|
||||||
overrideDecl.range
|
overrideDecl.range
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -6005,7 +6017,7 @@ export class Checker extends ParseTreeWalker {
|
|||||||
if (diag && origDecl) {
|
if (diag && origDecl) {
|
||||||
diag.addRelatedInfo(
|
diag.addRelatedInfo(
|
||||||
Localizer.DiagnosticAddendum.overriddenMethod(),
|
Localizer.DiagnosticAddendum.overriddenMethod(),
|
||||||
origDecl.path,
|
origDecl.uri,
|
||||||
origDecl.range
|
origDecl.range
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -6030,7 +6042,7 @@ export class Checker extends ParseTreeWalker {
|
|||||||
if (diag && origDecl) {
|
if (diag && origDecl) {
|
||||||
diag.addRelatedInfo(
|
diag.addRelatedInfo(
|
||||||
Localizer.DiagnosticAddendum.finalMethod(),
|
Localizer.DiagnosticAddendum.finalMethod(),
|
||||||
origDecl.path,
|
origDecl.uri,
|
||||||
origDecl.range
|
origDecl.range
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -6060,7 +6072,7 @@ export class Checker extends ParseTreeWalker {
|
|||||||
if (diag && origDecl) {
|
if (diag && origDecl) {
|
||||||
diag.addRelatedInfo(
|
diag.addRelatedInfo(
|
||||||
Localizer.DiagnosticAddendum.overriddenMethod(),
|
Localizer.DiagnosticAddendum.overriddenMethod(),
|
||||||
origDecl.path,
|
origDecl.uri,
|
||||||
origDecl.range
|
origDecl.range
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -6123,7 +6135,7 @@ export class Checker extends ParseTreeWalker {
|
|||||||
if (diag && origDecl) {
|
if (diag && origDecl) {
|
||||||
diag.addRelatedInfo(
|
diag.addRelatedInfo(
|
||||||
Localizer.DiagnosticAddendum.overriddenMethod(),
|
Localizer.DiagnosticAddendum.overriddenMethod(),
|
||||||
origDecl.path,
|
origDecl.uri,
|
||||||
origDecl.range
|
origDecl.range
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -6160,7 +6172,7 @@ export class Checker extends ParseTreeWalker {
|
|||||||
if (diag && origDecl) {
|
if (diag && origDecl) {
|
||||||
diag.addRelatedInfo(
|
diag.addRelatedInfo(
|
||||||
Localizer.DiagnosticAddendum.overriddenMethod(),
|
Localizer.DiagnosticAddendum.overriddenMethod(),
|
||||||
origDecl.path,
|
origDecl.uri,
|
||||||
origDecl.range
|
origDecl.range
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -6247,7 +6259,7 @@ export class Checker extends ParseTreeWalker {
|
|||||||
if (diag && origDecl) {
|
if (diag && origDecl) {
|
||||||
diag.addRelatedInfo(
|
diag.addRelatedInfo(
|
||||||
Localizer.DiagnosticAddendum.overriddenSymbol(),
|
Localizer.DiagnosticAddendum.overriddenSymbol(),
|
||||||
origDecl.path,
|
origDecl.uri,
|
||||||
origDecl.range
|
origDecl.range
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -6306,7 +6318,7 @@ export class Checker extends ParseTreeWalker {
|
|||||||
if (diag) {
|
if (diag) {
|
||||||
diag.addRelatedInfo(
|
diag.addRelatedInfo(
|
||||||
Localizer.DiagnosticAddendum.overriddenSymbol(),
|
Localizer.DiagnosticAddendum.overriddenSymbol(),
|
||||||
overrideFinalVarDecl.path,
|
overrideFinalVarDecl.uri,
|
||||||
overrideFinalVarDecl.range
|
overrideFinalVarDecl.range
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -6353,7 +6365,7 @@ export class Checker extends ParseTreeWalker {
|
|||||||
if (diag && origDecl) {
|
if (diag && origDecl) {
|
||||||
diag.addRelatedInfo(
|
diag.addRelatedInfo(
|
||||||
Localizer.DiagnosticAddendum.overriddenSymbol(),
|
Localizer.DiagnosticAddendum.overriddenSymbol(),
|
||||||
origDecl.path,
|
origDecl.uri,
|
||||||
origDecl.range
|
origDecl.range
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -10,10 +10,12 @@
|
|||||||
* by picking the alphabetically-first module in the cycle.
|
* by picking the alphabetically-first module in the cycle.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export class CircularDependency {
|
import { Uri } from '../common/uri/uri';
|
||||||
private _paths: string[] = [];
|
|
||||||
|
|
||||||
appendPath(path: string) {
|
export class CircularDependency {
|
||||||
|
private _paths: Uri[] = [];
|
||||||
|
|
||||||
|
appendPath(path: Uri) {
|
||||||
this._paths.push(path);
|
this._paths.push(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -867,7 +867,7 @@ function getDescriptorForConverterField(
|
|||||||
descriptorName,
|
descriptorName,
|
||||||
getClassFullName(converterNode, fileInfo.moduleName, descriptorName),
|
getClassFullName(converterNode, fileInfo.moduleName, descriptorName),
|
||||||
fileInfo.moduleName,
|
fileInfo.moduleName,
|
||||||
fileInfo.filePath,
|
fileInfo.fileUri,
|
||||||
ClassTypeFlags.None,
|
ClassTypeFlags.None,
|
||||||
getTypeSourceId(converterNode),
|
getTypeSourceId(converterNode),
|
||||||
/* declaredMetaclass */ undefined,
|
/* declaredMetaclass */ undefined,
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Range } from '../common/textRange';
|
import { Range } from '../common/textRange';
|
||||||
|
import { Uri } from '../common/uri/uri';
|
||||||
import {
|
import {
|
||||||
ClassNode,
|
ClassNode,
|
||||||
ExpressionNode,
|
ExpressionNode,
|
||||||
@ -31,7 +32,7 @@ import {
|
|||||||
YieldNode,
|
YieldNode,
|
||||||
} from '../parser/parseNodes';
|
} from '../parser/parseNodes';
|
||||||
|
|
||||||
export const UnresolvedModuleMarker = '*** unresolved ***';
|
export const UnresolvedModuleMarker = Uri.parse('unresolved-module-marker://**/*', /* isCaseSensitive */ true);
|
||||||
|
|
||||||
export const enum DeclarationType {
|
export const enum DeclarationType {
|
||||||
Intrinsic,
|
Intrinsic,
|
||||||
@ -57,9 +58,9 @@ export interface DeclarationBase {
|
|||||||
node: ParseNode;
|
node: ParseNode;
|
||||||
|
|
||||||
// The file and range within that file that
|
// The file and range within that file that
|
||||||
// contains the declaration. Unless this is an alias, then path refers to the
|
// contains the declaration. Unless this is an alias, then uri refers to the
|
||||||
// file the alias is referring to.
|
// file the alias is referring to.
|
||||||
path: string;
|
uri: Uri;
|
||||||
range: Range;
|
range: Range;
|
||||||
|
|
||||||
// The dot-separated import name for the file that
|
// The dot-separated import name for the file that
|
||||||
@ -222,10 +223,10 @@ export interface AliasDeclaration extends DeclarationBase {
|
|||||||
// This interface represents a set of actions that the python loader
|
// This interface represents a set of actions that the python loader
|
||||||
// performs when a module import is encountered.
|
// performs when a module import is encountered.
|
||||||
export interface ModuleLoaderActions {
|
export interface ModuleLoaderActions {
|
||||||
// The resolved path of the implicit import. This can be empty
|
// The resolved uri of the implicit import. This can be empty
|
||||||
// if the resolved path doesn't reference a module (e.g. it's
|
// if the resolved uri doesn't reference a module (e.g. it's
|
||||||
// a directory).
|
// a directory).
|
||||||
path: string;
|
uri: Uri;
|
||||||
|
|
||||||
// Is this a dummy entry for an unresolved import?
|
// Is this a dummy entry for an unresolved import?
|
||||||
isUnresolved?: boolean;
|
isUnresolved?: boolean;
|
||||||
@ -285,5 +286,5 @@ export function isIntrinsicDeclaration(decl: Declaration): decl is IntrinsicDecl
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function isUnresolvedAliasDeclaration(decl: Declaration): boolean {
|
export function isUnresolvedAliasDeclaration(decl: Declaration): boolean {
|
||||||
return isAliasDeclaration(decl) && decl.path === UnresolvedModuleMarker;
|
return isAliasDeclaration(decl) && decl.uri === UnresolvedModuleMarker;
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { getEmptyRange } from '../common/textRange';
|
import { getEmptyRange } from '../common/textRange';
|
||||||
|
import { Uri } from '../common/uri/uri';
|
||||||
import { NameNode, ParseNodeType } from '../parser/parseNodes';
|
import { NameNode, ParseNodeType } from '../parser/parseNodes';
|
||||||
import { ImportLookup, ImportLookupResult } from './analyzerFileInfo';
|
import { ImportLookup, ImportLookupResult } from './analyzerFileInfo';
|
||||||
import { AliasDeclaration, Declaration, DeclarationType, ModuleLoaderActions, isAliasDeclaration } from './declaration';
|
import { AliasDeclaration, Declaration, DeclarationType, ModuleLoaderActions, isAliasDeclaration } from './declaration';
|
||||||
@ -78,7 +79,7 @@ export function areDeclarationsSame(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (decl1.path !== decl2.path) {
|
if (!decl1.uri.equals(decl2.uri)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -174,16 +175,16 @@ export function getNameNodeForDeclaration(declaration: Declaration): NameNode |
|
|||||||
throw new Error(`Shouldn't reach here`);
|
throw new Error(`Shouldn't reach here`);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isDefinedInFile(decl: Declaration, filePath: string) {
|
export function isDefinedInFile(decl: Declaration, fileUri: Uri) {
|
||||||
if (isAliasDeclaration(decl)) {
|
if (isAliasDeclaration(decl)) {
|
||||||
// Alias decl's path points to the original symbol
|
// Alias decl's path points to the original symbol
|
||||||
// the alias is pointing to. So, we need to get the
|
// the alias is pointing to. So, we need to get the
|
||||||
// filepath in that the alias is defined from the node.
|
// filepath in that the alias is defined from the node.
|
||||||
return getFileInfoFromNode(decl.node)?.filePath === filePath;
|
return getFileInfoFromNode(decl.node)?.fileUri.equals(fileUri);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Other decls, the path points to the file the symbol is defined in.
|
// Other decls, the path points to the file the symbol is defined in.
|
||||||
return decl.path === filePath;
|
return decl.uri.equals(fileUri);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getDeclarationsWithUsesLocalNameRemoved(decls: Declaration[]) {
|
export function getDeclarationsWithUsesLocalNameRemoved(decls: Declaration[]) {
|
||||||
@ -199,13 +200,13 @@ export function getDeclarationsWithUsesLocalNameRemoved(decls: Declaration[]) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createSynthesizedAliasDeclaration(path: string): AliasDeclaration {
|
export function createSynthesizedAliasDeclaration(uri: Uri): AliasDeclaration {
|
||||||
// The only time this decl is used is for IDE services such as
|
// The only time this decl is used is for IDE services such as
|
||||||
// the find all references, hover provider and etc.
|
// the find all references, hover provider and etc.
|
||||||
return {
|
return {
|
||||||
type: DeclarationType.Alias,
|
type: DeclarationType.Alias,
|
||||||
node: undefined!,
|
node: undefined!,
|
||||||
path,
|
uri,
|
||||||
loadSymbolsFromPath: false,
|
loadSymbolsFromPath: false,
|
||||||
range: getEmptyRange(),
|
range: getEmptyRange(),
|
||||||
implicitImports: new Map<string, ModuleLoaderActions>(),
|
implicitImports: new Map<string, ModuleLoaderActions>(),
|
||||||
@ -267,8 +268,10 @@ export function resolveAliasDeclaration(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let lookupResult: ImportLookupResult | undefined;
|
let lookupResult: ImportLookupResult | undefined;
|
||||||
if (curDeclaration.path && curDeclaration.loadSymbolsFromPath) {
|
if (!curDeclaration.uri.isEmpty() && curDeclaration.loadSymbolsFromPath) {
|
||||||
lookupResult = importLookup(curDeclaration.path, { skipFileNeededCheck: options.skipFileNeededCheck });
|
lookupResult = importLookup(curDeclaration.uri, {
|
||||||
|
skipFileNeededCheck: options.skipFileNeededCheck,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const symbol: Symbol | undefined = lookupResult
|
const symbol: Symbol | undefined = lookupResult
|
||||||
@ -284,11 +287,11 @@ export function resolveAliasDeclaration(
|
|||||||
// when useLibraryCodeForTypes is disabled), b should be evaluated as Unknown,
|
// when useLibraryCodeForTypes is disabled), b should be evaluated as Unknown,
|
||||||
// not as a module.
|
// not as a module.
|
||||||
if (
|
if (
|
||||||
curDeclaration.path &&
|
!curDeclaration.uri.isEmpty() &&
|
||||||
curDeclaration.submoduleFallback.type === DeclarationType.Alias &&
|
curDeclaration.submoduleFallback.type === DeclarationType.Alias &&
|
||||||
curDeclaration.submoduleFallback.path
|
!curDeclaration.submoduleFallback.uri.isEmpty()
|
||||||
) {
|
) {
|
||||||
const lookupResult = importLookup(curDeclaration.submoduleFallback.path, {
|
const lookupResult = importLookup(curDeclaration.submoduleFallback.uri, {
|
||||||
skipFileNeededCheck: options.skipFileNeededCheck,
|
skipFileNeededCheck: options.skipFileNeededCheck,
|
||||||
skipParsing: true,
|
skipParsing: true,
|
||||||
});
|
});
|
||||||
@ -393,7 +396,7 @@ export function resolveAliasDeclaration(
|
|||||||
// the module is foo, and the foo.__init__.py file contains the statement
|
// the module is foo, and the foo.__init__.py file contains the statement
|
||||||
// "from foo import bar", we want to import the foo/bar.py submodule.
|
// "from foo import bar", we want to import the foo/bar.py submodule.
|
||||||
if (
|
if (
|
||||||
curDeclaration.path === declaration.path &&
|
curDeclaration.uri.equals(declaration.uri) &&
|
||||||
curDeclaration.type === DeclarationType.Alias &&
|
curDeclaration.type === DeclarationType.Alias &&
|
||||||
curDeclaration.submoduleFallback
|
curDeclaration.submoduleFallback
|
||||||
) {
|
) {
|
||||||
|
@ -81,7 +81,7 @@ export function createEnumType(
|
|||||||
className,
|
className,
|
||||||
getClassFullName(errorNode, fileInfo.moduleName, className),
|
getClassFullName(errorNode, fileInfo.moduleName, className),
|
||||||
fileInfo.moduleName,
|
fileInfo.moduleName,
|
||||||
fileInfo.filePath,
|
fileInfo.fileUri,
|
||||||
ClassTypeFlags.EnumClass,
|
ClassTypeFlags.EnumClass,
|
||||||
getTypeSourceId(errorNode),
|
getTypeSourceId(errorNode),
|
||||||
/* declaredMetaclass */ undefined,
|
/* declaredMetaclass */ undefined,
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -7,6 +7,7 @@
|
|||||||
* Interface that describes the output of the import resolver.
|
* Interface that describes the output of the import resolver.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { Uri } from '../common/uri/uri';
|
||||||
import { PyTypedInfo } from './pyTypedUtils';
|
import { PyTypedInfo } from './pyTypedUtils';
|
||||||
|
|
||||||
export const enum ImportType {
|
export const enum ImportType {
|
||||||
@ -19,7 +20,7 @@ export interface ImplicitImport {
|
|||||||
isStubFile: boolean;
|
isStubFile: boolean;
|
||||||
isNativeLib: boolean;
|
isNativeLib: boolean;
|
||||||
name: string;
|
name: string;
|
||||||
path: string;
|
uri: Uri;
|
||||||
pyTypedInfo?: PyTypedInfo | undefined;
|
pyTypedInfo?: PyTypedInfo | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,11 +63,11 @@ export interface ImportResult {
|
|||||||
// The resolved absolute paths for each of the files in the module name.
|
// The resolved absolute paths for each of the files in the module name.
|
||||||
// Parts that have no files (e.g. directories within a namespace
|
// Parts that have no files (e.g. directories within a namespace
|
||||||
// package) have empty strings for a resolvedPath.
|
// package) have empty strings for a resolvedPath.
|
||||||
resolvedPaths: string[];
|
resolvedUris: Uri[];
|
||||||
|
|
||||||
// For absolute imports, the search path that was used to resolve
|
// For absolute imports, the search path that was used to resolve
|
||||||
// (or partially resolve) the module.
|
// (or partially resolve) the module.
|
||||||
searchPath?: string;
|
searchPath?: Uri;
|
||||||
|
|
||||||
// True if resolved file is a type hint (.pyi) file rather than
|
// True if resolved file is a type hint (.pyi) file rather than
|
||||||
// a python (.py) file.
|
// a python (.py) file.
|
||||||
@ -103,5 +104,5 @@ export interface ImportResult {
|
|||||||
pyTypedInfo?: PyTypedInfo | undefined;
|
pyTypedInfo?: PyTypedInfo | undefined;
|
||||||
|
|
||||||
// The directory of the package, if found.
|
// The directory of the package, if found.
|
||||||
packageDirectory?: string | undefined;
|
packageDirectory?: Uri | undefined;
|
||||||
}
|
}
|
||||||
|
@ -14,16 +14,11 @@ import { throwIfCancellationRequested } from '../common/cancellationUtils';
|
|||||||
import { addIfUnique, appendArray, createMapFromItems } from '../common/collectionUtils';
|
import { addIfUnique, appendArray, createMapFromItems } from '../common/collectionUtils';
|
||||||
import { TextEditAction } from '../common/editAction';
|
import { TextEditAction } from '../common/editAction';
|
||||||
import { ReadOnlyFileSystem } from '../common/fileSystem';
|
import { ReadOnlyFileSystem } from '../common/fileSystem';
|
||||||
import {
|
|
||||||
getDirectoryPath,
|
|
||||||
getFileName,
|
|
||||||
getRelativePathComponentsFromDirectory,
|
|
||||||
isFile,
|
|
||||||
stripFileExtension,
|
|
||||||
} from '../common/pathUtils';
|
|
||||||
import { convertOffsetToPosition, convertPositionToOffset } from '../common/positionUtils';
|
import { convertOffsetToPosition, convertPositionToOffset } from '../common/positionUtils';
|
||||||
import { compareStringsCaseSensitive } from '../common/stringUtils';
|
import { compareStringsCaseSensitive } from '../common/stringUtils';
|
||||||
import { Position, Range, TextRange } from '../common/textRange';
|
import { Position, Range, TextRange } from '../common/textRange';
|
||||||
|
import { Uri } from '../common/uri/uri';
|
||||||
|
import { isFile } from '../common/uri/uriUtils';
|
||||||
import {
|
import {
|
||||||
ImportAsNode,
|
ImportAsNode,
|
||||||
ImportFromAsNode,
|
ImportFromAsNode,
|
||||||
@ -35,18 +30,18 @@ import {
|
|||||||
ParseNodeType,
|
ParseNodeType,
|
||||||
} from '../parser/parseNodes';
|
} from '../parser/parseNodes';
|
||||||
import { ParseResults } from '../parser/parser';
|
import { ParseResults } from '../parser/parser';
|
||||||
|
import { TokenType } from '../parser/tokenizerTypes';
|
||||||
import * as AnalyzerNodeInfo from './analyzerNodeInfo';
|
import * as AnalyzerNodeInfo from './analyzerNodeInfo';
|
||||||
import { ModuleNameAndType } from './importResolver';
|
import { ModuleNameAndType } from './importResolver';
|
||||||
import { ImportResult, ImportType } from './importResult';
|
import { ImportResult, ImportType } from './importResult';
|
||||||
import * as SymbolNameUtils from './symbolNameUtils';
|
|
||||||
import { findTokenAfter, getTokenAt } from './parseTreeUtils';
|
import { findTokenAfter, getTokenAt } from './parseTreeUtils';
|
||||||
import { TokenType } from '../parser/tokenizerTypes';
|
import * as SymbolNameUtils from './symbolNameUtils';
|
||||||
|
|
||||||
export interface ImportStatement {
|
export interface ImportStatement {
|
||||||
node: ImportNode | ImportFromNode;
|
node: ImportNode | ImportFromNode;
|
||||||
subnode?: ImportAsNode;
|
subnode?: ImportAsNode;
|
||||||
importResult: ImportResult | undefined;
|
importResult: ImportResult | undefined;
|
||||||
resolvedPath: string | undefined;
|
resolvedPath: Uri | undefined;
|
||||||
moduleName: string;
|
moduleName: string;
|
||||||
followsNonImportStatement: boolean;
|
followsNonImportStatement: boolean;
|
||||||
}
|
}
|
||||||
@ -625,10 +620,10 @@ function _getInsertionEditForAutoImportInsertion(
|
|||||||
function _processImportNode(node: ImportNode, localImports: ImportStatements, followsNonImportStatement: boolean) {
|
function _processImportNode(node: ImportNode, localImports: ImportStatements, followsNonImportStatement: boolean) {
|
||||||
node.list.forEach((importAsNode) => {
|
node.list.forEach((importAsNode) => {
|
||||||
const importResult = AnalyzerNodeInfo.getImportInfo(importAsNode.module);
|
const importResult = AnalyzerNodeInfo.getImportInfo(importAsNode.module);
|
||||||
let resolvedPath: string | undefined;
|
let resolvedPath: Uri | undefined;
|
||||||
|
|
||||||
if (importResult && importResult.isImportFound) {
|
if (importResult && importResult.isImportFound) {
|
||||||
resolvedPath = importResult.resolvedPaths[importResult.resolvedPaths.length - 1];
|
resolvedPath = importResult.resolvedUris[importResult.resolvedUris.length - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
const localImport: ImportStatement = {
|
const localImport: ImportStatement = {
|
||||||
@ -647,8 +642,8 @@ function _processImportNode(node: ImportNode, localImports: ImportStatements, fo
|
|||||||
// Don't overwrite existing import or import from statements
|
// Don't overwrite existing import or import from statements
|
||||||
// because we always want to prefer 'import from' over 'import'
|
// because we always want to prefer 'import from' over 'import'
|
||||||
// in the map.
|
// in the map.
|
||||||
if (!localImports.mapByFilePath.has(resolvedPath)) {
|
if (!localImports.mapByFilePath.has(resolvedPath.key)) {
|
||||||
localImports.mapByFilePath.set(resolvedPath, localImport);
|
localImports.mapByFilePath.set(resolvedPath.key, localImport);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -661,10 +656,10 @@ function _processImportFromNode(
|
|||||||
includeImplicitImports: boolean
|
includeImplicitImports: boolean
|
||||||
) {
|
) {
|
||||||
const importResult = AnalyzerNodeInfo.getImportInfo(node.module);
|
const importResult = AnalyzerNodeInfo.getImportInfo(node.module);
|
||||||
let resolvedPath: string | undefined;
|
let resolvedPath: Uri | undefined;
|
||||||
|
|
||||||
if (importResult && importResult.isImportFound) {
|
if (importResult && importResult.isImportFound) {
|
||||||
resolvedPath = importResult.resolvedPaths[importResult.resolvedPaths.length - 1];
|
resolvedPath = importResult.resolvedUris[importResult.resolvedUris.length - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (includeImplicitImports && importResult) {
|
if (includeImplicitImports && importResult) {
|
||||||
@ -673,7 +668,7 @@ function _processImportFromNode(
|
|||||||
for (const implicitImport of importResult.implicitImports.values()) {
|
for (const implicitImport of importResult.implicitImports.values()) {
|
||||||
const importFromAs = node.imports.find((i) => i.name.value === implicitImport.name);
|
const importFromAs = node.imports.find((i) => i.name.value === implicitImport.name);
|
||||||
if (importFromAs) {
|
if (importFromAs) {
|
||||||
localImports.implicitImports.set(implicitImport.path, importFromAs);
|
localImports.implicitImports.set(implicitImport.uri.key, importFromAs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -690,7 +685,7 @@ function _processImportFromNode(
|
|||||||
|
|
||||||
// Add it to the map.
|
// Add it to the map.
|
||||||
if (resolvedPath) {
|
if (resolvedPath) {
|
||||||
const prevEntry = localImports.mapByFilePath.get(resolvedPath);
|
const prevEntry = localImports.mapByFilePath.get(resolvedPath.key);
|
||||||
// Overwrite existing import statements because we always want to prefer
|
// Overwrite existing import statements because we always want to prefer
|
||||||
// 'import from' over 'import'. Also, overwrite existing 'import from' if
|
// 'import from' over 'import'. Also, overwrite existing 'import from' if
|
||||||
// the module name is shorter.
|
// the module name is shorter.
|
||||||
@ -699,7 +694,7 @@ function _processImportFromNode(
|
|||||||
prevEntry.node.nodeType === ParseNodeType.Import ||
|
prevEntry.node.nodeType === ParseNodeType.Import ||
|
||||||
prevEntry.moduleName.length > localImport.moduleName.length
|
prevEntry.moduleName.length > localImport.moduleName.length
|
||||||
) {
|
) {
|
||||||
localImports.mapByFilePath.set(resolvedPath, localImport);
|
localImports.mapByFilePath.set(resolvedPath.key, localImport);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -850,23 +845,23 @@ function getConsecutiveNumberPairs(indices: number[]) {
|
|||||||
|
|
||||||
export function getRelativeModuleName(
|
export function getRelativeModuleName(
|
||||||
fs: ReadOnlyFileSystem,
|
fs: ReadOnlyFileSystem,
|
||||||
sourcePath: string,
|
sourcePath: Uri,
|
||||||
targetPath: string,
|
targetPath: Uri,
|
||||||
ignoreFolderStructure = false,
|
ignoreFolderStructure = false,
|
||||||
sourceIsFile?: boolean
|
sourceIsFile?: boolean
|
||||||
) {
|
) {
|
||||||
let srcPath = sourcePath;
|
let srcPath = sourcePath;
|
||||||
sourceIsFile = sourceIsFile !== undefined ? sourceIsFile : isFile(fs, sourcePath);
|
sourceIsFile = sourceIsFile !== undefined ? sourceIsFile : isFile(fs, sourcePath);
|
||||||
if (sourceIsFile) {
|
if (sourceIsFile) {
|
||||||
srcPath = getDirectoryPath(sourcePath);
|
srcPath = sourcePath.getDirectory();
|
||||||
}
|
}
|
||||||
|
|
||||||
let symbolName: string | undefined;
|
let symbolName: string | undefined;
|
||||||
let destPath = targetPath;
|
let destPath = targetPath;
|
||||||
if (sourceIsFile) {
|
if (sourceIsFile) {
|
||||||
destPath = getDirectoryPath(targetPath);
|
destPath = targetPath.getDirectory();
|
||||||
|
|
||||||
const fileName = stripFileExtension(getFileName(targetPath));
|
const fileName = targetPath.stripAllExtensions().fileName;
|
||||||
if (fileName !== '__init__') {
|
if (fileName !== '__init__') {
|
||||||
// ex) src: a.py, dest: b.py -> ".b" will be returned.
|
// ex) src: a.py, dest: b.py -> ".b" will be returned.
|
||||||
symbolName = fileName;
|
symbolName = fileName;
|
||||||
@ -875,18 +870,18 @@ export function getRelativeModuleName(
|
|||||||
// like how it would return for sibling folder.
|
// like how it would return for sibling folder.
|
||||||
//
|
//
|
||||||
// if folder structure is not ignored, ".." will be returned
|
// if folder structure is not ignored, ".." will be returned
|
||||||
symbolName = getFileName(destPath);
|
symbolName = destPath.fileName;
|
||||||
destPath = getDirectoryPath(destPath);
|
destPath = destPath.getDirectory();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const relativePaths = getRelativePathComponentsFromDirectory(srcPath, destPath, (f) => fs.realCasePath(f));
|
const relativePaths = srcPath.getRelativePathComponents(destPath);
|
||||||
|
|
||||||
// This assumes both file paths are under the same importing root.
|
// This assumes both file paths are under the same importing root.
|
||||||
// So this doesn't handle paths pointing to 2 different import roots.
|
// So this doesn't handle paths pointing to 2 different import roots.
|
||||||
// ex) user file A to library file B
|
// ex) user file A to library file B
|
||||||
let currentPaths = '.';
|
let currentPaths = '.';
|
||||||
for (let i = 1; i < relativePaths.length; i++) {
|
for (let i = 0; i < relativePaths.length; i++) {
|
||||||
const relativePath = relativePaths[i];
|
const relativePath = relativePaths[i];
|
||||||
if (relativePath === '..') {
|
if (relativePath === '..') {
|
||||||
currentPaths += '.';
|
currentPaths += '.';
|
||||||
@ -907,25 +902,25 @@ export function getRelativeModuleName(
|
|||||||
return currentPaths;
|
return currentPaths;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getDirectoryLeadingDotsPointsTo(fromDirectory: string, leadingDots: number) {
|
export function getDirectoryLeadingDotsPointsTo(fromDirectory: Uri, leadingDots: number) {
|
||||||
let currentDirectory = fromDirectory;
|
let currentDirectory = fromDirectory;
|
||||||
for (let i = 1; i < leadingDots; i++) {
|
for (let i = 1; i < leadingDots; i++) {
|
||||||
if (currentDirectory === '') {
|
if (currentDirectory.isRoot()) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
currentDirectory = getDirectoryPath(currentDirectory);
|
currentDirectory = currentDirectory.getDirectory();
|
||||||
}
|
}
|
||||||
|
|
||||||
return currentDirectory;
|
return currentDirectory;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getResolvedFilePath(importResult: ImportResult | undefined) {
|
export function getResolvedFilePath(importResult: ImportResult | undefined) {
|
||||||
if (!importResult || !importResult.isImportFound || importResult.resolvedPaths.length === 0) {
|
if (!importResult || !importResult.isImportFound || importResult.resolvedUris.length === 0) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (importResult.resolvedPaths.length === 1 && importResult.resolvedPaths[0] === '') {
|
if (importResult.resolvedUris.length === 1 && importResult.resolvedUris[0].equals(Uri.empty())) {
|
||||||
// Import is resolved to namespace package folder.
|
// Import is resolved to namespace package folder.
|
||||||
if (importResult.packageDirectory) {
|
if (importResult.packageDirectory) {
|
||||||
return importResult.packageDirectory;
|
return importResult.packageDirectory;
|
||||||
@ -940,7 +935,7 @@ export function getResolvedFilePath(importResult: ImportResult | undefined) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Regular case.
|
// Regular case.
|
||||||
return importResult.resolvedPaths[importResult.resolvedPaths.length - 1];
|
return importResult.resolvedUris[importResult.resolvedUris.length - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function haveSameParentModule(module1: string[], module2: string[]) {
|
export function haveSameParentModule(module1: string[], module2: string[]) {
|
||||||
|
@ -114,7 +114,7 @@ export function createNamedTupleType(
|
|||||||
className,
|
className,
|
||||||
ParseTreeUtils.getClassFullName(errorNode, fileInfo.moduleName, className),
|
ParseTreeUtils.getClassFullName(errorNode, fileInfo.moduleName, className),
|
||||||
fileInfo.moduleName,
|
fileInfo.moduleName,
|
||||||
fileInfo.filePath,
|
fileInfo.fileUri,
|
||||||
ClassTypeFlags.ReadOnlyInstanceVariables,
|
ClassTypeFlags.ReadOnlyInstanceVariables,
|
||||||
ParseTreeUtils.getTypeSourceId(errorNode),
|
ParseTreeUtils.getTypeSourceId(errorNode),
|
||||||
/* declaredMetaclass */ undefined,
|
/* declaredMetaclass */ undefined,
|
||||||
@ -207,7 +207,7 @@ export function createNamedTupleType(
|
|||||||
type: DeclarationType.Variable,
|
type: DeclarationType.Variable,
|
||||||
node: stringNode as StringListNode,
|
node: stringNode as StringListNode,
|
||||||
isRuntimeTypeExpression: true,
|
isRuntimeTypeExpression: true,
|
||||||
path: fileInfo.filePath,
|
uri: fileInfo.fileUri,
|
||||||
range: convertOffsetsToRange(
|
range: convertOffsetsToRange(
|
||||||
stringNode.start,
|
stringNode.start,
|
||||||
TextRange.getEnd(stringNode),
|
TextRange.getEnd(stringNode),
|
||||||
@ -303,7 +303,7 @@ export function createNamedTupleType(
|
|||||||
const declaration: VariableDeclaration = {
|
const declaration: VariableDeclaration = {
|
||||||
type: DeclarationType.Variable,
|
type: DeclarationType.Variable,
|
||||||
node: entryNameNode,
|
node: entryNameNode,
|
||||||
path: fileInfo.filePath,
|
uri: fileInfo.fileUri,
|
||||||
typeAnnotationNode: entryTypeNode,
|
typeAnnotationNode: entryTypeNode,
|
||||||
range: convertOffsetsToRange(
|
range: convertOffsetsToRange(
|
||||||
entryNameNode.start,
|
entryNameNode.start,
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Diagnostic, DiagnosticWithinFile } from '../common/diagnostic';
|
import { Diagnostic, DiagnosticWithinFile } from '../common/diagnostic';
|
||||||
|
import { Uri } from '../common/uri/uri';
|
||||||
import { ScopeType } from './scope';
|
import { ScopeType } from './scope';
|
||||||
|
|
||||||
export enum SymbolCategory {
|
export enum SymbolCategory {
|
||||||
@ -37,7 +38,7 @@ export interface SymbolInfo {
|
|||||||
category: SymbolCategory;
|
category: SymbolCategory;
|
||||||
name: string;
|
name: string;
|
||||||
fullName: string;
|
fullName: string;
|
||||||
filePath: string;
|
fileUri: Uri;
|
||||||
isExported: boolean;
|
isExported: boolean;
|
||||||
typeKnownStatus: TypeKnownStatus;
|
typeKnownStatus: TypeKnownStatus;
|
||||||
referenceCount: number;
|
referenceCount: number;
|
||||||
@ -47,7 +48,7 @@ export interface SymbolInfo {
|
|||||||
|
|
||||||
export interface ModuleInfo {
|
export interface ModuleInfo {
|
||||||
name: string;
|
name: string;
|
||||||
path: string;
|
uri: Uri;
|
||||||
isExported: boolean;
|
isExported: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,10 +58,10 @@ export interface PackageTypeReport {
|
|||||||
packageName: string;
|
packageName: string;
|
||||||
moduleName: string;
|
moduleName: string;
|
||||||
ignoreExternal: boolean;
|
ignoreExternal: boolean;
|
||||||
packageRootDirectory: string | undefined;
|
packageRootDirectoryUri: Uri | undefined;
|
||||||
moduleRootDirectory: string | undefined;
|
moduleRootDirectoryUri: Uri | undefined;
|
||||||
isModuleSingleFile: boolean;
|
isModuleSingleFile: boolean;
|
||||||
pyTypedPath: string | undefined;
|
pyTypedPathUri: Uri | undefined;
|
||||||
missingFunctionDocStringCount: number;
|
missingFunctionDocStringCount: number;
|
||||||
missingClassDocStringCount: number;
|
missingClassDocStringCount: number;
|
||||||
missingDefaultParamCount: number;
|
missingDefaultParamCount: number;
|
||||||
@ -85,20 +86,20 @@ export interface PackageTypeReport {
|
|||||||
|
|
||||||
export function getEmptyReport(
|
export function getEmptyReport(
|
||||||
packageName: string,
|
packageName: string,
|
||||||
packageRootDirectory: string,
|
packageRootUri: Uri,
|
||||||
moduleName: string,
|
moduleName: string,
|
||||||
moduleRootDirectory: string,
|
moduleRootUri: Uri,
|
||||||
isModuleSingleFile: boolean,
|
isModuleSingleFile: boolean,
|
||||||
ignoreExternal: boolean
|
ignoreExternal: boolean
|
||||||
) {
|
) {
|
||||||
const report: PackageTypeReport = {
|
const report: PackageTypeReport = {
|
||||||
packageName,
|
packageName,
|
||||||
ignoreExternal,
|
ignoreExternal,
|
||||||
packageRootDirectory,
|
packageRootDirectoryUri: packageRootUri,
|
||||||
moduleName,
|
moduleName,
|
||||||
moduleRootDirectory,
|
moduleRootDirectoryUri: moduleRootUri,
|
||||||
isModuleSingleFile,
|
isModuleSingleFile,
|
||||||
pyTypedPath: undefined,
|
pyTypedPathUri: undefined,
|
||||||
missingFunctionDocStringCount: 0,
|
missingFunctionDocStringCount: 0,
|
||||||
missingClassDocStringCount: 0,
|
missingClassDocStringCount: 0,
|
||||||
missingDefaultParamCount: 0,
|
missingDefaultParamCount: 0,
|
||||||
|
@ -14,16 +14,11 @@ import { NullConsole } from '../common/console';
|
|||||||
import { assert } from '../common/debug';
|
import { assert } from '../common/debug';
|
||||||
import { Diagnostic, DiagnosticAddendum, DiagnosticCategory } from '../common/diagnostic';
|
import { Diagnostic, DiagnosticAddendum, DiagnosticCategory } from '../common/diagnostic';
|
||||||
import { FullAccessHost } from '../common/fullAccessHost';
|
import { FullAccessHost } from '../common/fullAccessHost';
|
||||||
import {
|
import { getFileExtension, stripFileExtension } from '../common/pathUtils';
|
||||||
combinePaths,
|
|
||||||
getDirectoryPath,
|
|
||||||
getFileExtension,
|
|
||||||
getFileName,
|
|
||||||
stripFileExtension,
|
|
||||||
tryStat,
|
|
||||||
} from '../common/pathUtils';
|
|
||||||
import { ServiceProvider } from '../common/serviceProvider';
|
import { ServiceProvider } from '../common/serviceProvider';
|
||||||
import { getEmptyRange, Range } from '../common/textRange';
|
import { getEmptyRange, Range } from '../common/textRange';
|
||||||
|
import { Uri } from '../common/uri/uri';
|
||||||
|
import { tryStat } from '../common/uri/uriUtils';
|
||||||
import { DeclarationType, FunctionDeclaration, VariableDeclaration } from './declaration';
|
import { DeclarationType, FunctionDeclaration, VariableDeclaration } from './declaration';
|
||||||
import { createImportedModuleDescriptor, ImportResolver } from './importResolver';
|
import { createImportedModuleDescriptor, ImportResolver } from './importResolver';
|
||||||
import {
|
import {
|
||||||
@ -67,7 +62,7 @@ import {
|
|||||||
type PublicSymbolSet = Set<string>;
|
type PublicSymbolSet = Set<string>;
|
||||||
|
|
||||||
interface ModuleDirectoryInfo {
|
interface ModuleDirectoryInfo {
|
||||||
moduleDirectory: string;
|
moduleDirectory: Uri;
|
||||||
isModuleSingleFile: boolean;
|
isModuleSingleFile: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,8 +78,8 @@ export class PackageTypeVerifier {
|
|||||||
private _packageName: string,
|
private _packageName: string,
|
||||||
private _ignoreExternal = false
|
private _ignoreExternal = false
|
||||||
) {
|
) {
|
||||||
const host = new FullAccessHost(_serviceProvider.fs());
|
const host = new FullAccessHost(_serviceProvider);
|
||||||
this._configOptions = new ConfigOptions('');
|
this._configOptions = new ConfigOptions(Uri.empty());
|
||||||
|
|
||||||
this._configOptions.defaultPythonPlatform = commandLineOptions.pythonPlatform;
|
this._configOptions.defaultPythonPlatform = commandLineOptions.pythonPlatform;
|
||||||
this._configOptions.defaultPythonVersion = commandLineOptions.pythonVersion;
|
this._configOptions.defaultPythonVersion = commandLineOptions.pythonVersion;
|
||||||
@ -99,11 +94,11 @@ export class PackageTypeVerifier {
|
|||||||
this._configOptions.evaluateUnknownImportsAsAny = true;
|
this._configOptions.evaluateUnknownImportsAsAny = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._execEnv = this._configOptions.findExecEnvironment('.');
|
this._execEnv = this._configOptions.findExecEnvironment(Uri.file('.', _serviceProvider.fs().isCaseSensitive));
|
||||||
this._importResolver = new ImportResolver(
|
this._importResolver = new ImportResolver(
|
||||||
this._serviceProvider,
|
this._serviceProvider,
|
||||||
this._configOptions,
|
this._configOptions,
|
||||||
new FullAccessHost(this._serviceProvider.fs())
|
new FullAccessHost(this._serviceProvider)
|
||||||
);
|
);
|
||||||
this._program = new Program(this._importResolver, this._configOptions, this._serviceProvider);
|
this._program = new Program(this._importResolver, this._configOptions, this._serviceProvider);
|
||||||
}
|
}
|
||||||
@ -117,9 +112,9 @@ export class PackageTypeVerifier {
|
|||||||
|
|
||||||
const report = getEmptyReport(
|
const report = getEmptyReport(
|
||||||
moduleNameParts[0],
|
moduleNameParts[0],
|
||||||
packageDirectoryInfo?.moduleDirectory ?? '',
|
packageDirectoryInfo?.moduleDirectory ?? Uri.empty(),
|
||||||
trimmedModuleName,
|
trimmedModuleName,
|
||||||
moduleDirectoryInfo?.moduleDirectory ?? '',
|
moduleDirectoryInfo?.moduleDirectory ?? Uri.empty(),
|
||||||
moduleDirectoryInfo?.isModuleSingleFile ?? false,
|
moduleDirectoryInfo?.isModuleSingleFile ?? false,
|
||||||
this._ignoreExternal
|
this._ignoreExternal
|
||||||
);
|
);
|
||||||
@ -134,7 +129,7 @@ export class PackageTypeVerifier {
|
|||||||
getEmptyRange()
|
getEmptyRange()
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
} else if (!report.moduleRootDirectory) {
|
} else if (!report.moduleRootDirectoryUri) {
|
||||||
commonDiagnostics.push(
|
commonDiagnostics.push(
|
||||||
new Diagnostic(
|
new Diagnostic(
|
||||||
DiagnosticCategory.Error,
|
DiagnosticCategory.Error,
|
||||||
@ -144,14 +139,14 @@ export class PackageTypeVerifier {
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
let pyTypedInfo: PyTypedInfo | undefined;
|
let pyTypedInfo: PyTypedInfo | undefined;
|
||||||
if (report.moduleRootDirectory) {
|
if (report.moduleRootDirectoryUri) {
|
||||||
pyTypedInfo = this._getDeepestPyTypedInfo(report.moduleRootDirectory, moduleNameParts);
|
pyTypedInfo = this._getDeepestPyTypedInfo(report.moduleRootDirectoryUri, moduleNameParts);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we couldn't find any "py.typed" info in the module path, search again
|
// If we couldn't find any "py.typed" info in the module path, search again
|
||||||
// starting at the package root.
|
// starting at the package root.
|
||||||
if (!pyTypedInfo && report.packageRootDirectory) {
|
if (!pyTypedInfo && report.packageRootDirectoryUri) {
|
||||||
pyTypedInfo = this._getDeepestPyTypedInfo(report.packageRootDirectory, moduleNameParts);
|
pyTypedInfo = this._getDeepestPyTypedInfo(report.packageRootDirectoryUri, moduleNameParts);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!pyTypedInfo) {
|
if (!pyTypedInfo) {
|
||||||
@ -159,10 +154,10 @@ export class PackageTypeVerifier {
|
|||||||
new Diagnostic(DiagnosticCategory.Error, 'No py.typed file found', getEmptyRange())
|
new Diagnostic(DiagnosticCategory.Error, 'No py.typed file found', getEmptyRange())
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
report.pyTypedPath = pyTypedInfo.pyTypedPath;
|
report.pyTypedPathUri = pyTypedInfo.pyTypedPath;
|
||||||
|
|
||||||
const publicModules = this._getListOfPublicModules(
|
const publicModules = this._getListOfPublicModules(
|
||||||
report.moduleRootDirectory,
|
report.moduleRootDirectoryUri,
|
||||||
report.isModuleSingleFile,
|
report.isModuleSingleFile,
|
||||||
trimmedModuleName
|
trimmedModuleName
|
||||||
);
|
);
|
||||||
@ -239,12 +234,12 @@ export class PackageTypeVerifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getDeepestPyTypedInfo(rootDirectory: string, packageNameParts: string[]) {
|
private _getDeepestPyTypedInfo(rootDirectory: Uri, packageNameParts: string[]) {
|
||||||
let subNameParts = Array.from(packageNameParts);
|
let subNameParts = Array.from(packageNameParts);
|
||||||
|
|
||||||
// Find the deepest py.typed file that corresponds to the requested submodule.
|
// Find the deepest py.typed file that corresponds to the requested submodule.
|
||||||
while (subNameParts.length >= 1) {
|
while (subNameParts.length >= 1) {
|
||||||
const packageSubdir = combinePaths(rootDirectory, ...subNameParts.slice(1));
|
const packageSubdir = rootDirectory.combinePaths(...subNameParts.slice(1));
|
||||||
const pyTypedInfo = getPyTypedInfo(this._serviceProvider.fs(), packageSubdir);
|
const pyTypedInfo = getPyTypedInfo(this._serviceProvider.fs(), packageSubdir);
|
||||||
if (pyTypedInfo) {
|
if (pyTypedInfo) {
|
||||||
return pyTypedInfo;
|
return pyTypedInfo;
|
||||||
@ -257,7 +252,11 @@ export class PackageTypeVerifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _resolveImport(moduleName: string) {
|
private _resolveImport(moduleName: string) {
|
||||||
return this._importResolver.resolveImport('', this._execEnv, createImportedModuleDescriptor(moduleName));
|
return this._importResolver.resolveImport(
|
||||||
|
Uri.empty(),
|
||||||
|
this._execEnv,
|
||||||
|
createImportedModuleDescriptor(moduleName)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getPublicSymbolsForModule(
|
private _getPublicSymbolsForModule(
|
||||||
@ -268,7 +267,7 @@ export class PackageTypeVerifier {
|
|||||||
const importResult = this._resolveImport(moduleName);
|
const importResult = this._resolveImport(moduleName);
|
||||||
|
|
||||||
if (importResult.isImportFound) {
|
if (importResult.isImportFound) {
|
||||||
const modulePath = importResult.resolvedPaths[importResult.resolvedPaths.length - 1];
|
const modulePath = importResult.resolvedUris[importResult.resolvedUris.length - 1];
|
||||||
this._program.addTrackedFiles([modulePath], /* isThirdPartyImport */ true, /* isInPyTypedPackage */ true);
|
this._program.addTrackedFiles([modulePath], /* isThirdPartyImport */ true, /* isInPyTypedPackage */ true);
|
||||||
|
|
||||||
const sourceFile = this._program.getBoundSourceFile(modulePath);
|
const sourceFile = this._program.getBoundSourceFile(modulePath);
|
||||||
@ -276,7 +275,7 @@ export class PackageTypeVerifier {
|
|||||||
if (sourceFile) {
|
if (sourceFile) {
|
||||||
const module: ModuleInfo = {
|
const module: ModuleInfo = {
|
||||||
name: moduleName,
|
name: moduleName,
|
||||||
path: modulePath,
|
uri: modulePath,
|
||||||
isExported: true,
|
isExported: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -379,15 +378,15 @@ export class PackageTypeVerifier {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
const modulePath = importResult.resolvedPaths[importResult.resolvedPaths.length - 1];
|
const modulePath = importResult.resolvedUris[importResult.resolvedUris.length - 1];
|
||||||
|
|
||||||
const module: ModuleInfo = {
|
const module: ModuleInfo = {
|
||||||
name: moduleName,
|
name: moduleName,
|
||||||
path: modulePath,
|
uri: modulePath,
|
||||||
isExported: true,
|
isExported: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
report.modules.set(modulePath, module);
|
report.modules.set(modulePath.key, module);
|
||||||
this._program.addTrackedFiles([modulePath], /* isThirdPartyImport */ true, /* isInPyTypedPackage */ true);
|
this._program.addTrackedFiles([modulePath], /* isThirdPartyImport */ true, /* isInPyTypedPackage */ true);
|
||||||
|
|
||||||
const sourceFile = this._program.getBoundSourceFile(modulePath);
|
const sourceFile = this._program.getBoundSourceFile(modulePath);
|
||||||
@ -413,9 +412,9 @@ export class PackageTypeVerifier {
|
|||||||
|
|
||||||
// Scans the directory structure for a list of public modules
|
// Scans the directory structure for a list of public modules
|
||||||
// within the package.
|
// within the package.
|
||||||
private _getListOfPublicModules(moduleRootPath: string, isModuleSingleFile: boolean, moduleName: string): string[] {
|
private _getListOfPublicModules(moduleRoot: Uri, isModuleSingleFile: boolean, moduleName: string): string[] {
|
||||||
const publicModules: string[] = [];
|
const publicModules: string[] = [];
|
||||||
this._addPublicModulesRecursive(moduleRootPath, isModuleSingleFile, moduleName, publicModules);
|
this._addPublicModulesRecursive(moduleRoot, isModuleSingleFile, moduleName, publicModules);
|
||||||
|
|
||||||
// Make sure modules are unique. There may be duplicates if a ".py" and ".pyi"
|
// Make sure modules are unique. There may be duplicates if a ".py" and ".pyi"
|
||||||
// exist for some modules.
|
// exist for some modules.
|
||||||
@ -433,7 +432,7 @@ export class PackageTypeVerifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _addPublicModulesRecursive(
|
private _addPublicModulesRecursive(
|
||||||
dirPath: string,
|
dirPath: Uri,
|
||||||
isModuleSingleFile: boolean,
|
isModuleSingleFile: boolean,
|
||||||
modulePath: string,
|
modulePath: string,
|
||||||
publicModules: string[]
|
publicModules: string[]
|
||||||
@ -444,7 +443,7 @@ export class PackageTypeVerifier {
|
|||||||
let isFile = entry.isFile();
|
let isFile = entry.isFile();
|
||||||
let isDirectory = entry.isDirectory();
|
let isDirectory = entry.isDirectory();
|
||||||
if (entry.isSymbolicLink()) {
|
if (entry.isSymbolicLink()) {
|
||||||
const stat = tryStat(this._serviceProvider.fs(), combinePaths(dirPath, entry.name));
|
const stat = tryStat(this._serviceProvider.fs(), dirPath.combinePaths(entry.name));
|
||||||
if (stat) {
|
if (stat) {
|
||||||
isFile = stat.isFile();
|
isFile = stat.isFile();
|
||||||
isDirectory = stat.isDirectory();
|
isDirectory = stat.isDirectory();
|
||||||
@ -477,7 +476,7 @@ export class PackageTypeVerifier {
|
|||||||
} else if (isDirectory) {
|
} else if (isDirectory) {
|
||||||
if (!isPrivateOrProtectedName(entry.name) && this._isLegalModulePartName(entry.name)) {
|
if (!isPrivateOrProtectedName(entry.name) && this._isLegalModulePartName(entry.name)) {
|
||||||
this._addPublicModulesRecursive(
|
this._addPublicModulesRecursive(
|
||||||
combinePaths(dirPath, entry.name),
|
dirPath.combinePaths(entry.name),
|
||||||
isModuleSingleFile,
|
isModuleSingleFile,
|
||||||
`${modulePath}.${entry.name}`,
|
`${modulePath}.${entry.name}`,
|
||||||
publicModules
|
publicModules
|
||||||
@ -572,7 +571,7 @@ export class PackageTypeVerifier {
|
|||||||
const decls = symbol.getDeclarations();
|
const decls = symbol.getDeclarations();
|
||||||
const primaryDecl = decls.length > 0 ? decls[decls.length - 1] : undefined;
|
const primaryDecl = decls.length > 0 ? decls[decls.length - 1] : undefined;
|
||||||
const declRange = primaryDecl?.range || getEmptyRange();
|
const declRange = primaryDecl?.range || getEmptyRange();
|
||||||
const declPath = primaryDecl?.path || '';
|
const declPath = primaryDecl?.uri || Uri.empty();
|
||||||
const symbolCategory = this._getSymbolCategory(symbol, symbolType);
|
const symbolCategory = this._getSymbolCategory(symbol, symbolType);
|
||||||
const isExported = publicSymbols.has(fullName);
|
const isExported = publicSymbols.has(fullName);
|
||||||
|
|
||||||
@ -590,7 +589,7 @@ export class PackageTypeVerifier {
|
|||||||
category: symbolCategory,
|
category: symbolCategory,
|
||||||
name,
|
name,
|
||||||
fullName,
|
fullName,
|
||||||
filePath: module.path,
|
fileUri: Uri.file(module.path, this._serviceProvider.fs().isCaseSensitive),
|
||||||
isExported,
|
isExported,
|
||||||
typeKnownStatus: TypeKnownStatus.Known,
|
typeKnownStatus: TypeKnownStatus.Known,
|
||||||
referenceCount: 1,
|
referenceCount: 1,
|
||||||
@ -616,7 +615,7 @@ export class PackageTypeVerifier {
|
|||||||
const decls = symbol.getDeclarations();
|
const decls = symbol.getDeclarations();
|
||||||
const primaryDecl = decls.length > 0 ? decls[decls.length - 1] : undefined;
|
const primaryDecl = decls.length > 0 ? decls[decls.length - 1] : undefined;
|
||||||
const declRange = primaryDecl?.range || getEmptyRange();
|
const declRange = primaryDecl?.range || getEmptyRange();
|
||||||
const declPath = primaryDecl?.path || '';
|
const declPath = primaryDecl?.uri || Uri.empty();
|
||||||
|
|
||||||
const extraInfo = new DiagnosticAddendum();
|
const extraInfo = new DiagnosticAddendum();
|
||||||
if (baseSymbolType) {
|
if (baseSymbolType) {
|
||||||
@ -664,7 +663,7 @@ export class PackageTypeVerifier {
|
|||||||
symbolInfo: SymbolInfo,
|
symbolInfo: SymbolInfo,
|
||||||
type: Type,
|
type: Type,
|
||||||
declRange: Range,
|
declRange: Range,
|
||||||
declFilePath: string,
|
declFileUri: Uri,
|
||||||
publicSymbols: PublicSymbolSet,
|
publicSymbols: PublicSymbolSet,
|
||||||
skipDocStringCheck = false
|
skipDocStringCheck = false
|
||||||
): TypeKnownStatus {
|
): TypeKnownStatus {
|
||||||
@ -677,7 +676,7 @@ export class PackageTypeVerifier {
|
|||||||
symbolInfo,
|
symbolInfo,
|
||||||
`Type argument ${index + 1} for type alias "${type.typeAliasInfo!.name}" has unknown type`,
|
`Type argument ${index + 1} for type alias "${type.typeAliasInfo!.name}" has unknown type`,
|
||||||
declRange,
|
declRange,
|
||||||
declFilePath
|
declFileUri
|
||||||
);
|
);
|
||||||
knownStatus = TypeKnownStatus.Unknown;
|
knownStatus = TypeKnownStatus.Unknown;
|
||||||
} else if (isPartlyUnknown(typeArg)) {
|
} else if (isPartlyUnknown(typeArg)) {
|
||||||
@ -687,7 +686,7 @@ export class PackageTypeVerifier {
|
|||||||
type.typeAliasInfo!.name
|
type.typeAliasInfo!.name
|
||||||
}" has partially unknown type`,
|
}" has partially unknown type`,
|
||||||
declRange,
|
declRange,
|
||||||
declFilePath
|
declFileUri
|
||||||
);
|
);
|
||||||
knownStatus = TypeKnownStatus.PartiallyUnknown;
|
knownStatus = TypeKnownStatus.PartiallyUnknown;
|
||||||
}
|
}
|
||||||
@ -702,7 +701,7 @@ export class PackageTypeVerifier {
|
|||||||
'Type is missing type annotation and could be inferred differently by type checkers' +
|
'Type is missing type annotation and could be inferred differently by type checkers' +
|
||||||
ambiguousDiag.getString(),
|
ambiguousDiag.getString(),
|
||||||
declRange,
|
declRange,
|
||||||
declFilePath
|
declFileUri
|
||||||
);
|
);
|
||||||
knownStatus = this._updateKnownStatusIfWorse(knownStatus, TypeKnownStatus.Ambiguous);
|
knownStatus = this._updateKnownStatusIfWorse(knownStatus, TypeKnownStatus.Ambiguous);
|
||||||
}
|
}
|
||||||
@ -721,7 +720,7 @@ export class PackageTypeVerifier {
|
|||||||
symbolInfo.fullName
|
symbolInfo.fullName
|
||||||
}"`,
|
}"`,
|
||||||
declRange,
|
declRange,
|
||||||
declFilePath
|
declFileUri
|
||||||
);
|
);
|
||||||
knownStatus = this._updateKnownStatusIfWorse(knownStatus, TypeKnownStatus.Unknown);
|
knownStatus = this._updateKnownStatusIfWorse(knownStatus, TypeKnownStatus.Unknown);
|
||||||
break;
|
break;
|
||||||
@ -736,7 +735,7 @@ export class PackageTypeVerifier {
|
|||||||
symbolInfo,
|
symbolInfo,
|
||||||
subtype,
|
subtype,
|
||||||
declRange,
|
declRange,
|
||||||
declFilePath,
|
declFileUri,
|
||||||
publicSymbols
|
publicSymbols
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@ -753,7 +752,7 @@ export class PackageTypeVerifier {
|
|||||||
symbolInfo,
|
symbolInfo,
|
||||||
overload,
|
overload,
|
||||||
declRange,
|
declRange,
|
||||||
declFilePath,
|
declFileUri,
|
||||||
publicSymbols
|
publicSymbols
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@ -771,7 +770,7 @@ export class PackageTypeVerifier {
|
|||||||
publicSymbols,
|
publicSymbols,
|
||||||
symbolInfo,
|
symbolInfo,
|
||||||
declRange,
|
declRange,
|
||||||
declFilePath,
|
declFileUri,
|
||||||
undefined /* diag */,
|
undefined /* diag */,
|
||||||
skipDocStringCheck
|
skipDocStringCheck
|
||||||
)
|
)
|
||||||
@ -821,7 +820,7 @@ export class PackageTypeVerifier {
|
|||||||
symbolInfo,
|
symbolInfo,
|
||||||
accessType,
|
accessType,
|
||||||
getEmptyRange(),
|
getEmptyRange(),
|
||||||
'',
|
Uri.empty(),
|
||||||
publicSymbols,
|
publicSymbols,
|
||||||
skipDocStringCheck
|
skipDocStringCheck
|
||||||
)
|
)
|
||||||
@ -847,7 +846,7 @@ export class PackageTypeVerifier {
|
|||||||
symbolInfo,
|
symbolInfo,
|
||||||
`Type argument ${index + 1} for class "${type.details.name}" has unknown type`,
|
`Type argument ${index + 1} for class "${type.details.name}" has unknown type`,
|
||||||
declRange,
|
declRange,
|
||||||
declFilePath
|
declFileUri
|
||||||
);
|
);
|
||||||
knownStatus = this._updateKnownStatusIfWorse(knownStatus, TypeKnownStatus.Unknown);
|
knownStatus = this._updateKnownStatusIfWorse(knownStatus, TypeKnownStatus.Unknown);
|
||||||
} else if (isPartlyUnknown(typeArg)) {
|
} else if (isPartlyUnknown(typeArg)) {
|
||||||
@ -859,7 +858,7 @@ export class PackageTypeVerifier {
|
|||||||
type.details.name
|
type.details.name
|
||||||
}" has partially unknown type` + diag.getString(),
|
}" has partially unknown type` + diag.getString(),
|
||||||
declRange,
|
declRange,
|
||||||
declFilePath
|
declFileUri
|
||||||
);
|
);
|
||||||
knownStatus = this._updateKnownStatusIfWorse(knownStatus, TypeKnownStatus.PartiallyUnknown);
|
knownStatus = this._updateKnownStatusIfWorse(knownStatus, TypeKnownStatus.PartiallyUnknown);
|
||||||
}
|
}
|
||||||
@ -877,7 +876,7 @@ export class PackageTypeVerifier {
|
|||||||
symbolInfo,
|
symbolInfo,
|
||||||
`Module "${moduleSymbol.fullName}" is partially unknown`,
|
`Module "${moduleSymbol.fullName}" is partially unknown`,
|
||||||
declRange,
|
declRange,
|
||||||
declFilePath
|
declFileUri
|
||||||
);
|
);
|
||||||
knownStatus = this._updateKnownStatusIfWorse(knownStatus, moduleSymbol.typeKnownStatus);
|
knownStatus = this._updateKnownStatusIfWorse(knownStatus, moduleSymbol.typeKnownStatus);
|
||||||
}
|
}
|
||||||
@ -899,15 +898,15 @@ export class PackageTypeVerifier {
|
|||||||
publicSymbols: PublicSymbolSet,
|
publicSymbols: PublicSymbolSet,
|
||||||
symbolInfo?: SymbolInfo,
|
symbolInfo?: SymbolInfo,
|
||||||
declRange?: Range,
|
declRange?: Range,
|
||||||
declFilePath?: string,
|
declFileUri?: Uri,
|
||||||
diag?: DiagnosticAddendum,
|
diag?: DiagnosticAddendum,
|
||||||
skipDocStringCheck = false
|
skipDocStringCheck = false
|
||||||
): TypeKnownStatus {
|
): TypeKnownStatus {
|
||||||
let knownStatus = TypeKnownStatus.Known;
|
let knownStatus = TypeKnownStatus.Known;
|
||||||
|
|
||||||
// If the file path wasn't provided, try to get it from the type.
|
// If the file path wasn't provided, try to get it from the type.
|
||||||
if (type.details.declaration && !declFilePath) {
|
if (type.details.declaration && !declFileUri) {
|
||||||
declFilePath = type.details.declaration.path;
|
declFileUri = type.details.declaration.uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
type.details.parameters.forEach((param, index) => {
|
type.details.parameters.forEach((param, index) => {
|
||||||
@ -929,7 +928,7 @@ export class PackageTypeVerifier {
|
|||||||
symbolInfo,
|
symbolInfo,
|
||||||
`Type annotation for parameter "${param.name}" is missing`,
|
`Type annotation for parameter "${param.name}" is missing`,
|
||||||
declRange ?? getEmptyRange(),
|
declRange ?? getEmptyRange(),
|
||||||
declFilePath ?? ''
|
declFileUri ?? Uri.empty()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
diag?.createAddendum().addMessage(`Type annotation for parameter "${param.name}" is missing`);
|
diag?.createAddendum().addMessage(`Type annotation for parameter "${param.name}" is missing`);
|
||||||
@ -941,7 +940,7 @@ export class PackageTypeVerifier {
|
|||||||
symbolInfo,
|
symbolInfo,
|
||||||
`Type of parameter "${param.name}" is unknown`,
|
`Type of parameter "${param.name}" is unknown`,
|
||||||
declRange ?? getEmptyRange(),
|
declRange ?? getEmptyRange(),
|
||||||
declFilePath ?? ''
|
declFileUri ?? Uri.empty()
|
||||||
);
|
);
|
||||||
diag?.createAddendum().addMessage(`Type of parameter "${param.name}" is unknown`);
|
diag?.createAddendum().addMessage(`Type of parameter "${param.name}" is unknown`);
|
||||||
}
|
}
|
||||||
@ -963,7 +962,7 @@ export class PackageTypeVerifier {
|
|||||||
symbolInfo,
|
symbolInfo,
|
||||||
`Type of parameter "${param.name}" is partially unknown` + extraInfo.getString(),
|
`Type of parameter "${param.name}" is partially unknown` + extraInfo.getString(),
|
||||||
declRange ?? getEmptyRange(),
|
declRange ?? getEmptyRange(),
|
||||||
declFilePath ?? ''
|
declFileUri ?? Uri.empty()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -986,7 +985,7 @@ export class PackageTypeVerifier {
|
|||||||
symbolInfo,
|
symbolInfo,
|
||||||
`Return type is unknown`,
|
`Return type is unknown`,
|
||||||
declRange ?? getEmptyRange(),
|
declRange ?? getEmptyRange(),
|
||||||
declFilePath ?? ''
|
declFileUri ?? Uri.empty()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
knownStatus = this._updateKnownStatusIfWorse(knownStatus, TypeKnownStatus.Unknown);
|
knownStatus = this._updateKnownStatusIfWorse(knownStatus, TypeKnownStatus.Unknown);
|
||||||
@ -1009,7 +1008,7 @@ export class PackageTypeVerifier {
|
|||||||
symbolInfo,
|
symbolInfo,
|
||||||
`Return type is partially unknown` + extraInfo.getString(),
|
`Return type is partially unknown` + extraInfo.getString(),
|
||||||
declRange ?? getEmptyRange(),
|
declRange ?? getEmptyRange(),
|
||||||
declFilePath ?? ''
|
declFileUri ?? Uri.empty()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1030,7 +1029,7 @@ export class PackageTypeVerifier {
|
|||||||
symbolInfo,
|
symbolInfo,
|
||||||
`Return type annotation is missing`,
|
`Return type annotation is missing`,
|
||||||
declRange ?? getEmptyRange(),
|
declRange ?? getEmptyRange(),
|
||||||
declFilePath ?? ''
|
declFileUri ?? Uri.empty()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
diag?.createAddendum().addMessage(`Return type annotation is missing`);
|
diag?.createAddendum().addMessage(`Return type annotation is missing`);
|
||||||
@ -1060,7 +1059,7 @@ export class PackageTypeVerifier {
|
|||||||
symbolInfo,
|
symbolInfo,
|
||||||
`No docstring found for function "${symbolInfo.fullName}"`,
|
`No docstring found for function "${symbolInfo.fullName}"`,
|
||||||
declRange ?? getEmptyRange(),
|
declRange ?? getEmptyRange(),
|
||||||
declFilePath ?? ''
|
declFileUri ?? Uri.empty()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1074,7 +1073,7 @@ export class PackageTypeVerifier {
|
|||||||
symbolInfo,
|
symbolInfo,
|
||||||
`One or more default values in function "${symbolInfo.fullName}" is specified as "..."`,
|
`One or more default values in function "${symbolInfo.fullName}" is specified as "..."`,
|
||||||
declRange ?? getEmptyRange(),
|
declRange ?? getEmptyRange(),
|
||||||
declFilePath ?? ''
|
declFileUri ?? Uri.empty()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1100,7 +1099,7 @@ export class PackageTypeVerifier {
|
|||||||
category: SymbolCategory.Class,
|
category: SymbolCategory.Class,
|
||||||
name: type.details.name,
|
name: type.details.name,
|
||||||
fullName: type.details.fullName,
|
fullName: type.details.fullName,
|
||||||
filePath: type.details.filePath,
|
fileUri: type.details.fileUri,
|
||||||
isExported: publicSymbols.has(type.details.fullName),
|
isExported: publicSymbols.has(type.details.fullName),
|
||||||
typeKnownStatus: TypeKnownStatus.Known,
|
typeKnownStatus: TypeKnownStatus.Known,
|
||||||
referenceCount: 1,
|
referenceCount: 1,
|
||||||
@ -1116,7 +1115,7 @@ export class PackageTypeVerifier {
|
|||||||
symbolInfo,
|
symbolInfo,
|
||||||
`No docstring found for class "${type.details.fullName}"`,
|
`No docstring found for class "${type.details.fullName}"`,
|
||||||
getEmptyRange(),
|
getEmptyRange(),
|
||||||
''
|
Uri.empty()
|
||||||
);
|
);
|
||||||
|
|
||||||
report.missingClassDocStringCount++;
|
report.missingClassDocStringCount++;
|
||||||
@ -1155,7 +1154,7 @@ export class PackageTypeVerifier {
|
|||||||
// Add information for the metaclass.
|
// Add information for the metaclass.
|
||||||
if (type.details.effectiveMetaclass) {
|
if (type.details.effectiveMetaclass) {
|
||||||
if (!isInstantiableClass(type.details.effectiveMetaclass)) {
|
if (!isInstantiableClass(type.details.effectiveMetaclass)) {
|
||||||
this._addSymbolError(symbolInfo, `Type of metaclass unknown`, getEmptyRange(), '');
|
this._addSymbolError(symbolInfo, `Type of metaclass unknown`, getEmptyRange(), Uri.empty());
|
||||||
symbolInfo.typeKnownStatus = this._updateKnownStatusIfWorse(
|
symbolInfo.typeKnownStatus = this._updateKnownStatusIfWorse(
|
||||||
symbolInfo.typeKnownStatus,
|
symbolInfo.typeKnownStatus,
|
||||||
TypeKnownStatus.PartiallyUnknown
|
TypeKnownStatus.PartiallyUnknown
|
||||||
@ -1175,7 +1174,7 @@ export class PackageTypeVerifier {
|
|||||||
`Type of metaclass "${type.details.effectiveMetaclass}" is partially unknown` +
|
`Type of metaclass "${type.details.effectiveMetaclass}" is partially unknown` +
|
||||||
diag.getString(),
|
diag.getString(),
|
||||||
getEmptyRange(),
|
getEmptyRange(),
|
||||||
''
|
Uri.empty()
|
||||||
);
|
);
|
||||||
symbolInfo.typeKnownStatus = this._updateKnownStatusIfWorse(
|
symbolInfo.typeKnownStatus = this._updateKnownStatusIfWorse(
|
||||||
symbolInfo.typeKnownStatus,
|
symbolInfo.typeKnownStatus,
|
||||||
@ -1188,7 +1187,7 @@ export class PackageTypeVerifier {
|
|||||||
// Add information for base classes.
|
// Add information for base classes.
|
||||||
type.details.baseClasses.forEach((baseClass) => {
|
type.details.baseClasses.forEach((baseClass) => {
|
||||||
if (!isInstantiableClass(baseClass)) {
|
if (!isInstantiableClass(baseClass)) {
|
||||||
this._addSymbolError(symbolInfo, `Type of base class unknown`, getEmptyRange(), '');
|
this._addSymbolError(symbolInfo, `Type of base class unknown`, getEmptyRange(), Uri.empty());
|
||||||
symbolInfo.typeKnownStatus = this._updateKnownStatusIfWorse(
|
symbolInfo.typeKnownStatus = this._updateKnownStatusIfWorse(
|
||||||
symbolInfo.typeKnownStatus,
|
symbolInfo.typeKnownStatus,
|
||||||
TypeKnownStatus.PartiallyUnknown
|
TypeKnownStatus.PartiallyUnknown
|
||||||
@ -1208,7 +1207,7 @@ export class PackageTypeVerifier {
|
|||||||
symbolInfo,
|
symbolInfo,
|
||||||
`Type of base class "${baseClass.details.fullName}" is partially unknown` + diag.getString(),
|
`Type of base class "${baseClass.details.fullName}" is partially unknown` + diag.getString(),
|
||||||
getEmptyRange(),
|
getEmptyRange(),
|
||||||
''
|
Uri.empty()
|
||||||
);
|
);
|
||||||
|
|
||||||
symbolInfo.typeKnownStatus = this._updateKnownStatusIfWorse(
|
symbolInfo.typeKnownStatus = this._updateKnownStatusIfWorse(
|
||||||
@ -1238,7 +1237,7 @@ export class PackageTypeVerifier {
|
|||||||
category: SymbolCategory.Module,
|
category: SymbolCategory.Module,
|
||||||
name: type.moduleName,
|
name: type.moduleName,
|
||||||
fullName: type.moduleName,
|
fullName: type.moduleName,
|
||||||
filePath: type.filePath,
|
fileUri: type.fileUri,
|
||||||
isExported: publicSymbols.has(type.moduleName),
|
isExported: publicSymbols.has(type.moduleName),
|
||||||
typeKnownStatus: TypeKnownStatus.Known,
|
typeKnownStatus: TypeKnownStatus.Known,
|
||||||
referenceCount: 1,
|
referenceCount: 1,
|
||||||
@ -1448,19 +1447,21 @@ export class PackageTypeVerifier {
|
|||||||
|
|
||||||
private _getDirectoryInfoForModule(moduleName: string): ModuleDirectoryInfo | undefined {
|
private _getDirectoryInfoForModule(moduleName: string): ModuleDirectoryInfo | undefined {
|
||||||
const importResult = this._importResolver.resolveImport(
|
const importResult = this._importResolver.resolveImport(
|
||||||
'',
|
Uri.empty(),
|
||||||
this._execEnv,
|
this._execEnv,
|
||||||
createImportedModuleDescriptor(moduleName)
|
createImportedModuleDescriptor(moduleName)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (importResult.isImportFound) {
|
if (importResult.isImportFound) {
|
||||||
const resolvedPath = importResult.resolvedPaths[importResult.resolvedPaths.length - 1];
|
const resolvedPath = importResult.resolvedUris[importResult.resolvedUris.length - 1];
|
||||||
|
|
||||||
// If it's a namespace package with no __init__.py(i), use the package
|
// If it's a namespace package with no __init__.py(i), use the package
|
||||||
// directory instead.
|
// directory instead.
|
||||||
const moduleDirectory = resolvedPath ? getDirectoryPath(resolvedPath) : importResult.packageDirectory ?? '';
|
const moduleDirectory = resolvedPath
|
||||||
|
? resolvedPath.getDirectory()
|
||||||
|
: importResult.packageDirectory ?? Uri.empty();
|
||||||
let isModuleSingleFile = false;
|
let isModuleSingleFile = false;
|
||||||
if (resolvedPath && stripFileExtension(getFileName(resolvedPath)) !== '__init__') {
|
if (resolvedPath && stripFileExtension(resolvedPath.fileName) !== '__init__') {
|
||||||
isModuleSingleFile = true;
|
isModuleSingleFile = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1508,17 +1509,17 @@ export class PackageTypeVerifier {
|
|||||||
report.symbols.set(symbolInfo.fullName, symbolInfo);
|
report.symbols.set(symbolInfo.fullName, symbolInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _addSymbolError(symbolInfo: SymbolInfo, message: string, declRange: Range, declFilePath: string) {
|
private _addSymbolError(symbolInfo: SymbolInfo, message: string, declRange: Range, declUri: Uri) {
|
||||||
symbolInfo.diagnostics.push({
|
symbolInfo.diagnostics.push({
|
||||||
diagnostic: new Diagnostic(DiagnosticCategory.Error, message, declRange),
|
diagnostic: new Diagnostic(DiagnosticCategory.Error, message, declRange),
|
||||||
filePath: declFilePath,
|
uri: declUri,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _addSymbolWarning(symbolInfo: SymbolInfo, message: string, declRange: Range, declFilePath: string) {
|
private _addSymbolWarning(symbolInfo: SymbolInfo, message: string, declRange: Range, declUri: Uri) {
|
||||||
symbolInfo.diagnostics.push({
|
symbolInfo.diagnostics.push({
|
||||||
diagnostic: new Diagnostic(DiagnosticCategory.Warning, message, declRange),
|
diagnostic: new Diagnostic(DiagnosticCategory.Warning, message, declRange),
|
||||||
filePath: declFilePath,
|
uri: declUri,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,46 +9,46 @@
|
|||||||
|
|
||||||
import { getOrAdd } from '../common/collectionUtils';
|
import { getOrAdd } from '../common/collectionUtils';
|
||||||
import { FileSystem } from '../common/fileSystem';
|
import { FileSystem } from '../common/fileSystem';
|
||||||
import { ensureTrailingDirectorySeparator, normalizePath, realCasePath } from '../common/pathUtils';
|
import { Uri } from '../common/uri/uri';
|
||||||
import { ImportResult } from './importResult';
|
import { ImportResult } from './importResult';
|
||||||
|
|
||||||
export type ImportPath = { importPath: string | undefined };
|
export type ImportPath = { importPath: Uri | undefined };
|
||||||
|
|
||||||
type CacheEntry = { importResult: ImportResult; path: string; importName: string };
|
type CacheEntry = { importResult: ImportResult; path: Uri; importName: string };
|
||||||
|
|
||||||
export class ParentDirectoryCache {
|
export class ParentDirectoryCache {
|
||||||
private readonly _importChecked = new Map<string, Map<string, ImportPath>>();
|
private readonly _importChecked = new Map<string, Map<string, ImportPath>>();
|
||||||
private readonly _cachedResults = new Map<string, Map<string, ImportResult>>();
|
private readonly _cachedResults = new Map<string, Map<string, ImportResult>>();
|
||||||
|
|
||||||
private _libPathCache: string[] | undefined = undefined;
|
private _libPathCache: Uri[] | undefined = undefined;
|
||||||
|
|
||||||
constructor(private _importRootGetter: () => string[]) {
|
constructor(private _importRootGetter: () => Uri[]) {
|
||||||
// empty
|
// empty
|
||||||
}
|
}
|
||||||
|
|
||||||
getImportResult(path: string, importName: string, importResult: ImportResult): ImportResult | undefined {
|
getImportResult(path: Uri, importName: string, importResult: ImportResult): ImportResult | undefined {
|
||||||
const result = this._cachedResults.get(importName)?.get(path);
|
const result = this._cachedResults.get(importName)?.get(path.key);
|
||||||
if (result) {
|
if (result) {
|
||||||
// We already checked for the importName at the path.
|
// We already checked for the importName at the path.
|
||||||
// Return the result if succeeded otherwise, return regular import result given.
|
// Return the result if succeeded otherwise, return regular import result given.
|
||||||
return result ?? importResult;
|
return result ?? importResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
const checked = this._importChecked.get(importName)?.get(path);
|
const checked = this._importChecked.get(importName)?.get(path.key);
|
||||||
if (checked) {
|
if (checked) {
|
||||||
// We already checked for the importName at the path.
|
// We already checked for the importName at the path.
|
||||||
if (!checked.importPath) {
|
if (!checked.importPath) {
|
||||||
return importResult;
|
return importResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this._cachedResults.get(importName)?.get(checked.importPath) ?? importResult;
|
return this._cachedResults.get(importName)?.get(checked.importPath.key) ?? importResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
checkValidPath(fs: FileSystem, sourceFilePath: string, root: string): boolean {
|
checkValidPath(fs: FileSystem, sourceFileUri: Uri, root: Uri): boolean {
|
||||||
if (!sourceFilePath.startsWith(root)) {
|
if (!sourceFileUri.startsWith(root)) {
|
||||||
// We don't search containing folders for libs.
|
// We don't search containing folders for libs.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -56,11 +56,11 @@ export class ParentDirectoryCache {
|
|||||||
this._libPathCache =
|
this._libPathCache =
|
||||||
this._libPathCache ??
|
this._libPathCache ??
|
||||||
this._importRootGetter()
|
this._importRootGetter()
|
||||||
.map((r) => ensureTrailingDirectorySeparator(realCasePath(normalizePath(r), fs)))
|
.map((r) => fs.realCasePath(r))
|
||||||
.filter((r) => r !== root)
|
.filter((r) => r !== root)
|
||||||
.filter((r) => r.startsWith(root));
|
.filter((r) => r.startsWith(root));
|
||||||
|
|
||||||
if (this._libPathCache.some((p) => sourceFilePath.startsWith(p))) {
|
if (this._libPathCache.some((p) => sourceFileUri.startsWith(p))) {
|
||||||
// Make sure it is not lib folders under user code root.
|
// Make sure it is not lib folders under user code root.
|
||||||
// ex) .venv folder
|
// ex) .venv folder
|
||||||
return false;
|
return false;
|
||||||
@ -69,13 +69,13 @@ export class ParentDirectoryCache {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
checked(path: string, importName: string, importPath: ImportPath) {
|
checked(path: Uri, importName: string, importPath: ImportPath) {
|
||||||
getOrAdd(this._importChecked, importName, () => new Map<string, ImportPath>()).set(path, importPath);
|
getOrAdd(this._importChecked, importName, () => new Map<string, ImportPath>()).set(path.key, importPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
add(result: CacheEntry) {
|
add(result: CacheEntry) {
|
||||||
getOrAdd(this._cachedResults, result.importName, () => new Map<string, ImportResult>()).set(
|
getOrAdd(this._cachedResults, result.importName, () => new Map<string, ImportResult>()).set(
|
||||||
result.path,
|
result.path.key,
|
||||||
result.importResult
|
result.importResult
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -2718,7 +2718,7 @@ export function getScopeIdForNode(node: ParseNode): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const fileInfo = AnalyzerNodeInfo.getFileInfo(node);
|
const fileInfo = AnalyzerNodeInfo.getFileInfo(node);
|
||||||
return `${fileInfo.filePath}.${node.start.toString()}-${name}`;
|
return `${fileInfo.fileUri.key}.${node.start.toString()}-${name}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Walks up the parse tree and finds all scopes that can provide
|
// Walks up the parse tree and finds all scopes that can provide
|
||||||
|
@ -21,21 +21,14 @@ import { FileDiagnostics } from '../common/diagnosticSink';
|
|||||||
import { FileEditAction } from '../common/editAction';
|
import { FileEditAction } from '../common/editAction';
|
||||||
import { EditableProgram, ProgramView } from '../common/extensibility';
|
import { EditableProgram, ProgramView } from '../common/extensibility';
|
||||||
import { LogTracker } from '../common/logTracker';
|
import { LogTracker } from '../common/logTracker';
|
||||||
import {
|
|
||||||
combinePaths,
|
|
||||||
getDirectoryPath,
|
|
||||||
getFileName,
|
|
||||||
getRelativePath,
|
|
||||||
makeDirectories,
|
|
||||||
normalizePath,
|
|
||||||
stripFileExtension,
|
|
||||||
} from '../common/pathUtils';
|
|
||||||
import { convertRangeToTextRange } from '../common/positionUtils';
|
import { convertRangeToTextRange } from '../common/positionUtils';
|
||||||
import { ServiceProvider } from '../common/serviceProvider';
|
import { ServiceProvider } from '../common/serviceProvider';
|
||||||
import '../common/serviceProviderExtensions';
|
import '../common/serviceProviderExtensions';
|
||||||
import { ServiceKeys } from '../common/serviceProviderExtensions';
|
import { ServiceKeys } from '../common/serviceProviderExtensions';
|
||||||
import { Range, doRangesIntersect } from '../common/textRange';
|
import { Range, doRangesIntersect } from '../common/textRange';
|
||||||
import { Duration, timingStats } from '../common/timing';
|
import { Duration, timingStats } from '../common/timing';
|
||||||
|
import { Uri } from '../common/uri/uri';
|
||||||
|
import { makeDirectories } from '../common/uri/uriUtils';
|
||||||
import { ParseResults } from '../parser/parser';
|
import { ParseResults } from '../parser/parser';
|
||||||
import { AbsoluteModuleDescriptor, ImportLookupResult, LookupImportOptions } from './analyzerFileInfo';
|
import { AbsoluteModuleDescriptor, ImportLookupResult, LookupImportOptions } from './analyzerFileInfo';
|
||||||
import * as AnalyzerNodeInfo from './analyzerNodeInfo';
|
import * as AnalyzerNodeInfo from './analyzerNodeInfo';
|
||||||
@ -73,7 +66,7 @@ export interface MaxAnalysisTime {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface UpdateImportInfo {
|
interface UpdateImportInfo {
|
||||||
path: string;
|
path: Uri;
|
||||||
isTypeshedFile: boolean;
|
isTypeshedFile: boolean;
|
||||||
isThirdPartyImport: boolean;
|
isThirdPartyImport: boolean;
|
||||||
isPyTypedPresent: boolean;
|
isPyTypedPresent: boolean;
|
||||||
@ -84,14 +77,13 @@ export type PreCheckCallback = (parseResults: ParseResults, evaluator: TypeEvalu
|
|||||||
export interface ISourceFileFactory {
|
export interface ISourceFileFactory {
|
||||||
createSourceFile(
|
createSourceFile(
|
||||||
serviceProvider: ServiceProvider,
|
serviceProvider: ServiceProvider,
|
||||||
filePath: string,
|
fileUri: Uri,
|
||||||
moduleName: string,
|
moduleName: string,
|
||||||
isThirdPartyImport: boolean,
|
isThirdPartyImport: boolean,
|
||||||
isThirdPartyPyTypedPresent: boolean,
|
isThirdPartyPyTypedPresent: boolean,
|
||||||
editMode: SourceFileEditMode,
|
editMode: SourceFileEditMode,
|
||||||
console?: ConsoleInterface,
|
console?: ConsoleInterface,
|
||||||
logTracker?: LogTracker,
|
logTracker?: LogTracker,
|
||||||
realFilePath?: string,
|
|
||||||
ipythonMode?: IPythonMode
|
ipythonMode?: IPythonMode
|
||||||
): SourceFile;
|
): SourceFile;
|
||||||
}
|
}
|
||||||
@ -105,8 +97,7 @@ export namespace ISourceFileFactory {
|
|||||||
export interface OpenFileOptions {
|
export interface OpenFileOptions {
|
||||||
isTracked: boolean;
|
isTracked: boolean;
|
||||||
ipythonMode: IPythonMode;
|
ipythonMode: IPythonMode;
|
||||||
chainedFilePath: string | undefined;
|
chainedFileUri: Uri | undefined;
|
||||||
realFilePath: string | undefined;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Track edit mode related information.
|
// Track edit mode related information.
|
||||||
@ -195,7 +186,7 @@ export class Program {
|
|||||||
return this._console;
|
return this._console;
|
||||||
}
|
}
|
||||||
|
|
||||||
get rootPath(): string {
|
get rootPath(): Uri {
|
||||||
return this._configOptions.projectRoot;
|
return this._configOptions.projectRoot;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -241,7 +232,7 @@ export class Program {
|
|||||||
if (newContents) {
|
if (newContents) {
|
||||||
// Create a text document so we can compute the edits.
|
// Create a text document so we can compute the edits.
|
||||||
const textDocument = TextDocument.create(
|
const textDocument = TextDocument.create(
|
||||||
fileInfo.sourceFile.getFilePath(),
|
fileInfo.sourceFile.getUri().toString(),
|
||||||
'python',
|
'python',
|
||||||
1,
|
1,
|
||||||
fileInfo.sourceFile.getFileContent() || ''
|
fileInfo.sourceFile.getFileContent() || ''
|
||||||
@ -249,7 +240,7 @@ export class Program {
|
|||||||
|
|
||||||
// Add an edit action to the list.
|
// Add an edit action to the list.
|
||||||
edits.push({
|
edits.push({
|
||||||
filePath: fileInfo.sourceFile.getFilePath(),
|
fileUri: fileInfo.sourceFile.getUri(),
|
||||||
range: {
|
range: {
|
||||||
start: { line: 0, character: 0 },
|
start: { line: 0, character: 0 },
|
||||||
end: { line: textDocument.lineCount, character: 0 },
|
end: { line: textDocument.lineCount, character: 0 },
|
||||||
@ -268,7 +259,7 @@ export class Program {
|
|||||||
// We don't need to care about file diagnostics since in edit mode
|
// We don't need to care about file diagnostics since in edit mode
|
||||||
// checker won't run.
|
// checker won't run.
|
||||||
v.sourceFile.prepareForClose();
|
v.sourceFile.prepareForClose();
|
||||||
this._removeSourceFileFromListAndMap(v.sourceFile.getFilePath(), i);
|
this._removeSourceFileFromListAndMap(v.sourceFile.getUri(), i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -299,26 +290,26 @@ export class Program {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Sets the list of tracked files that make up the program.
|
// Sets the list of tracked files that make up the program.
|
||||||
setTrackedFiles(filePaths: string[]): FileDiagnostics[] {
|
setTrackedFiles(fileUris: Uri[]): FileDiagnostics[] {
|
||||||
if (this._sourceFileList.length > 0) {
|
if (this._sourceFileList.length > 0) {
|
||||||
// We need to determine which files to remove from the existing file list.
|
// We need to determine which files to remove from the existing file list.
|
||||||
const newFileMap = new Map<string, string>();
|
const newFileMap = new Map<string, Uri>();
|
||||||
filePaths.forEach((path) => {
|
fileUris.forEach((path) => {
|
||||||
newFileMap.set(path, path);
|
newFileMap.set(path.key, path);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Files that are not in the tracked file list are
|
// Files that are not in the tracked file list are
|
||||||
// marked as no longer tracked.
|
// marked as no longer tracked.
|
||||||
this._sourceFileList.forEach((oldFile) => {
|
this._sourceFileList.forEach((oldFile) => {
|
||||||
const filePath = oldFile.sourceFile.getFilePath();
|
const fileUri = oldFile.sourceFile.getUri();
|
||||||
if (!newFileMap.has(filePath)) {
|
if (!newFileMap.has(fileUri.key)) {
|
||||||
oldFile.isTracked = false;
|
oldFile.isTracked = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the new files. Only the new items will be added.
|
// Add the new files. Only the new items will be added.
|
||||||
this.addTrackedFiles(filePaths);
|
this.addTrackedFiles(fileUris);
|
||||||
|
|
||||||
return this._removeUnneededFiles();
|
return this._removeUnneededFiles();
|
||||||
}
|
}
|
||||||
@ -338,25 +329,25 @@ export class Program {
|
|||||||
this._allowedThirdPartyImports = importNames;
|
this._allowedThirdPartyImports = importNames;
|
||||||
}
|
}
|
||||||
|
|
||||||
addTrackedFiles(filePaths: string[], isThirdPartyImport = false, isInPyTypedPackage = false) {
|
addTrackedFiles(fileUris: Uri[], isThirdPartyImport = false, isInPyTypedPackage = false) {
|
||||||
filePaths.forEach((filePath) => {
|
fileUris.forEach((fileUri) => {
|
||||||
this.addTrackedFile(filePath, isThirdPartyImport, isInPyTypedPackage);
|
this.addTrackedFile(fileUri, isThirdPartyImport, isInPyTypedPackage);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
addInterimFile(filePath: string): SourceFileInfo {
|
addInterimFile(fileUri: Uri): SourceFileInfo {
|
||||||
// Double check not already there.
|
// Double check not already there.
|
||||||
let fileInfo = this.getSourceFileInfo(filePath);
|
let fileInfo = this.getSourceFileInfo(fileUri);
|
||||||
if (!fileInfo) {
|
if (!fileInfo) {
|
||||||
fileInfo = this._createInterimFileInfo(filePath);
|
fileInfo = this._createInterimFileInfo(fileUri);
|
||||||
this._addToSourceFileListAndMap(fileInfo);
|
this._addToSourceFileListAndMap(fileInfo);
|
||||||
}
|
}
|
||||||
return fileInfo;
|
return fileInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
addTrackedFile(filePath: string, isThirdPartyImport = false, isInPyTypedPackage = false): SourceFile {
|
addTrackedFile(fileUri: Uri, isThirdPartyImport = false, isInPyTypedPackage = false): SourceFile {
|
||||||
let sourceFileInfo = this.getSourceFileInfo(filePath);
|
let sourceFileInfo = this.getSourceFileInfo(fileUri);
|
||||||
const moduleImportInfo = this._getModuleImportInfoForFile(filePath);
|
const moduleImportInfo = this._getModuleImportInfoForFile(fileUri);
|
||||||
const importName = moduleImportInfo.moduleName;
|
const importName = moduleImportInfo.moduleName;
|
||||||
|
|
||||||
if (sourceFileInfo) {
|
if (sourceFileInfo) {
|
||||||
@ -369,7 +360,7 @@ export class Program {
|
|||||||
|
|
||||||
const sourceFile = this._sourceFileFactory.createSourceFile(
|
const sourceFile = this._sourceFileFactory.createSourceFile(
|
||||||
this.serviceProvider,
|
this.serviceProvider,
|
||||||
filePath,
|
fileUri,
|
||||||
importName,
|
importName,
|
||||||
isThirdPartyImport,
|
isThirdPartyImport,
|
||||||
isInPyTypedPackage,
|
isInPyTypedPackage,
|
||||||
@ -391,23 +382,22 @@ export class Program {
|
|||||||
return sourceFile;
|
return sourceFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
setFileOpened(filePath: string, version: number | null, contents: string, options?: OpenFileOptions) {
|
setFileOpened(fileUri: Uri, version: number | null, contents: string, options?: OpenFileOptions) {
|
||||||
let sourceFileInfo = this.getSourceFileInfo(filePath);
|
let sourceFileInfo = this.getSourceFileInfo(fileUri);
|
||||||
if (!sourceFileInfo) {
|
if (!sourceFileInfo) {
|
||||||
const moduleImportInfo = this._getModuleImportInfoForFile(filePath);
|
const moduleImportInfo = this._getModuleImportInfoForFile(fileUri);
|
||||||
const sourceFile = this._sourceFileFactory.createSourceFile(
|
const sourceFile = this._sourceFileFactory.createSourceFile(
|
||||||
this.serviceProvider,
|
this.serviceProvider,
|
||||||
filePath,
|
fileUri,
|
||||||
moduleImportInfo.moduleName,
|
moduleImportInfo.moduleName,
|
||||||
/* isThirdPartyImport */ false,
|
/* isThirdPartyImport */ false,
|
||||||
moduleImportInfo.isThirdPartyPyTypedPresent,
|
moduleImportInfo.isThirdPartyPyTypedPresent,
|
||||||
this._editModeTracker,
|
this._editModeTracker,
|
||||||
this._console,
|
this._console,
|
||||||
this._logTracker,
|
this._logTracker,
|
||||||
options?.realFilePath,
|
|
||||||
options?.ipythonMode ?? IPythonMode.None
|
options?.ipythonMode ?? IPythonMode.None
|
||||||
);
|
);
|
||||||
const chainedFilePath = options?.chainedFilePath;
|
const chainedFilePath = options?.chainedFileUri;
|
||||||
sourceFileInfo = new SourceFileInfo(
|
sourceFileInfo = new SourceFileInfo(
|
||||||
sourceFile,
|
sourceFile,
|
||||||
/* isTypeshedFile */ false,
|
/* isTypeshedFile */ false,
|
||||||
@ -435,26 +425,26 @@ export class Program {
|
|||||||
sourceFileInfo.sourceFile.setClientVersion(version, contents);
|
sourceFileInfo.sourceFile.setClientVersion(version, contents);
|
||||||
}
|
}
|
||||||
|
|
||||||
getChainedFilePath(filePath: string): string | undefined {
|
getChainedUri(fileUri: Uri): Uri | undefined {
|
||||||
const sourceFileInfo = this.getSourceFileInfo(filePath);
|
const sourceFileInfo = this.getSourceFileInfo(fileUri);
|
||||||
return sourceFileInfo?.chainedSourceFile?.sourceFile.getFilePath();
|
return sourceFileInfo?.chainedSourceFile?.sourceFile.getUri();
|
||||||
}
|
}
|
||||||
|
|
||||||
updateChainedFilePath(filePath: string, chainedFilePath: string | undefined) {
|
updateChainedUri(fileUri: Uri, chainedFileUri: Uri | undefined) {
|
||||||
const sourceFileInfo = this.getSourceFileInfo(filePath);
|
const sourceFileInfo = this.getSourceFileInfo(fileUri);
|
||||||
if (!sourceFileInfo) {
|
if (!sourceFileInfo) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceFileInfo.chainedSourceFile = chainedFilePath ? this.getSourceFileInfo(chainedFilePath) : undefined;
|
sourceFileInfo.chainedSourceFile = chainedFileUri ? this.getSourceFileInfo(chainedFileUri) : undefined;
|
||||||
sourceFileInfo.sourceFile.markDirty();
|
sourceFileInfo.sourceFile.markDirty();
|
||||||
this._markFileDirtyRecursive(sourceFileInfo, new Set<string>());
|
this._markFileDirtyRecursive(sourceFileInfo, new Set<string>());
|
||||||
|
|
||||||
verifyNoCyclesInChainedFiles(this, sourceFileInfo);
|
verifyNoCyclesInChainedFiles(this, sourceFileInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
setFileClosed(filePath: string, isTracked?: boolean): FileDiagnostics[] {
|
setFileClosed(fileUri: Uri, isTracked?: boolean): FileDiagnostics[] {
|
||||||
const sourceFileInfo = this.getSourceFileInfo(filePath);
|
const sourceFileInfo = this.getSourceFileInfo(fileUri);
|
||||||
if (sourceFileInfo) {
|
if (sourceFileInfo) {
|
||||||
sourceFileInfo.isOpenByClient = false;
|
sourceFileInfo.isOpenByClient = false;
|
||||||
sourceFileInfo.isTracked = isTracked ?? sourceFileInfo.isTracked;
|
sourceFileInfo.isTracked = isTracked ?? sourceFileInfo.isTracked;
|
||||||
@ -493,12 +483,12 @@ export class Program {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
markFilesDirty(filePaths: string[], evenIfContentsAreSame: boolean) {
|
markFilesDirty(fileUris: Uri[], evenIfContentsAreSame: boolean) {
|
||||||
const markDirtySet = new Set<string>();
|
const markDirtySet = new Set<string>();
|
||||||
filePaths.forEach((filePath) => {
|
fileUris.forEach((fileUri) => {
|
||||||
const sourceFileInfo = this.getSourceFileInfo(filePath);
|
const sourceFileInfo = this.getSourceFileInfo(fileUri);
|
||||||
if (sourceFileInfo) {
|
if (sourceFileInfo) {
|
||||||
const fileName = getFileName(filePath);
|
const fileName = fileUri.fileName;
|
||||||
|
|
||||||
// Handle builtins and __builtins__ specially. They are implicitly
|
// Handle builtins and __builtins__ specially. They are implicitly
|
||||||
// included by all source files.
|
// included by all source files.
|
||||||
@ -576,9 +566,9 @@ export class Program {
|
|||||||
return this._configOptions.functionSignatureDisplay;
|
return this._configOptions.functionSignatureDisplay;
|
||||||
}
|
}
|
||||||
|
|
||||||
containsSourceFileIn(folder: string): boolean {
|
containsSourceFileIn(folder: Uri): boolean {
|
||||||
for (const normalizedSourceFilePath of this._sourceFileMap.keys()) {
|
for (const normalizedSourceFilePath of this._sourceFileMap.values()) {
|
||||||
if (normalizedSourceFilePath.startsWith(folder)) {
|
if (normalizedSourceFilePath.sourceFile.getUri().startsWith(folder)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -586,19 +576,19 @@ export class Program {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
owns(filePath: string) {
|
owns(uri: Uri) {
|
||||||
const fileInfo = this.getSourceFileInfo(filePath);
|
const fileInfo = this.getSourceFileInfo(uri);
|
||||||
if (fileInfo) {
|
if (fileInfo) {
|
||||||
// If we already determined whether the file is tracked or not, don't do it again.
|
// If we already determined whether the file is tracked or not, don't do it again.
|
||||||
// This will make sure we have consistent look at the state once it is loaded to the memory.
|
// This will make sure we have consistent look at the state once it is loaded to the memory.
|
||||||
return fileInfo.isTracked;
|
return fileInfo.isTracked;
|
||||||
}
|
}
|
||||||
|
|
||||||
return matchFileSpecs(this._configOptions, filePath);
|
return matchFileSpecs(this._configOptions, uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
getSourceFile(filePath: string): SourceFile | undefined {
|
getSourceFile(uri: Uri): SourceFile | undefined {
|
||||||
const sourceFileInfo = this.getSourceFileInfo(filePath);
|
const sourceFileInfo = this.getSourceFileInfo(uri);
|
||||||
if (!sourceFileInfo) {
|
if (!sourceFileInfo) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
@ -606,20 +596,23 @@ export class Program {
|
|||||||
return sourceFileInfo.sourceFile;
|
return sourceFileInfo.sourceFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
getBoundSourceFile(filePath: string): SourceFile | undefined {
|
getBoundSourceFile(uri: Uri): SourceFile | undefined {
|
||||||
return this.getBoundSourceFileInfo(filePath)?.sourceFile;
|
return this.getBoundSourceFileInfo(uri)?.sourceFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
getSourceFileInfoList(): readonly SourceFileInfo[] {
|
getSourceFileInfoList(): readonly SourceFileInfo[] {
|
||||||
return this._sourceFileList;
|
return this._sourceFileList;
|
||||||
}
|
}
|
||||||
|
|
||||||
getSourceFileInfo(filePath: string): SourceFileInfo | undefined {
|
getSourceFileInfo(uri: Uri): SourceFileInfo | undefined {
|
||||||
return this._sourceFileMap.get(filePath);
|
if (!uri.isEmpty()) {
|
||||||
|
return this._sourceFileMap.get(uri.key);
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
getBoundSourceFileInfo(filePath: string, content?: string, force?: boolean): SourceFileInfo | undefined {
|
getBoundSourceFileInfo(uri: Uri, content?: string, force?: boolean): SourceFileInfo | undefined {
|
||||||
const sourceFileInfo = this.getSourceFileInfo(filePath);
|
const sourceFileInfo = this.getSourceFileInfo(uri);
|
||||||
if (!sourceFileInfo) {
|
if (!sourceFileInfo) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
@ -685,9 +678,9 @@ export class Program {
|
|||||||
|
|
||||||
// Performs parsing and analysis of a single file in the program. If the file is not part of
|
// Performs parsing and analysis of a single file in the program. If the file is not part of
|
||||||
// the program returns false to indicate analysis was not performed.
|
// the program returns false to indicate analysis was not performed.
|
||||||
analyzeFile(filePath: string, token: CancellationToken = CancellationToken.None): boolean {
|
analyzeFile(fileUri: Uri, token: CancellationToken = CancellationToken.None): boolean {
|
||||||
return this._runEvaluatorWithCancellationToken(token, () => {
|
return this._runEvaluatorWithCancellationToken(token, () => {
|
||||||
const sourceFileInfo = this.getSourceFileInfo(filePath);
|
const sourceFileInfo = this.getSourceFileInfo(fileUri);
|
||||||
if (sourceFileInfo && this._checkTypes(sourceFileInfo, token)) {
|
if (sourceFileInfo && this._checkTypes(sourceFileInfo, token)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -710,19 +703,19 @@ export class Program {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getSourceMapper(
|
getSourceMapper(
|
||||||
filePath: string,
|
fileUri: Uri,
|
||||||
token: CancellationToken,
|
token: CancellationToken,
|
||||||
mapCompiled?: boolean,
|
mapCompiled?: boolean,
|
||||||
preferStubs?: boolean
|
preferStubs?: boolean
|
||||||
): SourceMapper {
|
): SourceMapper {
|
||||||
const sourceFileInfo = this.getSourceFileInfo(filePath);
|
const sourceFileInfo = this.getSourceFileInfo(fileUri);
|
||||||
const execEnv = this._configOptions.findExecEnvironment(filePath);
|
const execEnv = this._configOptions.findExecEnvironment(fileUri);
|
||||||
return this._createSourceMapper(execEnv, token, sourceFileInfo, mapCompiled, preferStubs);
|
return this._createSourceMapper(execEnv, token, sourceFileInfo, mapCompiled, preferStubs);
|
||||||
}
|
}
|
||||||
|
|
||||||
getParseResults(filePath: string): ParseResults | undefined {
|
getParseResults(fileUri: Uri): ParseResults | undefined {
|
||||||
return this.getBoundSourceFileInfo(
|
return this.getBoundSourceFileInfo(
|
||||||
filePath,
|
fileUri,
|
||||||
/* content */ undefined,
|
/* content */ undefined,
|
||||||
/* force */ true
|
/* force */ true
|
||||||
)?.sourceFile.getParseResults();
|
)?.sourceFile.getParseResults();
|
||||||
@ -746,41 +739,39 @@ export class Program {
|
|||||||
|
|
||||||
sortedFiles.forEach((sfInfo) => {
|
sortedFiles.forEach((sfInfo) => {
|
||||||
const checkTimeInMs = sfInfo.sourceFile.getCheckTime()!;
|
const checkTimeInMs = sfInfo.sourceFile.getCheckTime()!;
|
||||||
this._console.info(`${checkTimeInMs}ms: ${sfInfo.sourceFile.getFilePath()}`);
|
this._console.info(`${checkTimeInMs}ms: ${sfInfo.sourceFile.getUri()}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prints import dependency information for each of the files in
|
// Prints import dependency information for each of the files in
|
||||||
// the program, skipping any typeshed files.
|
// the program, skipping any typeshed files.
|
||||||
printDependencies(projectRootDir: string, verbose: boolean) {
|
printDependencies(projectRootDir: Uri, verbose: boolean) {
|
||||||
const fs = this._importResolver.fileSystem;
|
const fs = this._importResolver.fileSystem;
|
||||||
const sortedFiles = this._sourceFileList
|
const sortedFiles = this._sourceFileList
|
||||||
.filter((s) => !s.isTypeshedFile)
|
.filter((s) => !s.isTypeshedFile)
|
||||||
.sort((a, b) => {
|
.sort((a, b) => {
|
||||||
return fs.getOriginalFilePath(a.sourceFile.getFilePath()) <
|
return fs.getOriginalUri(a.sourceFile.getUri()) < fs.getOriginalUri(b.sourceFile.getUri()) ? 1 : -1;
|
||||||
fs.getOriginalFilePath(b.sourceFile.getFilePath())
|
|
||||||
? 1
|
|
||||||
: -1;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const zeroImportFiles: SourceFile[] = [];
|
const zeroImportFiles: SourceFile[] = [];
|
||||||
|
|
||||||
sortedFiles.forEach((sfInfo) => {
|
sortedFiles.forEach((sfInfo) => {
|
||||||
this._console.info('');
|
this._console.info('');
|
||||||
let filePath = fs.getOriginalFilePath(sfInfo.sourceFile.getFilePath());
|
const fileUri = fs.getOriginalUri(sfInfo.sourceFile.getUri());
|
||||||
const relPath = getRelativePath(filePath, projectRootDir);
|
let fileString = fileUri.toString();
|
||||||
|
const relPath = projectRootDir.getRelativePathComponents(fileUri);
|
||||||
if (relPath) {
|
if (relPath) {
|
||||||
filePath = relPath;
|
fileString = relPath.join('/');
|
||||||
}
|
}
|
||||||
|
|
||||||
this._console.info(`${filePath}`);
|
this._console.info(`${fileString}`);
|
||||||
|
|
||||||
this._console.info(
|
this._console.info(
|
||||||
` Imports ${sfInfo.imports.length} ` + `file${sfInfo.imports.length === 1 ? '' : 's'}`
|
` Imports ${sfInfo.imports.length} ` + `file${sfInfo.imports.length === 1 ? '' : 's'}`
|
||||||
);
|
);
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
sfInfo.imports.forEach((importInfo) => {
|
sfInfo.imports.forEach((importInfo) => {
|
||||||
this._console.info(` ${fs.getOriginalFilePath(importInfo.sourceFile.getFilePath())}`);
|
this._console.info(` ${fs.getOriginalUri(importInfo.sourceFile.getUri())}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -789,7 +780,7 @@ export class Program {
|
|||||||
);
|
);
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
sfInfo.importedBy.forEach((importInfo) => {
|
sfInfo.importedBy.forEach((importInfo) => {
|
||||||
this._console.info(` ${fs.getOriginalFilePath(importInfo.sourceFile.getFilePath())}`);
|
this._console.info(` ${fs.getOriginalUri(importInfo.sourceFile.getUri())}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -804,33 +795,33 @@ export class Program {
|
|||||||
`${zeroImportFiles.length} file${zeroImportFiles.length === 1 ? '' : 's'}` + ` not explicitly imported`
|
`${zeroImportFiles.length} file${zeroImportFiles.length === 1 ? '' : 's'}` + ` not explicitly imported`
|
||||||
);
|
);
|
||||||
zeroImportFiles.forEach((importFile) => {
|
zeroImportFiles.forEach((importFile) => {
|
||||||
this._console.info(` ${fs.getOriginalFilePath(importFile.getFilePath())}`);
|
this._console.info(` ${fs.getOriginalUri(importFile.getUri())}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
writeTypeStub(targetImportPath: string, targetIsSingleFile: boolean, stubPath: string, token: CancellationToken) {
|
writeTypeStub(targetImportPath: Uri, targetIsSingleFile: boolean, stubPath: Uri, token: CancellationToken) {
|
||||||
for (const sourceFileInfo of this._sourceFileList) {
|
for (const sourceFileInfo of this._sourceFileList) {
|
||||||
throwIfCancellationRequested(token);
|
throwIfCancellationRequested(token);
|
||||||
|
|
||||||
const filePath = sourceFileInfo.sourceFile.getFilePath();
|
const fileUri = sourceFileInfo.sourceFile.getUri();
|
||||||
|
|
||||||
// Generate type stubs only for the files within the target path,
|
// Generate type stubs only for the files within the target path,
|
||||||
// not any files that the target module happened to import.
|
// not any files that the target module happened to import.
|
||||||
const relativePath = getRelativePath(filePath, targetImportPath);
|
const relativePath = targetImportPath.getRelativePath(fileUri);
|
||||||
if (relativePath !== undefined) {
|
if (relativePath !== undefined) {
|
||||||
let typeStubPath = normalizePath(combinePaths(stubPath, relativePath));
|
let typeStubPath = stubPath.combinePaths(relativePath);
|
||||||
|
|
||||||
// If the target is a single file implementation, as opposed to
|
// If the target is a single file implementation, as opposed to
|
||||||
// a package in a directory, transform the name of the type stub
|
// a package in a directory, transform the name of the type stub
|
||||||
// to __init__.pyi because we're placing it in a directory.
|
// to __init__.pyi because we're placing it in a directory.
|
||||||
if (targetIsSingleFile) {
|
if (targetIsSingleFile) {
|
||||||
typeStubPath = combinePaths(getDirectoryPath(typeStubPath), '__init__.pyi');
|
typeStubPath = typeStubPath.getDirectory().combinePaths('__init__.pyi');
|
||||||
} else {
|
} else {
|
||||||
typeStubPath = stripFileExtension(typeStubPath) + '.pyi';
|
typeStubPath = typeStubPath.replaceExtension('.pyi');
|
||||||
}
|
}
|
||||||
|
|
||||||
const typeStubDir = getDirectoryPath(typeStubPath);
|
const typeStubDir = typeStubPath.getDirectory();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
makeDirectories(this.fileSystem, typeStubDir, stubPath);
|
makeDirectories(this.fileSystem, typeStubDir, stubPath);
|
||||||
@ -867,8 +858,8 @@ export class Program {
|
|||||||
return evaluator.printType(type, options);
|
return evaluator.printType(type, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
getTextOnRange(filePath: string, range: Range, token: CancellationToken): string | undefined {
|
getTextOnRange(fileUri: Uri, range: Range, token: CancellationToken): string | undefined {
|
||||||
const sourceFileInfo = this.getSourceFileInfo(filePath);
|
const sourceFileInfo = this.getSourceFileInfo(fileUri);
|
||||||
if (!sourceFileInfo) {
|
if (!sourceFileInfo) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
@ -904,7 +895,7 @@ export class Program {
|
|||||||
);
|
);
|
||||||
if (diagnostics !== undefined) {
|
if (diagnostics !== undefined) {
|
||||||
fileDiagnostics.push({
|
fileDiagnostics.push({
|
||||||
filePath: sourceFileInfo.sourceFile.getFilePath(),
|
fileUri: sourceFileInfo.sourceFile.getUri(),
|
||||||
version: sourceFileInfo.sourceFile.getClientVersion(),
|
version: sourceFileInfo.sourceFile.getClientVersion(),
|
||||||
diagnostics,
|
diagnostics,
|
||||||
});
|
});
|
||||||
@ -921,7 +912,7 @@ export class Program {
|
|||||||
// This condition occurs when the user switches from workspace to
|
// This condition occurs when the user switches from workspace to
|
||||||
// "open files only" mode. Clear all diagnostics for this file.
|
// "open files only" mode. Clear all diagnostics for this file.
|
||||||
fileDiagnostics.push({
|
fileDiagnostics.push({
|
||||||
filePath: sourceFileInfo.sourceFile.getFilePath(),
|
fileUri: sourceFileInfo.sourceFile.getUri(),
|
||||||
version: sourceFileInfo.sourceFile.getClientVersion(),
|
version: sourceFileInfo.sourceFile.getClientVersion(),
|
||||||
diagnostics: [],
|
diagnostics: [],
|
||||||
});
|
});
|
||||||
@ -932,8 +923,8 @@ export class Program {
|
|||||||
return fileDiagnostics;
|
return fileDiagnostics;
|
||||||
}
|
}
|
||||||
|
|
||||||
getDiagnosticsForRange(filePath: string, range: Range): Diagnostic[] {
|
getDiagnosticsForRange(fileUri: Uri, range: Range): Diagnostic[] {
|
||||||
const sourceFile = this.getSourceFile(filePath);
|
const sourceFile = this.getSourceFile(fileUri);
|
||||||
if (!sourceFile) {
|
if (!sourceFile) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
@ -958,7 +949,7 @@ export class Program {
|
|||||||
|
|
||||||
// Cloned program will use whatever user files the program currently has.
|
// Cloned program will use whatever user files the program currently has.
|
||||||
const userFiles = this.getUserFiles();
|
const userFiles = this.getUserFiles();
|
||||||
program.setTrackedFiles(userFiles.map((i) => i.sourceFile.getFilePath()));
|
program.setTrackedFiles(userFiles.map((i) => i.sourceFile.getUri()));
|
||||||
program.markAllFilesDirty(/* evenIfContentsAreSame */ true);
|
program.markAllFilesDirty(/* evenIfContentsAreSame */ true);
|
||||||
|
|
||||||
// Make sure we keep editor content (open file) which could be different than one in the file system.
|
// Make sure we keep editor content (open file) which could be different than one in the file system.
|
||||||
@ -969,14 +960,13 @@ export class Program {
|
|||||||
}
|
}
|
||||||
|
|
||||||
program.setFileOpened(
|
program.setFileOpened(
|
||||||
fileInfo.sourceFile.getFilePath(),
|
fileInfo.sourceFile.getUri(),
|
||||||
version,
|
version,
|
||||||
fileInfo.sourceFile.getOpenFileContents() ?? '',
|
fileInfo.sourceFile.getOpenFileContents() ?? '',
|
||||||
{
|
{
|
||||||
chainedFilePath: fileInfo.chainedSourceFile?.sourceFile.getFilePath(),
|
chainedFileUri: fileInfo.chainedSourceFile?.sourceFile.getUri(),
|
||||||
ipythonMode: fileInfo.sourceFile.getIPythonMode(),
|
ipythonMode: fileInfo.sourceFile.getIPythonMode(),
|
||||||
isTracked: fileInfo.isTracked,
|
isTracked: fileInfo.isTracked,
|
||||||
realFilePath: fileInfo.sourceFile.getRealFilePath(),
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -1071,14 +1061,14 @@ export class Program {
|
|||||||
// Clear only if there are any errors for this file.
|
// Clear only if there are any errors for this file.
|
||||||
if (fileInfo.diagnosticsVersion !== undefined) {
|
if (fileInfo.diagnosticsVersion !== undefined) {
|
||||||
fileDiagnostics.push({
|
fileDiagnostics.push({
|
||||||
filePath: fileInfo.sourceFile.getFilePath(),
|
fileUri: fileInfo.sourceFile.getUri(),
|
||||||
version: fileInfo.sourceFile.getClientVersion(),
|
version: fileInfo.sourceFile.getClientVersion(),
|
||||||
diagnostics: [],
|
diagnostics: [],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fileInfo.sourceFile.prepareForClose();
|
fileInfo.sourceFile.prepareForClose();
|
||||||
this._removeSourceFileFromListAndMap(fileInfo.sourceFile.getFilePath(), i);
|
this._removeSourceFileFromListAndMap(fileInfo.sourceFile.getUri(), i);
|
||||||
|
|
||||||
// Unlink any imports and remove them from the list if
|
// Unlink any imports and remove them from the list if
|
||||||
// they are no longer referenced.
|
// they are no longer referenced.
|
||||||
@ -1099,14 +1089,14 @@ export class Program {
|
|||||||
// Clear if there are any errors for this import.
|
// Clear if there are any errors for this import.
|
||||||
if (importedFile.diagnosticsVersion !== undefined) {
|
if (importedFile.diagnosticsVersion !== undefined) {
|
||||||
fileDiagnostics.push({
|
fileDiagnostics.push({
|
||||||
filePath: importedFile.sourceFile.getFilePath(),
|
fileUri: importedFile.sourceFile.getUri(),
|
||||||
version: importedFile.sourceFile.getClientVersion(),
|
version: importedFile.sourceFile.getClientVersion(),
|
||||||
diagnostics: [],
|
diagnostics: [],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
importedFile.sourceFile.prepareForClose();
|
importedFile.sourceFile.prepareForClose();
|
||||||
this._removeSourceFileFromListAndMap(importedFile.sourceFile.getFilePath(), indexToRemove);
|
this._removeSourceFileFromListAndMap(importedFile.sourceFile.getUri(), indexToRemove);
|
||||||
i--;
|
i--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1122,7 +1112,7 @@ export class Program {
|
|||||||
// out the errors for the now-closed file.
|
// out the errors for the now-closed file.
|
||||||
if (!this._shouldCheckFile(fileInfo) && fileInfo.diagnosticsVersion !== undefined) {
|
if (!this._shouldCheckFile(fileInfo) && fileInfo.diagnosticsVersion !== undefined) {
|
||||||
fileDiagnostics.push({
|
fileDiagnostics.push({
|
||||||
filePath: fileInfo.sourceFile.getFilePath(),
|
fileUri: fileInfo.sourceFile.getUri(),
|
||||||
version: fileInfo.sourceFile.getClientVersion(),
|
version: fileInfo.sourceFile.getClientVersion(),
|
||||||
diagnostics: [],
|
diagnostics: [],
|
||||||
});
|
});
|
||||||
@ -1165,14 +1155,14 @@ export class Program {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const filePath = fileInfo.sourceFile.getFilePath();
|
const fileUri = fileInfo.sourceFile.getUri();
|
||||||
|
|
||||||
// Avoid infinite recursion.
|
// Avoid infinite recursion.
|
||||||
if (recursionSet.has(filePath)) {
|
if (recursionSet.has(fileUri.key)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
recursionSet.add(filePath);
|
recursionSet.add(fileUri.key);
|
||||||
|
|
||||||
for (const importerInfo of fileInfo.importedBy) {
|
for (const importerInfo of fileInfo.importedBy) {
|
||||||
if (this._isImportNeededRecursive(importerInfo, recursionSet)) {
|
if (this._isImportNeededRecursive(importerInfo, recursionSet)) {
|
||||||
@ -1194,16 +1184,16 @@ export class Program {
|
|||||||
this._importResolver,
|
this._importResolver,
|
||||||
execEnv,
|
execEnv,
|
||||||
this._evaluator!,
|
this._evaluator!,
|
||||||
(stubFilePath: string, implFilePath: string) => {
|
(stubFileUri: Uri, implFileUri: Uri) => {
|
||||||
let stubFileInfo = this.getSourceFileInfo(stubFilePath);
|
let stubFileInfo = this.getSourceFileInfo(stubFileUri);
|
||||||
if (!stubFileInfo) {
|
if (!stubFileInfo) {
|
||||||
// Special case for import statement like "import X.Y". The SourceFile
|
// Special case for import statement like "import X.Y". The SourceFile
|
||||||
// for X might not be in memory since import `X.Y` only brings in Y.
|
// for X might not be in memory since import `X.Y` only brings in Y.
|
||||||
stubFileInfo = this.addInterimFile(stubFilePath);
|
stubFileInfo = this.addInterimFile(stubFileUri);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._addShadowedFile(stubFileInfo, implFilePath);
|
this._addShadowedFile(stubFileInfo, implFileUri);
|
||||||
return this.getBoundSourceFile(implFilePath);
|
return this.getBoundSourceFile(implFileUri);
|
||||||
},
|
},
|
||||||
(f) => {
|
(f) => {
|
||||||
let fileInfo = this.getBoundSourceFileInfo(f);
|
let fileInfo = this.getBoundSourceFileInfo(f);
|
||||||
@ -1296,6 +1286,10 @@ export class Program {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _getSourceFileInfoFromKey(key: string) {
|
||||||
|
return this._sourceFileMap.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
private _updateSourceFileImports(sourceFileInfo: SourceFileInfo, options: ConfigOptions): SourceFileInfo[] {
|
private _updateSourceFileImports(sourceFileInfo: SourceFileInfo, options: ConfigOptions): SourceFileInfo[] {
|
||||||
const filesAdded: SourceFileInfo[] = [];
|
const filesAdded: SourceFileInfo[] = [];
|
||||||
|
|
||||||
@ -1338,9 +1332,9 @@ export class Program {
|
|||||||
if (sourceFileInfo.chainedSourceFile.sourceFile.isFileDeleted()) {
|
if (sourceFileInfo.chainedSourceFile.sourceFile.isFileDeleted()) {
|
||||||
sourceFileInfo.chainedSourceFile = undefined;
|
sourceFileInfo.chainedSourceFile = undefined;
|
||||||
} else {
|
} else {
|
||||||
const filePath = sourceFileInfo.chainedSourceFile.sourceFile.getFilePath();
|
const fileUri = sourceFileInfo.chainedSourceFile.sourceFile.getUri();
|
||||||
newImportPathMap.set(filePath, {
|
newImportPathMap.set(fileUri.key, {
|
||||||
path: filePath,
|
path: fileUri,
|
||||||
isTypeshedFile: false,
|
isTypeshedFile: false,
|
||||||
isThirdPartyImport: false,
|
isThirdPartyImport: false,
|
||||||
isPyTypedPresent: false,
|
isPyTypedPresent: false,
|
||||||
@ -1351,12 +1345,12 @@ export class Program {
|
|||||||
imports.forEach((importResult) => {
|
imports.forEach((importResult) => {
|
||||||
if (importResult.isImportFound) {
|
if (importResult.isImportFound) {
|
||||||
if (this._isImportAllowed(sourceFileInfo, importResult, importResult.isStubFile)) {
|
if (this._isImportAllowed(sourceFileInfo, importResult, importResult.isStubFile)) {
|
||||||
if (importResult.resolvedPaths.length > 0) {
|
if (importResult.resolvedUris.length > 0) {
|
||||||
const filePath = importResult.resolvedPaths[importResult.resolvedPaths.length - 1];
|
const fileUri = importResult.resolvedUris[importResult.resolvedUris.length - 1];
|
||||||
if (filePath) {
|
if (!fileUri.isEmpty()) {
|
||||||
const thirdPartyTypeInfo = getThirdPartyImportInfo(importResult);
|
const thirdPartyTypeInfo = getThirdPartyImportInfo(importResult);
|
||||||
newImportPathMap.set(filePath, {
|
newImportPathMap.set(fileUri.key, {
|
||||||
path: filePath,
|
path: fileUri,
|
||||||
isTypeshedFile:
|
isTypeshedFile:
|
||||||
!!importResult.isStdlibTypeshedFile || !!importResult.isThirdPartyTypeshedFile,
|
!!importResult.isStdlibTypeshedFile || !!importResult.isThirdPartyTypeshedFile,
|
||||||
isThirdPartyImport: thirdPartyTypeInfo.isThirdPartyImport,
|
isThirdPartyImport: thirdPartyTypeInfo.isThirdPartyImport,
|
||||||
@ -1370,8 +1364,8 @@ export class Program {
|
|||||||
if (this._isImportAllowed(sourceFileInfo, importResult, implicitImport.isStubFile)) {
|
if (this._isImportAllowed(sourceFileInfo, importResult, implicitImport.isStubFile)) {
|
||||||
if (!implicitImport.isNativeLib) {
|
if (!implicitImport.isNativeLib) {
|
||||||
const thirdPartyTypeInfo = getThirdPartyImportInfo(importResult);
|
const thirdPartyTypeInfo = getThirdPartyImportInfo(importResult);
|
||||||
newImportPathMap.set(implicitImport.path, {
|
newImportPathMap.set(implicitImport.uri.key, {
|
||||||
path: implicitImport.path,
|
path: implicitImport.uri,
|
||||||
isTypeshedFile:
|
isTypeshedFile:
|
||||||
!!importResult.isStdlibTypeshedFile || !!importResult.isThirdPartyTypeshedFile,
|
!!importResult.isStdlibTypeshedFile || !!importResult.isThirdPartyTypeshedFile,
|
||||||
isThirdPartyImport: thirdPartyTypeInfo.isThirdPartyImport,
|
isThirdPartyImport: thirdPartyTypeInfo.isThirdPartyImport,
|
||||||
@ -1391,7 +1385,7 @@ export class Program {
|
|||||||
if (options.verboseOutput) {
|
if (options.verboseOutput) {
|
||||||
this._console.info(
|
this._console.info(
|
||||||
`Could not resolve source for '${importResult.importName}' ` +
|
`Could not resolve source for '${importResult.importName}' ` +
|
||||||
`in file '${sourceFileInfo.sourceFile.getFilePath()}'`
|
`in file '${sourceFileInfo.sourceFile.getUri().toUserVisibleString()}'`
|
||||||
);
|
);
|
||||||
|
|
||||||
if (importResult.nonStubImportResult.importFailureInfo) {
|
if (importResult.nonStubImportResult.importFailureInfo) {
|
||||||
@ -1405,7 +1399,7 @@ export class Program {
|
|||||||
} else if (options.verboseOutput) {
|
} else if (options.verboseOutput) {
|
||||||
this._console.info(
|
this._console.info(
|
||||||
`Could not import '${importResult.importName}' ` +
|
`Could not import '${importResult.importName}' ` +
|
||||||
`in file '${sourceFileInfo.sourceFile.getFilePath()}'`
|
`in file '${sourceFileInfo.sourceFile.getUri().toUserVisibleString()}'`
|
||||||
);
|
);
|
||||||
if (importResult.importFailureInfo) {
|
if (importResult.importFailureInfo) {
|
||||||
importResult.importFailureInfo.forEach((diag) => {
|
importResult.importFailureInfo.forEach((diag) => {
|
||||||
@ -1417,17 +1411,17 @@ export class Program {
|
|||||||
|
|
||||||
const updatedImportMap = new Map<string, SourceFileInfo>();
|
const updatedImportMap = new Map<string, SourceFileInfo>();
|
||||||
sourceFileInfo.imports.forEach((importInfo) => {
|
sourceFileInfo.imports.forEach((importInfo) => {
|
||||||
const oldFilePath = importInfo.sourceFile.getFilePath();
|
const oldFilePath = importInfo.sourceFile.getUri();
|
||||||
|
|
||||||
// A previous import was removed.
|
// A previous import was removed.
|
||||||
if (!newImportPathMap.has(oldFilePath)) {
|
if (!newImportPathMap.has(oldFilePath.key)) {
|
||||||
importInfo.mutate((s) => {
|
importInfo.mutate((s) => {
|
||||||
s.importedBy = s.importedBy.filter(
|
s.importedBy = s.importedBy.filter(
|
||||||
(fi) => fi.sourceFile.getFilePath() !== sourceFileInfo.sourceFile.getFilePath()
|
(fi) => !fi.sourceFile.getUri().equals(sourceFileInfo.sourceFile.getUri())
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
updatedImportMap.set(oldFilePath, importInfo);
|
updatedImportMap.set(oldFilePath.key, importInfo);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1469,9 +1463,9 @@ export class Program {
|
|||||||
// Update the imports list. It should now map the set of imports
|
// Update the imports list. It should now map the set of imports
|
||||||
// specified by the source file.
|
// specified by the source file.
|
||||||
sourceFileInfo.mutate((s) => (s.imports = []));
|
sourceFileInfo.mutate((s) => (s.imports = []));
|
||||||
newImportPathMap.forEach((_, path) => {
|
newImportPathMap.forEach((_, key) => {
|
||||||
if (this.getSourceFileInfo(path)) {
|
if (this._getSourceFileInfoFromKey(key)) {
|
||||||
sourceFileInfo.mutate((s) => s.imports.push(this.getSourceFileInfo(path)!));
|
sourceFileInfo.mutate((s) => s.imports.push(this._getSourceFileInfoFromKey(key)!));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1480,26 +1474,29 @@ export class Program {
|
|||||||
sourceFileInfo.builtinsImport = undefined;
|
sourceFileInfo.builtinsImport = undefined;
|
||||||
const builtinsImport = sourceFileInfo.sourceFile.getBuiltinsImport();
|
const builtinsImport = sourceFileInfo.sourceFile.getBuiltinsImport();
|
||||||
if (builtinsImport && builtinsImport.isImportFound) {
|
if (builtinsImport && builtinsImport.isImportFound) {
|
||||||
const resolvedBuiltinsPath = builtinsImport.resolvedPaths[builtinsImport.resolvedPaths.length - 1];
|
const resolvedBuiltinsPath = builtinsImport.resolvedUris[builtinsImport.resolvedUris.length - 1];
|
||||||
sourceFileInfo.builtinsImport = this.getSourceFileInfo(resolvedBuiltinsPath);
|
sourceFileInfo.builtinsImport = this.getSourceFileInfo(resolvedBuiltinsPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
return filesAdded;
|
return filesAdded;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _removeSourceFileFromListAndMap(filePath: string, indexToRemove: number) {
|
private _removeSourceFileFromListAndMap(fileUri: Uri, indexToRemove: number) {
|
||||||
this._sourceFileMap.delete(filePath);
|
this._sourceFileMap.delete(fileUri.key);
|
||||||
this._sourceFileList.splice(indexToRemove, 1);
|
this._sourceFileList.splice(indexToRemove, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _addToSourceFileListAndMap(fileInfo: SourceFileInfo) {
|
private _addToSourceFileListAndMap(fileInfo: SourceFileInfo) {
|
||||||
const filePath = fileInfo.sourceFile.getFilePath();
|
const fileUri = fileInfo.sourceFile.getUri();
|
||||||
|
|
||||||
// We should never add a file with the same path twice.
|
// We should never add a file with the same path twice.
|
||||||
assert(!this._sourceFileMap.has(filePath));
|
assert(!this._sourceFileMap.has(fileUri.key));
|
||||||
|
|
||||||
|
// We should never have an empty URI for a source file.
|
||||||
|
assert(!fileInfo.sourceFile.getUri().isEmpty());
|
||||||
|
|
||||||
this._sourceFileList.push(fileInfo);
|
this._sourceFileList.push(fileInfo);
|
||||||
this._sourceFileMap.set(filePath, fileInfo);
|
this._sourceFileMap.set(fileUri.key, fileInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static _getPrintTypeFlags(configOptions: ConfigOptions): PrintTypeFlags {
|
private static _getPrintTypeFlags(configOptions: ConfigOptions): PrintTypeFlags {
|
||||||
@ -1528,7 +1525,7 @@ export class Program {
|
|||||||
return flags;
|
return flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getModuleImportInfoForFile(filePath: string) {
|
private _getModuleImportInfoForFile(fileUri: Uri) {
|
||||||
// We allow illegal module names (e.g. names that include "-" in them)
|
// We allow illegal module names (e.g. names that include "-" in them)
|
||||||
// because we want a unique name for each module even if it cannot be
|
// because we want a unique name for each module even if it cannot be
|
||||||
// imported through an "import" statement. It's important to have a
|
// imported through an "import" statement. It's important to have a
|
||||||
@ -1536,7 +1533,7 @@ export class Program {
|
|||||||
// name. The type checker uses the fully-qualified (unique) module name
|
// name. The type checker uses the fully-qualified (unique) module name
|
||||||
// to differentiate between such types.
|
// to differentiate between such types.
|
||||||
const moduleNameAndType = this._importResolver.getModuleNameForImport(
|
const moduleNameAndType = this._importResolver.getModuleNameForImport(
|
||||||
filePath,
|
fileUri,
|
||||||
this._configOptions.getDefaultExecEnvironment(),
|
this._configOptions.getDefaultExecEnvironment(),
|
||||||
/* allowIllegalModuleName */ true,
|
/* allowIllegalModuleName */ true,
|
||||||
/* detectPyTyped */ true
|
/* detectPyTyped */ true
|
||||||
@ -1549,7 +1546,7 @@ export class Program {
|
|||||||
// it "shadows" a type stub file for purposes of finding doc strings and definitions.
|
// it "shadows" a type stub file for purposes of finding doc strings and definitions.
|
||||||
// We need to track the relationship so if the original type stub is removed from the
|
// We need to track the relationship so if the original type stub is removed from the
|
||||||
// program, we can remove the corresponding shadowed file and any files it imports.
|
// program, we can remove the corresponding shadowed file and any files it imports.
|
||||||
private _addShadowedFile(stubFile: SourceFileInfo, shadowImplPath: string): SourceFile {
|
private _addShadowedFile(stubFile: SourceFileInfo, shadowImplPath: Uri): SourceFile {
|
||||||
let shadowFileInfo = this.getSourceFileInfo(shadowImplPath);
|
let shadowFileInfo = this.getSourceFileInfo(shadowImplPath);
|
||||||
|
|
||||||
if (!shadowFileInfo) {
|
if (!shadowFileInfo) {
|
||||||
@ -1567,11 +1564,11 @@ export class Program {
|
|||||||
return shadowFileInfo.sourceFile;
|
return shadowFileInfo.sourceFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _createInterimFileInfo(filePath: string) {
|
private _createInterimFileInfo(fileUri: Uri) {
|
||||||
const moduleImportInfo = this._getModuleImportInfoForFile(filePath);
|
const moduleImportInfo = this._getModuleImportInfoForFile(fileUri);
|
||||||
const sourceFile = this._sourceFileFactory.createSourceFile(
|
const sourceFile = this._sourceFileFactory.createSourceFile(
|
||||||
this.serviceProvider,
|
this.serviceProvider,
|
||||||
filePath,
|
fileUri,
|
||||||
moduleImportInfo.moduleName,
|
moduleImportInfo.moduleName,
|
||||||
/* isThirdPartyImport */ false,
|
/* isThirdPartyImport */ false,
|
||||||
/* isInPyTypedPackage */ false,
|
/* isInPyTypedPackage */ false,
|
||||||
@ -1673,8 +1670,8 @@ export class Program {
|
|||||||
|
|
||||||
let nextImplicitImport = this._getImplicitImports(fileToAnalyze);
|
let nextImplicitImport = this._getImplicitImports(fileToAnalyze);
|
||||||
while (nextImplicitImport) {
|
while (nextImplicitImport) {
|
||||||
const implicitPath = nextImplicitImport.sourceFile.getFilePath();
|
const implicitPath = nextImplicitImport.sourceFile.getUri();
|
||||||
if (implicitSet.has(implicitPath)) {
|
if (implicitSet.has(implicitPath.key)) {
|
||||||
// We've found a cycle. Break out of the loop.
|
// We've found a cycle. Break out of the loop.
|
||||||
debug.fail(
|
debug.fail(
|
||||||
this.serviceProvider
|
this.serviceProvider
|
||||||
@ -1683,7 +1680,7 @@ export class Program {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
implicitSet.add(implicitPath);
|
implicitSet.add(implicitPath.key);
|
||||||
implicitImports.push(nextImplicitImport);
|
implicitImports.push(nextImplicitImport);
|
||||||
|
|
||||||
this._parseFile(nextImplicitImport, /* content */ undefined, skipFileNeededCheck);
|
this._parseFile(nextImplicitImport, /* content */ undefined, skipFileNeededCheck);
|
||||||
@ -1773,27 +1770,27 @@ export class Program {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _lookUpImport = (
|
private _lookUpImport = (
|
||||||
filePathOrModule: string | AbsoluteModuleDescriptor,
|
fileUriOrModule: Uri | AbsoluteModuleDescriptor,
|
||||||
options?: LookupImportOptions
|
options?: LookupImportOptions
|
||||||
): ImportLookupResult | undefined => {
|
): ImportLookupResult | undefined => {
|
||||||
let sourceFileInfo: SourceFileInfo | undefined;
|
let sourceFileInfo: SourceFileInfo | undefined;
|
||||||
|
|
||||||
if (typeof filePathOrModule === 'string') {
|
if (Uri.isUri(fileUriOrModule)) {
|
||||||
sourceFileInfo = this.getSourceFileInfo(filePathOrModule);
|
sourceFileInfo = this.getSourceFileInfo(fileUriOrModule);
|
||||||
} else {
|
} else {
|
||||||
// Resolve the import.
|
// Resolve the import.
|
||||||
const importResult = this._importResolver.resolveImport(
|
const importResult = this._importResolver.resolveImport(
|
||||||
filePathOrModule.importingFilePath,
|
fileUriOrModule.importingFileUri,
|
||||||
this._configOptions.findExecEnvironment(filePathOrModule.importingFilePath),
|
this._configOptions.findExecEnvironment(fileUriOrModule.importingFileUri),
|
||||||
{
|
{
|
||||||
leadingDots: 0,
|
leadingDots: 0,
|
||||||
nameParts: filePathOrModule.nameParts,
|
nameParts: fileUriOrModule.nameParts,
|
||||||
importedSymbols: undefined,
|
importedSymbols: undefined,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
if (importResult.isImportFound && !importResult.isNativeLib && importResult.resolvedPaths.length > 0) {
|
if (importResult.isImportFound && !importResult.isNativeLib && importResult.resolvedUris.length > 0) {
|
||||||
const resolvedPath = importResult.resolvedPaths[importResult.resolvedPaths.length - 1];
|
const resolvedPath = importResult.resolvedUris[importResult.resolvedUris.length - 1];
|
||||||
if (resolvedPath) {
|
if (resolvedPath) {
|
||||||
// See if the source file already exists in the program.
|
// See if the source file already exists in the program.
|
||||||
sourceFileInfo = this.getSourceFileInfo(resolvedPath);
|
sourceFileInfo = this.getSourceFileInfo(resolvedPath);
|
||||||
@ -1872,7 +1869,7 @@ export class Program {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _checkTypes(fileToCheck: SourceFileInfo, token: CancellationToken, chainedByList?: SourceFileInfo[]) {
|
private _checkTypes(fileToCheck: SourceFileInfo, token: CancellationToken, chainedByList?: SourceFileInfo[]) {
|
||||||
return this._logTracker.log(`analyzing: ${fileToCheck.sourceFile.getFilePath()}`, (logState) => {
|
return this._logTracker.log(`analyzing: ${fileToCheck.sourceFile.getUri()}`, (logState) => {
|
||||||
// If the file isn't needed because it was eliminated from the
|
// If the file isn't needed because it was eliminated from the
|
||||||
// transitive closure or deleted, skip the file rather than wasting
|
// transitive closure or deleted, skip the file rather than wasting
|
||||||
// time on it.
|
// time on it.
|
||||||
@ -1911,7 +1908,7 @@ export class Program {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (boundFile) {
|
if (boundFile) {
|
||||||
const execEnv = this._configOptions.findExecEnvironment(fileToCheck.sourceFile.getFilePath());
|
const execEnv = this._configOptions.findExecEnvironment(fileToCheck.sourceFile.getUri());
|
||||||
fileToCheck.sourceFile.check(
|
fileToCheck.sourceFile.check(
|
||||||
this.configOptions,
|
this.configOptions,
|
||||||
this._importResolver,
|
this._importResolver,
|
||||||
@ -2028,8 +2025,8 @@ export class Program {
|
|||||||
) {
|
) {
|
||||||
// If the file is already in the closure map, we found a cyclical
|
// If the file is already in the closure map, we found a cyclical
|
||||||
// dependency. Don't recur further.
|
// dependency. Don't recur further.
|
||||||
const filePath = file.sourceFile.getFilePath();
|
const fileUri = file.sourceFile.getUri();
|
||||||
if (closureMap.has(filePath)) {
|
if (closureMap.has(fileUri.key)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2041,7 +2038,7 @@ export class Program {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add the file to the closure map.
|
// Add the file to the closure map.
|
||||||
closureMap.set(filePath, file);
|
closureMap.set(fileUri.key, file);
|
||||||
|
|
||||||
// If this file hasn't already been parsed, parse it now. This will
|
// If this file hasn't already been parsed, parse it now. This will
|
||||||
// discover any files it imports. Skip this if the file is part
|
// discover any files it imports. Skip this if the file is part
|
||||||
@ -2074,13 +2071,13 @@ export class Program {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const filePath = sourceFileInfo.sourceFile.getFilePath();
|
const fileUri = sourceFileInfo.sourceFile.getUri();
|
||||||
|
|
||||||
filesVisited.set(filePath, sourceFileInfo);
|
filesVisited.set(fileUri.key, sourceFileInfo);
|
||||||
|
|
||||||
let detectedCycle = false;
|
let detectedCycle = false;
|
||||||
|
|
||||||
if (dependencyMap.has(filePath)) {
|
if (dependencyMap.has(fileUri.key)) {
|
||||||
// We detect a cycle (partial or full). A full cycle is one that is
|
// We detect a cycle (partial or full). A full cycle is one that is
|
||||||
// rooted in the file at the start of our dependency chain. A partial
|
// rooted in the file at the start of our dependency chain. A partial
|
||||||
// cycle loops back on some other file in the dependency chain. We
|
// cycle loops back on some other file in the dependency chain. We
|
||||||
@ -2097,7 +2094,7 @@ export class Program {
|
|||||||
} else {
|
} else {
|
||||||
// If we've already checked this dependency along
|
// If we've already checked this dependency along
|
||||||
// some other path, we can skip it.
|
// some other path, we can skip it.
|
||||||
if (dependencyMap.has(filePath)) {
|
if (dependencyMap.has(fileUri.key)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2105,7 +2102,7 @@ export class Program {
|
|||||||
// (for ordering information). Set the dependency map
|
// (for ordering information). Set the dependency map
|
||||||
// entry to true to indicate that we're actively exploring
|
// entry to true to indicate that we're actively exploring
|
||||||
// that dependency.
|
// that dependency.
|
||||||
dependencyMap.set(filePath, true);
|
dependencyMap.set(fileUri.key, true);
|
||||||
dependencyChain.push(sourceFileInfo);
|
dependencyChain.push(sourceFileInfo);
|
||||||
|
|
||||||
for (const imp of sourceFileInfo.imports) {
|
for (const imp of sourceFileInfo.imports) {
|
||||||
@ -2116,7 +2113,7 @@ export class Program {
|
|||||||
|
|
||||||
// Set the dependencyMap entry to false to indicate that we have
|
// Set the dependencyMap entry to false to indicate that we have
|
||||||
// already explored this file and don't need to explore it again.
|
// already explored this file and don't need to explore it again.
|
||||||
dependencyMap.set(filePath, false);
|
dependencyMap.set(fileUri.key, false);
|
||||||
dependencyChain.pop();
|
dependencyChain.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2126,7 +2123,7 @@ export class Program {
|
|||||||
private _logImportCycle(dependencyChain: SourceFileInfo[]) {
|
private _logImportCycle(dependencyChain: SourceFileInfo[]) {
|
||||||
const circDep = new CircularDependency();
|
const circDep = new CircularDependency();
|
||||||
dependencyChain.forEach((sourceFileInfo) => {
|
dependencyChain.forEach((sourceFileInfo) => {
|
||||||
circDep.appendPath(sourceFileInfo.sourceFile.getFilePath());
|
circDep.appendPath(sourceFileInfo.sourceFile.getUri());
|
||||||
});
|
});
|
||||||
|
|
||||||
circDep.normalizeOrder();
|
circDep.normalizeOrder();
|
||||||
@ -2137,15 +2134,15 @@ export class Program {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _markFileDirtyRecursive(sourceFileInfo: SourceFileInfo, markSet: Set<string>, forceRebinding = false) {
|
private _markFileDirtyRecursive(sourceFileInfo: SourceFileInfo, markSet: Set<string>, forceRebinding = false) {
|
||||||
const filePath = sourceFileInfo.sourceFile.getFilePath();
|
const fileUri = sourceFileInfo.sourceFile.getUri();
|
||||||
|
|
||||||
// Don't mark it again if it's already been visited.
|
// Don't mark it again if it's already been visited.
|
||||||
if (markSet.has(filePath)) {
|
if (markSet.has(fileUri.key)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceFileInfo.sourceFile.markReanalysisRequired(forceRebinding);
|
sourceFileInfo.sourceFile.markReanalysisRequired(forceRebinding);
|
||||||
markSet.add(filePath);
|
markSet.add(fileUri.key);
|
||||||
|
|
||||||
sourceFileInfo.importedBy.forEach((dep) => {
|
sourceFileInfo.importedBy.forEach((dep) => {
|
||||||
// Changes on chained source file can change symbols in the symbol table and
|
// Changes on chained source file can change symbols in the symbol table and
|
||||||
|
@ -70,7 +70,7 @@ export function createProperty(
|
|||||||
decoratorType.details.name,
|
decoratorType.details.name,
|
||||||
getClassFullName(decoratorNode, fileInfo.moduleName, `__property_${fget.details.name}`),
|
getClassFullName(decoratorNode, fileInfo.moduleName, `__property_${fget.details.name}`),
|
||||||
fileInfo.moduleName,
|
fileInfo.moduleName,
|
||||||
fileInfo.filePath,
|
fileInfo.fileUri,
|
||||||
ClassTypeFlags.PropertyClass | ClassTypeFlags.BuiltInClass,
|
ClassTypeFlags.PropertyClass | ClassTypeFlags.BuiltInClass,
|
||||||
typeSourceId,
|
typeSourceId,
|
||||||
/* declaredMetaclass */ undefined,
|
/* declaredMetaclass */ undefined,
|
||||||
@ -171,7 +171,7 @@ export function clonePropertyWithSetter(
|
|||||||
classType.details.name,
|
classType.details.name,
|
||||||
classType.details.fullName,
|
classType.details.fullName,
|
||||||
classType.details.moduleName,
|
classType.details.moduleName,
|
||||||
getFileInfo(errorNode).filePath,
|
getFileInfo(errorNode).fileUri,
|
||||||
flagsToClone,
|
flagsToClone,
|
||||||
classType.details.typeSourceId,
|
classType.details.typeSourceId,
|
||||||
classType.details.declaredMetaclass,
|
classType.details.declaredMetaclass,
|
||||||
@ -228,7 +228,7 @@ export function clonePropertyWithDeleter(
|
|||||||
classType.details.name,
|
classType.details.name,
|
||||||
classType.details.fullName,
|
classType.details.fullName,
|
||||||
classType.details.moduleName,
|
classType.details.moduleName,
|
||||||
getFileInfo(errorNode).filePath,
|
getFileInfo(errorNode).fileUri,
|
||||||
classType.details.flags,
|
classType.details.flags,
|
||||||
classType.details.typeSourceId,
|
classType.details.typeSourceId,
|
||||||
classType.details.declaredMetaclass,
|
classType.details.declaredMetaclass,
|
||||||
|
@ -8,24 +8,23 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { FileSystem } from '../common/fileSystem';
|
import { FileSystem } from '../common/fileSystem';
|
||||||
import { combinePaths, isDirectory, isFile } from '../common/pathUtils';
|
import { Uri } from '../common/uri/uri';
|
||||||
|
import { isDirectory, isFile } from '../common/uri/uriUtils';
|
||||||
|
|
||||||
export interface PyTypedInfo {
|
export interface PyTypedInfo {
|
||||||
pyTypedPath: string;
|
pyTypedPath: Uri;
|
||||||
isPartiallyTyped: boolean;
|
isPartiallyTyped: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const _pyTypedFileName = 'py.typed';
|
export function getPyTypedInfo(fileSystem: FileSystem, dirPath: Uri): PyTypedInfo | undefined {
|
||||||
|
|
||||||
export function getPyTypedInfo(fileSystem: FileSystem, dirPath: string): PyTypedInfo | undefined {
|
|
||||||
if (!fileSystem.existsSync(dirPath) || !isDirectory(fileSystem, dirPath)) {
|
if (!fileSystem.existsSync(dirPath) || !isDirectory(fileSystem, dirPath)) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
let isPartiallyTyped = false;
|
let isPartiallyTyped = false;
|
||||||
const pyTypedPath = combinePaths(dirPath, _pyTypedFileName);
|
const pyTypedPath = dirPath.pytypedUri;
|
||||||
|
|
||||||
if (!fileSystem.existsSync(dirPath) || !isFile(fileSystem, pyTypedPath)) {
|
if (!fileSystem.existsSync(pyTypedPath) || !isFile(fileSystem, pyTypedPath)) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,43 +12,32 @@ import { compareComparableValues } from '../common/core';
|
|||||||
import { FileSystem } from '../common/fileSystem';
|
import { FileSystem } from '../common/fileSystem';
|
||||||
import { Host } from '../common/host';
|
import { Host } from '../common/host';
|
||||||
import * as pathConsts from '../common/pathConsts';
|
import * as pathConsts from '../common/pathConsts';
|
||||||
import {
|
import { PythonVersion, versionToString } from '../common/pythonVersion';
|
||||||
combinePaths,
|
import { Uri } from '../common/uri/uri';
|
||||||
containsPath,
|
import { getFileSystemEntries, isDirectory, tryStat } from '../common/uri/uriUtils';
|
||||||
ensureTrailingDirectorySeparator,
|
|
||||||
getDirectoryPath,
|
|
||||||
getFileSystemEntries,
|
|
||||||
isDirectory,
|
|
||||||
normalizePath,
|
|
||||||
tryStat,
|
|
||||||
} from '../common/pathUtils';
|
|
||||||
import { versionToString } from '../common/pythonVersion';
|
|
||||||
import { PythonVersion } from '../common/pythonVersion';
|
|
||||||
|
|
||||||
export interface PythonPathResult {
|
export interface PythonPathResult {
|
||||||
paths: string[];
|
paths: Uri[];
|
||||||
prefix: string;
|
prefix: Uri | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const stdLibFolderName = 'stdlib';
|
export const stdLibFolderName = 'stdlib';
|
||||||
export const thirdPartyFolderName = 'stubs';
|
export const thirdPartyFolderName = 'stubs';
|
||||||
|
|
||||||
export function getTypeShedFallbackPath(fs: FileSystem) {
|
export function getTypeShedFallbackPath(fs: FileSystem) {
|
||||||
let moduleDirectory = fs.getModulePath();
|
const moduleDirectory = fs.getModulePath();
|
||||||
if (!moduleDirectory) {
|
if (!moduleDirectory || moduleDirectory.isEmpty()) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
moduleDirectory = getDirectoryPath(ensureTrailingDirectorySeparator(normalizePath(moduleDirectory)));
|
const typeshedPath = moduleDirectory.combinePaths(pathConsts.typeshedFallback);
|
||||||
|
|
||||||
const typeshedPath = combinePaths(moduleDirectory, pathConsts.typeshedFallback);
|
|
||||||
if (fs.existsSync(typeshedPath)) {
|
if (fs.existsSync(typeshedPath)) {
|
||||||
return fs.realCasePath(typeshedPath);
|
return fs.realCasePath(typeshedPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
// In the debug version of Pyright, the code is one level
|
// In the debug version of Pyright, the code is one level
|
||||||
// deeper, so we need to look one level up for the typeshed fallback.
|
// deeper, so we need to look one level up for the typeshed fallback.
|
||||||
const debugTypeshedPath = combinePaths(getDirectoryPath(moduleDirectory), pathConsts.typeshedFallback);
|
const debugTypeshedPath = moduleDirectory.getDirectory().combinePaths(pathConsts.typeshedFallback);
|
||||||
if (fs.existsSync(debugTypeshedPath)) {
|
if (fs.existsSync(debugTypeshedPath)) {
|
||||||
return fs.realCasePath(debugTypeshedPath);
|
return fs.realCasePath(debugTypeshedPath);
|
||||||
}
|
}
|
||||||
@ -56,8 +45,8 @@ export function getTypeShedFallbackPath(fs: FileSystem) {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getTypeshedSubdirectory(typeshedPath: string, isStdLib: boolean) {
|
export function getTypeshedSubdirectory(typeshedPath: Uri, isStdLib: boolean) {
|
||||||
return combinePaths(typeshedPath, isStdLib ? stdLibFolderName : thirdPartyFolderName);
|
return typeshedPath.combinePaths(isStdLib ? stdLibFolderName : thirdPartyFolderName);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function findPythonSearchPaths(
|
export function findPythonSearchPaths(
|
||||||
@ -66,21 +55,21 @@ export function findPythonSearchPaths(
|
|||||||
host: Host,
|
host: Host,
|
||||||
importFailureInfo: string[],
|
importFailureInfo: string[],
|
||||||
includeWatchPathsOnly?: boolean | undefined,
|
includeWatchPathsOnly?: boolean | undefined,
|
||||||
workspaceRoot?: string | undefined
|
workspaceRoot?: Uri | undefined
|
||||||
): string[] | undefined {
|
): Uri[] | undefined {
|
||||||
importFailureInfo.push('Finding python search paths');
|
importFailureInfo.push('Finding python search paths');
|
||||||
|
|
||||||
if (configOptions.venvPath !== undefined && configOptions.venv) {
|
if (configOptions.venvPath !== undefined && configOptions.venv) {
|
||||||
const venvDir = configOptions.venv;
|
const venvDir = configOptions.venv;
|
||||||
const venvPath = combinePaths(configOptions.venvPath, venvDir);
|
const venvPath = configOptions.venvPath.combinePaths(venvDir);
|
||||||
|
|
||||||
const foundPaths: string[] = [];
|
const foundPaths: Uri[] = [];
|
||||||
const sitePackagesPaths: string[] = [];
|
const sitePackagesPaths: Uri[] = [];
|
||||||
|
|
||||||
[pathConsts.lib, pathConsts.lib64, pathConsts.libAlternate].forEach((libPath) => {
|
[pathConsts.lib, pathConsts.lib64, pathConsts.libAlternate].forEach((libPath) => {
|
||||||
const sitePackagesPath = findSitePackagesPath(
|
const sitePackagesPath = findSitePackagesPath(
|
||||||
fs,
|
fs,
|
||||||
combinePaths(venvPath, libPath),
|
venvPath.combinePaths(libPath),
|
||||||
configOptions.defaultPythonVersion,
|
configOptions.defaultPythonVersion,
|
||||||
importFailureInfo
|
importFailureInfo
|
||||||
);
|
);
|
||||||
@ -115,11 +104,7 @@ export function findPythonSearchPaths(
|
|||||||
const pathResult = host.getPythonSearchPaths(configOptions.pythonPath, importFailureInfo);
|
const pathResult = host.getPythonSearchPaths(configOptions.pythonPath, importFailureInfo);
|
||||||
if (includeWatchPathsOnly && workspaceRoot) {
|
if (includeWatchPathsOnly && workspaceRoot) {
|
||||||
const paths = pathResult.paths
|
const paths = pathResult.paths
|
||||||
.filter(
|
.filter((p) => !p.startsWith(workspaceRoot) || p.startsWith(pathResult.prefix))
|
||||||
(p) =>
|
|
||||||
!containsPath(workspaceRoot, p, /* ignoreCase */ true) ||
|
|
||||||
containsPath(pathResult.prefix, p, /* ignoreCase */ true)
|
|
||||||
)
|
|
||||||
.map((p) => fs.realCasePath(p));
|
.map((p) => fs.realCasePath(p));
|
||||||
|
|
||||||
return paths;
|
return paths;
|
||||||
@ -135,10 +120,10 @@ export function isPythonBinary(p: string): boolean {
|
|||||||
|
|
||||||
function findSitePackagesPath(
|
function findSitePackagesPath(
|
||||||
fs: FileSystem,
|
fs: FileSystem,
|
||||||
libPath: string,
|
libPath: Uri,
|
||||||
pythonVersion: PythonVersion | undefined,
|
pythonVersion: PythonVersion | undefined,
|
||||||
importFailureInfo: string[]
|
importFailureInfo: string[]
|
||||||
): string | undefined {
|
): Uri | undefined {
|
||||||
if (fs.existsSync(libPath)) {
|
if (fs.existsSync(libPath)) {
|
||||||
importFailureInfo.push(`Found path '${libPath}'; looking for ${pathConsts.sitePackages}`);
|
importFailureInfo.push(`Found path '${libPath}'; looking for ${pathConsts.sitePackages}`);
|
||||||
} else {
|
} else {
|
||||||
@ -146,7 +131,7 @@ function findSitePackagesPath(
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const sitePackagesPath = combinePaths(libPath, pathConsts.sitePackages);
|
const sitePackagesPath = libPath.combinePaths(pathConsts.sitePackages);
|
||||||
if (fs.existsSync(sitePackagesPath)) {
|
if (fs.existsSync(sitePackagesPath)) {
|
||||||
importFailureInfo.push(`Found path '${sitePackagesPath}'`);
|
importFailureInfo.push(`Found path '${sitePackagesPath}'`);
|
||||||
return sitePackagesPath;
|
return sitePackagesPath;
|
||||||
@ -160,8 +145,8 @@ function findSitePackagesPath(
|
|||||||
|
|
||||||
// Candidate directories start with "python3.".
|
// Candidate directories start with "python3.".
|
||||||
const candidateDirs = entries.directories.filter((dirName) => {
|
const candidateDirs = entries.directories.filter((dirName) => {
|
||||||
if (dirName.startsWith('python3.')) {
|
if (dirName.fileName.startsWith('python3.')) {
|
||||||
const dirPath = combinePaths(libPath, dirName, pathConsts.sitePackages);
|
const dirPath = dirName.combinePaths(pathConsts.sitePackages);
|
||||||
return fs.existsSync(dirPath);
|
return fs.existsSync(dirPath);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@ -170,9 +155,11 @@ function findSitePackagesPath(
|
|||||||
// If there is a python3.X directory (where 3.X matches the configured python
|
// If there is a python3.X directory (where 3.X matches the configured python
|
||||||
// version), prefer that over other python directories.
|
// version), prefer that over other python directories.
|
||||||
if (pythonVersion) {
|
if (pythonVersion) {
|
||||||
const preferredDir = candidateDirs.find((dirName) => dirName === `python${versionToString(pythonVersion)}`);
|
const preferredDir = candidateDirs.find(
|
||||||
|
(dirName) => dirName.fileName === `python${versionToString(pythonVersion)}`
|
||||||
|
);
|
||||||
if (preferredDir) {
|
if (preferredDir) {
|
||||||
const dirPath = combinePaths(libPath, preferredDir, pathConsts.sitePackages);
|
const dirPath = preferredDir.combinePaths(pathConsts.sitePackages);
|
||||||
importFailureInfo.push(`Found path '${dirPath}'`);
|
importFailureInfo.push(`Found path '${dirPath}'`);
|
||||||
return dirPath;
|
return dirPath;
|
||||||
}
|
}
|
||||||
@ -182,7 +169,7 @@ function findSitePackagesPath(
|
|||||||
// first directory that starts with "python". Most of the time, there will be
|
// first directory that starts with "python". Most of the time, there will be
|
||||||
// only one.
|
// only one.
|
||||||
if (candidateDirs.length > 0) {
|
if (candidateDirs.length > 0) {
|
||||||
const dirPath = combinePaths(libPath, candidateDirs[0], pathConsts.sitePackages);
|
const dirPath = candidateDirs[0].combinePaths(pathConsts.sitePackages);
|
||||||
importFailureInfo.push(`Found path '${dirPath}'`);
|
importFailureInfo.push(`Found path '${dirPath}'`);
|
||||||
return dirPath;
|
return dirPath;
|
||||||
}
|
}
|
||||||
@ -190,8 +177,8 @@ function findSitePackagesPath(
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getPathsFromPthFiles(fs: FileSystem, parentDir: string): string[] {
|
export function getPathsFromPthFiles(fs: FileSystem, parentDir: Uri): Uri[] {
|
||||||
const searchPaths: string[] = [];
|
const searchPaths: Uri[] = [];
|
||||||
|
|
||||||
// Get a list of all *.pth files within the specified directory.
|
// Get a list of all *.pth files within the specified directory.
|
||||||
const pthFiles = fs
|
const pthFiles = fs
|
||||||
@ -200,7 +187,7 @@ export function getPathsFromPthFiles(fs: FileSystem, parentDir: string): string[
|
|||||||
.sort((a, b) => compareComparableValues(a.name, b.name));
|
.sort((a, b) => compareComparableValues(a.name, b.name));
|
||||||
|
|
||||||
pthFiles.forEach((pthFile) => {
|
pthFiles.forEach((pthFile) => {
|
||||||
const filePath = fs.realCasePath(combinePaths(parentDir, pthFile.name));
|
const filePath = fs.realCasePath(parentDir.combinePaths(pthFile.name));
|
||||||
const fileStats = tryStat(fs, filePath);
|
const fileStats = tryStat(fs, filePath);
|
||||||
|
|
||||||
// Skip all files that are much larger than expected.
|
// Skip all files that are much larger than expected.
|
||||||
@ -210,7 +197,7 @@ export function getPathsFromPthFiles(fs: FileSystem, parentDir: string): string[
|
|||||||
lines.forEach((line) => {
|
lines.forEach((line) => {
|
||||||
const trimmedLine = line.trim();
|
const trimmedLine = line.trim();
|
||||||
if (trimmedLine.length > 0 && !trimmedLine.startsWith('#') && !trimmedLine.match(/^import\s/)) {
|
if (trimmedLine.length > 0 && !trimmedLine.startsWith('#') && !trimmedLine.match(/^import\s/)) {
|
||||||
const pthPath = combinePaths(parentDir, trimmedLine);
|
const pthPath = parentDir.combinePaths(trimmedLine);
|
||||||
if (fs.existsSync(pthPath) && isDirectory(fs, pthPath)) {
|
if (fs.existsSync(pthPath) && isDirectory(fs, pthPath)) {
|
||||||
searchPaths.push(fs.realCasePath(pthPath));
|
searchPaths.push(fs.realCasePath(pthPath));
|
||||||
}
|
}
|
||||||
@ -222,8 +209,8 @@ export function getPathsFromPthFiles(fs: FileSystem, parentDir: string): string[
|
|||||||
return searchPaths;
|
return searchPaths;
|
||||||
}
|
}
|
||||||
|
|
||||||
function addPathIfUnique(pathList: string[], pathToAdd: string) {
|
function addPathIfUnique(pathList: Uri[], pathToAdd: Uri) {
|
||||||
if (!pathList.some((path) => path === pathToAdd)) {
|
if (!pathList.some((path) => path.key === pathToAdd.key)) {
|
||||||
pathList.push(pathToAdd);
|
pathList.push(pathToAdd);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -20,13 +20,14 @@ import { DiagnosticSink, TextRangeDiagnosticSink } from '../common/diagnosticSin
|
|||||||
import { ServiceProvider } from '../common/extensibility';
|
import { ServiceProvider } from '../common/extensibility';
|
||||||
import { FileSystem } from '../common/fileSystem';
|
import { FileSystem } from '../common/fileSystem';
|
||||||
import { LogTracker, getPathForLogging } from '../common/logTracker';
|
import { LogTracker, getPathForLogging } from '../common/logTracker';
|
||||||
import { getFileName, normalizeSlashes, stripFileExtension } from '../common/pathUtils';
|
import { stripFileExtension } from '../common/pathUtils';
|
||||||
import { convertOffsetsToRange, convertTextRangeToRange } from '../common/positionUtils';
|
import { convertOffsetsToRange, convertTextRangeToRange } from '../common/positionUtils';
|
||||||
import { ServiceKeys } from '../common/serviceProviderExtensions';
|
import { ServiceKeys } from '../common/serviceProviderExtensions';
|
||||||
import * as StringUtils from '../common/stringUtils';
|
import * as StringUtils from '../common/stringUtils';
|
||||||
import { Range, TextRange, getEmptyRange } from '../common/textRange';
|
import { Range, TextRange, getEmptyRange } from '../common/textRange';
|
||||||
import { TextRangeCollection } from '../common/textRangeCollection';
|
import { TextRangeCollection } from '../common/textRangeCollection';
|
||||||
import { Duration, timingStats } from '../common/timing';
|
import { Duration, timingStats } from '../common/timing';
|
||||||
|
import { Uri } from '../common/uri/uri';
|
||||||
import { Localizer } from '../localization/localize';
|
import { Localizer } from '../localization/localize';
|
||||||
import { ModuleNode } from '../parser/parseNodes';
|
import { ModuleNode } from '../parser/parseNodes';
|
||||||
import { IParser, ModuleImport, ParseOptions, ParseResults, Parser } from '../parser/parser';
|
import { IParser, ModuleImport, ParseOptions, ParseResults, Parser } from '../parser/parser';
|
||||||
@ -179,12 +180,9 @@ export class SourceFile {
|
|||||||
// Console interface to use for debugging.
|
// Console interface to use for debugging.
|
||||||
private _console: ConsoleInterface;
|
private _console: ConsoleInterface;
|
||||||
|
|
||||||
// File path unique to this file within the workspace. May not represent
|
// Uri unique to this file within the workspace. May not represent
|
||||||
// a real file on disk.
|
// a real file on disk.
|
||||||
private readonly _filePath: string;
|
private readonly _uri: Uri;
|
||||||
|
|
||||||
// File path on disk. May not be unique.
|
|
||||||
private readonly _realFilePath: string;
|
|
||||||
|
|
||||||
// Period-delimited import path for the module.
|
// Period-delimited import path for the module.
|
||||||
private _moduleName: string;
|
private _moduleName: string;
|
||||||
@ -232,46 +230,42 @@ export class SourceFile {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
readonly serviceProvider: ServiceProvider,
|
readonly serviceProvider: ServiceProvider,
|
||||||
filePath: string,
|
uri: Uri,
|
||||||
moduleName: string,
|
moduleName: string,
|
||||||
isThirdPartyImport: boolean,
|
isThirdPartyImport: boolean,
|
||||||
isThirdPartyPyTypedPresent: boolean,
|
isThirdPartyPyTypedPresent: boolean,
|
||||||
editMode: SourceFileEditMode,
|
editMode: SourceFileEditMode,
|
||||||
console?: ConsoleInterface,
|
console?: ConsoleInterface,
|
||||||
logTracker?: LogTracker,
|
logTracker?: LogTracker,
|
||||||
realFilePath?: string,
|
|
||||||
ipythonMode?: IPythonMode
|
ipythonMode?: IPythonMode
|
||||||
) {
|
) {
|
||||||
this.fileSystem = serviceProvider.get(ServiceKeys.fs);
|
this.fileSystem = serviceProvider.get(ServiceKeys.fs);
|
||||||
this._console = console || new StandardConsole();
|
this._console = console || new StandardConsole();
|
||||||
this._editMode = editMode;
|
this._editMode = editMode;
|
||||||
this._filePath = filePath;
|
this._uri = uri;
|
||||||
this._realFilePath = realFilePath ?? filePath;
|
|
||||||
this._moduleName = moduleName;
|
this._moduleName = moduleName;
|
||||||
this._isStubFile = filePath.endsWith('.pyi');
|
this._isStubFile = uri.hasExtension('.pyi');
|
||||||
this._isThirdPartyImport = isThirdPartyImport;
|
this._isThirdPartyImport = isThirdPartyImport;
|
||||||
this._isThirdPartyPyTypedPresent = isThirdPartyPyTypedPresent;
|
this._isThirdPartyPyTypedPresent = isThirdPartyPyTypedPresent;
|
||||||
const fileName = getFileName(filePath);
|
const fileName = uri.fileName;
|
||||||
this._isTypingStubFile =
|
this._isTypingStubFile =
|
||||||
this._isStubFile &&
|
this._isStubFile && (this._uri.pathEndsWith('stdlib/typing.pyi') || fileName === 'typing_extensions.pyi');
|
||||||
(this._filePath.endsWith(normalizeSlashes('stdlib/typing.pyi')) || fileName === 'typing_extensions.pyi');
|
|
||||||
this._isTypingExtensionsStubFile = this._isStubFile && fileName === 'typing_extensions.pyi';
|
this._isTypingExtensionsStubFile = this._isStubFile && fileName === 'typing_extensions.pyi';
|
||||||
this._isTypeshedStubFile =
|
this._isTypeshedStubFile = this._isStubFile && this._uri.pathEndsWith('stdlib/_typeshed/__init__.pyi');
|
||||||
this._isStubFile && this._filePath.endsWith(normalizeSlashes('stdlib/_typeshed/__init__.pyi'));
|
|
||||||
|
|
||||||
this._isBuiltInStubFile = false;
|
this._isBuiltInStubFile = false;
|
||||||
if (this._isStubFile) {
|
if (this._isStubFile) {
|
||||||
if (
|
if (
|
||||||
this._filePath.endsWith(normalizeSlashes('stdlib/collections/__init__.pyi')) ||
|
this._uri.pathEndsWith('stdlib/collections/__init__.pyi') ||
|
||||||
this._filePath.endsWith(normalizeSlashes('stdlib/asyncio/futures.pyi')) ||
|
this._uri.pathEndsWith('stdlib/asyncio/futures.pyi') ||
|
||||||
this._filePath.endsWith(normalizeSlashes('stdlib/asyncio/tasks.pyi')) ||
|
this._uri.pathEndsWith('stdlib/asyncio/tasks.pyi') ||
|
||||||
this._filePath.endsWith(normalizeSlashes('stdlib/builtins.pyi')) ||
|
this._uri.pathEndsWith('stdlib/builtins.pyi') ||
|
||||||
this._filePath.endsWith(normalizeSlashes('stdlib/_importlib_modulespec.pyi')) ||
|
this._uri.pathEndsWith('stdlib/_importlib_modulespec.pyi') ||
|
||||||
this._filePath.endsWith(normalizeSlashes('stdlib/dataclasses.pyi')) ||
|
this._uri.pathEndsWith('stdlib/dataclasses.pyi') ||
|
||||||
this._filePath.endsWith(normalizeSlashes('stdlib/abc.pyi')) ||
|
this._uri.pathEndsWith('stdlib/abc.pyi') ||
|
||||||
this._filePath.endsWith(normalizeSlashes('stdlib/enum.pyi')) ||
|
this._uri.pathEndsWith('stdlib/enum.pyi') ||
|
||||||
this._filePath.endsWith(normalizeSlashes('stdlib/queue.pyi')) ||
|
this._uri.pathEndsWith('stdlib/queue.pyi') ||
|
||||||
this._filePath.endsWith(normalizeSlashes('stdlib/types.pyi'))
|
this._uri.pathEndsWith('stdlib/types.pyi')
|
||||||
) {
|
) {
|
||||||
this._isBuiltInStubFile = true;
|
this._isBuiltInStubFile = true;
|
||||||
}
|
}
|
||||||
@ -282,16 +276,12 @@ export class SourceFile {
|
|||||||
this._ipythonMode = ipythonMode ?? IPythonMode.None;
|
this._ipythonMode = ipythonMode ?? IPythonMode.None;
|
||||||
}
|
}
|
||||||
|
|
||||||
getRealFilePath(): string {
|
|
||||||
return this._realFilePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
getIPythonMode(): IPythonMode {
|
getIPythonMode(): IPythonMode {
|
||||||
return this._ipythonMode;
|
return this._ipythonMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
getFilePath(): string {
|
getUri(): Uri {
|
||||||
return this._filePath;
|
return this._uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
getModuleName(): string {
|
getModuleName(): string {
|
||||||
@ -300,7 +290,7 @@ export class SourceFile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Synthesize a module name using the file path.
|
// Synthesize a module name using the file path.
|
||||||
return stripFileExtension(getFileName(this._filePath));
|
return stripFileExtension(this._uri.fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
setModuleName(name: string) {
|
setModuleName(name: string) {
|
||||||
@ -378,8 +368,8 @@ export class SourceFile {
|
|||||||
// that of the previous contents.
|
// that of the previous contents.
|
||||||
try {
|
try {
|
||||||
// Read the file's contents.
|
// Read the file's contents.
|
||||||
if (this.fileSystem.existsSync(this._filePath)) {
|
if (this.fileSystem.existsSync(this._uri)) {
|
||||||
const fileContents = this.fileSystem.readFileSync(this._filePath, 'utf8');
|
const fileContents = this.fileSystem.readFileSync(this._uri, 'utf8');
|
||||||
|
|
||||||
if (fileContents.length !== this._writableData.lastFileContentLength) {
|
if (fileContents.length !== this._writableData.lastFileContentLength) {
|
||||||
return true;
|
return true;
|
||||||
@ -465,16 +455,16 @@ export class SourceFile {
|
|||||||
// Otherwise, get content from file system.
|
// Otherwise, get content from file system.
|
||||||
try {
|
try {
|
||||||
// Check the file's length before attempting to read its full contents.
|
// Check the file's length before attempting to read its full contents.
|
||||||
const fileStat = this.fileSystem.statSync(this._filePath);
|
const fileStat = this.fileSystem.statSync(this._uri);
|
||||||
if (fileStat.size > _maxSourceFileSize) {
|
if (fileStat.size > _maxSourceFileSize) {
|
||||||
this._console.error(
|
this._console.error(
|
||||||
`File length of "${this._filePath}" is ${fileStat.size} ` +
|
`File length of "${this._uri}" is ${fileStat.size} ` +
|
||||||
`which exceeds the maximum supported file size of ${_maxSourceFileSize}`
|
`which exceeds the maximum supported file size of ${_maxSourceFileSize}`
|
||||||
);
|
);
|
||||||
throw new Error('File larger than max');
|
throw new Error('File larger than max');
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.fileSystem.readFileSync(this._filePath, 'utf8');
|
return this.fileSystem.readFileSync(this._uri, 'utf8');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
@ -580,7 +570,7 @@ export class SourceFile {
|
|||||||
// (or at least cancel) prior to calling again. It returns true if a parse
|
// (or at least cancel) prior to calling again. It returns true if a parse
|
||||||
// was required and false if the parse information was up to date already.
|
// was required and false if the parse information was up to date already.
|
||||||
parse(configOptions: ConfigOptions, importResolver: ImportResolver, content?: string): boolean {
|
parse(configOptions: ConfigOptions, importResolver: ImportResolver, content?: string): boolean {
|
||||||
return this._logTracker.log(`parsing: ${this._getPathForLogging(this._filePath)}`, (logState) => {
|
return this._logTracker.log(`parsing: ${this._getPathForLogging(this._uri)}`, (logState) => {
|
||||||
// If the file is already parsed, we can skip.
|
// If the file is already parsed, we can skip.
|
||||||
if (!this.isParseRequired()) {
|
if (!this.isParseRequired()) {
|
||||||
logState.suppress();
|
logState.suppress();
|
||||||
@ -608,7 +598,7 @@ export class SourceFile {
|
|||||||
diagSink.addError(`Source file could not be read`, getEmptyRange());
|
diagSink.addError(`Source file could not be read`, getEmptyRange());
|
||||||
fileContents = '';
|
fileContents = '';
|
||||||
|
|
||||||
if (!this.fileSystem.existsSync(this._realFilePath)) {
|
if (!this.fileSystem.existsSync(this._uri)) {
|
||||||
this._writableData.isFileDeleted = true;
|
this._writableData.isFileDeleted = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -618,7 +608,7 @@ export class SourceFile {
|
|||||||
// Parse the token stream, building the abstract syntax tree.
|
// Parse the token stream, building the abstract syntax tree.
|
||||||
const parseResults = this._parseFile(
|
const parseResults = this._parseFile(
|
||||||
configOptions,
|
configOptions,
|
||||||
this._filePath,
|
this._uri,
|
||||||
fileContents!,
|
fileContents!,
|
||||||
this._ipythonMode,
|
this._ipythonMode,
|
||||||
diagSink
|
diagSink
|
||||||
@ -632,7 +622,7 @@ export class SourceFile {
|
|||||||
this._writableData.parseResults.tokenizerOutput.pyrightIgnoreLines;
|
this._writableData.parseResults.tokenizerOutput.pyrightIgnoreLines;
|
||||||
|
|
||||||
// Resolve imports.
|
// Resolve imports.
|
||||||
const execEnvironment = configOptions.findExecEnvironment(this._filePath);
|
const execEnvironment = configOptions.findExecEnvironment(this._uri);
|
||||||
timingStats.resolveImportsTime.timeOperation(() => {
|
timingStats.resolveImportsTime.timeOperation(() => {
|
||||||
const importResult = this._resolveImports(
|
const importResult = this._resolveImports(
|
||||||
importResolver,
|
importResolver,
|
||||||
@ -648,7 +638,7 @@ export class SourceFile {
|
|||||||
|
|
||||||
// Is this file in a "strict" path?
|
// Is this file in a "strict" path?
|
||||||
const useStrict =
|
const useStrict =
|
||||||
configOptions.strict.find((strictFileSpec) => strictFileSpec.regExp.test(this._realFilePath)) !==
|
configOptions.strict.find((strictFileSpec) => this._uri.matchesRegex(strictFileSpec.regExp)) !==
|
||||||
undefined;
|
undefined;
|
||||||
|
|
||||||
const commentDiags: CommentUtils.CommentDiagnostic[] = [];
|
const commentDiags: CommentUtils.CommentDiagnostic[] = [];
|
||||||
@ -680,7 +670,10 @@ export class SourceFile {
|
|||||||
(typeof e.message === 'string' ? e.message : undefined) ||
|
(typeof e.message === 'string' ? e.message : undefined) ||
|
||||||
JSON.stringify(e);
|
JSON.stringify(e);
|
||||||
this._console.error(
|
this._console.error(
|
||||||
Localizer.Diagnostic.internalParseError().format({ file: this.getFilePath(), message })
|
Localizer.Diagnostic.internalParseError().format({
|
||||||
|
file: this.getUri().toUserVisibleString(),
|
||||||
|
message,
|
||||||
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
// Create dummy parse results.
|
// Create dummy parse results.
|
||||||
@ -708,7 +701,10 @@ export class SourceFile {
|
|||||||
|
|
||||||
const diagSink = this.createDiagnosticSink();
|
const diagSink = this.createDiagnosticSink();
|
||||||
diagSink.addError(
|
diagSink.addError(
|
||||||
Localizer.Diagnostic.internalParseError().format({ file: this.getFilePath(), message }),
|
Localizer.Diagnostic.internalParseError().format({
|
||||||
|
file: this.getUri().toUserVisibleString(),
|
||||||
|
message,
|
||||||
|
}),
|
||||||
getEmptyRange()
|
getEmptyRange()
|
||||||
);
|
);
|
||||||
this._writableData.parseDiagnostics = diagSink.fetchAndClear();
|
this._writableData.parseDiagnostics = diagSink.fetchAndClear();
|
||||||
@ -740,7 +736,7 @@ export class SourceFile {
|
|||||||
assert(!this._writableData.isBindingInProgress, 'Bind called while binding in progress');
|
assert(!this._writableData.isBindingInProgress, 'Bind called while binding in progress');
|
||||||
assert(this._writableData.parseResults !== undefined, 'Parse results not available');
|
assert(this._writableData.parseResults !== undefined, 'Parse results not available');
|
||||||
|
|
||||||
return this._logTracker.log(`binding: ${this._getPathForLogging(this._filePath)}`, () => {
|
return this._logTracker.log(`binding: ${this._getPathForLogging(this._uri)}`, () => {
|
||||||
try {
|
try {
|
||||||
// Perform name binding.
|
// Perform name binding.
|
||||||
timingStats.bindTime.timeOperation(() => {
|
timingStats.bindTime.timeOperation(() => {
|
||||||
@ -777,12 +773,18 @@ export class SourceFile {
|
|||||||
(typeof e.message === 'string' ? e.message : undefined) ||
|
(typeof e.message === 'string' ? e.message : undefined) ||
|
||||||
JSON.stringify(e);
|
JSON.stringify(e);
|
||||||
this._console.error(
|
this._console.error(
|
||||||
Localizer.Diagnostic.internalBindError().format({ file: this.getFilePath(), message })
|
Localizer.Diagnostic.internalBindError().format({
|
||||||
|
file: this.getUri().toUserVisibleString(),
|
||||||
|
message,
|
||||||
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
const diagSink = this.createDiagnosticSink();
|
const diagSink = this.createDiagnosticSink();
|
||||||
diagSink.addError(
|
diagSink.addError(
|
||||||
Localizer.Diagnostic.internalBindError().format({ file: this.getFilePath(), message }),
|
Localizer.Diagnostic.internalBindError().format({
|
||||||
|
file: this.getUri().toUserVisibleString(),
|
||||||
|
message,
|
||||||
|
}),
|
||||||
getEmptyRange()
|
getEmptyRange()
|
||||||
);
|
);
|
||||||
this._writableData.bindDiagnostics = diagSink.fetchAndClear();
|
this._writableData.bindDiagnostics = diagSink.fetchAndClear();
|
||||||
@ -814,7 +816,7 @@ export class SourceFile {
|
|||||||
assert(this.isCheckingRequired(), 'Check called unnecessarily');
|
assert(this.isCheckingRequired(), 'Check called unnecessarily');
|
||||||
assert(this._writableData.parseResults !== undefined, 'Parse results not available');
|
assert(this._writableData.parseResults !== undefined, 'Parse results not available');
|
||||||
|
|
||||||
return this._logTracker.log(`checking: ${this._getPathForLogging(this._filePath)}`, () => {
|
return this._logTracker.log(`checking: ${this._getPathForLogging(this._uri)}`, () => {
|
||||||
try {
|
try {
|
||||||
timingStats.typeCheckerTime.timeOperation(() => {
|
timingStats.typeCheckerTime.timeOperation(() => {
|
||||||
const checkDuration = new Duration();
|
const checkDuration = new Duration();
|
||||||
@ -840,11 +842,17 @@ export class SourceFile {
|
|||||||
(typeof e.message === 'string' ? e.message : undefined) ||
|
(typeof e.message === 'string' ? e.message : undefined) ||
|
||||||
JSON.stringify(e);
|
JSON.stringify(e);
|
||||||
this._console.error(
|
this._console.error(
|
||||||
Localizer.Diagnostic.internalTypeCheckingError().format({ file: this.getFilePath(), message })
|
Localizer.Diagnostic.internalTypeCheckingError().format({
|
||||||
|
file: this.getUri().toUserVisibleString(),
|
||||||
|
message,
|
||||||
|
})
|
||||||
);
|
);
|
||||||
const diagSink = this.createDiagnosticSink();
|
const diagSink = this.createDiagnosticSink();
|
||||||
diagSink.addError(
|
diagSink.addError(
|
||||||
Localizer.Diagnostic.internalTypeCheckingError().format({ file: this.getFilePath(), message }),
|
Localizer.Diagnostic.internalTypeCheckingError().format({
|
||||||
|
file: this.getUri().toUserVisibleString(),
|
||||||
|
message,
|
||||||
|
}),
|
||||||
getEmptyRange()
|
getEmptyRange()
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1097,7 +1105,7 @@ export class SourceFile {
|
|||||||
'\n' +
|
'\n' +
|
||||||
cirDep
|
cirDep
|
||||||
.getPaths()
|
.getPaths()
|
||||||
.map((path) => ' ' + path)
|
.map((path) => ' ' + path.toUserVisibleString())
|
||||||
.join('\n'),
|
.join('\n'),
|
||||||
getEmptyRange()
|
getEmptyRange()
|
||||||
);
|
);
|
||||||
@ -1120,7 +1128,7 @@ export class SourceFile {
|
|||||||
this._addTaskListDiagnostics(configOptions.taskListTokens, diagList);
|
this._addTaskListDiagnostics(configOptions.taskListTokens, diagList);
|
||||||
|
|
||||||
// If the file is in the ignore list, clear the diagnostic list.
|
// If the file is in the ignore list, clear the diagnostic list.
|
||||||
if (configOptions.ignore.find((ignoreFileSpec) => ignoreFileSpec.regExp.test(this._realFilePath))) {
|
if (configOptions.ignore.find((ignoreFileSpec) => this._uri.matchesRegex(ignoreFileSpec.regExp))) {
|
||||||
diagList = [];
|
diagList = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1243,13 +1251,13 @@ export class SourceFile {
|
|||||||
futureImports,
|
futureImports,
|
||||||
builtinsScope,
|
builtinsScope,
|
||||||
diagnosticSink: analysisDiagnostics,
|
diagnosticSink: analysisDiagnostics,
|
||||||
executionEnvironment: configOptions.findExecEnvironment(this._filePath),
|
executionEnvironment: configOptions.findExecEnvironment(this._uri),
|
||||||
diagnosticRuleSet: this._diagnosticRuleSet,
|
diagnosticRuleSet: this._diagnosticRuleSet,
|
||||||
fileContents,
|
fileContents,
|
||||||
lines: this._writableData.parseResults!.tokenizerOutput.lines,
|
lines: this._writableData.parseResults!.tokenizerOutput.lines,
|
||||||
typingSymbolAliases: this._writableData.parseResults!.typingSymbolAliases,
|
typingSymbolAliases: this._writableData.parseResults!.typingSymbolAliases,
|
||||||
definedConstants: configOptions.defineConstant,
|
definedConstants: configOptions.defineConstant,
|
||||||
filePath: this._filePath,
|
fileUri: this._uri,
|
||||||
moduleName: this.getModuleName(),
|
moduleName: this.getModuleName(),
|
||||||
isStubFile: this._isStubFile,
|
isStubFile: this._isStubFile,
|
||||||
isTypingStubFile: this._isTypingStubFile,
|
isTypingStubFile: this._isTypingStubFile,
|
||||||
@ -1281,7 +1289,7 @@ export class SourceFile {
|
|||||||
const imports: ImportResult[] = [];
|
const imports: ImportResult[] = [];
|
||||||
|
|
||||||
const resolveAndAddIfNotSelf = (nameParts: string[], skipMissingImport = false) => {
|
const resolveAndAddIfNotSelf = (nameParts: string[], skipMissingImport = false) => {
|
||||||
const importResult = importResolver.resolveImport(this._filePath, execEnv, {
|
const importResult = importResolver.resolveImport(this._uri, execEnv, {
|
||||||
leadingDots: 0,
|
leadingDots: 0,
|
||||||
nameParts,
|
nameParts,
|
||||||
importedSymbols: undefined,
|
importedSymbols: undefined,
|
||||||
@ -1292,7 +1300,7 @@ export class SourceFile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Avoid importing module from the module file itself.
|
// Avoid importing module from the module file itself.
|
||||||
if (importResult.resolvedPaths.length === 0 || importResult.resolvedPaths[0] !== this._filePath) {
|
if (importResult.resolvedUris.length === 0 || importResult.resolvedUris[0] !== this._uri) {
|
||||||
imports.push(importResult);
|
imports.push(importResult);
|
||||||
return importResult;
|
return importResult;
|
||||||
}
|
}
|
||||||
@ -1314,7 +1322,7 @@ export class SourceFile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const moduleImport of moduleImports) {
|
for (const moduleImport of moduleImports) {
|
||||||
const importResult = importResolver.resolveImport(this._filePath, execEnv, {
|
const importResult = importResolver.resolveImport(this._uri, execEnv, {
|
||||||
leadingDots: moduleImport.leadingDots,
|
leadingDots: moduleImport.leadingDots,
|
||||||
nameParts: moduleImport.nameParts,
|
nameParts: moduleImport.nameParts,
|
||||||
importedSymbols: moduleImport.importedSymbols,
|
importedSymbols: moduleImport.importedSymbols,
|
||||||
@ -1347,24 +1355,24 @@ export class SourceFile {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getPathForLogging(filepath: string) {
|
private _getPathForLogging(fileUri: Uri) {
|
||||||
return getPathForLogging(this.fileSystem, filepath);
|
return getPathForLogging(this.fileSystem, fileUri);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _parseFile(
|
private _parseFile(
|
||||||
configOptions: ConfigOptions,
|
configOptions: ConfigOptions,
|
||||||
filePath: string,
|
fileUri: Uri,
|
||||||
fileContents: string,
|
fileContents: string,
|
||||||
ipythonMode: IPythonMode,
|
ipythonMode: IPythonMode,
|
||||||
diagSink: DiagnosticSink
|
diagSink: DiagnosticSink
|
||||||
) {
|
) {
|
||||||
// Use the configuration options to determine the environment in which
|
// Use the configuration options to determine the environment in which
|
||||||
// this source file will be executed.
|
// this source file will be executed.
|
||||||
const execEnvironment = configOptions.findExecEnvironment(filePath);
|
const execEnvironment = configOptions.findExecEnvironment(fileUri);
|
||||||
|
|
||||||
const parseOptions = new ParseOptions();
|
const parseOptions = new ParseOptions();
|
||||||
parseOptions.ipythonMode = ipythonMode;
|
parseOptions.ipythonMode = ipythonMode;
|
||||||
if (filePath.endsWith('pyi')) {
|
if (fileUri.pathEndsWith('pyi')) {
|
||||||
parseOptions.isStubFile = true;
|
parseOptions.isStubFile = true;
|
||||||
}
|
}
|
||||||
parseOptions.pythonVersion = execEnvironment.pythonVersion;
|
parseOptions.pythonVersion = execEnvironment.pythonVersion;
|
||||||
@ -1378,7 +1386,7 @@ export class SourceFile {
|
|||||||
private _fireFileDirtyEvent() {
|
private _fireFileDirtyEvent() {
|
||||||
this.serviceProvider.tryGet(ServiceKeys.stateMutationListeners)?.forEach((l) => {
|
this.serviceProvider.tryGet(ServiceKeys.stateMutationListeners)?.forEach((l) => {
|
||||||
try {
|
try {
|
||||||
l.fileDirty?.(this._filePath);
|
l.fileDirty?.(this._uri);
|
||||||
} catch (ex: any) {
|
} catch (ex: any) {
|
||||||
const console = this.serviceProvider.tryGet(ServiceKeys.console);
|
const console = this.serviceProvider.tryGet(ServiceKeys.console);
|
||||||
if (console) {
|
if (console) {
|
||||||
|
@ -34,9 +34,9 @@ export function verifyNoCyclesInChainedFiles<T extends SourceFileInfo>(program:
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const set = new Set<string>([fileInfo.sourceFile.getFilePath()]);
|
const set = new Set<string>([fileInfo.sourceFile.getUri().key]);
|
||||||
while (nextChainedFile) {
|
while (nextChainedFile) {
|
||||||
const path = nextChainedFile.sourceFile.getFilePath();
|
const path = nextChainedFile.sourceFile.getUri().key;
|
||||||
if (set.has(path)) {
|
if (set.has(path)) {
|
||||||
// We found a cycle.
|
// We found a cycle.
|
||||||
fail(
|
fail(
|
||||||
@ -90,7 +90,7 @@ function _parseAllOpenCells(program: ProgramView): void {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
program.getParseResults(file.sourceFile.getFilePath());
|
program.getParseResults(file.sourceFile.getUri());
|
||||||
program.handleMemoryHighUsage();
|
program.handleMemoryHighUsage();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,31 +14,31 @@ import { appendArray } from '../common/collectionUtils';
|
|||||||
import { ExecutionEnvironment } from '../common/configOptions';
|
import { ExecutionEnvironment } from '../common/configOptions';
|
||||||
import { isDefined } from '../common/core';
|
import { isDefined } from '../common/core';
|
||||||
import { assertNever } from '../common/debug';
|
import { assertNever } from '../common/debug';
|
||||||
import { combinePaths, getAnyExtensionFromPath, stripFileExtension } from '../common/pathUtils';
|
import { Uri } from '../common/uri/uri';
|
||||||
import { ClassNode, ImportFromNode, ModuleNode, ParseNode, ParseNodeType } from '../parser/parseNodes';
|
import { ClassNode, ImportFromNode, ModuleNode, ParseNode, ParseNodeType } from '../parser/parseNodes';
|
||||||
import {
|
import {
|
||||||
AliasDeclaration,
|
AliasDeclaration,
|
||||||
ClassDeclaration,
|
ClassDeclaration,
|
||||||
Declaration,
|
Declaration,
|
||||||
FunctionDeclaration,
|
FunctionDeclaration,
|
||||||
|
ParameterDeclaration,
|
||||||
|
SpecialBuiltInClassDeclaration,
|
||||||
|
VariableDeclaration,
|
||||||
isAliasDeclaration,
|
isAliasDeclaration,
|
||||||
isClassDeclaration,
|
isClassDeclaration,
|
||||||
isFunctionDeclaration,
|
isFunctionDeclaration,
|
||||||
isParameterDeclaration,
|
isParameterDeclaration,
|
||||||
isSpecialBuiltInClassDeclaration,
|
isSpecialBuiltInClassDeclaration,
|
||||||
isVariableDeclaration,
|
isVariableDeclaration,
|
||||||
ParameterDeclaration,
|
|
||||||
SpecialBuiltInClassDeclaration,
|
|
||||||
VariableDeclaration,
|
|
||||||
} from './declaration';
|
} from './declaration';
|
||||||
import { ImportResolver } from './importResolver';
|
import { ImportResolver } from './importResolver';
|
||||||
import { SourceFileInfo } from './sourceFileInfo';
|
|
||||||
import { SourceFile } from './sourceFile';
|
import { SourceFile } from './sourceFile';
|
||||||
|
import { SourceFileInfo } from './sourceFileInfo';
|
||||||
import { isUserCode } from './sourceFileInfoUtils';
|
import { isUserCode } from './sourceFileInfoUtils';
|
||||||
import { buildImportTree } from './sourceMapperUtils';
|
import { buildImportTree } from './sourceMapperUtils';
|
||||||
import { TypeEvaluator } from './typeEvaluatorTypes';
|
import { TypeEvaluator } from './typeEvaluatorTypes';
|
||||||
import { ClassType, isFunction, isInstantiableClass, isOverloadedFunction } from './types';
|
|
||||||
import { lookUpClassMember } from './typeUtils';
|
import { lookUpClassMember } from './typeUtils';
|
||||||
|
import { ClassType, isFunction, isInstantiableClass, isOverloadedFunction } from './types';
|
||||||
|
|
||||||
type ClassOrFunctionOrVariableDeclaration =
|
type ClassOrFunctionOrVariableDeclaration =
|
||||||
| ClassDeclaration
|
| ClassDeclaration
|
||||||
@ -47,8 +47,8 @@ type ClassOrFunctionOrVariableDeclaration =
|
|||||||
| VariableDeclaration;
|
| VariableDeclaration;
|
||||||
|
|
||||||
// Creates and binds a shadowed file within the program.
|
// Creates and binds a shadowed file within the program.
|
||||||
export type ShadowFileBinder = (stubFilePath: string, implFilePath: string) => SourceFile | undefined;
|
export type ShadowFileBinder = (stubFileUri: Uri, implFileUri: Uri) => SourceFile | undefined;
|
||||||
export type BoundSourceGetter = (filePath: string) => SourceFileInfo | undefined;
|
export type BoundSourceGetter = (fileUri: Uri) => SourceFileInfo | undefined;
|
||||||
|
|
||||||
export class SourceMapper {
|
export class SourceMapper {
|
||||||
constructor(
|
constructor(
|
||||||
@ -63,10 +63,10 @@ export class SourceMapper {
|
|||||||
private _cancelToken: CancellationToken
|
private _cancelToken: CancellationToken
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
findModules(stubFilePath: string): ModuleNode[] {
|
findModules(stubFileUri: Uri): ModuleNode[] {
|
||||||
const sourceFiles = this._isStubThatShouldBeMappedToImplementation(stubFilePath)
|
const sourceFiles = this._isStubThatShouldBeMappedToImplementation(stubFileUri)
|
||||||
? this._getBoundSourceFilesFromStubFile(stubFilePath)
|
? this._getBoundSourceFilesFromStubFile(stubFileUri)
|
||||||
: [this._boundSourceGetter(stubFilePath)?.sourceFile];
|
: [this._boundSourceGetter(stubFileUri)?.sourceFile];
|
||||||
|
|
||||||
return sourceFiles
|
return sourceFiles
|
||||||
.filter(isDefined)
|
.filter(isDefined)
|
||||||
@ -74,8 +74,8 @@ export class SourceMapper {
|
|||||||
.filter(isDefined);
|
.filter(isDefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
getModuleNode(filePath: string): ModuleNode | undefined {
|
getModuleNode(fileUri: Uri): ModuleNode | undefined {
|
||||||
return this._boundSourceGetter(filePath)?.sourceFile.getParseResults()?.parseTree;
|
return this._boundSourceGetter(fileUri)?.sourceFile.getParseResults()?.parseTree;
|
||||||
}
|
}
|
||||||
|
|
||||||
findDeclarations(stubDecl: Declaration): Declaration[] {
|
findDeclarations(stubDecl: Declaration): Declaration[] {
|
||||||
@ -94,13 +94,13 @@ export class SourceMapper {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
findDeclarationsByType(originatedPath: string, type: ClassType, useTypeAlias = false): Declaration[] {
|
findDeclarationsByType(originatedPath: Uri, type: ClassType, useTypeAlias = false): Declaration[] {
|
||||||
const result: ClassOrFunctionOrVariableDeclaration[] = [];
|
const result: ClassOrFunctionOrVariableDeclaration[] = [];
|
||||||
this._addClassTypeDeclarations(originatedPath, type, result, new Set<string>(), useTypeAlias);
|
this._addClassTypeDeclarations(originatedPath, type, result, new Set<string>(), useTypeAlias);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
findClassDeclarationsByType(originatedPath: string, type: ClassType): ClassDeclaration[] {
|
findClassDeclarationsByType(originatedPath: Uri, type: ClassType): ClassDeclaration[] {
|
||||||
const result = this.findDeclarationsByType(originatedPath, type);
|
const result = this.findDeclarationsByType(originatedPath, type);
|
||||||
return result.filter((r) => isClassDeclaration(r)).map((r) => r as ClassDeclaration);
|
return result.filter((r) => isClassDeclaration(r)).map((r) => r as ClassDeclaration);
|
||||||
}
|
}
|
||||||
@ -111,17 +111,17 @@ export class SourceMapper {
|
|||||||
.map((d) => d as FunctionDeclaration);
|
.map((d) => d as FunctionDeclaration);
|
||||||
}
|
}
|
||||||
|
|
||||||
isUserCode(path: string): boolean {
|
isUserCode(uri: Uri): boolean {
|
||||||
return isUserCode(this._boundSourceGetter(path));
|
return isUserCode(this._boundSourceGetter(uri));
|
||||||
}
|
}
|
||||||
|
|
||||||
getNextFileName(path: string) {
|
getNextFileName(uri: Uri) {
|
||||||
const withoutExtension = stripFileExtension(path);
|
const withoutExtension = uri.stripExtension();
|
||||||
let suffix = 1;
|
let suffix = 1;
|
||||||
let result = `${withoutExtension}_${suffix}.py`;
|
let result = withoutExtension.addExtension(`_${suffix}.py`);
|
||||||
while (this.isUserCode(result) && suffix < 1000) {
|
while (this.isUserCode(result) && suffix < 1000) {
|
||||||
suffix += 1;
|
suffix += 1;
|
||||||
result = `${withoutExtension}_${suffix}.py`;
|
result = withoutExtension.addExtension(`_${suffix}.py`);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -132,7 +132,7 @@ export class SourceMapper {
|
|||||||
) {
|
) {
|
||||||
if (stubDecl.node.valueExpression.nodeType === ParseNodeType.Name) {
|
if (stubDecl.node.valueExpression.nodeType === ParseNodeType.Name) {
|
||||||
const className = stubDecl.node.valueExpression.value;
|
const className = stubDecl.node.valueExpression.value;
|
||||||
const sourceFiles = this._getBoundSourceFilesFromStubFile(stubDecl.path);
|
const sourceFiles = this._getBoundSourceFilesFromStubFile(stubDecl.uri);
|
||||||
|
|
||||||
return sourceFiles.flatMap((sourceFile) =>
|
return sourceFiles.flatMap((sourceFile) =>
|
||||||
this._findClassDeclarationsByName(sourceFile, className, recursiveDeclCache)
|
this._findClassDeclarationsByName(sourceFile, className, recursiveDeclCache)
|
||||||
@ -144,7 +144,7 @@ export class SourceMapper {
|
|||||||
|
|
||||||
private _findClassOrTypeAliasDeclarations(stubDecl: ClassDeclaration, recursiveDeclCache = new Set<string>()) {
|
private _findClassOrTypeAliasDeclarations(stubDecl: ClassDeclaration, recursiveDeclCache = new Set<string>()) {
|
||||||
const className = this._getFullClassName(stubDecl.node);
|
const className = this._getFullClassName(stubDecl.node);
|
||||||
const sourceFiles = this._getBoundSourceFilesFromStubFile(stubDecl.path);
|
const sourceFiles = this._getBoundSourceFilesFromStubFile(stubDecl.uri);
|
||||||
|
|
||||||
return sourceFiles.flatMap((sourceFile) =>
|
return sourceFiles.flatMap((sourceFile) =>
|
||||||
this._findClassDeclarationsByName(sourceFile, className, recursiveDeclCache)
|
this._findClassDeclarationsByName(sourceFile, className, recursiveDeclCache)
|
||||||
@ -156,7 +156,7 @@ export class SourceMapper {
|
|||||||
recursiveDeclCache = new Set<string>()
|
recursiveDeclCache = new Set<string>()
|
||||||
): ClassOrFunctionOrVariableDeclaration[] {
|
): ClassOrFunctionOrVariableDeclaration[] {
|
||||||
const functionName = stubDecl.node.name.value;
|
const functionName = stubDecl.node.name.value;
|
||||||
const sourceFiles = this._getBoundSourceFilesFromStubFile(stubDecl.path);
|
const sourceFiles = this._getBoundSourceFilesFromStubFile(stubDecl.uri);
|
||||||
|
|
||||||
if (stubDecl.isMethod) {
|
if (stubDecl.isMethod) {
|
||||||
const classNode = ParseTreeUtils.getEnclosingClass(stubDecl.node);
|
const classNode = ParseTreeUtils.getEnclosingClass(stubDecl.node);
|
||||||
@ -184,7 +184,7 @@ export class SourceMapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const variableName = stubDecl.node.value;
|
const variableName = stubDecl.node.value;
|
||||||
const sourceFiles = this._getBoundSourceFilesFromStubFile(stubDecl.path);
|
const sourceFiles = this._getBoundSourceFilesFromStubFile(stubDecl.uri);
|
||||||
const classNode = ParseTreeUtils.getEnclosingClass(stubDecl.node);
|
const classNode = ParseTreeUtils.getEnclosingClass(stubDecl.node);
|
||||||
|
|
||||||
if (classNode) {
|
if (classNode) {
|
||||||
@ -270,7 +270,7 @@ export class SourceMapper {
|
|||||||
): VariableDeclaration[] {
|
): VariableDeclaration[] {
|
||||||
let result: VariableDeclaration[] = [];
|
let result: VariableDeclaration[] = [];
|
||||||
|
|
||||||
const uniqueId = `@${sourceFile.getFilePath()}/c/${className}/v/${variableName}`;
|
const uniqueId = `@${sourceFile.getUri()}/c/${className}/v/${variableName}`;
|
||||||
if (recursiveDeclCache.has(uniqueId)) {
|
if (recursiveDeclCache.has(uniqueId)) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -283,7 +283,7 @@ export class SourceMapper {
|
|||||||
variableName,
|
variableName,
|
||||||
(decl, cache, result) => {
|
(decl, cache, result) => {
|
||||||
if (isVariableDeclaration(decl)) {
|
if (isVariableDeclaration(decl)) {
|
||||||
if (this._isStubThatShouldBeMappedToImplementation(decl.path)) {
|
if (this._isStubThatShouldBeMappedToImplementation(decl.uri)) {
|
||||||
for (const implDecl of this._findVariableDeclarations(decl, cache)) {
|
for (const implDecl of this._findVariableDeclarations(decl, cache)) {
|
||||||
if (isVariableDeclaration(implDecl)) {
|
if (isVariableDeclaration(implDecl)) {
|
||||||
result.push(implDecl);
|
result.push(implDecl);
|
||||||
@ -309,7 +309,7 @@ export class SourceMapper {
|
|||||||
): ClassOrFunctionOrVariableDeclaration[] {
|
): ClassOrFunctionOrVariableDeclaration[] {
|
||||||
let result: ClassOrFunctionOrVariableDeclaration[] = [];
|
let result: ClassOrFunctionOrVariableDeclaration[] = [];
|
||||||
|
|
||||||
const uniqueId = `@${sourceFile.getFilePath()}/c/${className}/f/${functionName}`;
|
const uniqueId = `@${sourceFile.getUri()}/c/${className}/f/${functionName}`;
|
||||||
if (recursiveDeclCache.has(uniqueId)) {
|
if (recursiveDeclCache.has(uniqueId)) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -322,7 +322,7 @@ export class SourceMapper {
|
|||||||
functionName,
|
functionName,
|
||||||
(decl, cache, result) => {
|
(decl, cache, result) => {
|
||||||
if (isFunctionDeclaration(decl)) {
|
if (isFunctionDeclaration(decl)) {
|
||||||
if (this._isStubThatShouldBeMappedToImplementation(decl.path)) {
|
if (this._isStubThatShouldBeMappedToImplementation(decl.uri)) {
|
||||||
appendArray(result, this._findFunctionOrTypeAliasDeclarations(decl, cache));
|
appendArray(result, this._findFunctionOrTypeAliasDeclarations(decl, cache));
|
||||||
} else {
|
} else {
|
||||||
result.push(decl);
|
result.push(decl);
|
||||||
@ -343,7 +343,7 @@ export class SourceMapper {
|
|||||||
): ClassOrFunctionOrVariableDeclaration[] {
|
): ClassOrFunctionOrVariableDeclaration[] {
|
||||||
const result: ClassOrFunctionOrVariableDeclaration[] = [];
|
const result: ClassOrFunctionOrVariableDeclaration[] = [];
|
||||||
|
|
||||||
const uniqueId = `@${sourceFile.getFilePath()}/v/${variableName}`;
|
const uniqueId = `@${sourceFile.getUri()}/v/${variableName}`;
|
||||||
if (recursiveDeclCache.has(uniqueId)) {
|
if (recursiveDeclCache.has(uniqueId)) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -377,7 +377,7 @@ export class SourceMapper {
|
|||||||
): ClassOrFunctionOrVariableDeclaration[] {
|
): ClassOrFunctionOrVariableDeclaration[] {
|
||||||
const result: ClassOrFunctionOrVariableDeclaration[] = [];
|
const result: ClassOrFunctionOrVariableDeclaration[] = [];
|
||||||
|
|
||||||
const uniqueId = `@${sourceFile.getFilePath()}/f/${functionName}`;
|
const uniqueId = `@${sourceFile.getUri()}/f/${functionName}`;
|
||||||
if (recursiveDeclCache.has(uniqueId)) {
|
if (recursiveDeclCache.has(uniqueId)) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -438,7 +438,7 @@ export class SourceMapper {
|
|||||||
): ClassOrFunctionOrVariableDeclaration[] {
|
): ClassOrFunctionOrVariableDeclaration[] {
|
||||||
const result: ClassOrFunctionOrVariableDeclaration[] = [];
|
const result: ClassOrFunctionOrVariableDeclaration[] = [];
|
||||||
|
|
||||||
const uniqueId = `@${sourceFile.getFilePath()}[${parentNode.start}]${className}`;
|
const uniqueId = `@${sourceFile.getUri()}[${parentNode.start}]${className}`;
|
||||||
if (recursiveDeclCache.has(uniqueId)) {
|
if (recursiveDeclCache.has(uniqueId)) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -464,7 +464,7 @@ export class SourceMapper {
|
|||||||
recursiveDeclCache: Set<string>
|
recursiveDeclCache: Set<string>
|
||||||
) {
|
) {
|
||||||
if (isVariableDeclaration(decl)) {
|
if (isVariableDeclaration(decl)) {
|
||||||
if (this._isStubThatShouldBeMappedToImplementation(decl.path)) {
|
if (this._isStubThatShouldBeMappedToImplementation(decl.uri)) {
|
||||||
appendArray(result, this._findVariableDeclarations(decl, recursiveDeclCache));
|
appendArray(result, this._findVariableDeclarations(decl, recursiveDeclCache));
|
||||||
} else {
|
} else {
|
||||||
result.push(decl);
|
result.push(decl);
|
||||||
@ -487,7 +487,7 @@ export class SourceMapper {
|
|||||||
recursiveDeclCache: Set<string>
|
recursiveDeclCache: Set<string>
|
||||||
) {
|
) {
|
||||||
if (isClassDeclaration(decl)) {
|
if (isClassDeclaration(decl)) {
|
||||||
if (this._isStubThatShouldBeMappedToImplementation(decl.path)) {
|
if (this._isStubThatShouldBeMappedToImplementation(decl.uri)) {
|
||||||
appendArray(result, this._findClassOrTypeAliasDeclarations(decl, recursiveDeclCache));
|
appendArray(result, this._findClassOrTypeAliasDeclarations(decl, recursiveDeclCache));
|
||||||
} else {
|
} else {
|
||||||
result.push(decl);
|
result.push(decl);
|
||||||
@ -495,7 +495,7 @@ export class SourceMapper {
|
|||||||
} else if (isSpecialBuiltInClassDeclaration(decl)) {
|
} else if (isSpecialBuiltInClassDeclaration(decl)) {
|
||||||
result.push(decl);
|
result.push(decl);
|
||||||
} else if (isFunctionDeclaration(decl)) {
|
} else if (isFunctionDeclaration(decl)) {
|
||||||
if (this._isStubThatShouldBeMappedToImplementation(decl.path)) {
|
if (this._isStubThatShouldBeMappedToImplementation(decl.uri)) {
|
||||||
appendArray(result, this._findFunctionOrTypeAliasDeclarations(decl, recursiveDeclCache));
|
appendArray(result, this._findFunctionOrTypeAliasDeclarations(decl, recursiveDeclCache));
|
||||||
} else {
|
} else {
|
||||||
result.push(decl);
|
result.push(decl);
|
||||||
@ -525,7 +525,7 @@ export class SourceMapper {
|
|||||||
this._addClassOrFunctionDeclarations(overloadDecl, result, recursiveDeclCache);
|
this._addClassOrFunctionDeclarations(overloadDecl, result, recursiveDeclCache);
|
||||||
}
|
}
|
||||||
} else if (isInstantiableClass(type)) {
|
} else if (isInstantiableClass(type)) {
|
||||||
this._addClassTypeDeclarations(decl.path, type, result, recursiveDeclCache);
|
this._addClassTypeDeclarations(decl.uri, type, result, recursiveDeclCache);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -541,7 +541,7 @@ export class SourceMapper {
|
|||||||
// and second, clone the given decl and set path to the generated pyi for the
|
// and second, clone the given decl and set path to the generated pyi for the
|
||||||
// builtin module (ex, _io) to make resolveAliasDeclaration to work.
|
// builtin module (ex, _io) to make resolveAliasDeclaration to work.
|
||||||
// once the path is set, our regular code path will work as expected.
|
// once the path is set, our regular code path will work as expected.
|
||||||
if (decl.path || !decl.node) {
|
if (!decl.uri.isEmpty() || !decl.node) {
|
||||||
// If module actually exists, nothing we need to do.
|
// If module actually exists, nothing we need to do.
|
||||||
return decl;
|
return decl;
|
||||||
}
|
}
|
||||||
@ -563,20 +563,19 @@ export class SourceMapper {
|
|||||||
|
|
||||||
// ImportResolver might be able to generate or extract builtin module's info
|
// ImportResolver might be able to generate or extract builtin module's info
|
||||||
// from runtime if we provide right synthesized stub path.
|
// from runtime if we provide right synthesized stub path.
|
||||||
const fakeStubPath = combinePaths(
|
const fakeStubPath = stdLibPath.combinePaths(
|
||||||
stdLibPath,
|
|
||||||
getModuleName()
|
getModuleName()
|
||||||
.nameParts.map((n) => n.value)
|
.nameParts.map((n) => n.value)
|
||||||
.join('.') + '.pyi'
|
.join('.') + '.pyi'
|
||||||
);
|
);
|
||||||
|
|
||||||
const sources = this._getSourceFiles(fakeStubPath, fileInfo.filePath);
|
const sources = this._getSourceFiles(fakeStubPath, fileInfo.fileUri);
|
||||||
if (sources.length === 0) {
|
if (sources.length === 0) {
|
||||||
return decl;
|
return decl;
|
||||||
}
|
}
|
||||||
|
|
||||||
const synthesizedDecl = { ...decl };
|
const synthesizedDecl = { ...decl };
|
||||||
synthesizedDecl.path = sources[0].getFilePath();
|
synthesizedDecl.uri = sources[0].getUri();
|
||||||
|
|
||||||
return synthesizedDecl;
|
return synthesizedDecl;
|
||||||
|
|
||||||
@ -595,14 +594,14 @@ export class SourceMapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _addClassTypeDeclarations(
|
private _addClassTypeDeclarations(
|
||||||
originated: string,
|
originated: Uri,
|
||||||
type: ClassType,
|
type: ClassType,
|
||||||
result: ClassOrFunctionOrVariableDeclaration[],
|
result: ClassOrFunctionOrVariableDeclaration[],
|
||||||
recursiveDeclCache: Set<string>,
|
recursiveDeclCache: Set<string>,
|
||||||
useTypeAlias = false
|
useTypeAlias = false
|
||||||
) {
|
) {
|
||||||
const filePath = type.details.filePath;
|
const fileUri = type.details.fileUri;
|
||||||
const sourceFiles = this._getSourceFiles(filePath, /* stubToShadow */ undefined, originated);
|
const sourceFiles = this._getSourceFiles(fileUri, /* stubToShadow */ undefined, originated);
|
||||||
|
|
||||||
const fullName = useTypeAlias && type.typeAliasInfo ? type.typeAliasInfo.fullName : type.details.fullName;
|
const fullName = useTypeAlias && type.typeAliasInfo ? type.typeAliasInfo.fullName : type.details.fullName;
|
||||||
const fullClassName = fullName.substring(type.details.moduleName.length + 1 /* +1 for trailing dot */);
|
const fullClassName = fullName.substring(type.details.moduleName.length + 1 /* +1 for trailing dot */);
|
||||||
@ -612,13 +611,13 @@ export class SourceMapper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getSourceFiles(filePath: string, stubToShadow?: string, originated?: string) {
|
private _getSourceFiles(fileUri: Uri, stubToShadow?: Uri, originated?: Uri) {
|
||||||
const sourceFiles: SourceFile[] = [];
|
const sourceFiles: SourceFile[] = [];
|
||||||
|
|
||||||
if (this._isStubThatShouldBeMappedToImplementation(filePath)) {
|
if (this._isStubThatShouldBeMappedToImplementation(fileUri)) {
|
||||||
appendArray(sourceFiles, this._getBoundSourceFilesFromStubFile(filePath, stubToShadow, originated));
|
appendArray(sourceFiles, this._getBoundSourceFilesFromStubFile(fileUri, stubToShadow, originated));
|
||||||
} else {
|
} else {
|
||||||
const sourceFileInfo = this._boundSourceGetter(filePath);
|
const sourceFileInfo = this._boundSourceGetter(fileUri);
|
||||||
if (sourceFileInfo) {
|
if (sourceFileInfo) {
|
||||||
sourceFiles.push(sourceFileInfo.sourceFile);
|
sourceFiles.push(sourceFileInfo.sourceFile);
|
||||||
}
|
}
|
||||||
@ -645,14 +644,14 @@ export class SourceMapper {
|
|||||||
for (const decl of symbol.getDeclarations()) {
|
for (const decl of symbol.getDeclarations()) {
|
||||||
if (
|
if (
|
||||||
!isAliasDeclaration(decl) ||
|
!isAliasDeclaration(decl) ||
|
||||||
!decl.path ||
|
decl.uri.isEmpty() ||
|
||||||
decl.node.nodeType !== ParseNodeType.ImportFrom ||
|
decl.node.nodeType !== ParseNodeType.ImportFrom ||
|
||||||
!decl.node.isWildcardImport
|
!decl.node.isWildcardImport
|
||||||
) {
|
) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const uniqueId = `@${decl.path}/l/${symbolName}`;
|
const uniqueId = `@${decl.uri.key}/l/${symbolName}`;
|
||||||
if (recursiveDeclCache.has(uniqueId)) {
|
if (recursiveDeclCache.has(uniqueId)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -667,7 +666,7 @@ export class SourceMapper {
|
|||||||
// function.
|
// function.
|
||||||
recursiveDeclCache.add(uniqueId);
|
recursiveDeclCache.add(uniqueId);
|
||||||
|
|
||||||
const sourceFiles = this._getSourceFiles(decl.path);
|
const sourceFiles = this._getSourceFiles(decl.uri);
|
||||||
for (const sourceFile of sourceFiles) {
|
for (const sourceFile of sourceFiles) {
|
||||||
const moduleNode = sourceFile.getParseResults()?.parseTree;
|
const moduleNode = sourceFile.getParseResults()?.parseTree;
|
||||||
if (!moduleNode) {
|
if (!moduleNode) {
|
||||||
@ -728,28 +727,21 @@ export class SourceMapper {
|
|||||||
return fullName.reverse().join('.');
|
return fullName.reverse().join('.');
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getBoundSourceFilesFromStubFile(
|
private _getBoundSourceFilesFromStubFile(stubFileUri: Uri, stubToShadow?: Uri, originated?: Uri): SourceFile[] {
|
||||||
stubFilePath: string,
|
const paths = this._getSourcePathsFromStub(stubFileUri, originated ?? this._fromFile?.sourceFile.getUri());
|
||||||
stubToShadow?: string,
|
return paths.map((fp) => this._fileBinder(stubToShadow ?? stubFileUri, fp)).filter(isDefined);
|
||||||
originated?: string
|
|
||||||
): SourceFile[] {
|
|
||||||
const paths = this._getSourcePathsFromStub(
|
|
||||||
stubFilePath,
|
|
||||||
originated ?? this._fromFile?.sourceFile.getFilePath()
|
|
||||||
);
|
|
||||||
return paths.map((fp) => this._fileBinder(stubToShadow ?? stubFilePath, fp)).filter(isDefined);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getSourcePathsFromStub(stubFilePath: string, fromFile: string | undefined): string[] {
|
private _getSourcePathsFromStub(stubFileUri: Uri, fromFile: Uri | undefined): Uri[] {
|
||||||
// Attempt our stubFilePath to see if we can resolve it as a source file path
|
// Attempt our stubFileUri to see if we can resolve it as a source file path
|
||||||
let results = this._importResolver.getSourceFilesFromStub(stubFilePath, this._execEnv, this._mapCompiled);
|
let results = this._importResolver.getSourceFilesFromStub(stubFileUri, this._execEnv, this._mapCompiled);
|
||||||
if (results.length > 0) {
|
if (results.length > 0) {
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If that didn't work, try looking through the graph up to our fromFile.
|
// If that didn't work, try looking through the graph up to our fromFile.
|
||||||
// One of them should be able to resolve to an actual file.
|
// One of them should be able to resolve to an actual file.
|
||||||
const stubFileImportTree = this._getStubFileImportTree(stubFilePath, fromFile);
|
const stubFileImportTree = this._getStubFileImportTree(stubFileUri, fromFile);
|
||||||
|
|
||||||
// Go through the items in this tree until we find at least one path.
|
// Go through the items in this tree until we find at least one path.
|
||||||
for (let i = 0; i < stubFileImportTree.length; i++) {
|
for (let i = 0; i < stubFileImportTree.length; i++) {
|
||||||
@ -766,43 +758,41 @@ export class SourceMapper {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getStubFileImportTree(stubFilePath: string, fromFile: string | undefined): string[] {
|
private _getStubFileImportTree(stubFileUri: Uri, fromFile: Uri | undefined): Uri[] {
|
||||||
if (!fromFile || !this._isStubThatShouldBeMappedToImplementation(stubFilePath)) {
|
if (!fromFile || !this._isStubThatShouldBeMappedToImplementation(stubFileUri)) {
|
||||||
// No path to search, just return the starting point.
|
// No path to search, just return the starting point.
|
||||||
return [stubFilePath];
|
return [stubFileUri];
|
||||||
} else {
|
} else {
|
||||||
// Otherwise recurse through the importedBy list up to our 'fromFile'.
|
// Otherwise recurse through the importedBy list up to our 'fromFile'.
|
||||||
return buildImportTree(
|
return buildImportTree(
|
||||||
fromFile,
|
fromFile,
|
||||||
stubFilePath,
|
stubFileUri,
|
||||||
(p) => {
|
(p) => {
|
||||||
const boundSourceInfo = this._boundSourceGetter(p);
|
const boundSourceInfo = this._boundSourceGetter(p);
|
||||||
return boundSourceInfo
|
return boundSourceInfo ? boundSourceInfo.importedBy.map((info) => info.sourceFile.getUri()) : [];
|
||||||
? boundSourceInfo.importedBy.map((info) => info.sourceFile.getFilePath())
|
|
||||||
: [];
|
|
||||||
},
|
},
|
||||||
this._cancelToken
|
this._cancelToken
|
||||||
).filter((p) => this._isStubThatShouldBeMappedToImplementation(p));
|
).filter((p) => this._isStubThatShouldBeMappedToImplementation(p));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _isStubThatShouldBeMappedToImplementation(filePath: string): boolean {
|
private _isStubThatShouldBeMappedToImplementation(fileUri: Uri): boolean {
|
||||||
if (this._preferStubs) {
|
if (this._preferStubs) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const stub = isStubFile(filePath);
|
const stub = isStubFile(fileUri);
|
||||||
if (!stub) {
|
if (!stub) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we get the same file as a source file, then we treat the file as a regular file even if it has "pyi" extension.
|
// If we get the same file as a source file, then we treat the file as a regular file even if it has "pyi" extension.
|
||||||
return this._importResolver
|
return this._importResolver
|
||||||
.getSourceFilesFromStub(filePath, this._execEnv, this._mapCompiled)
|
.getSourceFilesFromStub(fileUri, this._execEnv, this._mapCompiled)
|
||||||
.every((f) => f !== filePath);
|
.every((f) => f !== fileUri);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isStubFile(filePath: string): boolean {
|
export function isStubFile(uri: Uri): boolean {
|
||||||
return getAnyExtensionFromPath(filePath, ['.pyi'], /* ignoreCase */ false) === '.pyi';
|
return uri.lastExtension === '.pyi';
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { CancellationToken } from 'vscode-jsonrpc';
|
import { CancellationToken } from 'vscode-jsonrpc';
|
||||||
|
import { Uri } from '../common/uri/uri';
|
||||||
|
|
||||||
const MAX_TREE_SEARCH_COUNT = 1000;
|
const MAX_TREE_SEARCH_COUNT = 1000;
|
||||||
|
|
||||||
@ -15,12 +16,7 @@ class NumberReference {
|
|||||||
// Builds an array of imports from the 'from' to the 'to' entry where 'from'
|
// Builds an array of imports from the 'from' to the 'to' entry where 'from'
|
||||||
// is on the front of the array and the item just before 'to' is on the
|
// is on the front of the array and the item just before 'to' is on the
|
||||||
// back of the array.
|
// back of the array.
|
||||||
export function buildImportTree(
|
export function buildImportTree(to: Uri, from: Uri, next: (from: Uri) => Uri[], token: CancellationToken): Uri[] {
|
||||||
to: string,
|
|
||||||
from: string,
|
|
||||||
next: (from: string) => string[],
|
|
||||||
token: CancellationToken
|
|
||||||
): string[] {
|
|
||||||
const totalCountRef = new NumberReference();
|
const totalCountRef = new NumberReference();
|
||||||
const results = _buildImportTreeImpl(to, from, next, [], totalCountRef, token);
|
const results = _buildImportTreeImpl(to, from, next, [], totalCountRef, token);
|
||||||
|
|
||||||
@ -29,25 +25,25 @@ export function buildImportTree(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function _buildImportTreeImpl(
|
function _buildImportTreeImpl(
|
||||||
to: string,
|
to: Uri,
|
||||||
from: string,
|
from: Uri,
|
||||||
next: (from: string) => string[],
|
next: (from: Uri) => Uri[],
|
||||||
previous: string[],
|
previous: Uri[],
|
||||||
totalSearched: NumberReference,
|
totalSearched: NumberReference,
|
||||||
token: CancellationToken
|
token: CancellationToken
|
||||||
): string[] {
|
): Uri[] {
|
||||||
// Exit early if cancellation is requested or we've exceeded max count
|
// Exit early if cancellation is requested or we've exceeded max count
|
||||||
if (totalSearched.value > MAX_TREE_SEARCH_COUNT || token.isCancellationRequested) {
|
if (totalSearched.value > MAX_TREE_SEARCH_COUNT || token.isCancellationRequested) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
totalSearched.value += 1;
|
totalSearched.value += 1;
|
||||||
|
|
||||||
if (from === to) {
|
if (from.equals(to)) {
|
||||||
// At the top, previous should have our way into this recursion.
|
// At the top, previous should have our way into this recursion.
|
||||||
return previous.length ? previous : [from];
|
return previous.length ? previous : [from];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (previous.length > 1 && previous.find((s) => s === from)) {
|
if (previous.length > 1 && previous.find((s) => s.equals(from))) {
|
||||||
// Fail the search, we're stuck in a loop.
|
// Fail the search, we're stuck in a loop.
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
@ -8,8 +8,9 @@
|
|||||||
|
|
||||||
import { isNumber, isString } from '../common/core';
|
import { isNumber, isString } from '../common/core';
|
||||||
import { assertNever } from '../common/debug';
|
import { assertNever } from '../common/debug';
|
||||||
import { ensureTrailingDirectorySeparator, stripFileExtension } from '../common/pathUtils';
|
import { stripFileExtension } from '../common/pathUtils';
|
||||||
import { convertOffsetToPosition } from '../common/positionUtils';
|
import { convertOffsetToPosition } from '../common/positionUtils';
|
||||||
|
import { Uri } from '../common/uri/uri';
|
||||||
import { ParseNode, ParseNodeType, isExpressionNode } from '../parser/parseNodes';
|
import { ParseNode, ParseNodeType, isExpressionNode } from '../parser/parseNodes';
|
||||||
import { AbsoluteModuleDescriptor } from './analyzerFileInfo';
|
import { AbsoluteModuleDescriptor } from './analyzerFileInfo';
|
||||||
import * as AnalyzerNodeInfo from './analyzerNodeInfo';
|
import * as AnalyzerNodeInfo from './analyzerNodeInfo';
|
||||||
@ -22,10 +23,10 @@ export type PrintableType = ParseNode | Declaration | Symbol | Type | undefined;
|
|||||||
|
|
||||||
export interface TracePrinter {
|
export interface TracePrinter {
|
||||||
print(o: PrintableType): string;
|
print(o: PrintableType): string;
|
||||||
printFileOrModuleName(filePathOrModule: string | AbsoluteModuleDescriptor): string;
|
printFileOrModuleName(fileUriOrModule: Uri | AbsoluteModuleDescriptor): string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createTracePrinter(roots: string[]): TracePrinter {
|
export function createTracePrinter(roots: Uri[]): TracePrinter {
|
||||||
function wrap(value: string | undefined, ch = "'") {
|
function wrap(value: string | undefined, ch = "'") {
|
||||||
return value ? `${ch}${value}${ch}` : '';
|
return value ? `${ch}${value}${ch}` : '';
|
||||||
}
|
}
|
||||||
@ -33,25 +34,22 @@ export function createTracePrinter(roots: string[]): TracePrinter {
|
|||||||
// Sort roots in desc order so that we compare longer path first
|
// Sort roots in desc order so that we compare longer path first
|
||||||
// when getting relative path.
|
// when getting relative path.
|
||||||
// ex) d:/root/.env/lib/site-packages, d:/root/.env
|
// ex) d:/root/.env/lib/site-packages, d:/root/.env
|
||||||
roots = roots
|
roots = roots.sort((a, b) => a.key.localeCompare(b.key)).reverse();
|
||||||
.map((r) => ensureTrailingDirectorySeparator(r))
|
|
||||||
.sort((a, b) => a.localeCompare(b))
|
|
||||||
.reverse();
|
|
||||||
|
|
||||||
const separatorRegExp = /[\\/]/g;
|
const separatorRegExp = /[\\/]/g;
|
||||||
function printFileOrModuleName(filePathOrModule: string | AbsoluteModuleDescriptor | undefined) {
|
function printFileOrModuleName(fileUriOrModule: Uri | AbsoluteModuleDescriptor | undefined) {
|
||||||
if (filePathOrModule) {
|
if (fileUriOrModule) {
|
||||||
if (typeof filePathOrModule === 'string') {
|
if (Uri.isUri(fileUriOrModule)) {
|
||||||
for (const root of roots) {
|
for (const root of roots) {
|
||||||
if (filePathOrModule.startsWith(root)) {
|
if (fileUriOrModule.isChild(root)) {
|
||||||
const subFile = filePathOrModule.substring(root.length);
|
const subFile = root.getRelativePath(fileUriOrModule);
|
||||||
return stripFileExtension(subFile).replace(separatorRegExp, '.');
|
return stripFileExtension(subFile!).replace(separatorRegExp, '.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return filePathOrModule;
|
return fileUriOrModule.toUserVisibleString();
|
||||||
} else if (filePathOrModule.nameParts) {
|
} else if (fileUriOrModule.nameParts) {
|
||||||
return filePathOrModule.nameParts.join('.');
|
return fileUriOrModule.nameParts.join('.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return '';
|
return '';
|
||||||
@ -117,33 +115,33 @@ export function createTracePrinter(roots: string[]): TracePrinter {
|
|||||||
if (decl) {
|
if (decl) {
|
||||||
switch (decl.type) {
|
switch (decl.type) {
|
||||||
case DeclarationType.Alias:
|
case DeclarationType.Alias:
|
||||||
return `Alias, ${printNode(decl.node)} (${printFileOrModuleName(decl.path)})`;
|
return `Alias, ${printNode(decl.node)} (${printFileOrModuleName(decl.uri)})`;
|
||||||
|
|
||||||
case DeclarationType.Class:
|
case DeclarationType.Class:
|
||||||
return `Class, ${printNode(decl.node)} (${printFileOrModuleName(decl.path)})`;
|
return `Class, ${printNode(decl.node)} (${printFileOrModuleName(decl.uri)})`;
|
||||||
|
|
||||||
case DeclarationType.Function:
|
case DeclarationType.Function:
|
||||||
return `Function, ${printNode(decl.node)} (${printFileOrModuleName(decl.path)})`;
|
return `Function, ${printNode(decl.node)} (${printFileOrModuleName(decl.uri)})`;
|
||||||
|
|
||||||
case DeclarationType.Intrinsic:
|
case DeclarationType.Intrinsic:
|
||||||
return `Intrinsic, ${printNode(decl.node)} ${decl.intrinsicType} (${printFileOrModuleName(
|
return `Intrinsic, ${printNode(decl.node)} ${decl.intrinsicType} (${printFileOrModuleName(
|
||||||
decl.path
|
decl.uri
|
||||||
)})`;
|
)})`;
|
||||||
|
|
||||||
case DeclarationType.Parameter:
|
case DeclarationType.Parameter:
|
||||||
return `Parameter, ${printNode(decl.node)} (${printFileOrModuleName(decl.path)})`;
|
return `Parameter, ${printNode(decl.node)} (${printFileOrModuleName(decl.uri)})`;
|
||||||
|
|
||||||
case DeclarationType.TypeParameter:
|
case DeclarationType.TypeParameter:
|
||||||
return `TypeParameter, ${printNode(decl.node)} (${printFileOrModuleName(decl.path)})`;
|
return `TypeParameter, ${printNode(decl.node)} (${printFileOrModuleName(decl.uri)})`;
|
||||||
|
|
||||||
case DeclarationType.SpecialBuiltInClass:
|
case DeclarationType.SpecialBuiltInClass:
|
||||||
return `SpecialBuiltInClass, ${printNode(decl.node)} (${printFileOrModuleName(decl.path)})`;
|
return `SpecialBuiltInClass, ${printNode(decl.node)} (${printFileOrModuleName(decl.uri)})`;
|
||||||
|
|
||||||
case DeclarationType.Variable:
|
case DeclarationType.Variable:
|
||||||
return `Variable, ${printNode(decl.node)} (${printFileOrModuleName(decl.path)})`;
|
return `Variable, ${printNode(decl.node)} (${printFileOrModuleName(decl.uri)})`;
|
||||||
|
|
||||||
case DeclarationType.TypeAlias:
|
case DeclarationType.TypeAlias:
|
||||||
return `TypeAlias, ${printNode(decl.node)} (${printFileOrModuleName(decl.path)})`;
|
return `TypeAlias, ${printNode(decl.node)} (${printFileOrModuleName(decl.uri)})`;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
assertNever(decl);
|
assertNever(decl);
|
||||||
@ -174,7 +172,7 @@ export function createTracePrinter(roots: string[]): TracePrinter {
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
let path = printPath ? `(${printFileOrModuleName(getFileInfo(node)?.filePath)})` : '';
|
let path = printPath ? `(${printFileOrModuleName(getFileInfo(node)?.fileUri)})` : '';
|
||||||
|
|
||||||
const fileInfo = getFileInfo(node);
|
const fileInfo = getFileInfo(node);
|
||||||
if (fileInfo?.lines) {
|
if (fileInfo?.lines) {
|
||||||
@ -228,7 +226,7 @@ export function createTracePrinter(roots: string[]): TracePrinter {
|
|||||||
|
|
||||||
function isDeclaration(o: any): o is Declaration {
|
function isDeclaration(o: any): o is Declaration {
|
||||||
const d = o as Declaration;
|
const d = o as Declaration;
|
||||||
return d && isNumber(d.type) && isString(d.path) && isString(d.moduleName);
|
return d && isNumber(d.type) && Uri.isUri(d.uri) && isString(d.moduleName);
|
||||||
}
|
}
|
||||||
|
|
||||||
function isType(o: any): o is Type {
|
function isType(o: any): o is Type {
|
||||||
|
@ -33,6 +33,7 @@ import {
|
|||||||
TypeCategory,
|
TypeCategory,
|
||||||
} from '../analyzer/types';
|
} from '../analyzer/types';
|
||||||
import { addIfNotNull, appendArray } from '../common/collectionUtils';
|
import { addIfNotNull, appendArray } from '../common/collectionUtils';
|
||||||
|
import { Uri } from '../common/uri/uri';
|
||||||
import { ModuleNode, ParseNodeType } from '../parser/parseNodes';
|
import { ModuleNode, ParseNodeType } from '../parser/parseNodes';
|
||||||
import { TypeEvaluator } from './typeEvaluatorTypes';
|
import { TypeEvaluator } from './typeEvaluatorTypes';
|
||||||
import {
|
import {
|
||||||
@ -160,7 +161,7 @@ export function getPropertyDocStringInherited(
|
|||||||
|
|
||||||
export function getVariableInStubFileDocStrings(decl: VariableDeclaration, sourceMapper: SourceMapper) {
|
export function getVariableInStubFileDocStrings(decl: VariableDeclaration, sourceMapper: SourceMapper) {
|
||||||
const docStrings: string[] = [];
|
const docStrings: string[] = [];
|
||||||
if (!isStubFile(decl.path)) {
|
if (!isStubFile(decl.uri)) {
|
||||||
return docStrings;
|
return docStrings;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,14 +194,14 @@ export function getModuleDocStringFromModuleNodes(modules: ModuleNode[]): string
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getModuleDocStringFromPaths(filePaths: string[], sourceMapper: SourceMapper) {
|
export function getModuleDocStringFromUris(uris: Uri[], sourceMapper: SourceMapper) {
|
||||||
const modules: ModuleNode[] = [];
|
const modules: ModuleNode[] = [];
|
||||||
for (const filePath of filePaths) {
|
for (const uri of uris) {
|
||||||
if (isStubFile(filePath)) {
|
if (isStubFile(uri)) {
|
||||||
addIfNotNull(modules, sourceMapper.getModuleNode(filePath));
|
addIfNotNull(modules, sourceMapper.getModuleNode(uri));
|
||||||
}
|
}
|
||||||
|
|
||||||
appendArray(modules, sourceMapper.findModules(filePath));
|
appendArray(modules, sourceMapper.findModules(uri));
|
||||||
}
|
}
|
||||||
|
|
||||||
return getModuleDocStringFromModuleNodes(modules);
|
return getModuleDocStringFromModuleNodes(modules);
|
||||||
@ -213,8 +214,8 @@ export function getModuleDocString(
|
|||||||
) {
|
) {
|
||||||
let docString = type.docString;
|
let docString = type.docString;
|
||||||
if (!docString) {
|
if (!docString) {
|
||||||
const filePath = resolvedDecl?.path ?? type.filePath;
|
const uri = resolvedDecl?.uri ?? type.fileUri;
|
||||||
docString = getModuleDocStringFromPaths([filePath], sourceMapper);
|
docString = getModuleDocStringFromUris([uri], sourceMapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
return docString;
|
return docString;
|
||||||
@ -228,12 +229,7 @@ export function getClassDocString(
|
|||||||
let docString = classType.details.docString;
|
let docString = classType.details.docString;
|
||||||
if (!docString && resolvedDecl && isClassDeclaration(resolvedDecl)) {
|
if (!docString && resolvedDecl && isClassDeclaration(resolvedDecl)) {
|
||||||
docString = _getFunctionOrClassDeclsDocString([resolvedDecl]);
|
docString = _getFunctionOrClassDeclsDocString([resolvedDecl]);
|
||||||
if (
|
if (!docString && resolvedDecl && isStubFile(resolvedDecl.uri) && resolvedDecl.type === DeclarationType.Class) {
|
||||||
!docString &&
|
|
||||||
resolvedDecl &&
|
|
||||||
isStubFile(resolvedDecl.path) &&
|
|
||||||
resolvedDecl.type === DeclarationType.Class
|
|
||||||
) {
|
|
||||||
for (const implDecl of sourceMapper.findDeclarations(resolvedDecl)) {
|
for (const implDecl of sourceMapper.findDeclarations(resolvedDecl)) {
|
||||||
if (isVariableDeclaration(implDecl) && !!implDecl.docString) {
|
if (isVariableDeclaration(implDecl) && !!implDecl.docString) {
|
||||||
docString = implDecl.docString;
|
docString = implDecl.docString;
|
||||||
@ -249,7 +245,7 @@ export function getClassDocString(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!docString && resolvedDecl) {
|
if (!docString && resolvedDecl) {
|
||||||
const implDecls = sourceMapper.findClassDeclarationsByType(resolvedDecl.path, classType);
|
const implDecls = sourceMapper.findClassDeclarationsByType(resolvedDecl.uri, classType);
|
||||||
if (implDecls) {
|
if (implDecls) {
|
||||||
const classDecls = implDecls.filter((d) => isClassDeclaration(d)).map((d) => d);
|
const classDecls = implDecls.filter((d) => isClassDeclaration(d)).map((d) => d);
|
||||||
docString = _getFunctionOrClassDeclsDocString(classDecls);
|
docString = _getFunctionOrClassDeclsDocString(classDecls);
|
||||||
@ -294,7 +290,7 @@ function _getOverloadedFunctionDocStrings(
|
|||||||
docStrings.push(overload.details.docString);
|
docStrings.push(overload.details.docString);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else if (resolvedDecl && isStubFile(resolvedDecl.path) && isFunctionDeclaration(resolvedDecl)) {
|
} else if (resolvedDecl && isStubFile(resolvedDecl.uri) && isFunctionDeclaration(resolvedDecl)) {
|
||||||
const implDecls = sourceMapper.findFunctionDeclarations(resolvedDecl);
|
const implDecls = sourceMapper.findFunctionDeclarations(resolvedDecl);
|
||||||
const docString = _getFunctionOrClassDeclsDocString(implDecls);
|
const docString = _getFunctionOrClassDeclsDocString(implDecls);
|
||||||
if (docString) {
|
if (docString) {
|
||||||
@ -372,7 +368,7 @@ function _getFunctionDocString(type: Type, resolvedDecl: FunctionDeclaration | u
|
|||||||
|
|
||||||
function _getFunctionDocStringFromDeclaration(resolvedDecl: FunctionDeclaration, sourceMapper: SourceMapper) {
|
function _getFunctionDocStringFromDeclaration(resolvedDecl: FunctionDeclaration, sourceMapper: SourceMapper) {
|
||||||
let docString = _getFunctionOrClassDeclsDocString([resolvedDecl]);
|
let docString = _getFunctionOrClassDeclsDocString([resolvedDecl]);
|
||||||
if (!docString && isStubFile(resolvedDecl.path)) {
|
if (!docString && isStubFile(resolvedDecl.uri)) {
|
||||||
const implDecls = sourceMapper.findFunctionDeclarations(resolvedDecl);
|
const implDecls = sourceMapper.findFunctionDeclarations(resolvedDecl);
|
||||||
docString = _getFunctionOrClassDeclsDocString(implDecls);
|
docString = _getFunctionOrClassDeclsDocString(implDecls);
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,7 @@ import { DiagnosticRule } from '../common/diagnosticRules';
|
|||||||
import { convertOffsetToPosition, convertOffsetsToRange } from '../common/positionUtils';
|
import { convertOffsetToPosition, convertOffsetsToRange } from '../common/positionUtils';
|
||||||
import { PythonVersion } from '../common/pythonVersion';
|
import { PythonVersion } from '../common/pythonVersion';
|
||||||
import { TextRange } from '../common/textRange';
|
import { TextRange } from '../common/textRange';
|
||||||
|
import { Uri } from '../common/uri/uri';
|
||||||
import { Localizer, ParameterizedString } from '../localization/localize';
|
import { Localizer, ParameterizedString } from '../localization/localize';
|
||||||
import {
|
import {
|
||||||
ArgumentCategory,
|
ArgumentCategory,
|
||||||
@ -665,7 +666,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
|||||||
`Type cache flag mismatch for node type ${node.nodeType} ` +
|
`Type cache flag mismatch for node type ${node.nodeType} ` +
|
||||||
`(parent ${node.parent?.nodeType ?? 'none'}): ` +
|
`(parent ${node.parent?.nodeType ?? 'none'}): ` +
|
||||||
`cached flags = ${expectedFlags}, access flags = ${flags}, ` +
|
`cached flags = ${expectedFlags}, access flags = ${flags}, ` +
|
||||||
`file = {${fileInfo.filePath} [${position.line + 1}:${position.character + 1}]}`;
|
`file = {${fileInfo.fileUri} [${position.line + 1}:${position.character + 1}]}`;
|
||||||
if (evaluatorOptions.verifyTypeCacheEvaluatorFlags) {
|
if (evaluatorOptions.verifyTypeCacheEvaluatorFlags) {
|
||||||
fail(message);
|
fail(message);
|
||||||
} else {
|
} else {
|
||||||
@ -2895,7 +2896,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
|||||||
|
|
||||||
function getTypeOfModule(node: ParseNode, symbolName: string, nameParts: string[]) {
|
function getTypeOfModule(node: ParseNode, symbolName: string, nameParts: string[]) {
|
||||||
const fileInfo = AnalyzerNodeInfo.getFileInfo(node);
|
const fileInfo = AnalyzerNodeInfo.getFileInfo(node);
|
||||||
const lookupResult = importLookup({ nameParts, importingFilePath: fileInfo.filePath });
|
const lookupResult = importLookup({ nameParts, importingFileUri: fileInfo.fileUri });
|
||||||
|
|
||||||
if (!lookupResult) {
|
if (!lookupResult) {
|
||||||
return undefined;
|
return undefined;
|
||||||
@ -5355,9 +5356,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
|||||||
if (getAttrSymbol) {
|
if (getAttrSymbol) {
|
||||||
const isModuleGetAttrSupported =
|
const isModuleGetAttrSupported =
|
||||||
fileInfo.executionEnvironment.pythonVersion >= PythonVersion.V3_7 ||
|
fileInfo.executionEnvironment.pythonVersion >= PythonVersion.V3_7 ||
|
||||||
getAttrSymbol
|
getAttrSymbol.getDeclarations().some((decl) => decl.uri.hasExtension('.pyi'));
|
||||||
.getDeclarations()
|
|
||||||
.some((decl) => decl.path.toLowerCase().endsWith('.pyi'));
|
|
||||||
|
|
||||||
if (isModuleGetAttrSupported) {
|
if (isModuleGetAttrSupported) {
|
||||||
const getAttrTypeResult = getEffectiveTypeOfSymbolForUsage(getAttrSymbol);
|
const getAttrTypeResult = getEffectiveTypeOfSymbolForUsage(getAttrSymbol);
|
||||||
@ -8904,7 +8903,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
|||||||
if (diagnostic && overrideDecl) {
|
if (diagnostic && overrideDecl) {
|
||||||
diagnostic.addRelatedInfo(
|
diagnostic.addRelatedInfo(
|
||||||
Localizer.DiagnosticAddendum.overloadIndex().format({ index: bestMatch.overloadIndex + 1 }),
|
Localizer.DiagnosticAddendum.overloadIndex().format({ index: bestMatch.overloadIndex + 1 }),
|
||||||
overrideDecl.path,
|
overrideDecl.uri,
|
||||||
overrideDecl.range
|
overrideDecl.range
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -9717,7 +9716,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
|||||||
newClassName,
|
newClassName,
|
||||||
'',
|
'',
|
||||||
'',
|
'',
|
||||||
AnalyzerNodeInfo.getFileInfo(errorNode).filePath,
|
AnalyzerNodeInfo.getFileInfo(errorNode).fileUri,
|
||||||
ClassTypeFlags.None,
|
ClassTypeFlags.None,
|
||||||
ParseTreeUtils.getTypeSourceId(errorNode),
|
ParseTreeUtils.getTypeSourceId(errorNode),
|
||||||
ClassType.cloneAsInstantiable(returnType),
|
ClassType.cloneAsInstantiable(returnType),
|
||||||
@ -12628,7 +12627,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
|||||||
className,
|
className,
|
||||||
ParseTreeUtils.getClassFullName(errorNode, fileInfo.moduleName, className),
|
ParseTreeUtils.getClassFullName(errorNode, fileInfo.moduleName, className),
|
||||||
fileInfo.moduleName,
|
fileInfo.moduleName,
|
||||||
fileInfo.filePath,
|
fileInfo.fileUri,
|
||||||
classFlags,
|
classFlags,
|
||||||
ParseTreeUtils.getTypeSourceId(errorNode),
|
ParseTreeUtils.getTypeSourceId(errorNode),
|
||||||
/* declaredMetaclass */ undefined,
|
/* declaredMetaclass */ undefined,
|
||||||
@ -12689,7 +12688,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
|||||||
className,
|
className,
|
||||||
ParseTreeUtils.getClassFullName(errorNode, fileInfo.moduleName, className),
|
ParseTreeUtils.getClassFullName(errorNode, fileInfo.moduleName, className),
|
||||||
fileInfo.moduleName,
|
fileInfo.moduleName,
|
||||||
fileInfo.filePath,
|
fileInfo.fileUri,
|
||||||
ClassTypeFlags.None,
|
ClassTypeFlags.None,
|
||||||
ParseTreeUtils.getTypeSourceId(errorNode),
|
ParseTreeUtils.getTypeSourceId(errorNode),
|
||||||
/* declaredMetaclass */ undefined,
|
/* declaredMetaclass */ undefined,
|
||||||
@ -15269,7 +15268,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
|||||||
assignedName,
|
assignedName,
|
||||||
ParseTreeUtils.getClassFullName(node, fileInfo.moduleName, assignedName),
|
ParseTreeUtils.getClassFullName(node, fileInfo.moduleName, assignedName),
|
||||||
fileInfo.moduleName,
|
fileInfo.moduleName,
|
||||||
fileInfo.filePath,
|
fileInfo.fileUri,
|
||||||
ClassTypeFlags.BuiltInClass | ClassTypeFlags.SpecialBuiltIn,
|
ClassTypeFlags.BuiltInClass | ClassTypeFlags.SpecialBuiltIn,
|
||||||
/* typeSourceId */ 0,
|
/* typeSourceId */ 0,
|
||||||
/* declaredMetaclass */ undefined,
|
/* declaredMetaclass */ undefined,
|
||||||
@ -15835,7 +15834,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
|||||||
node.name.value,
|
node.name.value,
|
||||||
ParseTreeUtils.getClassFullName(node, fileInfo.moduleName, node.name.value),
|
ParseTreeUtils.getClassFullName(node, fileInfo.moduleName, node.name.value),
|
||||||
fileInfo.moduleName,
|
fileInfo.moduleName,
|
||||||
fileInfo.filePath,
|
fileInfo.fileUri,
|
||||||
classFlags,
|
classFlags,
|
||||||
/* typeSourceId */ 0,
|
/* typeSourceId */ 0,
|
||||||
/* declaredMetaclass */ undefined,
|
/* declaredMetaclass */ undefined,
|
||||||
@ -16615,7 +16614,16 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
|||||||
);
|
);
|
||||||
const updatedClassType = ClassType.cloneWithNewTypeParameters(classType, updatedTypeParams);
|
const updatedClassType = ClassType.cloneWithNewTypeParameters(classType, updatedTypeParams);
|
||||||
|
|
||||||
const dummyTypeObject = ClassType.createInstantiable('__varianceDummy', '', '', '', 0, 0, undefined, undefined);
|
const dummyTypeObject = ClassType.createInstantiable(
|
||||||
|
'__varianceDummy',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
Uri.empty(),
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
undefined,
|
||||||
|
undefined
|
||||||
|
);
|
||||||
|
|
||||||
updatedTypeParams.forEach((param, paramIndex) => {
|
updatedTypeParams.forEach((param, paramIndex) => {
|
||||||
// Skip variadics and ParamSpecs.
|
// Skip variadics and ParamSpecs.
|
||||||
@ -16999,7 +17007,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
|||||||
Localizer.DiagnosticAddendum.initSubclassLocation().format({
|
Localizer.DiagnosticAddendum.initSubclassLocation().format({
|
||||||
name: printType(convertToInstance(initSubclassMethodInfo.classType)),
|
name: printType(convertToInstance(initSubclassMethodInfo.classType)),
|
||||||
}),
|
}),
|
||||||
initSubclassDecl.path,
|
initSubclassDecl.uri,
|
||||||
initSubclassDecl.range
|
initSubclassDecl.range
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -18381,7 +18389,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
|||||||
|
|
||||||
const importInfo = AnalyzerNodeInfo.getImportInfo(parentNode.module);
|
const importInfo = AnalyzerNodeInfo.getImportInfo(parentNode.module);
|
||||||
if (importInfo && importInfo.isImportFound && !importInfo.isNativeLib) {
|
if (importInfo && importInfo.isImportFound && !importInfo.isNativeLib) {
|
||||||
const resolvedPath = importInfo.resolvedPaths[importInfo.resolvedPaths.length - 1];
|
const resolvedPath = importInfo.resolvedUris[importInfo.resolvedUris.length - 1];
|
||||||
|
|
||||||
const importLookupInfo = importLookup(resolvedPath);
|
const importLookupInfo = importLookup(resolvedPath);
|
||||||
let reportError = false;
|
let reportError = false;
|
||||||
@ -18403,7 +18411,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (!resolvedPath) {
|
} else if (resolvedPath.isEmpty()) {
|
||||||
// This corresponds to the "from . import a" form.
|
// This corresponds to the "from . import a" form.
|
||||||
reportError = true;
|
reportError = true;
|
||||||
}
|
}
|
||||||
@ -20207,15 +20215,15 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
|||||||
namePartIndex >= 0 &&
|
namePartIndex >= 0 &&
|
||||||
importInfo &&
|
importInfo &&
|
||||||
!importInfo.isNativeLib &&
|
!importInfo.isNativeLib &&
|
||||||
namePartIndex < importInfo.resolvedPaths.length
|
namePartIndex < importInfo.resolvedUris.length
|
||||||
) {
|
) {
|
||||||
if (importInfo.resolvedPaths[namePartIndex]) {
|
if (importInfo.resolvedUris[namePartIndex]) {
|
||||||
evaluateTypesForStatement(node);
|
evaluateTypesForStatement(node);
|
||||||
|
|
||||||
// Synthesize an alias declaration for this name part. The only
|
// Synthesize an alias declaration for this name part. The only
|
||||||
// time this case is used is for IDE services such as
|
// time this case is used is for IDE services such as
|
||||||
// the find all references, hover provider and etc.
|
// the find all references, hover provider and etc.
|
||||||
declarations.push(createSynthesizedAliasDeclaration(importInfo.resolvedPaths[namePartIndex]));
|
declarations.push(createSynthesizedAliasDeclaration(importInfo.resolvedUris[namePartIndex]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (node.parent && node.parent.nodeType === ParseNodeType.Argument && node === node.parent.name) {
|
} else if (node.parent && node.parent.nodeType === ParseNodeType.Argument && node === node.parent.name) {
|
||||||
@ -20634,8 +20642,8 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
|||||||
loaderActions: ModuleLoaderActions,
|
loaderActions: ModuleLoaderActions,
|
||||||
importLookup: ImportLookup
|
importLookup: ImportLookup
|
||||||
): Type {
|
): Type {
|
||||||
if (loaderActions.path && loaderActions.loadSymbolsFromPath) {
|
if (!loaderActions.uri.isEmpty() && loaderActions.loadSymbolsFromPath) {
|
||||||
const lookupResults = importLookup(loaderActions.path);
|
const lookupResults = importLookup(loaderActions.uri);
|
||||||
if (lookupResults) {
|
if (lookupResults) {
|
||||||
moduleType.fields = lookupResults.symbolTable;
|
moduleType.fields = lookupResults.symbolTable;
|
||||||
moduleType.docString = lookupResults.docString;
|
moduleType.docString = lookupResults.docString;
|
||||||
@ -20658,7 +20666,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
|||||||
symbolType = UnknownType.create();
|
symbolType = UnknownType.create();
|
||||||
} else {
|
} else {
|
||||||
const moduleName = moduleType.moduleName ? moduleType.moduleName + '.' + name : '';
|
const moduleName = moduleType.moduleName ? moduleType.moduleName + '.' + name : '';
|
||||||
const importedModuleType = ModuleType.create(moduleName, implicitImport.path);
|
const importedModuleType = ModuleType.create(moduleName, implicitImport.uri);
|
||||||
symbolType = applyLoaderActionsToModuleType(importedModuleType, implicitImport, importLookup);
|
symbolType = applyLoaderActionsToModuleType(importedModuleType, implicitImport, importLookup);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -20676,7 +20684,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
|||||||
if (resolvedDecl.type === DeclarationType.Alias) {
|
if (resolvedDecl.type === DeclarationType.Alias) {
|
||||||
// Build a module type that corresponds to the declaration and
|
// Build a module type that corresponds to the declaration and
|
||||||
// its associated loader actions.
|
// its associated loader actions.
|
||||||
const moduleType = ModuleType.create(resolvedDecl.moduleName, resolvedDecl.path);
|
const moduleType = ModuleType.create(resolvedDecl.moduleName, resolvedDecl.uri);
|
||||||
if (resolvedDecl.symbolName && resolvedDecl.submoduleFallback) {
|
if (resolvedDecl.symbolName && resolvedDecl.submoduleFallback) {
|
||||||
return applyLoaderActionsToModuleType(moduleType, resolvedDecl.submoduleFallback, importLookup);
|
return applyLoaderActionsToModuleType(moduleType, resolvedDecl.submoduleFallback, importLookup);
|
||||||
} else {
|
} else {
|
||||||
|
@ -1480,7 +1480,7 @@ function narrowTypeForIsInstance(
|
|||||||
className,
|
className,
|
||||||
ParseTreeUtils.getClassFullName(errorNode, fileInfo.moduleName, className),
|
ParseTreeUtils.getClassFullName(errorNode, fileInfo.moduleName, className),
|
||||||
fileInfo.moduleName,
|
fileInfo.moduleName,
|
||||||
fileInfo.filePath,
|
fileInfo.fileUri,
|
||||||
ClassTypeFlags.None,
|
ClassTypeFlags.None,
|
||||||
ParseTreeUtils.getTypeSourceId(errorNode),
|
ParseTreeUtils.getTypeSourceId(errorNode),
|
||||||
/* declaredMetaclass */ undefined,
|
/* declaredMetaclass */ undefined,
|
||||||
@ -2545,7 +2545,7 @@ function narrowTypeForCallable(
|
|||||||
className,
|
className,
|
||||||
ParseTreeUtils.getClassFullName(errorNode, fileInfo.moduleName, className),
|
ParseTreeUtils.getClassFullName(errorNode, fileInfo.moduleName, className),
|
||||||
fileInfo.moduleName,
|
fileInfo.moduleName,
|
||||||
fileInfo.filePath,
|
fileInfo.fileUri,
|
||||||
ClassTypeFlags.None,
|
ClassTypeFlags.None,
|
||||||
ParseTreeUtils.getTypeSourceId(errorNode),
|
ParseTreeUtils.getTypeSourceId(errorNode),
|
||||||
/* declaredMetaclass */ undefined,
|
/* declaredMetaclass */ undefined,
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
* and analyzed python source file.
|
* and analyzed python source file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { Uri } from '../common/uri/uri';
|
||||||
import {
|
import {
|
||||||
ArgumentCategory,
|
ArgumentCategory,
|
||||||
AssignmentNode,
|
AssignmentNode,
|
||||||
@ -158,13 +159,13 @@ export class TypeStubWriter extends ParseTreeWalker {
|
|||||||
private _trackedImportFrom = new Map<string, TrackedImportFrom>();
|
private _trackedImportFrom = new Map<string, TrackedImportFrom>();
|
||||||
private _accessedImportedSymbols = new Set<string>();
|
private _accessedImportedSymbols = new Set<string>();
|
||||||
|
|
||||||
constructor(private _stubPath: string, private _sourceFile: SourceFile, private _evaluator: TypeEvaluator) {
|
constructor(private _stubPath: Uri, private _sourceFile: SourceFile, private _evaluator: TypeEvaluator) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
// As a heuristic, we'll include all of the import statements
|
// As a heuristic, we'll include all of the import statements
|
||||||
// in "__init__.pyi" files even if they're not locally referenced
|
// in "__init__.pyi" files even if they're not locally referenced
|
||||||
// because these are often used as ways to re-export symbols.
|
// because these are often used as ways to re-export symbols.
|
||||||
if (this._stubPath.endsWith('__init__.pyi')) {
|
if (this._stubPath.fileName === '__init__.pyi') {
|
||||||
this._includeAllImports = true;
|
this._includeAllImports = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -97,7 +97,7 @@ export function createTypedDictType(
|
|||||||
className,
|
className,
|
||||||
ParseTreeUtils.getClassFullName(errorNode, fileInfo.moduleName, className),
|
ParseTreeUtils.getClassFullName(errorNode, fileInfo.moduleName, className),
|
||||||
fileInfo.moduleName,
|
fileInfo.moduleName,
|
||||||
fileInfo.filePath,
|
fileInfo.fileUri,
|
||||||
ClassTypeFlags.TypedDictClass,
|
ClassTypeFlags.TypedDictClass,
|
||||||
ParseTreeUtils.getTypeSourceId(errorNode),
|
ParseTreeUtils.getTypeSourceId(errorNode),
|
||||||
/* declaredMetaclass */ undefined,
|
/* declaredMetaclass */ undefined,
|
||||||
@ -147,7 +147,7 @@ export function createTypedDictType(
|
|||||||
const declaration: VariableDeclaration = {
|
const declaration: VariableDeclaration = {
|
||||||
type: DeclarationType.Variable,
|
type: DeclarationType.Variable,
|
||||||
node: entry.name,
|
node: entry.name,
|
||||||
path: fileInfo.filePath,
|
uri: fileInfo.fileUri,
|
||||||
typeAnnotationNode: entry.valueExpression,
|
typeAnnotationNode: entry.valueExpression,
|
||||||
isRuntimeTypeExpression: true,
|
isRuntimeTypeExpression: true,
|
||||||
range: convertOffsetsToRange(
|
range: convertOffsetsToRange(
|
||||||
@ -209,7 +209,7 @@ export function createTypedDictTypeInlined(
|
|||||||
className,
|
className,
|
||||||
ParseTreeUtils.getClassFullName(dictNode, fileInfo.moduleName, className),
|
ParseTreeUtils.getClassFullName(dictNode, fileInfo.moduleName, className),
|
||||||
fileInfo.moduleName,
|
fileInfo.moduleName,
|
||||||
fileInfo.filePath,
|
fileInfo.fileUri,
|
||||||
ClassTypeFlags.TypedDictClass,
|
ClassTypeFlags.TypedDictClass,
|
||||||
ParseTreeUtils.getTypeSourceId(dictNode),
|
ParseTreeUtils.getTypeSourceId(dictNode),
|
||||||
/* declaredMetaclass */ undefined,
|
/* declaredMetaclass */ undefined,
|
||||||
@ -773,7 +773,7 @@ function getTypedDictFieldsFromDictSyntax(
|
|||||||
const declaration: VariableDeclaration = {
|
const declaration: VariableDeclaration = {
|
||||||
type: DeclarationType.Variable,
|
type: DeclarationType.Variable,
|
||||||
node: entry.keyExpression,
|
node: entry.keyExpression,
|
||||||
path: fileInfo.filePath,
|
uri: fileInfo.fileUri,
|
||||||
typeAnnotationNode: entry.valueExpression,
|
typeAnnotationNode: entry.valueExpression,
|
||||||
isRuntimeTypeExpression: !isInline,
|
isRuntimeTypeExpression: !isInline,
|
||||||
range: convertOffsetsToRange(
|
range: convertOffsetsToRange(
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { assert } from '../common/debug';
|
import { assert } from '../common/debug';
|
||||||
|
import { Uri } from '../common/uri/uri';
|
||||||
import { ArgumentNode, ExpressionNode, NameNode, ParameterCategory } from '../parser/parseNodes';
|
import { ArgumentNode, ExpressionNode, NameNode, ParameterCategory } from '../parser/parseNodes';
|
||||||
import { FunctionDeclaration } from './declaration';
|
import { FunctionDeclaration } from './declaration';
|
||||||
import { Symbol, SymbolTable } from './symbol';
|
import { Symbol, SymbolTable } from './symbol';
|
||||||
@ -383,18 +384,18 @@ export interface ModuleType extends TypeBase {
|
|||||||
// The period-delimited import name of this module.
|
// The period-delimited import name of this module.
|
||||||
moduleName: string;
|
moduleName: string;
|
||||||
|
|
||||||
filePath: string;
|
fileUri: Uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
export namespace ModuleType {
|
export namespace ModuleType {
|
||||||
export function create(moduleName: string, filePath: string, symbolTable?: SymbolTable) {
|
export function create(moduleName: string, fileUri: Uri, symbolTable?: SymbolTable) {
|
||||||
const newModuleType: ModuleType = {
|
const newModuleType: ModuleType = {
|
||||||
category: TypeCategory.Module,
|
category: TypeCategory.Module,
|
||||||
fields: symbolTable || new Map<string, Symbol>(),
|
fields: symbolTable || new Map<string, Symbol>(),
|
||||||
loaderFields: new Map<string, Symbol>(),
|
loaderFields: new Map<string, Symbol>(),
|
||||||
flags: TypeFlags.Instantiable | TypeFlags.Instantiable,
|
flags: TypeFlags.Instantiable | TypeFlags.Instantiable,
|
||||||
moduleName,
|
moduleName,
|
||||||
filePath,
|
fileUri,
|
||||||
};
|
};
|
||||||
return newModuleType;
|
return newModuleType;
|
||||||
}
|
}
|
||||||
@ -561,7 +562,7 @@ interface ClassDetails {
|
|||||||
name: string;
|
name: string;
|
||||||
fullName: string;
|
fullName: string;
|
||||||
moduleName: string;
|
moduleName: string;
|
||||||
filePath: string;
|
fileUri: Uri;
|
||||||
flags: ClassTypeFlags;
|
flags: ClassTypeFlags;
|
||||||
typeSourceId: TypeSourceId;
|
typeSourceId: TypeSourceId;
|
||||||
baseClasses: Type[];
|
baseClasses: Type[];
|
||||||
@ -709,7 +710,7 @@ export namespace ClassType {
|
|||||||
name: string,
|
name: string,
|
||||||
fullName: string,
|
fullName: string,
|
||||||
moduleName: string,
|
moduleName: string,
|
||||||
filePath: string,
|
fileUri: Uri,
|
||||||
flags: ClassTypeFlags,
|
flags: ClassTypeFlags,
|
||||||
typeSourceId: TypeSourceId,
|
typeSourceId: TypeSourceId,
|
||||||
declaredMetaclass: ClassType | UnknownType | undefined,
|
declaredMetaclass: ClassType | UnknownType | undefined,
|
||||||
@ -722,7 +723,7 @@ export namespace ClassType {
|
|||||||
name,
|
name,
|
||||||
fullName,
|
fullName,
|
||||||
moduleName,
|
moduleName,
|
||||||
filePath,
|
fileUri,
|
||||||
flags,
|
flags,
|
||||||
typeSourceId,
|
typeSourceId,
|
||||||
baseClasses: [],
|
baseClasses: [],
|
||||||
|
@ -13,17 +13,17 @@ import { BackgroundAnalysisBase, BackgroundAnalysisRunnerBase } from './backgrou
|
|||||||
import { InitializationData } from './backgroundThreadBase';
|
import { InitializationData } from './backgroundThreadBase';
|
||||||
import { getCancellationFolderName } from './common/cancellationUtils';
|
import { getCancellationFolderName } from './common/cancellationUtils';
|
||||||
import { ConfigOptions } from './common/configOptions';
|
import { ConfigOptions } from './common/configOptions';
|
||||||
import { ConsoleInterface } from './common/console';
|
|
||||||
import { FullAccessHost } from './common/fullAccessHost';
|
import { FullAccessHost } from './common/fullAccessHost';
|
||||||
import { Host } from './common/host';
|
import { Host } from './common/host';
|
||||||
import { ServiceProvider } from './common/serviceProvider';
|
import { ServiceProvider } from './common/serviceProvider';
|
||||||
|
import { getRootUri } from './common/uri/uriUtils';
|
||||||
|
|
||||||
export class BackgroundAnalysis extends BackgroundAnalysisBase {
|
export class BackgroundAnalysis extends BackgroundAnalysisBase {
|
||||||
constructor(console: ConsoleInterface) {
|
constructor(serviceProvider: ServiceProvider) {
|
||||||
super(console);
|
super(serviceProvider.console());
|
||||||
|
|
||||||
const initialData: InitializationData = {
|
const initialData: InitializationData = {
|
||||||
rootDirectory: (global as any).__rootDirectory as string,
|
rootUri: getRootUri(serviceProvider.fs().isCaseSensitive)?.toString() ?? '',
|
||||||
cancellationFolderName: getCancellationFolderName(),
|
cancellationFolderName: getCancellationFolderName(),
|
||||||
runner: undefined,
|
runner: undefined,
|
||||||
};
|
};
|
||||||
@ -40,7 +40,7 @@ export class BackgroundAnalysisRunner extends BackgroundAnalysisRunnerBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected override createHost(): Host {
|
protected override createHost(): Host {
|
||||||
return new FullAccessHost(this.fs);
|
return new FullAccessHost(this.getServiceProvider());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override createImportResolver(
|
protected override createImportResolver(
|
||||||
|
@ -10,6 +10,7 @@ import { CancellationToken } from 'vscode-languageserver';
|
|||||||
import { MessageChannel, MessagePort, Worker, parentPort, threadId, workerData } from 'worker_threads';
|
import { MessageChannel, MessagePort, Worker, parentPort, threadId, workerData } from 'worker_threads';
|
||||||
|
|
||||||
import { AnalysisCompleteCallback, AnalysisResults, analyzeProgram, nullCallback } from './analyzer/analysis';
|
import { AnalysisCompleteCallback, AnalysisResults, analyzeProgram, nullCallback } from './analyzer/analysis';
|
||||||
|
import { BackgroundAnalysisProgram, InvalidatedReason } from './analyzer/backgroundAnalysisProgram';
|
||||||
import { ImportResolver } from './analyzer/importResolver';
|
import { ImportResolver } from './analyzer/importResolver';
|
||||||
import { OpenFileOptions, Program } from './analyzer/program';
|
import { OpenFileOptions, Program } from './analyzer/program';
|
||||||
import {
|
import {
|
||||||
@ -33,9 +34,9 @@ import { FileDiagnostics } from './common/diagnosticSink';
|
|||||||
import { disposeCancellationToken, getCancellationTokenFromId } from './common/fileBasedCancellationUtils';
|
import { disposeCancellationToken, getCancellationTokenFromId } from './common/fileBasedCancellationUtils';
|
||||||
import { Host, HostKind } from './common/host';
|
import { Host, HostKind } from './common/host';
|
||||||
import { LogTracker } from './common/logTracker';
|
import { LogTracker } from './common/logTracker';
|
||||||
import { Range } from './common/textRange';
|
|
||||||
import { BackgroundAnalysisProgram, InvalidatedReason } from './analyzer/backgroundAnalysisProgram';
|
|
||||||
import { ServiceProvider } from './common/serviceProvider';
|
import { ServiceProvider } from './common/serviceProvider';
|
||||||
|
import { Range } from './common/textRange';
|
||||||
|
import { Uri } from './common/uri/uri';
|
||||||
|
|
||||||
export class BackgroundAnalysisBase {
|
export class BackgroundAnalysisBase {
|
||||||
private _worker: Worker | undefined;
|
private _worker: Worker | undefined;
|
||||||
@ -57,8 +58,8 @@ export class BackgroundAnalysisBase {
|
|||||||
this.enqueueRequest({ requestType: 'setConfigOptions', data: configOptions });
|
this.enqueueRequest({ requestType: 'setConfigOptions', data: configOptions });
|
||||||
}
|
}
|
||||||
|
|
||||||
setTrackedFiles(filePaths: string[]) {
|
setTrackedFiles(fileUris: Uri[]) {
|
||||||
this.enqueueRequest({ requestType: 'setTrackedFiles', data: filePaths });
|
this.enqueueRequest({ requestType: 'setTrackedFiles', data: fileUris.map((f) => f.toString()) });
|
||||||
}
|
}
|
||||||
|
|
||||||
setAllowedThirdPartyImports(importNames: string[]) {
|
setAllowedThirdPartyImports(importNames: string[]) {
|
||||||
@ -69,36 +70,36 @@ export class BackgroundAnalysisBase {
|
|||||||
this.enqueueRequest({ requestType: 'ensurePartialStubPackages', data: { executionRoot } });
|
this.enqueueRequest({ requestType: 'ensurePartialStubPackages', data: { executionRoot } });
|
||||||
}
|
}
|
||||||
|
|
||||||
setFileOpened(filePath: string, version: number | null, contents: string, options: OpenFileOptions) {
|
setFileOpened(fileUri: Uri, version: number | null, contents: string, options: OpenFileOptions) {
|
||||||
this.enqueueRequest({
|
this.enqueueRequest({
|
||||||
requestType: 'setFileOpened',
|
requestType: 'setFileOpened',
|
||||||
data: { filePath, version, contents, options },
|
data: { fileUri: fileUri.toString(), version, contents, options },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
updateChainedFilePath(filePath: string, chainedFilePath: string | undefined) {
|
updateChainedUri(fileUri: Uri, chainedUri: Uri | undefined) {
|
||||||
this.enqueueRequest({
|
this.enqueueRequest({
|
||||||
requestType: 'updateChainedFilePath',
|
requestType: 'updateChainedFileUri',
|
||||||
data: { filePath, chainedFilePath },
|
data: { fileUri: fileUri.toString(), chainedUri: chainedUri?.toString() },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
setFileClosed(filePath: string, isTracked?: boolean) {
|
setFileClosed(fileUri: Uri, isTracked?: boolean) {
|
||||||
this.enqueueRequest({ requestType: 'setFileClosed', data: { filePath, isTracked } });
|
this.enqueueRequest({ requestType: 'setFileClosed', data: { fileUri, isTracked } });
|
||||||
}
|
}
|
||||||
|
|
||||||
addInterimFile(filePath: string) {
|
addInterimFile(fileUri: Uri) {
|
||||||
this.enqueueRequest({ requestType: 'addInterimFile', data: { filePath } });
|
this.enqueueRequest({ requestType: 'addInterimFile', data: { fileUri: fileUri.toString() } });
|
||||||
}
|
}
|
||||||
|
|
||||||
markAllFilesDirty(evenIfContentsAreSame: boolean) {
|
markAllFilesDirty(evenIfContentsAreSame: boolean) {
|
||||||
this.enqueueRequest({ requestType: 'markAllFilesDirty', data: { evenIfContentsAreSame } });
|
this.enqueueRequest({ requestType: 'markAllFilesDirty', data: { evenIfContentsAreSame } });
|
||||||
}
|
}
|
||||||
|
|
||||||
markFilesDirty(filePaths: string[], evenIfContentsAreSame: boolean) {
|
markFilesDirty(fileUris: Uri[], evenIfContentsAreSame: boolean) {
|
||||||
this.enqueueRequest({
|
this.enqueueRequest({
|
||||||
requestType: 'markFilesDirty',
|
requestType: 'markFilesDirty',
|
||||||
data: { filePaths, evenIfContentsAreSame },
|
data: { fileUris: fileUris.map((f) => f.toString()), evenIfContentsAreSame },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,7 +107,7 @@ export class BackgroundAnalysisBase {
|
|||||||
this._startOrResumeAnalysis('analyze', program, token);
|
this._startOrResumeAnalysis('analyze', program, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
async analyzeFile(filePath: string, token: CancellationToken): Promise<boolean> {
|
async analyzeFile(fileUri: Uri, token: CancellationToken): Promise<boolean> {
|
||||||
throwIfCancellationRequested(token);
|
throwIfCancellationRequested(token);
|
||||||
|
|
||||||
const { port1, port2 } = new MessageChannel();
|
const { port1, port2 } = new MessageChannel();
|
||||||
@ -115,7 +116,7 @@ export class BackgroundAnalysisBase {
|
|||||||
const cancellationId = getCancellationTokenId(token);
|
const cancellationId = getCancellationTokenId(token);
|
||||||
this.enqueueRequest({
|
this.enqueueRequest({
|
||||||
requestType: 'analyzeFile',
|
requestType: 'analyzeFile',
|
||||||
data: { filePath, cancellationId },
|
data: { fileUri: fileUri.toString(), cancellationId },
|
||||||
port: port2,
|
port: port2,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -127,7 +128,7 @@ export class BackgroundAnalysisBase {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getDiagnosticsForRange(filePath: string, range: Range, token: CancellationToken): Promise<Diagnostic[]> {
|
async getDiagnosticsForRange(fileUri: Uri, range: Range, token: CancellationToken): Promise<Diagnostic[]> {
|
||||||
throwIfCancellationRequested(token);
|
throwIfCancellationRequested(token);
|
||||||
|
|
||||||
const { port1, port2 } = new MessageChannel();
|
const { port1, port2 } = new MessageChannel();
|
||||||
@ -136,7 +137,7 @@ export class BackgroundAnalysisBase {
|
|||||||
const cancellationId = getCancellationTokenId(token);
|
const cancellationId = getCancellationTokenId(token);
|
||||||
this.enqueueRequest({
|
this.enqueueRequest({
|
||||||
requestType: 'getDiagnosticsForRange',
|
requestType: 'getDiagnosticsForRange',
|
||||||
data: { filePath, range, cancellationId },
|
data: { fileUri: fileUri.toString(), range, cancellationId },
|
||||||
port: port2,
|
port: port2,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -149,9 +150,9 @@ export class BackgroundAnalysisBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async writeTypeStub(
|
async writeTypeStub(
|
||||||
targetImportPath: string,
|
targetImportPath: Uri,
|
||||||
targetIsSingleFile: boolean,
|
targetIsSingleFile: boolean,
|
||||||
stubPath: string,
|
stubPath: Uri,
|
||||||
token: CancellationToken
|
token: CancellationToken
|
||||||
): Promise<any> {
|
): Promise<any> {
|
||||||
throwIfCancellationRequested(token);
|
throwIfCancellationRequested(token);
|
||||||
@ -162,7 +163,12 @@ export class BackgroundAnalysisBase {
|
|||||||
const cancellationId = getCancellationTokenId(token);
|
const cancellationId = getCancellationTokenId(token);
|
||||||
this.enqueueRequest({
|
this.enqueueRequest({
|
||||||
requestType: 'writeTypeStub',
|
requestType: 'writeTypeStub',
|
||||||
data: { targetImportPath, targetIsSingleFile, stubPath, cancellationId },
|
data: {
|
||||||
|
targetImportPath: targetImportPath.toString(),
|
||||||
|
targetIsSingleFile,
|
||||||
|
stubPath: stubPath.toString(),
|
||||||
|
cancellationId,
|
||||||
|
},
|
||||||
port: port2,
|
port: port2,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -283,15 +289,15 @@ export abstract class BackgroundAnalysisRunnerBase extends BackgroundThreadBase
|
|||||||
|
|
||||||
protected importResolver: ImportResolver;
|
protected importResolver: ImportResolver;
|
||||||
protected logTracker: LogTracker;
|
protected logTracker: LogTracker;
|
||||||
|
protected isCaseSensitive = true;
|
||||||
|
|
||||||
protected constructor(serviceProvider: ServiceProvider) {
|
protected constructor(serviceProvider: ServiceProvider) {
|
||||||
super(workerData as InitializationData, serviceProvider);
|
super(workerData as InitializationData, serviceProvider);
|
||||||
|
|
||||||
// Stash the base directory into a global variable.
|
// Stash the base directory into a global variable.
|
||||||
const data = workerData as InitializationData;
|
const data = workerData as InitializationData;
|
||||||
this.log(LogLevel.Info, `Background analysis(${threadId}) root directory: ${data.rootDirectory}`);
|
this.log(LogLevel.Info, `Background analysis(${threadId}) root directory: ${data.rootUri}`);
|
||||||
|
this._configOptions = new ConfigOptions(Uri.parse(data.rootUri, serviceProvider.fs().isCaseSensitive));
|
||||||
this._configOptions = new ConfigOptions(data.rootDirectory);
|
|
||||||
this.importResolver = this.createImportResolver(serviceProvider, this._configOptions, this.createHost());
|
this.importResolver = this.createImportResolver(serviceProvider, this._configOptions, this.createHost());
|
||||||
|
|
||||||
const console = this.getConsole();
|
const console = this.getConsole();
|
||||||
@ -339,20 +345,20 @@ export abstract class BackgroundAnalysisRunnerBase extends BackgroundThreadBase
|
|||||||
|
|
||||||
case 'analyzeFile': {
|
case 'analyzeFile': {
|
||||||
run(() => {
|
run(() => {
|
||||||
const { filePath, cancellationId } = msg.data;
|
const { fileUri, cancellationId } = msg.data;
|
||||||
const token = getCancellationTokenFromId(cancellationId);
|
const token = getCancellationTokenFromId(cancellationId);
|
||||||
|
|
||||||
return this.handleAnalyzeFile(filePath, token);
|
return this.handleAnalyzeFile(fileUri, token);
|
||||||
}, msg.port!);
|
}, msg.port!);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'getDiagnosticsForRange': {
|
case 'getDiagnosticsForRange': {
|
||||||
run(() => {
|
run(() => {
|
||||||
const { filePath, range, cancellationId } = msg.data;
|
const { fileUri, range, cancellationId } = msg.data;
|
||||||
const token = getCancellationTokenFromId(cancellationId);
|
const token = getCancellationTokenFromId(cancellationId);
|
||||||
|
|
||||||
return this.handleGetDiagnosticsForRange(filePath, range, token);
|
return this.handleGetDiagnosticsForRange(fileUri, range, token);
|
||||||
}, msg.port!);
|
}, msg.port!);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -394,26 +400,26 @@ export abstract class BackgroundAnalysisRunnerBase extends BackgroundThreadBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
case 'setFileOpened': {
|
case 'setFileOpened': {
|
||||||
const { filePath, version, contents, options } = msg.data;
|
const { fileUri, version, contents, options } = msg.data;
|
||||||
this.handleSetFileOpened(filePath, version, contents, options);
|
this.handleSetFileOpened(fileUri, version, contents, options);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'updateChainedFilePath': {
|
case 'updateChainedFileUri': {
|
||||||
const { filePath, chainedFilePath } = msg.data;
|
const { fileUri, chainedUri } = msg.data;
|
||||||
this.handleUpdateChainedFilePath(filePath, chainedFilePath);
|
this.handleUpdateChainedfileUri(fileUri, chainedUri);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'setFileClosed': {
|
case 'setFileClosed': {
|
||||||
const { filePath, isTracked } = msg.data;
|
const { fileUri, isTracked } = msg.data;
|
||||||
this.handleSetFileClosed(filePath, isTracked);
|
this.handleSetFileClosed(fileUri, isTracked);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'addInterimFile': {
|
case 'addInterimFile': {
|
||||||
const { filePath } = msg.data;
|
const { fileUri } = msg.data;
|
||||||
this.handleAddInterimFile(filePath);
|
this.handleAddInterimFile(fileUri);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -424,8 +430,8 @@ export abstract class BackgroundAnalysisRunnerBase extends BackgroundThreadBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
case 'markFilesDirty': {
|
case 'markFilesDirty': {
|
||||||
const { filePaths, evenIfContentsAreSame } = msg.data;
|
const { fileUris, evenIfContentsAreSame } = msg.data;
|
||||||
this.handleMarkFilesDirty(filePaths, evenIfContentsAreSame);
|
this.handleMarkFilesDirty(fileUris, evenIfContentsAreSame);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -499,14 +505,14 @@ export abstract class BackgroundAnalysisRunnerBase extends BackgroundThreadBase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected handleAnalyzeFile(filePath: string, token: CancellationToken) {
|
protected handleAnalyzeFile(fileUri: string, token: CancellationToken) {
|
||||||
throwIfCancellationRequested(token);
|
throwIfCancellationRequested(token);
|
||||||
return this.program.analyzeFile(filePath, token);
|
return this.program.analyzeFile(Uri.parse(fileUri, this.isCaseSensitive), token);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected handleGetDiagnosticsForRange(filePath: string, range: Range, token: CancellationToken) {
|
protected handleGetDiagnosticsForRange(fileUri: string, range: Range, token: CancellationToken) {
|
||||||
throwIfCancellationRequested(token);
|
throwIfCancellationRequested(token);
|
||||||
return this.program.getDiagnosticsForRange(filePath, range);
|
return this.program.getDiagnosticsForRange(Uri.parse(fileUri, this.isCaseSensitive), range);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected handleWriteTypeStub(
|
protected handleWriteTypeStub(
|
||||||
@ -524,7 +530,12 @@ export abstract class BackgroundAnalysisRunnerBase extends BackgroundThreadBase
|
|||||||
token
|
token
|
||||||
);
|
);
|
||||||
|
|
||||||
this.program.writeTypeStub(targetImportPath, targetIsSingleFile, stubPath, token);
|
this.program.writeTypeStub(
|
||||||
|
Uri.parse(targetImportPath, this.isCaseSensitive),
|
||||||
|
targetIsSingleFile,
|
||||||
|
Uri.parse(stubPath, this.isCaseSensitive),
|
||||||
|
token
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected handleSetImportResolver(hostKind: HostKind) {
|
protected handleSetImportResolver(hostKind: HostKind) {
|
||||||
@ -548,8 +559,8 @@ export abstract class BackgroundAnalysisRunnerBase extends BackgroundThreadBase
|
|||||||
this.program.setImportResolver(this.importResolver);
|
this.program.setImportResolver(this.importResolver);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected handleSetTrackedFiles(filePaths: string[]) {
|
protected handleSetTrackedFiles(fileUris: string[]) {
|
||||||
const diagnostics = this.program.setTrackedFiles(filePaths);
|
const diagnostics = this.program.setTrackedFiles(fileUris.map((f) => Uri.parse(f, this.isCaseSensitive)));
|
||||||
this._reportDiagnostics(diagnostics, this.program.getFilesToAnalyzeCount(), 0);
|
this._reportDiagnostics(diagnostics, this.program.getFilesToAnalyzeCount(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -565,29 +576,35 @@ export abstract class BackgroundAnalysisRunnerBase extends BackgroundThreadBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected handleSetFileOpened(
|
protected handleSetFileOpened(
|
||||||
filePath: string,
|
fileUri: string,
|
||||||
version: number | null,
|
version: number | null,
|
||||||
contents: string,
|
contents: string,
|
||||||
options: OpenFileOptions | undefined
|
options: OpenFileOptions | undefined
|
||||||
) {
|
) {
|
||||||
this.program.setFileOpened(filePath, version, contents, options);
|
this.program.setFileOpened(Uri.parse(fileUri, this.isCaseSensitive), version, contents, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected handleUpdateChainedFilePath(filePath: string, chainedFilePath: string | undefined) {
|
protected handleUpdateChainedfileUri(fileUri: string, chainedfileUri: string | undefined) {
|
||||||
this.program.updateChainedFilePath(filePath, chainedFilePath);
|
this.program.updateChainedUri(
|
||||||
|
Uri.parse(fileUri, this.isCaseSensitive),
|
||||||
|
chainedfileUri ? Uri.parse(chainedfileUri, this.isCaseSensitive) : undefined
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected handleSetFileClosed(filePath: string, isTracked: boolean | undefined) {
|
protected handleSetFileClosed(fileUri: string, isTracked: boolean | undefined) {
|
||||||
const diagnostics = this.program.setFileClosed(filePath, isTracked);
|
const diagnostics = this.program.setFileClosed(Uri.parse(fileUri, this.isCaseSensitive), isTracked);
|
||||||
this._reportDiagnostics(diagnostics, this.program.getFilesToAnalyzeCount(), 0);
|
this._reportDiagnostics(diagnostics, this.program.getFilesToAnalyzeCount(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected handleAddInterimFile(filePath: string) {
|
protected handleAddInterimFile(fileUri: string) {
|
||||||
this.program.addInterimFile(filePath);
|
this.program.addInterimFile(Uri.parse(fileUri, this.isCaseSensitive));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected handleMarkFilesDirty(filePaths: string[], evenIfContentsAreSame: boolean) {
|
protected handleMarkFilesDirty(fileUris: string[], evenIfContentsAreSame: boolean) {
|
||||||
this.program.markFilesDirty(filePaths, evenIfContentsAreSame);
|
this.program.markFilesDirty(
|
||||||
|
fileUris.map((f) => Uri.parse(f, this.isCaseSensitive)),
|
||||||
|
evenIfContentsAreSame
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected handleMarkAllFilesDirty(evenIfContentsAreSame: boolean) {
|
protected handleMarkAllFilesDirty(evenIfContentsAreSame: boolean) {
|
||||||
@ -666,7 +683,7 @@ export abstract class BackgroundAnalysisRunnerBase extends BackgroundThreadBase
|
|||||||
function convertAnalysisResults(result: AnalysisResults): AnalysisResults {
|
function convertAnalysisResults(result: AnalysisResults): AnalysisResults {
|
||||||
result.diagnostics = result.diagnostics.map((f: FileDiagnostics) => {
|
result.diagnostics = result.diagnostics.map((f: FileDiagnostics) => {
|
||||||
return {
|
return {
|
||||||
filePath: f.filePath,
|
fileUri: f.fileUri,
|
||||||
version: f.version,
|
version: f.version,
|
||||||
diagnostics: convertDiagnostics(f.diagnostics),
|
diagnostics: convertDiagnostics(f.diagnostics),
|
||||||
};
|
};
|
||||||
@ -692,7 +709,7 @@ function convertDiagnostics(diagnostics: Diagnostic[]) {
|
|||||||
|
|
||||||
if (d._relatedInfo) {
|
if (d._relatedInfo) {
|
||||||
for (const info of d._relatedInfo) {
|
for (const info of d._relatedInfo) {
|
||||||
diag.addRelatedInfo(info.message, info.filePath, info.range);
|
diag.addRelatedInfo(info.message, info.fileUri, info.range);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -708,7 +725,7 @@ export type AnalysisRequestKind =
|
|||||||
| 'setAllowedThirdPartyImports'
|
| 'setAllowedThirdPartyImports'
|
||||||
| 'ensurePartialStubPackages'
|
| 'ensurePartialStubPackages'
|
||||||
| 'setFileOpened'
|
| 'setFileOpened'
|
||||||
| 'updateChainedFilePath'
|
| 'updateChainedFileUri'
|
||||||
| 'setFileClosed'
|
| 'setFileClosed'
|
||||||
| 'markAllFilesDirty'
|
| 'markAllFilesDirty'
|
||||||
| 'markFilesDirty'
|
| 'markFilesDirty'
|
||||||
|
@ -12,11 +12,12 @@ import { OperationCanceledException, setCancellationFolderName } from './common/
|
|||||||
import { ConfigOptions } from './common/configOptions';
|
import { ConfigOptions } from './common/configOptions';
|
||||||
import { ConsoleInterface, LogLevel } from './common/console';
|
import { ConsoleInterface, LogLevel } from './common/console';
|
||||||
import * as debug from './common/debug';
|
import * as debug from './common/debug';
|
||||||
import { FileSpec } from './common/pathUtils';
|
|
||||||
import { createFromRealFileSystem, RealTempFile } from './common/realFileSystem';
|
import { createFromRealFileSystem, RealTempFile } from './common/realFileSystem';
|
||||||
import { ServiceProvider } from './common/serviceProvider';
|
import { ServiceProvider } from './common/serviceProvider';
|
||||||
import './common/serviceProviderExtensions';
|
import './common/serviceProviderExtensions';
|
||||||
import { ServiceKeys } from './common/serviceProviderExtensions';
|
import { ServiceKeys } from './common/serviceProviderExtensions';
|
||||||
|
import { Uri } from './common/uri/uri';
|
||||||
|
import { FileSpec } from './common/uri/uriUtils';
|
||||||
|
|
||||||
export class BackgroundConsole implements ConsoleInterface {
|
export class BackgroundConsole implements ConsoleInterface {
|
||||||
// We always generate logs in the background. For the foreground,
|
// We always generate logs in the background. For the foreground,
|
||||||
@ -48,9 +49,6 @@ export class BackgroundThreadBase {
|
|||||||
protected constructor(data: InitializationData, serviceProvider?: ServiceProvider) {
|
protected constructor(data: InitializationData, serviceProvider?: ServiceProvider) {
|
||||||
setCancellationFolderName(data.cancellationFolderName);
|
setCancellationFolderName(data.cancellationFolderName);
|
||||||
|
|
||||||
// Stash the base directory into a global variable.
|
|
||||||
(global as any).__rootDirectory = data.rootDirectory;
|
|
||||||
|
|
||||||
// Make sure there's a file system and a console interface.
|
// Make sure there's a file system and a console interface.
|
||||||
this._serviceProvider = serviceProvider ?? new ServiceProvider();
|
this._serviceProvider = serviceProvider ?? new ServiceProvider();
|
||||||
if (!this._serviceProvider.tryGet(ServiceKeys.console)) {
|
if (!this._serviceProvider.tryGet(ServiceKeys.console)) {
|
||||||
@ -60,8 +58,17 @@ export class BackgroundThreadBase {
|
|||||||
this._serviceProvider.add(ServiceKeys.fs, createFromRealFileSystem(this.getConsole()));
|
this._serviceProvider.add(ServiceKeys.fs, createFromRealFileSystem(this.getConsole()));
|
||||||
}
|
}
|
||||||
if (!this._serviceProvider.tryGet(ServiceKeys.tempFile)) {
|
if (!this._serviceProvider.tryGet(ServiceKeys.tempFile)) {
|
||||||
this._serviceProvider.add(ServiceKeys.tempFile, new RealTempFile());
|
this._serviceProvider.add(
|
||||||
|
ServiceKeys.tempFile,
|
||||||
|
new RealTempFile(this._serviceProvider.fs().isCaseSensitive)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Stash the base directory into a global variable.
|
||||||
|
(global as any).__rootDirectory = Uri.parse(
|
||||||
|
data.rootUri,
|
||||||
|
this._serviceProvider.fs().isCaseSensitive
|
||||||
|
).getFilePath();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected get fs() {
|
protected get fs() {
|
||||||
@ -170,7 +177,7 @@ export function getBackgroundWaiter<T>(port: MessagePort): Promise<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface InitializationData {
|
export interface InitializationData {
|
||||||
rootDirectory: string;
|
rootUri: string;
|
||||||
cancellationFolderName: string | undefined;
|
cancellationFolderName: string | undefined;
|
||||||
runner: string | undefined;
|
runner: string | undefined;
|
||||||
title?: string;
|
title?: string;
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
import { CancellationToken, ExecuteCommandParams } from 'vscode-languageserver';
|
import { CancellationToken, ExecuteCommandParams } from 'vscode-languageserver';
|
||||||
|
|
||||||
import { OperationCanceledException } from '../common/cancellationUtils';
|
import { OperationCanceledException } from '../common/cancellationUtils';
|
||||||
|
import { Uri } from '../common/uri/uri';
|
||||||
import { LanguageServerInterface } from '../languageServerBase';
|
import { LanguageServerInterface } from '../languageServerBase';
|
||||||
import { AnalyzerServiceExecutor } from '../languageService/analyzerServiceExecutor';
|
import { AnalyzerServiceExecutor } from '../languageService/analyzerServiceExecutor';
|
||||||
import { ServerCommand } from './commandController';
|
import { ServerCommand } from './commandController';
|
||||||
@ -18,9 +19,9 @@ export class CreateTypeStubCommand implements ServerCommand {
|
|||||||
|
|
||||||
async execute(cmdParams: ExecuteCommandParams, token: CancellationToken): Promise<any> {
|
async execute(cmdParams: ExecuteCommandParams, token: CancellationToken): Promise<any> {
|
||||||
if (cmdParams.arguments && cmdParams.arguments.length >= 2) {
|
if (cmdParams.arguments && cmdParams.arguments.length >= 2) {
|
||||||
const workspaceRoot = cmdParams.arguments[0] as string;
|
const workspaceRoot = Uri.parse(cmdParams.arguments[0] as string, this._ls.rootUri.isCaseSensitive);
|
||||||
const importName = cmdParams.arguments[1] as string;
|
const importName = cmdParams.arguments[1] as string;
|
||||||
const callingFile = cmdParams.arguments[2] as string;
|
const callingFile = Uri.parse(cmdParams.arguments[2] as string, this._ls.rootUri.isCaseSensitive);
|
||||||
|
|
||||||
const service = await AnalyzerServiceExecutor.cloneService(
|
const service = await AnalyzerServiceExecutor.cloneService(
|
||||||
this._ls,
|
this._ls,
|
||||||
|
@ -29,6 +29,7 @@ import { isNumber, isString } from '../common/core';
|
|||||||
import { convertOffsetToPosition, convertOffsetsToRange } from '../common/positionUtils';
|
import { convertOffsetToPosition, convertOffsetsToRange } from '../common/positionUtils';
|
||||||
import { TextRange } from '../common/textRange';
|
import { TextRange } from '../common/textRange';
|
||||||
import { TextRangeCollection } from '../common/textRangeCollection';
|
import { TextRangeCollection } from '../common/textRangeCollection';
|
||||||
|
import { Uri } from '../common/uri/uri';
|
||||||
import { LanguageServerInterface } from '../languageServerBase';
|
import { LanguageServerInterface } from '../languageServerBase';
|
||||||
import {
|
import {
|
||||||
ArgumentCategory,
|
ArgumentCategory,
|
||||||
@ -132,11 +133,11 @@ export class DumpFileDebugInfoCommand implements ServerCommand {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
const filePath = params.arguments[0] as string;
|
const fileUri = Uri.parse(params.arguments[0] as string, this._ls.rootUri.isCaseSensitive);
|
||||||
const kind = params.arguments[1];
|
const kind = params.arguments[1];
|
||||||
|
|
||||||
const workspace = await this._ls.getWorkspaceForFile(filePath);
|
const workspace = await this._ls.getWorkspaceForFile(fileUri);
|
||||||
const parseResults = workspace.service.getParseResult(workspace.service.fs.realCasePath(filePath));
|
const parseResults = workspace.service.getParseResult(workspace.service.fs.realCasePath(fileUri));
|
||||||
if (!parseResults) {
|
if (!parseResults) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
@ -157,7 +158,7 @@ export class DumpFileDebugInfoCommand implements ServerCommand {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
collectingConsole.info(`* Dump debug info for '${filePath}'`);
|
collectingConsole.info(`* Dump debug info for '${fileUri.toUserVisibleString()}'`);
|
||||||
|
|
||||||
switch (kind) {
|
switch (kind) {
|
||||||
case 'tokens': {
|
case 'tokens': {
|
||||||
@ -166,7 +167,7 @@ export class DumpFileDebugInfoCommand implements ServerCommand {
|
|||||||
for (let i = 0; i < parseResults.tokenizerOutput.tokens.count; i++) {
|
for (let i = 0; i < parseResults.tokenizerOutput.tokens.count; i++) {
|
||||||
const token = parseResults.tokenizerOutput.tokens.getItemAt(i);
|
const token = parseResults.tokenizerOutput.tokens.getItemAt(i);
|
||||||
collectingConsole.info(
|
collectingConsole.info(
|
||||||
`[${i}] ${getTokenString(filePath, token, parseResults.tokenizerOutput.lines)}`
|
`[${i}] ${getTokenString(fileUri, token, parseResults.tokenizerOutput.lines)}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -174,7 +175,7 @@ export class DumpFileDebugInfoCommand implements ServerCommand {
|
|||||||
case 'nodes': {
|
case 'nodes': {
|
||||||
collectingConsole.info(`* Node info`);
|
collectingConsole.info(`* Node info`);
|
||||||
|
|
||||||
const dumper = new TreeDumper(filePath, parseResults.tokenizerOutput.lines);
|
const dumper = new TreeDumper(fileUri, parseResults.tokenizerOutput.lines);
|
||||||
dumper.walk(parseResults.parseTree);
|
dumper.walk(parseResults.parseTree);
|
||||||
|
|
||||||
collectingConsole.info(dumper.output);
|
collectingConsole.info(dumper.output);
|
||||||
@ -189,7 +190,7 @@ export class DumpFileDebugInfoCommand implements ServerCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
collectingConsole.info(`* Type info`);
|
collectingConsole.info(`* Type info`);
|
||||||
collectingConsole.info(`${getTypeEvaluatorString(filePath, evaluator, parseResults, start, end)}`);
|
collectingConsole.info(`${getTypeEvaluatorString(fileUri, evaluator, parseResults, start, end)}`);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'cachedtypes': {
|
case 'cachedtypes': {
|
||||||
@ -201,9 +202,7 @@ export class DumpFileDebugInfoCommand implements ServerCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
collectingConsole.info(`* Cached Type info`);
|
collectingConsole.info(`* Cached Type info`);
|
||||||
collectingConsole.info(
|
collectingConsole.info(`${getTypeEvaluatorString(fileUri, evaluator, parseResults, start, end, true)}`);
|
||||||
`${getTypeEvaluatorString(filePath, evaluator, parseResults, start, end, true)}`
|
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -239,14 +238,14 @@ function stringify(value: any, replacer: (this: any, key: string, value: any) =>
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getTypeEvaluatorString(
|
function getTypeEvaluatorString(
|
||||||
file: string,
|
uri: Uri,
|
||||||
evaluator: TypeEvaluator,
|
evaluator: TypeEvaluator,
|
||||||
results: ParseResults,
|
results: ParseResults,
|
||||||
start: number,
|
start: number,
|
||||||
end: number,
|
end: number,
|
||||||
cacheOnly?: boolean
|
cacheOnly?: boolean
|
||||||
) {
|
) {
|
||||||
const dumper = new TreeDumper(file, results.tokenizerOutput.lines);
|
const dumper = new TreeDumper(uri, results.tokenizerOutput.lines);
|
||||||
const node = findNodeByOffset(results.parseTree, start) ?? findNodeByOffset(results.parseTree, end);
|
const node = findNodeByOffset(results.parseTree, start) ?? findNodeByOffset(results.parseTree, end);
|
||||||
if (!node) {
|
if (!node) {
|
||||||
return 'N/A';
|
return 'N/A';
|
||||||
@ -549,7 +548,7 @@ class TreeDumper extends ParseTreeWalker {
|
|||||||
private _indentation = '';
|
private _indentation = '';
|
||||||
private _output = '';
|
private _output = '';
|
||||||
|
|
||||||
constructor(private _file: string, private _lines: TextRangeCollection<TextRange>) {
|
constructor(private _uri: Uri, private _lines: TextRangeCollection<TextRange>) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -604,7 +603,7 @@ class TreeDumper extends ParseTreeWalker {
|
|||||||
override visitBinaryOperation(node: BinaryOperationNode) {
|
override visitBinaryOperation(node: BinaryOperationNode) {
|
||||||
this._log(
|
this._log(
|
||||||
`${this._getPrefix(node)} ${getTokenString(
|
`${this._getPrefix(node)} ${getTokenString(
|
||||||
this._file,
|
this._uri,
|
||||||
node.operatorToken,
|
node.operatorToken,
|
||||||
this._lines
|
this._lines
|
||||||
)} ${getOperatorTypeString(node.operator)}} parenthesized:(${node.parenthesized})`
|
)} ${getOperatorTypeString(node.operator)}} parenthesized:(${node.parenthesized})`
|
||||||
@ -697,7 +696,7 @@ class TreeDumper extends ParseTreeWalker {
|
|||||||
`${this._getPrefix(node)} wildcard import:(${node.isWildcardImport}) paren:(${
|
`${this._getPrefix(node)} wildcard import:(${node.isWildcardImport}) paren:(${
|
||||||
node.usesParens
|
node.usesParens
|
||||||
}) wildcard token:(${
|
}) wildcard token:(${
|
||||||
node.wildcardToken ? getTokenString(this._file, node.wildcardToken, this._lines) : 'N/A'
|
node.wildcardToken ? getTokenString(this._uri, node.wildcardToken, this._lines) : 'N/A'
|
||||||
}) missing import keyword:(${node.missingImportKeyword})`
|
}) missing import keyword:(${node.missingImportKeyword})`
|
||||||
);
|
);
|
||||||
return true;
|
return true;
|
||||||
@ -784,7 +783,7 @@ class TreeDumper extends ParseTreeWalker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override visitName(node: NameNode) {
|
override visitName(node: NameNode) {
|
||||||
this._log(`${this._getPrefix(node)} ${getTokenString(this._file, node.token, this._lines)} ${node.value}`);
|
this._log(`${this._getPrefix(node)} ${getTokenString(this._uri, node.token, this._lines)} ${node.value}`);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -834,7 +833,7 @@ class TreeDumper extends ParseTreeWalker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override visitString(node: StringNode) {
|
override visitString(node: StringNode) {
|
||||||
this._log(`${this._getPrefix(node)} ${getTokenString(this._file, node.token, this._lines)} ${node.value}`);
|
this._log(`${this._getPrefix(node)} ${getTokenString(this._uri, node.token, this._lines)} ${node.value}`);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -866,7 +865,7 @@ class TreeDumper extends ParseTreeWalker {
|
|||||||
override visitUnaryOperation(node: UnaryOperationNode) {
|
override visitUnaryOperation(node: UnaryOperationNode) {
|
||||||
this._log(
|
this._log(
|
||||||
`${this._getPrefix(node)} ${getTokenString(
|
`${this._getPrefix(node)} ${getTokenString(
|
||||||
this._file,
|
this._uri,
|
||||||
node.operatorToken,
|
node.operatorToken,
|
||||||
this._lines
|
this._lines
|
||||||
)} ${getOperatorTypeString(node.operator)}`
|
)} ${getOperatorTypeString(node.operator)}`
|
||||||
@ -988,7 +987,7 @@ class TreeDumper extends ParseTreeWalker {
|
|||||||
private _getPrefix(node: ParseNode) {
|
private _getPrefix(node: ParseNode) {
|
||||||
const pos = convertOffsetToPosition(node.start, this._lines);
|
const pos = convertOffsetToPosition(node.start, this._lines);
|
||||||
// VS code's output window expects 1 based values, print the line/char with 1 based.
|
// VS code's output window expects 1 based values, print the line/char with 1 based.
|
||||||
return `[${node.id}] '${this._file}:${pos.line + 1}:${pos.character + 1}' => ${printParseNodeType(
|
return `[${node.id}] '${this._uri.toString()}:${pos.line + 1}:${pos.character + 1}' => ${printParseNodeType(
|
||||||
node.nodeType
|
node.nodeType
|
||||||
)} ${getTextSpanString(node, this._lines)} =>`;
|
)} ${getTextSpanString(node, this._lines)} =>`;
|
||||||
}
|
}
|
||||||
@ -1066,9 +1065,9 @@ function getErrorExpressionCategoryString(type: ErrorExpressionCategory) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTokenString(file: string, token: Token, lines: TextRangeCollection<TextRange>) {
|
function getTokenString(uri: Uri, token: Token, lines: TextRangeCollection<TextRange>) {
|
||||||
const pos = convertOffsetToPosition(token.start, lines);
|
const pos = convertOffsetToPosition(token.start, lines);
|
||||||
let str = `'${file}:${pos.line + 1}:${pos.character + 1}' (`;
|
let str = `'${uri.toUserVisibleString()}:${pos.line + 1}:${pos.character + 1}' (`;
|
||||||
str += getTokenTypeString(token.type);
|
str += getTokenTypeString(token.type);
|
||||||
str += getNewLineInfo(token);
|
str += getNewLineInfo(token);
|
||||||
str += getOperatorInfo(token);
|
str += getOperatorInfo(token);
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
import { CancellationToken, ExecuteCommandParams } from 'vscode-languageserver';
|
import { CancellationToken, ExecuteCommandParams } from 'vscode-languageserver';
|
||||||
|
|
||||||
|
import { Uri } from '../common/uri/uri';
|
||||||
import { convertToFileTextEdits, convertToWorkspaceEdit } from '../common/workspaceEditUtils';
|
import { convertToFileTextEdits, convertToWorkspaceEdit } from '../common/workspaceEditUtils';
|
||||||
import { LanguageServerInterface } from '../languageServerBase';
|
import { LanguageServerInterface } from '../languageServerBase';
|
||||||
import { performQuickAction } from '../languageService/quickActions';
|
import { performQuickAction } from '../languageService/quickActions';
|
||||||
@ -19,20 +20,19 @@ export class QuickActionCommand implements ServerCommand {
|
|||||||
|
|
||||||
async execute(params: ExecuteCommandParams, token: CancellationToken): Promise<any> {
|
async execute(params: ExecuteCommandParams, token: CancellationToken): Promise<any> {
|
||||||
if (params.arguments && params.arguments.length >= 1) {
|
if (params.arguments && params.arguments.length >= 1) {
|
||||||
const docUri = params.arguments[0] as string;
|
const docUri = Uri.parse(params.arguments[0] as string, this._ls.rootUri.isCaseSensitive);
|
||||||
const otherArgs = params.arguments.slice(1);
|
const otherArgs = params.arguments.slice(1);
|
||||||
const filePath = this._ls.decodeTextDocumentUri(docUri);
|
const workspace = await this._ls.getWorkspaceForFile(docUri);
|
||||||
const workspace = await this._ls.getWorkspaceForFile(filePath);
|
|
||||||
|
|
||||||
if (params.command === Commands.orderImports && workspace.disableOrganizeImports) {
|
if (params.command === Commands.orderImports && workspace.disableOrganizeImports) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
const editActions = workspace.service.run((p) => {
|
const editActions = workspace.service.run((p) => {
|
||||||
return performQuickAction(p, filePath, params.command, otherArgs, token);
|
return performQuickAction(p, docUri, params.command, otherArgs, token);
|
||||||
}, token);
|
}, token);
|
||||||
|
|
||||||
return convertToWorkspaceEdit(workspace.service.fs, convertToFileTextEdits(filePath, editActions ?? []));
|
return convertToWorkspaceEdit(convertToFileTextEdits(docUri, editActions ?? []));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,19 +18,11 @@ import { TaskListToken } from './diagnostic';
|
|||||||
import { DiagnosticRule } from './diagnosticRules';
|
import { DiagnosticRule } from './diagnosticRules';
|
||||||
import { FileSystem } from './fileSystem';
|
import { FileSystem } from './fileSystem';
|
||||||
import { Host } from './host';
|
import { Host } from './host';
|
||||||
import {
|
|
||||||
FileSpec,
|
|
||||||
combinePaths,
|
|
||||||
ensureTrailingDirectorySeparator,
|
|
||||||
getFileSpec,
|
|
||||||
isDirectory,
|
|
||||||
normalizePath,
|
|
||||||
realCasePath,
|
|
||||||
resolvePaths,
|
|
||||||
} from './pathUtils';
|
|
||||||
import { PythonVersion, latestStablePythonVersion, versionFromString, versionToString } from './pythonVersion';
|
import { PythonVersion, latestStablePythonVersion, versionFromString, versionToString } from './pythonVersion';
|
||||||
import { ServiceProvider } from './serviceProvider';
|
import { ServiceProvider } from './serviceProvider';
|
||||||
import { ServiceKeys } from './serviceProviderExtensions';
|
import { ServiceKeys } from './serviceProviderExtensions';
|
||||||
|
import { Uri } from './uri/uri';
|
||||||
|
import { FileSpec, getFileSpec, isDirectory } from './uri/uriUtils';
|
||||||
|
|
||||||
export enum PythonPlatform {
|
export enum PythonPlatform {
|
||||||
Darwin = 'Darwin',
|
Darwin = 'Darwin',
|
||||||
@ -39,10 +31,9 @@ export enum PythonPlatform {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class ExecutionEnvironment {
|
export class ExecutionEnvironment {
|
||||||
// Root directory for execution - absolute or relative to the
|
// Root directory for execution.
|
||||||
// project root.
|
|
||||||
// Undefined if this is a rootless environment (e.g., open file mode).
|
// Undefined if this is a rootless environment (e.g., open file mode).
|
||||||
root?: string;
|
root?: Uri;
|
||||||
|
|
||||||
// Name of a virtual environment if there is one, otherwise
|
// Name of a virtual environment if there is one, otherwise
|
||||||
// just the path to the python executable.
|
// just the path to the python executable.
|
||||||
@ -55,18 +46,18 @@ export class ExecutionEnvironment {
|
|||||||
pythonPlatform?: string | undefined;
|
pythonPlatform?: string | undefined;
|
||||||
|
|
||||||
// Default to no extra paths.
|
// Default to no extra paths.
|
||||||
extraPaths: string[] = [];
|
extraPaths: Uri[] = [];
|
||||||
|
|
||||||
// Default to "." which indicates every file in the project.
|
// Default to "." which indicates every file in the project.
|
||||||
constructor(
|
constructor(
|
||||||
name: string,
|
name: string,
|
||||||
root: string,
|
root: Uri,
|
||||||
defaultPythonVersion: PythonVersion | undefined,
|
defaultPythonVersion: PythonVersion | undefined,
|
||||||
defaultPythonPlatform: string | undefined,
|
defaultPythonPlatform: string | undefined,
|
||||||
defaultExtraPaths: string[] | undefined
|
defaultExtraPaths: Uri[] | undefined
|
||||||
) {
|
) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.root = root || undefined;
|
this.root = root;
|
||||||
this.pythonVersion = defaultPythonVersion || latestStablePythonVersion;
|
this.pythonVersion = defaultPythonVersion || latestStablePythonVersion;
|
||||||
this.pythonPlatform = defaultPythonPlatform;
|
this.pythonPlatform = defaultPythonPlatform;
|
||||||
this.extraPaths = Array.from(defaultExtraPaths ?? []);
|
this.extraPaths = Array.from(defaultExtraPaths ?? []);
|
||||||
@ -780,9 +771,9 @@ export function getStrictDiagnosticRuleSet(): DiagnosticRuleSet {
|
|||||||
return diagSettings;
|
return diagSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function matchFileSpecs(configOptions: ConfigOptions, filePath: string, isFile = true) {
|
export function matchFileSpecs(configOptions: ConfigOptions, uri: Uri, isFile = true) {
|
||||||
for (const includeSpec of configOptions.include) {
|
for (const includeSpec of configOptions.include) {
|
||||||
if (FileSpec.matchIncludeFileSpec(includeSpec.regExp, configOptions.exclude, filePath, isFile)) {
|
if (FileSpec.matchIncludeFileSpec(includeSpec.regExp, configOptions.exclude, uri, isFile)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -795,19 +786,19 @@ export function matchFileSpecs(configOptions: ConfigOptions, filePath: string, i
|
|||||||
export class ConfigOptions {
|
export class ConfigOptions {
|
||||||
// Absolute directory of project. All relative paths in the config
|
// Absolute directory of project. All relative paths in the config
|
||||||
// are based on this path.
|
// are based on this path.
|
||||||
projectRoot: string;
|
projectRoot: Uri;
|
||||||
|
|
||||||
// Path to python interpreter.
|
// Path to python interpreter.
|
||||||
pythonPath?: string | undefined;
|
pythonPath?: Uri | undefined;
|
||||||
|
|
||||||
// Name of the python environment.
|
// Name of the python environment.
|
||||||
pythonEnvironmentName?: string | undefined;
|
pythonEnvironmentName?: string | undefined;
|
||||||
|
|
||||||
// Path to use for typeshed definitions.
|
// Path to use for typeshed definitions.
|
||||||
typeshedPath?: string | undefined;
|
typeshedPath?: Uri | undefined;
|
||||||
|
|
||||||
// Path to custom typings (stub) modules.
|
// Path to custom typings (stub) modules.
|
||||||
stubPath?: string | undefined;
|
stubPath?: Uri | undefined;
|
||||||
|
|
||||||
// A list of file specs to include in the analysis. Can contain
|
// A list of file specs to include in the analysis. Can contain
|
||||||
// directories, in which case all "*.py" files within those directories
|
// directories, in which case all "*.py" files within those directories
|
||||||
@ -887,7 +878,7 @@ export class ConfigOptions {
|
|||||||
// directories. This is used in conjunction with the "venv" name in
|
// directories. This is used in conjunction with the "venv" name in
|
||||||
// the config file to identify the python environment used for resolving
|
// the config file to identify the python environment used for resolving
|
||||||
// third-party modules.
|
// third-party modules.
|
||||||
venvPath?: string | undefined;
|
venvPath?: Uri | undefined;
|
||||||
|
|
||||||
// Default venv environment.
|
// Default venv environment.
|
||||||
venv?: string | undefined;
|
venv?: string | undefined;
|
||||||
@ -899,7 +890,7 @@ export class ConfigOptions {
|
|||||||
defaultPythonPlatform?: string | undefined;
|
defaultPythonPlatform?: string | undefined;
|
||||||
|
|
||||||
// Default extraPaths. Can be overridden by executionEnvironment.
|
// Default extraPaths. Can be overridden by executionEnvironment.
|
||||||
defaultExtraPaths?: string[] | undefined;
|
defaultExtraPaths?: Uri[] | undefined;
|
||||||
|
|
||||||
//---------------------------------------------------------------
|
//---------------------------------------------------------------
|
||||||
// Internal-only switches
|
// Internal-only switches
|
||||||
@ -917,7 +908,7 @@ export class ConfigOptions {
|
|||||||
// Controls how hover and completion function signatures are displayed.
|
// Controls how hover and completion function signatures are displayed.
|
||||||
functionSignatureDisplay: SignatureDisplayType;
|
functionSignatureDisplay: SignatureDisplayType;
|
||||||
|
|
||||||
constructor(projectRoot: string, typeCheckingMode?: string) {
|
constructor(projectRoot: Uri, typeCheckingMode?: string) {
|
||||||
this.projectRoot = projectRoot;
|
this.projectRoot = projectRoot;
|
||||||
this.typeCheckingMode = typeCheckingMode;
|
this.typeCheckingMode = typeCheckingMode;
|
||||||
this.diagnosticRuleSet = ConfigOptions.getDiagnosticRuleSet(typeCheckingMode);
|
this.diagnosticRuleSet = ConfigOptions.getDiagnosticRuleSet(typeCheckingMode);
|
||||||
@ -950,17 +941,15 @@ export class ConfigOptions {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finds the best execution environment for a given file path. The
|
// Finds the best execution environment for a given file uri. The
|
||||||
// specified file path should be absolute.
|
// specified file path should be absolute.
|
||||||
// If no matching execution environment can be found, a default
|
// If no matching execution environment can be found, a default
|
||||||
// execution environment is used.
|
// execution environment is used.
|
||||||
findExecEnvironment(filePath: string): ExecutionEnvironment {
|
findExecEnvironment(file: Uri): ExecutionEnvironment {
|
||||||
return (
|
return (
|
||||||
this.executionEnvironments.find((env) => {
|
this.executionEnvironments.find((env) => {
|
||||||
const envRoot = ensureTrailingDirectorySeparator(
|
const envRoot = Uri.isUri(env.root) ? env.root : this.projectRoot.combinePaths(env.root || '');
|
||||||
normalizePath(combinePaths(this.projectRoot, env.root))
|
return file.startsWith(envRoot);
|
||||||
);
|
|
||||||
return filePath.startsWith(envRoot);
|
|
||||||
}) ?? this.getDefaultExecEnvironment()
|
}) ?? this.getDefaultExecEnvironment()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -997,7 +986,7 @@ export class ConfigOptions {
|
|||||||
} else if (isAbsolute(fileSpec)) {
|
} else if (isAbsolute(fileSpec)) {
|
||||||
console.error(`Ignoring path "${fileSpec}" in "include" array because it is not relative.`);
|
console.error(`Ignoring path "${fileSpec}" in "include" array because it is not relative.`);
|
||||||
} else {
|
} else {
|
||||||
this.include.push(getFileSpec(serviceProvider, this.projectRoot, fileSpec));
|
this.include.push(getFileSpec(this.projectRoot, fileSpec));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -1016,7 +1005,7 @@ export class ConfigOptions {
|
|||||||
} else if (isAbsolute(fileSpec)) {
|
} else if (isAbsolute(fileSpec)) {
|
||||||
console.error(`Ignoring path "${fileSpec}" in "exclude" array because it is not relative.`);
|
console.error(`Ignoring path "${fileSpec}" in "exclude" array because it is not relative.`);
|
||||||
} else {
|
} else {
|
||||||
this.exclude.push(getFileSpec(serviceProvider, this.projectRoot, fileSpec));
|
this.exclude.push(getFileSpec(this.projectRoot, fileSpec));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -1035,7 +1024,7 @@ export class ConfigOptions {
|
|||||||
} else if (isAbsolute(fileSpec)) {
|
} else if (isAbsolute(fileSpec)) {
|
||||||
console.error(`Ignoring path "${fileSpec}" in "ignore" array because it is not relative.`);
|
console.error(`Ignoring path "${fileSpec}" in "ignore" array because it is not relative.`);
|
||||||
} else {
|
} else {
|
||||||
this.ignore.push(getFileSpec(serviceProvider, this.projectRoot, fileSpec));
|
this.ignore.push(getFileSpec(this.projectRoot, fileSpec));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -1054,7 +1043,7 @@ export class ConfigOptions {
|
|||||||
} else if (isAbsolute(fileSpec)) {
|
} else if (isAbsolute(fileSpec)) {
|
||||||
console.error(`Ignoring path "${fileSpec}" in "strict" array because it is not relative.`);
|
console.error(`Ignoring path "${fileSpec}" in "strict" array because it is not relative.`);
|
||||||
} else {
|
} else {
|
||||||
this.strict.push(getFileSpec(serviceProvider, this.projectRoot, fileSpec));
|
this.strict.push(getFileSpec(this.projectRoot, fileSpec));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -1116,7 +1105,7 @@ export class ConfigOptions {
|
|||||||
if (typeof configObj.venvPath !== 'string') {
|
if (typeof configObj.venvPath !== 'string') {
|
||||||
console.error(`Config "venvPath" field must contain a string.`);
|
console.error(`Config "venvPath" field must contain a string.`);
|
||||||
} else {
|
} else {
|
||||||
this.venvPath = normalizePath(combinePaths(this.projectRoot, configObj.venvPath));
|
this.venvPath = this.projectRoot.combinePaths(configObj.venvPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1141,7 +1130,7 @@ export class ConfigOptions {
|
|||||||
if (typeof path !== 'string') {
|
if (typeof path !== 'string') {
|
||||||
console.error(`Config "extraPaths" field ${pathIndex} must be a string.`);
|
console.error(`Config "extraPaths" field ${pathIndex} must be a string.`);
|
||||||
} else {
|
} else {
|
||||||
this.defaultExtraPaths!.push(normalizePath(combinePaths(this.projectRoot, path)));
|
this.defaultExtraPaths!.push(this.projectRoot.combinePaths(path));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -1181,8 +1170,8 @@ export class ConfigOptions {
|
|||||||
console.error(`Config "typeshedPath" field must contain a string.`);
|
console.error(`Config "typeshedPath" field must contain a string.`);
|
||||||
} else {
|
} else {
|
||||||
this.typeshedPath = configObj.typeshedPath
|
this.typeshedPath = configObj.typeshedPath
|
||||||
? normalizePath(combinePaths(this.projectRoot, configObj.typeshedPath))
|
? this.projectRoot.combinePaths(configObj.typeshedPath)
|
||||||
: '';
|
: undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1195,7 +1184,7 @@ export class ConfigOptions {
|
|||||||
console.error(`Config "typingsPath" field must contain a string.`);
|
console.error(`Config "typingsPath" field must contain a string.`);
|
||||||
} else {
|
} else {
|
||||||
console.error(`Config "typingsPath" is now deprecated. Please, use stubPath instead.`);
|
console.error(`Config "typingsPath" is now deprecated. Please, use stubPath instead.`);
|
||||||
this.stubPath = normalizePath(combinePaths(this.projectRoot, configObj.typingsPath));
|
this.stubPath = this.projectRoot.combinePaths(configObj.typingsPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1203,7 +1192,7 @@ export class ConfigOptions {
|
|||||||
if (typeof configObj.stubPath !== 'string') {
|
if (typeof configObj.stubPath !== 'string') {
|
||||||
console.error(`Config "stubPath" field must contain a string.`);
|
console.error(`Config "stubPath" field must contain a string.`);
|
||||||
} else {
|
} else {
|
||||||
this.stubPath = normalizePath(combinePaths(this.projectRoot, configObj.stubPath));
|
this.stubPath = this.projectRoot.combinePaths(configObj.stubPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1345,20 +1334,20 @@ export class ConfigOptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ensureDefaultExtraPaths(fs: FileSystem, autoSearchPaths: boolean, extraPaths: string[] | undefined) {
|
ensureDefaultExtraPaths(fs: FileSystem, autoSearchPaths: boolean, extraPaths: string[] | undefined) {
|
||||||
const paths: string[] = [];
|
const paths: Uri[] = [];
|
||||||
|
|
||||||
if (autoSearchPaths) {
|
if (autoSearchPaths) {
|
||||||
// Auto-detect the common scenario where the sources are under the src folder
|
// Auto-detect the common scenario where the sources are under the src folder
|
||||||
const srcPath = resolvePaths(this.projectRoot, pathConsts.src);
|
const srcPath = this.projectRoot.combinePaths(pathConsts.src);
|
||||||
if (fs.existsSync(srcPath) && !fs.existsSync(resolvePaths(srcPath, '__init__.py'))) {
|
if (fs.existsSync(srcPath) && !fs.existsSync(srcPath.combinePaths('__init__.py'))) {
|
||||||
paths.push(realCasePath(srcPath, fs));
|
paths.push(fs.realCasePath(srcPath));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (extraPaths && extraPaths.length > 0) {
|
if (extraPaths && extraPaths.length > 0) {
|
||||||
for (const p of extraPaths) {
|
for (const p of extraPaths) {
|
||||||
const path = resolvePaths(this.projectRoot, p);
|
const path = this.projectRoot.combinePaths(p);
|
||||||
paths.push(realCasePath(path, fs));
|
paths.push(fs.realCasePath(path));
|
||||||
if (isDirectory(fs, path)) {
|
if (isDirectory(fs, path)) {
|
||||||
appendArray(paths, getPathsFromPthFiles(fs, path));
|
appendArray(paths, getPathsFromPthFiles(fs, path));
|
||||||
}
|
}
|
||||||
@ -1384,7 +1373,7 @@ export class ConfigOptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _getEnvironmentName(): string {
|
private _getEnvironmentName(): string {
|
||||||
return this.pythonEnvironmentName || this.pythonPath || 'python';
|
return this.pythonEnvironmentName || this.pythonPath?.toString() || 'python';
|
||||||
}
|
}
|
||||||
|
|
||||||
private _convertBoolean(value: any, fieldName: string, defaultValue: boolean): boolean {
|
private _convertBoolean(value: any, fieldName: string, defaultValue: boolean): boolean {
|
||||||
@ -1429,7 +1418,7 @@ export class ConfigOptions {
|
|||||||
|
|
||||||
// Validate the root.
|
// Validate the root.
|
||||||
if (envObj.root && typeof envObj.root === 'string') {
|
if (envObj.root && typeof envObj.root === 'string') {
|
||||||
newExecEnv.root = normalizePath(combinePaths(this.projectRoot, envObj.root));
|
newExecEnv.root = this.projectRoot.combinePaths(envObj.root);
|
||||||
} else {
|
} else {
|
||||||
console.error(`Config executionEnvironments index ${index}: missing root value.`);
|
console.error(`Config executionEnvironments index ${index}: missing root value.`);
|
||||||
}
|
}
|
||||||
@ -1449,7 +1438,7 @@ export class ConfigOptions {
|
|||||||
` extraPaths field ${pathIndex} must be a string.`
|
` extraPaths field ${pathIndex} must be a string.`
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
newExecEnv.extraPaths.push(normalizePath(combinePaths(this.projectRoot, path)));
|
newExecEnv.extraPaths.push(this.projectRoot.combinePaths(path));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ import { Commands } from '../commands/commands';
|
|||||||
import { appendArray } from './collectionUtils';
|
import { appendArray } from './collectionUtils';
|
||||||
import { DiagnosticLevel } from './configOptions';
|
import { DiagnosticLevel } from './configOptions';
|
||||||
import { Range, TextRange } from './textRange';
|
import { Range, TextRange } from './textRange';
|
||||||
|
import { Uri } from './uri/uri';
|
||||||
|
|
||||||
const defaultMaxDepth = 5;
|
const defaultMaxDepth = 5;
|
||||||
const defaultMaxLineCount = 8;
|
const defaultMaxLineCount = 8;
|
||||||
@ -63,7 +64,7 @@ export interface DiagnosticAction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface DiagnosticWithinFile {
|
export interface DiagnosticWithinFile {
|
||||||
filePath: string;
|
uri: Uri;
|
||||||
diagnostic: Diagnostic;
|
diagnostic: Diagnostic;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,13 +75,13 @@ export interface CreateTypeStubFileAction extends DiagnosticAction {
|
|||||||
|
|
||||||
export interface RenameShadowedFileAction extends DiagnosticAction {
|
export interface RenameShadowedFileAction extends DiagnosticAction {
|
||||||
action: ActionKind.RenameShadowedFileAction;
|
action: ActionKind.RenameShadowedFileAction;
|
||||||
oldFile: string;
|
oldUri: Uri;
|
||||||
newFile: string;
|
newUri: Uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DiagnosticRelatedInfo {
|
export interface DiagnosticRelatedInfo {
|
||||||
message: string;
|
message: string;
|
||||||
filePath: string;
|
uri: Uri;
|
||||||
range: Range;
|
range: Range;
|
||||||
priority: TaskListPriority;
|
priority: TaskListPriority;
|
||||||
}
|
}
|
||||||
@ -118,13 +119,8 @@ export class Diagnostic {
|
|||||||
return this._rule;
|
return this._rule;
|
||||||
}
|
}
|
||||||
|
|
||||||
addRelatedInfo(
|
addRelatedInfo(message: string, fileUri: Uri, range: Range, priority: TaskListPriority = TaskListPriority.Normal) {
|
||||||
message: string,
|
this._relatedInfo.push({ uri: fileUri, message, range, priority });
|
||||||
filePath: string,
|
|
||||||
range: Range,
|
|
||||||
priority: TaskListPriority = TaskListPriority.Normal
|
|
||||||
) {
|
|
||||||
this._relatedInfo.push({ filePath, message, range, priority });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getRelatedInfo() {
|
getRelatedInfo() {
|
||||||
|
@ -14,10 +14,11 @@ import { convertOffsetsToRange } from './positionUtils';
|
|||||||
import { hashString } from './stringUtils';
|
import { hashString } from './stringUtils';
|
||||||
import { Range, TextRange } from './textRange';
|
import { Range, TextRange } from './textRange';
|
||||||
import { TextRangeCollection } from './textRangeCollection';
|
import { TextRangeCollection } from './textRangeCollection';
|
||||||
|
import { Uri } from './uri/uri';
|
||||||
|
|
||||||
// Represents a collection of diagnostics within a file.
|
// Represents a collection of diagnostics within a file.
|
||||||
export interface FileDiagnostics {
|
export interface FileDiagnostics {
|
||||||
filePath: string;
|
fileUri: Uri;
|
||||||
version: number | undefined;
|
version: number | undefined;
|
||||||
diagnostics: Diagnostic[];
|
diagnostics: Diagnostic[];
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Range, rangesAreEqual } from './textRange';
|
import { Range, rangesAreEqual } from './textRange';
|
||||||
|
import { Uri } from './uri/uri';
|
||||||
|
|
||||||
export interface TextEditAction {
|
export interface TextEditAction {
|
||||||
range: Range;
|
range: Range;
|
||||||
@ -15,7 +16,7 @@ export interface TextEditAction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface FileEditAction extends TextEditAction {
|
export interface FileEditAction extends TextEditAction {
|
||||||
filePath: string;
|
fileUri: Uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FileEditActions {
|
export interface FileEditActions {
|
||||||
@ -31,18 +32,18 @@ export interface FileOperation {
|
|||||||
|
|
||||||
export interface RenameFileOperation extends FileOperation {
|
export interface RenameFileOperation extends FileOperation {
|
||||||
kind: 'rename';
|
kind: 'rename';
|
||||||
oldFilePath: string;
|
oldFileUri: Uri;
|
||||||
newFilePath: string;
|
newFileUri: Uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CreateFileOperation extends FileOperation {
|
export interface CreateFileOperation extends FileOperation {
|
||||||
kind: 'create';
|
kind: 'create';
|
||||||
filePath: string;
|
fileUri: Uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DeleteFileOperation extends FileOperation {
|
export interface DeleteFileOperation extends FileOperation {
|
||||||
kind: 'delete';
|
kind: 'delete';
|
||||||
filePath: string;
|
fileUri: Uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
export namespace TextEditAction {
|
export namespace TextEditAction {
|
||||||
@ -53,13 +54,13 @@ export namespace TextEditAction {
|
|||||||
|
|
||||||
export namespace FileEditAction {
|
export namespace FileEditAction {
|
||||||
export function is(value: any): value is FileEditAction {
|
export function is(value: any): value is FileEditAction {
|
||||||
return value.filePath !== undefined && TextEditAction.is(value);
|
return value.fileUri !== undefined && TextEditAction.is(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function areEqual(e1: FileEditAction, e2: FileEditAction) {
|
export function areEqual(e1: FileEditAction, e2: FileEditAction) {
|
||||||
return (
|
return (
|
||||||
e1 === e2 ||
|
e1 === e2 ||
|
||||||
(e1.filePath === e2.filePath &&
|
(e1.fileUri.equals(e2.fileUri) &&
|
||||||
rangesAreEqual(e1.range, e2.range) &&
|
rangesAreEqual(e1.range, e2.range) &&
|
||||||
e1.replacementText === e2.replacementText)
|
e1.replacementText === e2.replacementText)
|
||||||
);
|
);
|
||||||
|
@ -8,43 +8,35 @@
|
|||||||
|
|
||||||
import * as os from 'os';
|
import * as os from 'os';
|
||||||
|
|
||||||
import {
|
import { Uri } from './uri/uri';
|
||||||
combinePaths,
|
|
||||||
ensureTrailingDirectorySeparator,
|
|
||||||
getPathComponents,
|
|
||||||
hasTrailingDirectorySeparator,
|
|
||||||
} from './pathUtils';
|
|
||||||
|
|
||||||
// Expands certain predefined variables supported within VS Code settings.
|
// Expands certain predefined variables supported within VS Code settings.
|
||||||
// Ideally, VS Code would provide an API for doing this expansion, but
|
// Ideally, VS Code would provide an API for doing this expansion, but
|
||||||
// it doesn't. We'll handle the most common variables here as a convenience.
|
// it doesn't. We'll handle the most common variables here as a convenience.
|
||||||
export function expandPathVariables(rootPath: string, path: string): string {
|
export function expandPathVariables(rootPath: Uri, path: string): string {
|
||||||
const pathParts = getPathComponents(path);
|
// Make sure the pathStr looks like a URI path.
|
||||||
|
let pathStr = path.replace(/\\/g, '/');
|
||||||
|
|
||||||
const expandedParts: string[] = [];
|
// Make sure all replacements look like URI paths too.
|
||||||
for (const part of pathParts) {
|
const replace = (match: RegExp, replaceValue: string) => {
|
||||||
const trimmedPart = part.trim();
|
pathStr = pathStr.replace(match, replaceValue.replace(/\\/g, '/'));
|
||||||
|
};
|
||||||
|
|
||||||
if (trimmedPart === '${workspaceFolder}') {
|
// Replace everything inline.
|
||||||
expandedParts.push(rootPath);
|
pathStr = pathStr.replace(/\$\{workspaceFolder\}/g, rootPath.getPath());
|
||||||
} else if (trimmedPart === '${env:HOME}' && process.env.HOME !== undefined) {
|
if (process.env.HOME !== undefined) {
|
||||||
expandedParts.push(process.env.HOME);
|
replace(/\$\{env:HOME\}/g, process.env.HOME || '');
|
||||||
} else if (trimmedPart === '${env:USERNAME}' && process.env.USERNAME !== undefined) {
|
}
|
||||||
expandedParts.push(process.env.USERNAME);
|
if (process.env.USERNAME !== undefined) {
|
||||||
} else if (trimmedPart === '${env:VIRTUAL_ENV}' && process.env.VIRTUAL_ENV !== undefined) {
|
replace(/\$\{env:USERNAME\}/g, process.env.USERNAME || '');
|
||||||
expandedParts.push(process.env.VIRTUAL_ENV);
|
}
|
||||||
} else if (trimmedPart === '~' && os.homedir) {
|
if (process.env.VIRTUAL_ENV !== undefined) {
|
||||||
expandedParts.push(os.homedir() || process.env.HOME || process.env.USERPROFILE || '~');
|
replace(/\$\{env:VIRTUAL_ENV\}/g, process.env.VIRTUAL_ENV || '');
|
||||||
} else {
|
}
|
||||||
expandedParts.push(part);
|
if (os.homedir) {
|
||||||
}
|
replace(/\/~/g, os.homedir() || process.env.HOME || process.env.USERPROFILE || '~');
|
||||||
|
replace(/^~/g, os.homedir() || process.env.HOME || process.env.USERPROFILE || '~');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (expandedParts.length === 0) {
|
return pathStr;
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
const root = expandedParts.shift()!;
|
|
||||||
const expandedPath = combinePaths(root, ...expandedParts);
|
|
||||||
return hasTrailingDirectorySeparator(path) ? ensureTrailingDirectorySeparator(expandedPath) : expandedPath;
|
|
||||||
}
|
}
|
||||||
|
@ -11,19 +11,20 @@ import { CancellationToken } from 'vscode-languageserver';
|
|||||||
import { Declaration } from '../analyzer/declaration';
|
import { Declaration } from '../analyzer/declaration';
|
||||||
import { ImportResolver } from '../analyzer/importResolver';
|
import { ImportResolver } from '../analyzer/importResolver';
|
||||||
import * as prog from '../analyzer/program';
|
import * as prog from '../analyzer/program';
|
||||||
|
import { IPythonMode } from '../analyzer/sourceFile';
|
||||||
import { SourceMapper } from '../analyzer/sourceMapper';
|
import { SourceMapper } from '../analyzer/sourceMapper';
|
||||||
|
import { SymbolTable } from '../analyzer/symbol';
|
||||||
import { TypeEvaluator } from '../analyzer/typeEvaluatorTypes';
|
import { TypeEvaluator } from '../analyzer/typeEvaluatorTypes';
|
||||||
|
import { Diagnostic } from '../common/diagnostic';
|
||||||
import { ServerSettings } from '../languageServerBase';
|
import { ServerSettings } from '../languageServerBase';
|
||||||
import { ParseNode } from '../parser/parseNodes';
|
import { ParseNode } from '../parser/parseNodes';
|
||||||
import { ParseResults } from '../parser/parser';
|
import { ParseResults } from '../parser/parser';
|
||||||
import { ConfigOptions } from './configOptions';
|
import { ConfigOptions } from './configOptions';
|
||||||
import { ConsoleInterface } from './console';
|
import { ConsoleInterface } from './console';
|
||||||
import { ReadOnlyFileSystem } from './fileSystem';
|
import { ReadOnlyFileSystem } from './fileSystem';
|
||||||
import { Range } from './textRange';
|
|
||||||
import { SymbolTable } from '../analyzer/symbol';
|
|
||||||
import { Diagnostic } from '../common/diagnostic';
|
|
||||||
import { IPythonMode } from '../analyzer/sourceFile';
|
|
||||||
import { GroupServiceKey, ServiceKey } from './serviceProvider';
|
import { GroupServiceKey, ServiceKey } from './serviceProvider';
|
||||||
|
import { Range } from './textRange';
|
||||||
|
import { Uri } from './uri/uri';
|
||||||
|
|
||||||
export interface SourceFile {
|
export interface SourceFile {
|
||||||
// See whether we can convert these to regular properties.
|
// See whether we can convert these to regular properties.
|
||||||
@ -31,9 +32,8 @@ export interface SourceFile {
|
|||||||
isThirdPartyPyTypedPresent(): boolean;
|
isThirdPartyPyTypedPresent(): boolean;
|
||||||
|
|
||||||
getIPythonMode(): IPythonMode;
|
getIPythonMode(): IPythonMode;
|
||||||
getFilePath(): string;
|
getUri(): Uri;
|
||||||
getFileContent(): string | undefined;
|
getFileContent(): string | undefined;
|
||||||
getRealFilePath(): string | undefined;
|
|
||||||
getClientVersion(): number | undefined;
|
getClientVersion(): number | undefined;
|
||||||
getOpenFileContents(): string | undefined;
|
getOpenFileContents(): string | undefined;
|
||||||
getModuleSymbolTable(): SymbolTable | undefined;
|
getModuleSymbolTable(): SymbolTable | undefined;
|
||||||
@ -73,7 +73,7 @@ export interface ServiceProvider {
|
|||||||
// Readonly wrapper around a Program. Makes sure it doesn't mutate the program.
|
// Readonly wrapper around a Program. Makes sure it doesn't mutate the program.
|
||||||
export interface ProgramView {
|
export interface ProgramView {
|
||||||
readonly id: string;
|
readonly id: string;
|
||||||
readonly rootPath: string;
|
readonly rootPath: Uri;
|
||||||
readonly console: ConsoleInterface;
|
readonly console: ConsoleInterface;
|
||||||
readonly evaluator: TypeEvaluator | undefined;
|
readonly evaluator: TypeEvaluator | undefined;
|
||||||
readonly configOptions: ConfigOptions;
|
readonly configOptions: ConfigOptions;
|
||||||
@ -81,21 +81,16 @@ export interface ProgramView {
|
|||||||
readonly fileSystem: ReadOnlyFileSystem;
|
readonly fileSystem: ReadOnlyFileSystem;
|
||||||
readonly serviceProvider: ServiceProvider;
|
readonly serviceProvider: ServiceProvider;
|
||||||
|
|
||||||
owns(file: string): boolean;
|
owns(uri: Uri): boolean;
|
||||||
getSourceFileInfoList(): readonly SourceFileInfo[];
|
getSourceFileInfoList(): readonly SourceFileInfo[];
|
||||||
getParseResults(filePath: string): ParseResults | undefined;
|
getParseResults(fileUri: Uri): ParseResults | undefined;
|
||||||
getSourceFileInfo(filePath: string): SourceFileInfo | undefined;
|
getSourceFileInfo(fileUri: Uri): SourceFileInfo | undefined;
|
||||||
getChainedFilePath(filePath: string): string | undefined;
|
getChainedUri(fileUri: Uri): Uri | undefined;
|
||||||
getSourceMapper(
|
getSourceMapper(fileUri: Uri, token: CancellationToken, mapCompiled?: boolean, preferStubs?: boolean): SourceMapper;
|
||||||
filePath: string,
|
|
||||||
token: CancellationToken,
|
|
||||||
mapCompiled?: boolean,
|
|
||||||
preferStubs?: boolean
|
|
||||||
): SourceMapper;
|
|
||||||
|
|
||||||
// Consider getDiagnosticsForRange to call `analyzeFile` automatically if the file is not analyzed.
|
// Consider getDiagnosticsForRange to call `analyzeFile` automatically if the file is not analyzed.
|
||||||
analyzeFile(filePath: string, token: CancellationToken): boolean;
|
analyzeFile(fileUri: Uri, token: CancellationToken): boolean;
|
||||||
getDiagnosticsForRange(filePath: string, range: Range): Diagnostic[];
|
getDiagnosticsForRange(fileUri: Uri, range: Range): Diagnostic[];
|
||||||
|
|
||||||
// See whether we can get rid of these methods
|
// See whether we can get rid of these methods
|
||||||
handleMemoryHighUsage(): void;
|
handleMemoryHighUsage(): void;
|
||||||
@ -106,9 +101,9 @@ export interface ProgramView {
|
|||||||
// and doesn't forward the request to the BG thread.
|
// and doesn't forward the request to the BG thread.
|
||||||
// One can use this when edits are temporary such as `runEditMode` or `test`
|
// One can use this when edits are temporary such as `runEditMode` or `test`
|
||||||
export interface EditableProgram extends ProgramView {
|
export interface EditableProgram extends ProgramView {
|
||||||
addInterimFile(file: string): void;
|
addInterimFile(uri: Uri): void;
|
||||||
setFileOpened(filePath: string, version: number | null, contents: string, options?: prog.OpenFileOptions): void;
|
setFileOpened(fileUri: Uri, version: number | null, contents: string, options?: prog.OpenFileOptions): void;
|
||||||
updateChainedFilePath(filePath: string, chainedFilePath: string | undefined): void;
|
updateChainedUri(fileUri: Uri, chainedUri: Uri | undefined): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mutable wrapper around a program. Allows the FG thread to forward this request to the BG thread
|
// Mutable wrapper around a program. Allows the FG thread to forward this request to the BG thread
|
||||||
@ -116,7 +111,7 @@ export interface EditableProgram extends ProgramView {
|
|||||||
export interface ProgramMutator {
|
export interface ProgramMutator {
|
||||||
addInterimFile(file: string): void;
|
addInterimFile(file: string): void;
|
||||||
setFileOpened(
|
setFileOpened(
|
||||||
filePath: string,
|
fileUri: Uri,
|
||||||
version: number | null,
|
version: number | null,
|
||||||
contents: string,
|
contents: string,
|
||||||
ipythonMode: IPythonMode,
|
ipythonMode: IPythonMode,
|
||||||
@ -162,7 +157,7 @@ export interface SymbolUsageProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface StatusMutationListener {
|
export interface StatusMutationListener {
|
||||||
fileDirty?: (filePath: string) => void;
|
fileDirty?: (fileUri: Uri) => void;
|
||||||
clearCache?: () => void;
|
clearCache?: () => void;
|
||||||
updateSettings?: <T extends ServerSettings>(settings: T) => void;
|
updateSettings?: <T extends ServerSettings>(settings: T) => void;
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
// * NOTE * except tests, this should be only file that import "fs"
|
// * NOTE * except tests, this should be only file that import "fs"
|
||||||
import type * as fs from 'fs';
|
import type * as fs from 'fs';
|
||||||
import { FileWatcher, FileWatcherEventHandler } from './fileWatcher';
|
import { FileWatcher, FileWatcherEventHandler } from './fileWatcher';
|
||||||
|
import { Uri } from './uri/uri';
|
||||||
|
|
||||||
export interface Stats {
|
export interface Stats {
|
||||||
size: number;
|
size: number;
|
||||||
@ -33,48 +34,47 @@ export interface MkDirOptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface ReadOnlyFileSystem {
|
export interface ReadOnlyFileSystem {
|
||||||
existsSync(path: string): boolean;
|
readonly isCaseSensitive: boolean;
|
||||||
chdir(path: string): void;
|
existsSync(uri: Uri): boolean;
|
||||||
readdirEntriesSync(path: string): fs.Dirent[];
|
chdir(uri: Uri): void;
|
||||||
readdirSync(path: string): string[];
|
readdirEntriesSync(uri: Uri): fs.Dirent[];
|
||||||
readFileSync(path: string, encoding?: null): Buffer;
|
readdirSync(uri: Uri): string[];
|
||||||
readFileSync(path: string, encoding: BufferEncoding): string;
|
readFileSync(uri: Uri, encoding?: null): Buffer;
|
||||||
readFileSync(path: string, encoding?: BufferEncoding | null): string | Buffer;
|
readFileSync(uri: Uri, encoding: BufferEncoding): string;
|
||||||
|
readFileSync(uri: Uri, encoding?: BufferEncoding | null): string | Buffer;
|
||||||
|
|
||||||
statSync(path: string): Stats;
|
statSync(uri: Uri): Stats;
|
||||||
realpathSync(path: string): string;
|
realpathSync(uri: Uri): Uri;
|
||||||
getModulePath(): string;
|
getModulePath(): Uri;
|
||||||
// Async I/O
|
// Async I/O
|
||||||
readFile(path: string): Promise<Buffer>;
|
readFile(uri: Uri): Promise<Buffer>;
|
||||||
readFileText(path: string, encoding?: BufferEncoding): Promise<string>;
|
readFileText(uri: Uri, encoding?: BufferEncoding): Promise<string>;
|
||||||
// Return path in casing on OS.
|
// Return path in casing on OS.
|
||||||
realCasePath(path: string): string;
|
realCasePath(uri: Uri): Uri;
|
||||||
|
|
||||||
// See whether the file is mapped to another location.
|
// See whether the file is mapped to another location.
|
||||||
isMappedFilePath(filepath: string): boolean;
|
isMappedUri(uri: Uri): boolean;
|
||||||
|
|
||||||
// Get original filepath if the given filepath is mapped.
|
// Get original uri if the given uri is mapped.
|
||||||
getOriginalFilePath(mappedFilePath: string): string;
|
getOriginalUri(mappedUri: Uri): Uri;
|
||||||
|
|
||||||
// Get mapped filepath if the given filepath is mapped.
|
// Get mapped uri if the given uri is mapped.
|
||||||
getMappedFilePath(originalFilepath: string): string;
|
getMappedUri(originalUri: Uri): Uri;
|
||||||
|
|
||||||
getUri(path: string): string;
|
isInZip(uri: Uri): boolean;
|
||||||
|
|
||||||
isInZip(path: string): boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FileSystem extends ReadOnlyFileSystem {
|
export interface FileSystem extends ReadOnlyFileSystem {
|
||||||
mkdirSync(path: string, options?: MkDirOptions): void;
|
mkdirSync(uri: Uri, options?: MkDirOptions): void;
|
||||||
writeFileSync(path: string, data: string | Buffer, encoding: BufferEncoding | null): void;
|
writeFileSync(uri: Uri, data: string | Buffer, encoding: BufferEncoding | null): void;
|
||||||
|
|
||||||
unlinkSync(path: string): void;
|
unlinkSync(uri: Uri): void;
|
||||||
rmdirSync(path: string): void;
|
rmdirSync(uri: Uri): void;
|
||||||
|
|
||||||
createFileSystemWatcher(paths: string[], listener: FileWatcherEventHandler): FileWatcher;
|
createFileSystemWatcher(uris: Uri[], listener: FileWatcherEventHandler): FileWatcher;
|
||||||
createReadStream(path: string): fs.ReadStream;
|
createReadStream(uri: Uri): fs.ReadStream;
|
||||||
createWriteStream(path: string): fs.WriteStream;
|
createWriteStream(uri: Uri): fs.WriteStream;
|
||||||
copyFileSync(src: string, dst: string): void;
|
copyFileSync(uri: Uri, dst: Uri): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TmpfileOptions {
|
export interface TmpfileOptions {
|
||||||
@ -84,8 +84,8 @@ export interface TmpfileOptions {
|
|||||||
|
|
||||||
export interface TempFile {
|
export interface TempFile {
|
||||||
// The directory returned by tmpdir must exist and be the same each time tmpdir is called.
|
// The directory returned by tmpdir must exist and be the same each time tmpdir is called.
|
||||||
tmpdir(): string;
|
tmpdir(): Uri;
|
||||||
tmpfile(options?: TmpfileOptions): string;
|
tmpfile(options?: TmpfileOptions): Uri;
|
||||||
dispose(): void;
|
dispose(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,16 +6,17 @@
|
|||||||
* file watcher related functionality.
|
* file watcher related functionality.
|
||||||
*/
|
*/
|
||||||
import { Stats } from './fileSystem';
|
import { Stats } from './fileSystem';
|
||||||
|
import { Uri } from './uri/uri';
|
||||||
|
|
||||||
export type FileWatcherEventType = 'add' | 'addDir' | 'change' | 'unlink' | 'unlinkDir';
|
export type FileWatcherEventType = 'add' | 'addDir' | 'change' | 'unlink' | 'unlinkDir';
|
||||||
export type FileWatcherEventHandler = (eventName: FileWatcherEventType, path: string, stats?: Stats) => void;
|
export type FileWatcherEventHandler = (eventName: FileWatcherEventType, uri: Uri, stats?: Stats) => void;
|
||||||
|
|
||||||
export interface FileWatcher {
|
export interface FileWatcher {
|
||||||
close(): void;
|
close(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FileWatcherHandler {
|
export interface FileWatcherHandler {
|
||||||
onFileChange(eventType: FileWatcherEventType, path: string): void;
|
onFileChange(eventType: FileWatcherEventType, uri: Uri): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FileWatcherProvider {
|
export interface FileWatcherProvider {
|
||||||
@ -23,7 +24,7 @@ export interface FileWatcherProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const nullFileWatcherHandler: FileWatcherHandler = {
|
export const nullFileWatcherHandler: FileWatcherHandler = {
|
||||||
onFileChange(_1: FileWatcherEventType, _2: string): void {
|
onFileChange(_1: FileWatcherEventType, _2: Uri): void {
|
||||||
// do nothing
|
// do nothing
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -13,10 +13,12 @@ import { PythonPathResult } from '../analyzer/pythonPathUtils';
|
|||||||
import { OperationCanceledException, throwIfCancellationRequested } from './cancellationUtils';
|
import { OperationCanceledException, throwIfCancellationRequested } from './cancellationUtils';
|
||||||
import { PythonPlatform } from './configOptions';
|
import { PythonPlatform } from './configOptions';
|
||||||
import { assertNever } from './debug';
|
import { assertNever } from './debug';
|
||||||
import { FileSystem } from './fileSystem';
|
|
||||||
import { HostKind, NoAccessHost, ScriptOutput } from './host';
|
import { HostKind, NoAccessHost, ScriptOutput } from './host';
|
||||||
import { isDirectory, normalizePath } from './pathUtils';
|
import { normalizePath } from './pathUtils';
|
||||||
import { PythonVersion, versionFromMajorMinor } from './pythonVersion';
|
import { PythonVersion, versionFromMajorMinor } from './pythonVersion';
|
||||||
|
import { ServiceProvider } from './serviceProvider';
|
||||||
|
import { Uri } from './uri/uri';
|
||||||
|
import { isDirectory } from './uri/uriUtils';
|
||||||
|
|
||||||
// preventLocalImports removes the working directory from sys.path.
|
// preventLocalImports removes the working directory from sys.path.
|
||||||
// The -c flag adds it automatically, which can allow some stdlib
|
// The -c flag adds it automatically, which can allow some stdlib
|
||||||
@ -60,7 +62,7 @@ export class LimitedAccessHost extends NoAccessHost {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class FullAccessHost extends LimitedAccessHost {
|
export class FullAccessHost extends LimitedAccessHost {
|
||||||
constructor(protected fs: FileSystem) {
|
constructor(protected serviceProvider: ServiceProvider) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,29 +70,29 @@ export class FullAccessHost extends LimitedAccessHost {
|
|||||||
return HostKind.FullAccess;
|
return HostKind.FullAccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
static createHost(kind: HostKind, fs: FileSystem) {
|
static createHost(kind: HostKind, serviceProvider: ServiceProvider) {
|
||||||
switch (kind) {
|
switch (kind) {
|
||||||
case HostKind.NoAccess:
|
case HostKind.NoAccess:
|
||||||
return new NoAccessHost();
|
return new NoAccessHost();
|
||||||
case HostKind.LimitedAccess:
|
case HostKind.LimitedAccess:
|
||||||
return new LimitedAccessHost();
|
return new LimitedAccessHost();
|
||||||
case HostKind.FullAccess:
|
case HostKind.FullAccess:
|
||||||
return new FullAccessHost(fs);
|
return new FullAccessHost(serviceProvider);
|
||||||
default:
|
default:
|
||||||
assertNever(kind);
|
assertNever(kind);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override getPythonSearchPaths(pythonPath?: string, logInfo?: string[]): PythonPathResult {
|
override getPythonSearchPaths(pythonPath?: Uri, logInfo?: string[]): PythonPathResult {
|
||||||
const importFailureInfo = logInfo ?? [];
|
const importFailureInfo = logInfo ?? [];
|
||||||
let result = this._executePythonInterpreter(pythonPath, (p) =>
|
let result = this._executePythonInterpreter(pythonPath?.getFilePath(), (p) =>
|
||||||
this._getSearchPathResultFromInterpreter(this.fs, p, importFailureInfo)
|
this._getSearchPathResultFromInterpreter(p, importFailureInfo)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
result = {
|
result = {
|
||||||
paths: [],
|
paths: [],
|
||||||
prefix: '',
|
prefix: undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,12 +104,12 @@ export class FullAccessHost extends LimitedAccessHost {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
override getPythonVersion(pythonPath?: string, logInfo?: string[]): PythonVersion | undefined {
|
override getPythonVersion(pythonPath?: Uri, logInfo?: string[]): PythonVersion | undefined {
|
||||||
const importFailureInfo = logInfo ?? [];
|
const importFailureInfo = logInfo ?? [];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const commandLineArgs: string[] = ['-c', extractVersion];
|
const commandLineArgs: string[] = ['-c', extractVersion];
|
||||||
const execOutput = this._executePythonInterpreter(pythonPath, (p) =>
|
const execOutput = this._executePythonInterpreter(pythonPath?.getFilePath(), (p) =>
|
||||||
child_process.execFileSync(p, commandLineArgs, { encoding: 'utf8' })
|
child_process.execFileSync(p, commandLineArgs, { encoding: 'utf8' })
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -128,8 +130,8 @@ export class FullAccessHost extends LimitedAccessHost {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override runScript(
|
override runScript(
|
||||||
pythonPath: string | undefined,
|
pythonPath: Uri | undefined,
|
||||||
script: string,
|
script: Uri,
|
||||||
args: string[],
|
args: string[],
|
||||||
cwd: string,
|
cwd: string,
|
||||||
token: CancellationToken
|
token: CancellationToken
|
||||||
@ -141,9 +143,9 @@ export class FullAccessHost extends LimitedAccessHost {
|
|||||||
return new Promise<{ stdout: string; stderr: string }>((resolve, reject) => {
|
return new Promise<{ stdout: string; stderr: string }>((resolve, reject) => {
|
||||||
let stdout = '';
|
let stdout = '';
|
||||||
let stderr = '';
|
let stderr = '';
|
||||||
const commandLineArgs = [script, ...args];
|
const commandLineArgs = [script.getFilePath(), ...args];
|
||||||
|
|
||||||
const child = this._executePythonInterpreter(pythonPath, (p) =>
|
const child = this._executePythonInterpreter(pythonPath?.getFilePath(), (p) =>
|
||||||
child_process.spawn(p, commandLineArgs, { cwd })
|
child_process.spawn(p, commandLineArgs, { cwd })
|
||||||
);
|
);
|
||||||
const tokenWatch = token.onCancellationRequested(() => {
|
const tokenWatch = token.onCancellationRequested(() => {
|
||||||
@ -210,20 +212,19 @@ export class FullAccessHost extends LimitedAccessHost {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _getSearchPathResultFromInterpreter(
|
private _getSearchPathResultFromInterpreter(
|
||||||
fs: FileSystem,
|
interpreterPath: string,
|
||||||
interpreter: string,
|
|
||||||
importFailureInfo: string[]
|
importFailureInfo: string[]
|
||||||
): PythonPathResult | undefined {
|
): PythonPathResult | undefined {
|
||||||
const result: PythonPathResult = {
|
const result: PythonPathResult = {
|
||||||
paths: [],
|
paths: [],
|
||||||
prefix: '',
|
prefix: undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const commandLineArgs: string[] = ['-c', extractSys];
|
const commandLineArgs: string[] = ['-c', extractSys];
|
||||||
|
importFailureInfo.push(`Executing interpreter: '${interpreterPath}'`);
|
||||||
importFailureInfo.push(`Executing interpreter: '${interpreter}'`);
|
const execOutput = child_process.execFileSync(interpreterPath, commandLineArgs, { encoding: 'utf8' });
|
||||||
const execOutput = child_process.execFileSync(interpreter, commandLineArgs, { encoding: 'utf8' });
|
const isCaseSensitive = this.serviceProvider.fs().isCaseSensitive;
|
||||||
|
|
||||||
// Parse the execOutput. It should be a JSON-encoded array of paths.
|
// Parse the execOutput. It should be a JSON-encoded array of paths.
|
||||||
try {
|
try {
|
||||||
@ -232,16 +233,20 @@ export class FullAccessHost extends LimitedAccessHost {
|
|||||||
execSplitEntry = execSplitEntry.trim();
|
execSplitEntry = execSplitEntry.trim();
|
||||||
if (execSplitEntry) {
|
if (execSplitEntry) {
|
||||||
const normalizedPath = normalizePath(execSplitEntry);
|
const normalizedPath = normalizePath(execSplitEntry);
|
||||||
|
const normalizedUri = Uri.file(normalizedPath, isCaseSensitive);
|
||||||
// Skip non-existent paths and broken zips/eggs.
|
// Skip non-existent paths and broken zips/eggs.
|
||||||
if (fs.existsSync(normalizedPath) && isDirectory(fs, normalizedPath)) {
|
if (
|
||||||
result.paths.push(normalizedPath);
|
this.serviceProvider.fs().existsSync(normalizedUri) &&
|
||||||
|
isDirectory(this.serviceProvider.fs(), normalizedUri)
|
||||||
|
) {
|
||||||
|
result.paths.push(normalizedUri);
|
||||||
} else {
|
} else {
|
||||||
importFailureInfo.push(`Skipping '${normalizedPath}' because it is not a valid directory`);
|
importFailureInfo.push(`Skipping '${normalizedPath}' because it is not a valid directory`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result.prefix = execSplit.prefix;
|
result.prefix = Uri.file(execSplit.prefix, isCaseSensitive);
|
||||||
|
|
||||||
if (result.paths.length === 0) {
|
if (result.paths.length === 0) {
|
||||||
importFailureInfo.push(`Found no valid directories`);
|
importFailureInfo.push(`Found no valid directories`);
|
||||||
|
@ -11,6 +11,7 @@ import { CancellationToken } from 'vscode-languageserver';
|
|||||||
import { PythonPathResult } from '../analyzer/pythonPathUtils';
|
import { PythonPathResult } from '../analyzer/pythonPathUtils';
|
||||||
import { PythonPlatform } from './configOptions';
|
import { PythonPlatform } from './configOptions';
|
||||||
import { PythonVersion } from './pythonVersion';
|
import { PythonVersion } from './pythonVersion';
|
||||||
|
import { Uri } from './uri/uri';
|
||||||
|
|
||||||
export const enum HostKind {
|
export const enum HostKind {
|
||||||
FullAccess,
|
FullAccess,
|
||||||
@ -25,12 +26,12 @@ export interface ScriptOutput {
|
|||||||
|
|
||||||
export interface Host {
|
export interface Host {
|
||||||
readonly kind: HostKind;
|
readonly kind: HostKind;
|
||||||
getPythonSearchPaths(pythonPath?: string, logInfo?: string[]): PythonPathResult;
|
getPythonSearchPaths(pythonPath?: Uri, logInfo?: string[]): PythonPathResult;
|
||||||
getPythonVersion(pythonPath?: string, logInfo?: string[]): PythonVersion | undefined;
|
getPythonVersion(pythonPath?: Uri, logInfo?: string[]): PythonVersion | undefined;
|
||||||
getPythonPlatform(logInfo?: string[]): PythonPlatform | undefined;
|
getPythonPlatform(logInfo?: string[]): PythonPlatform | undefined;
|
||||||
runScript(
|
runScript(
|
||||||
pythonPath: string | undefined,
|
pythonPath: Uri | undefined,
|
||||||
script: string,
|
script: Uri,
|
||||||
args: string[],
|
args: string[],
|
||||||
cwd: string,
|
cwd: string,
|
||||||
token: CancellationToken
|
token: CancellationToken
|
||||||
@ -42,16 +43,16 @@ export class NoAccessHost implements Host {
|
|||||||
return HostKind.NoAccess;
|
return HostKind.NoAccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
getPythonSearchPaths(pythonPath?: string, logInfo?: string[]): PythonPathResult {
|
getPythonSearchPaths(pythonPath?: Uri, logInfo?: string[]): PythonPathResult {
|
||||||
logInfo?.push('No access to python executable.');
|
logInfo?.push('No access to python executable.');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
paths: [],
|
paths: [],
|
||||||
prefix: '',
|
prefix: undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
getPythonVersion(pythonPath?: string, logInfo?: string[]): PythonVersion | undefined {
|
getPythonVersion(pythonPath?: Uri, logInfo?: string[]): PythonVersion | undefined {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,8 +61,8 @@ export class NoAccessHost implements Host {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async runScript(
|
async runScript(
|
||||||
pythonPath: string | undefined,
|
pythonPath: Uri | undefined,
|
||||||
scriptPath: string,
|
scriptPath: Uri,
|
||||||
args: string[],
|
args: string[],
|
||||||
cwd: string,
|
cwd: string,
|
||||||
token: CancellationToken
|
token: CancellationToken
|
||||||
|
@ -9,16 +9,17 @@
|
|||||||
import { ConsoleInterface, LogLevel } from './console';
|
import { ConsoleInterface, LogLevel } from './console';
|
||||||
import { ReadOnlyFileSystem } from './fileSystem';
|
import { ReadOnlyFileSystem } from './fileSystem';
|
||||||
import { Duration, timingStats } from './timing';
|
import { Duration, timingStats } from './timing';
|
||||||
|
import { Uri } from './uri/uri';
|
||||||
|
|
||||||
// Consider an operation "long running" if it goes longer than this.
|
// Consider an operation "long running" if it goes longer than this.
|
||||||
const durationThresholdForInfoInMs = 2000;
|
const durationThresholdForInfoInMs = 2000;
|
||||||
|
|
||||||
export function getPathForLogging(fs: ReadOnlyFileSystem, filepath: string) {
|
export function getPathForLogging(fs: ReadOnlyFileSystem, fileUri: Uri) {
|
||||||
if (fs.isMappedFilePath(filepath)) {
|
if (fs.isMappedUri(fileUri)) {
|
||||||
return fs.getOriginalFilePath(filepath);
|
return fs.getOriginalUri(fileUri);
|
||||||
}
|
}
|
||||||
|
|
||||||
return filepath;
|
return fileUri;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class LogTracker {
|
export class LogTracker {
|
||||||
|
@ -7,24 +7,14 @@
|
|||||||
* Pathname utility functions.
|
* Pathname utility functions.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { Dirent } from 'fs';
|
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import { URI, Utils } from 'vscode-uri';
|
|
||||||
|
|
||||||
import { Char } from './charCodes';
|
import { Char } from './charCodes';
|
||||||
import { some } from './collectionUtils';
|
import { some } from './collectionUtils';
|
||||||
import { GetCanonicalFileName, identity } from './core';
|
import { GetCanonicalFileName, identity } from './core';
|
||||||
import { randomBytesHex } from './crypto';
|
|
||||||
import * as debug from './debug';
|
import * as debug from './debug';
|
||||||
import { ServiceProvider } from './extensibility';
|
|
||||||
import { FileSystem, ReadOnlyFileSystem, Stats, TempFile } from './fileSystem';
|
|
||||||
import { ServiceKeys } from './serviceProviderExtensions';
|
|
||||||
import { equateStringsCaseInsensitive, equateStringsCaseSensitive } from './stringUtils';
|
import { equateStringsCaseInsensitive, equateStringsCaseSensitive } from './stringUtils';
|
||||||
|
|
||||||
let _fsCaseSensitivity: boolean | undefined = undefined;
|
|
||||||
let _underTest: boolean = false;
|
|
||||||
const _uriSchemePattern = /^\w[\w\d+.-]*$/;
|
|
||||||
|
|
||||||
export interface FileSpec {
|
export interface FileSpec {
|
||||||
// File specs can contain wildcard characters (**, *, ?). This
|
// File specs can contain wildcard characters (**, *, ?). This
|
||||||
// specifies the first portion of the file spec that contains
|
// specifies the first portion of the file spec that contains
|
||||||
@ -72,52 +62,26 @@ export interface FileSystemEntries {
|
|||||||
directories: string[];
|
directories: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function forEachAncestorDirectory(
|
|
||||||
directory: string,
|
|
||||||
callback: (directory: string) => string | undefined
|
|
||||||
): string | undefined {
|
|
||||||
while (true) {
|
|
||||||
const result = callback(directory);
|
|
||||||
if (result !== undefined) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
const parentPath = getDirectoryPath(directory);
|
|
||||||
if (parentPath === directory) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
directory = parentPath;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getDirectoryPath(pathString: string): string {
|
export function getDirectoryPath(pathString: string): string {
|
||||||
if (isUri(pathString)) {
|
|
||||||
return Utils.dirname(URI.parse(pathString).with({ fragment: '', query: '' })).toString();
|
|
||||||
}
|
|
||||||
return pathString.substr(0, Math.max(getRootLength(pathString), pathString.lastIndexOf(path.sep)));
|
return pathString.substr(0, Math.max(getRootLength(pathString), pathString.lastIndexOf(path.sep)));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isUri(pathString: string) {
|
|
||||||
return pathString.indexOf(':') > 1 && _uriSchemePattern.test(pathString.split(':')[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns length of the root part of a path or URL (i.e. length of "/", "x:/", "//server/").
|
* Returns length of the root part of a path or URL (i.e. length of "/", "x:/", "//server/").
|
||||||
*/
|
*/
|
||||||
export function getRootLength(pathString: string, checkUri = true): number {
|
export function getRootLength(pathString: string, sep = path.sep): number {
|
||||||
if (pathString.charAt(0) === path.sep) {
|
if (pathString.charAt(0) === sep) {
|
||||||
if (pathString.charAt(1) !== path.sep) {
|
if (pathString.charAt(1) !== sep) {
|
||||||
return 1; // POSIX: "/" (or non-normalized "\")
|
return 1; // POSIX: "/" (or non-normalized "\")
|
||||||
}
|
}
|
||||||
const p1 = pathString.indexOf(path.sep, 2);
|
const p1 = pathString.indexOf(sep, 2);
|
||||||
if (p1 < 0) {
|
if (p1 < 0) {
|
||||||
return pathString.length; // UNC: "//server" or "\\server"
|
return pathString.length; // UNC: "//server" or "\\server"
|
||||||
}
|
}
|
||||||
return p1 + 1; // UNC: "//server/" or "\\server\"
|
return p1 + 1; // UNC: "//server/" or "\\server\"
|
||||||
}
|
}
|
||||||
if (pathString.charAt(1) === ':') {
|
if (pathString.charAt(1) === ':') {
|
||||||
if (pathString.charAt(2) === path.sep) {
|
if (pathString.charAt(2) === sep) {
|
||||||
return 3; // DOS: "c:/" or "c:\"
|
return 3; // DOS: "c:/" or "c:\"
|
||||||
}
|
}
|
||||||
if (pathString.length === 2) {
|
if (pathString.length === 2) {
|
||||||
@ -125,25 +89,16 @@ export function getRootLength(pathString: string, checkUri = true): number {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (checkUri && isUri(pathString)) {
|
|
||||||
const uri = URI.parse(pathString);
|
|
||||||
if (uri.authority) {
|
|
||||||
return uri.scheme.length + 3; // URI: "file://"
|
|
||||||
} else {
|
|
||||||
return uri.scheme.length + 1; // URI: "untitled:"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getPathSeparator(pathString: string) {
|
export function getPathSeparator(pathString: string) {
|
||||||
return isUri(pathString) ? '/' : path.sep;
|
return path.sep;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getPathComponents(pathString: string) {
|
export function getPathComponents(pathString: string) {
|
||||||
const normalizedPath = normalizeSlashes(pathString);
|
const normalizedPath = normalizeSlashes(pathString);
|
||||||
const rootLength = getRootLength(normalizedPath, /* checkUri */ isUri(normalizedPath));
|
const rootLength = getRootLength(normalizedPath);
|
||||||
const root = normalizedPath.substring(0, rootLength);
|
const root = normalizedPath.substring(0, rootLength);
|
||||||
const sep = getPathSeparator(pathString);
|
const sep = getPathSeparator(pathString);
|
||||||
const rest = normalizedPath.substring(rootLength).split(sep);
|
const rest = normalizedPath.substring(rootLength).split(sep);
|
||||||
@ -211,47 +166,11 @@ export function getRelativePath(dirPath: string, relativeTo: string) {
|
|||||||
return relativePath;
|
return relativePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates a directory hierarchy for a path, starting from some ancestor path.
|
|
||||||
export function makeDirectories(fs: FileSystem, dirPath: string, startingFromDirPath: string) {
|
|
||||||
if (!dirPath.startsWith(startingFromDirPath)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const pathComponents = getPathComponents(dirPath);
|
|
||||||
const relativeToComponents = getPathComponents(startingFromDirPath);
|
|
||||||
let curPath = startingFromDirPath;
|
|
||||||
|
|
||||||
for (let i = relativeToComponents.length; i < pathComponents.length; i++) {
|
|
||||||
curPath = combinePaths(curPath, pathComponents[i]);
|
|
||||||
if (!fs.existsSync(curPath)) {
|
|
||||||
fs.mkdirSync(curPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getFileSize(fs: ReadOnlyFileSystem, path: string) {
|
|
||||||
const stat = tryStat(fs, path);
|
|
||||||
if (stat?.isFile()) {
|
|
||||||
return stat.size;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function fileExists(fs: ReadOnlyFileSystem, path: string): boolean {
|
|
||||||
return fileSystemEntryExists(fs, path, FileSystemEntryKind.File);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function directoryExists(fs: ReadOnlyFileSystem, path: string): boolean {
|
|
||||||
return fileSystemEntryExists(fs, path, FileSystemEntryKind.Directory);
|
|
||||||
}
|
|
||||||
|
|
||||||
const getInvalidSeparator = (sep: string) => (sep === '/' ? '\\' : '/');
|
const getInvalidSeparator = (sep: string) => (sep === '/' ? '\\' : '/');
|
||||||
export function normalizeSlashes(pathString: string, sep = path.sep): string {
|
export function normalizeSlashes(pathString: string, sep = path.sep): string {
|
||||||
if (!isUri(pathString)) {
|
if (pathString.includes(getInvalidSeparator(sep))) {
|
||||||
if (pathString.includes(getInvalidSeparator(sep))) {
|
const separatorRegExp = /[\\/]/g;
|
||||||
const separatorRegExp = /[\\/]/g;
|
return pathString.replace(separatorRegExp, sep);
|
||||||
return pathString.replace(separatorRegExp, sep);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return pathString;
|
return pathString;
|
||||||
@ -271,7 +190,7 @@ export function resolvePaths(path: string, ...paths: (string | undefined)[]): st
|
|||||||
return normalizePath(some(paths) ? combinePaths(path, ...paths) : normalizeSlashes(path));
|
return normalizePath(some(paths) ? combinePaths(path, ...paths) : normalizeSlashes(path));
|
||||||
}
|
}
|
||||||
|
|
||||||
function combineFilePaths(pathString: string, ...paths: (string | undefined)[]): string {
|
export function combinePaths(pathString: string, ...paths: (string | undefined)[]): string {
|
||||||
if (pathString) {
|
if (pathString) {
|
||||||
pathString = normalizeSlashes(pathString);
|
pathString = normalizeSlashes(pathString);
|
||||||
}
|
}
|
||||||
@ -283,7 +202,7 @@ function combineFilePaths(pathString: string, ...paths: (string | undefined)[]):
|
|||||||
|
|
||||||
relativePath = normalizeSlashes(relativePath);
|
relativePath = normalizeSlashes(relativePath);
|
||||||
|
|
||||||
if (!pathString || getRootLength(relativePath, /* checkUri */ false) !== 0) {
|
if (!pathString || getRootLength(relativePath) !== 0) {
|
||||||
pathString = relativePath;
|
pathString = relativePath;
|
||||||
} else {
|
} else {
|
||||||
pathString = ensureTrailingDirectorySeparator(pathString) + relativePath;
|
pathString = ensureTrailingDirectorySeparator(pathString) + relativePath;
|
||||||
@ -293,29 +212,6 @@ function combineFilePaths(pathString: string, ...paths: (string | undefined)[]):
|
|||||||
return pathString;
|
return pathString;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function combinePaths(pathString: string, ...paths: (string | undefined)[]): string {
|
|
||||||
if (!isUri(pathString)) {
|
|
||||||
// Not a URI, or a URI with a single letter scheme.
|
|
||||||
return combineFilePaths(pathString, ...paths);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Go through the paths to see if any are rooted. If so, treat as
|
|
||||||
// a file path. On linux this might be wrong if a path starts with '/'.
|
|
||||||
if (some(paths, (p) => !!p && getRootLength(p, /* checkUri */ false) !== 0)) {
|
|
||||||
return combineFilePaths(pathString, ...paths);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise this is a URI
|
|
||||||
const nonEmptyPaths = paths.filter((p) => !!p) as string[];
|
|
||||||
const uri = URI.parse(pathString);
|
|
||||||
|
|
||||||
// Make sure we have a path to append to.
|
|
||||||
if (uri.path === '' || uri.path === undefined) {
|
|
||||||
nonEmptyPaths.unshift('/');
|
|
||||||
}
|
|
||||||
return Utils.joinPath(uri.with({ fragment: '', query: '' }), ...nonEmptyPaths).toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines whether a `parent` path contains a `child` path using the provide case sensitivity.
|
* Determines whether a `parent` path contains a `child` path using the provide case sensitivity.
|
||||||
*/
|
*/
|
||||||
@ -485,9 +381,7 @@ export function getBaseFileName(pathString: string, extensions?: string | readon
|
|||||||
// return the trailing portion of the path starting after the last (non-terminal) directory
|
// return the trailing portion of the path starting after the last (non-terminal) directory
|
||||||
// separator but not including any trailing directory separator.
|
// separator but not including any trailing directory separator.
|
||||||
pathString = stripTrailingDirectorySeparator(pathString);
|
pathString = stripTrailingDirectorySeparator(pathString);
|
||||||
const name = isUri(pathString)
|
const name = pathString.slice(Math.max(getRootLength(pathString), pathString.lastIndexOf(path.sep) + 1));
|
||||||
? Utils.basename(URI.parse(pathString).with({ fragment: '', query: '' }))
|
|
||||||
: pathString.slice(Math.max(getRootLength(pathString), pathString.lastIndexOf(path.sep) + 1));
|
|
||||||
const extension =
|
const extension =
|
||||||
extensions !== undefined && ignoreCase !== undefined
|
extensions !== undefined && ignoreCase !== undefined
|
||||||
? getAnyExtensionFromPath(name, extensions, ignoreCase)
|
? getAnyExtensionFromPath(name, extensions, ignoreCase)
|
||||||
@ -561,7 +455,7 @@ export function stripTrailingDirectorySeparator(pathString: string) {
|
|||||||
if (!hasTrailingDirectorySeparator(pathString)) {
|
if (!hasTrailingDirectorySeparator(pathString)) {
|
||||||
return pathString;
|
return pathString;
|
||||||
}
|
}
|
||||||
return pathString.substr(0, pathString.length - 1);
|
return pathString.slice(0, pathString.length - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getFileExtension(fileName: string, multiDotExtension = false) {
|
export function getFileExtension(fileName: string, multiDotExtension = false) {
|
||||||
@ -571,7 +465,7 @@ export function getFileExtension(fileName: string, multiDotExtension = false) {
|
|||||||
|
|
||||||
fileName = getFileName(fileName);
|
fileName = getFileName(fileName);
|
||||||
const firstDotIndex = fileName.indexOf('.');
|
const firstDotIndex = fileName.indexOf('.');
|
||||||
return fileName.substr(firstDotIndex);
|
return fileName.slice(firstDotIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getFileName(pathString: string) {
|
export function getFileName(pathString: string) {
|
||||||
@ -592,102 +486,8 @@ export function stripFileExtension(fileName: string, multiDotExtension = false)
|
|||||||
return fileName.substr(0, fileName.length - ext.length);
|
return fileName.substr(0, fileName.length - ext.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function realCasePath(pathString: string, fileSystem: ReadOnlyFileSystem): string {
|
|
||||||
return isUri(pathString) ? pathString : fileSystem.realCasePath(pathString);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function normalizePath(pathString: string): string {
|
export function normalizePath(pathString: string): string {
|
||||||
if (!isUri(pathString)) {
|
return normalizeSlashes(path.normalize(pathString));
|
||||||
return normalizeSlashes(path.normalize(pathString));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Must be a URI, already normalized.
|
|
||||||
return pathString;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isDirectory(fs: ReadOnlyFileSystem, path: string): boolean {
|
|
||||||
return tryStat(fs, path)?.isDirectory() ?? false;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isFile(fs: ReadOnlyFileSystem, path: string, treatZipDirectoryAsFile = false): boolean {
|
|
||||||
const stats = tryStat(fs, path);
|
|
||||||
if (stats?.isFile()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!treatZipDirectoryAsFile) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return stats?.isZipDirectory?.() ?? false;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function tryStat(fs: ReadOnlyFileSystem, path: string): Stats | undefined {
|
|
||||||
try {
|
|
||||||
if (fs.existsSync(path)) {
|
|
||||||
return fs.statSync(path);
|
|
||||||
}
|
|
||||||
} catch (e: any) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function tryRealpath(fs: ReadOnlyFileSystem, path: string): string | undefined {
|
|
||||||
try {
|
|
||||||
return fs.realCasePath(path);
|
|
||||||
} catch (e: any) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getFileSystemEntries(fs: ReadOnlyFileSystem, path: string): FileSystemEntries {
|
|
||||||
try {
|
|
||||||
return getFileSystemEntriesFromDirEntries(fs.readdirEntriesSync(path || '.'), fs, path);
|
|
||||||
} catch (e: any) {
|
|
||||||
return { files: [], directories: [] };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sorts the entires into files and directories, including any symbolic links.
|
|
||||||
export function getFileSystemEntriesFromDirEntries(
|
|
||||||
dirEntries: Dirent[],
|
|
||||||
fs: ReadOnlyFileSystem,
|
|
||||||
path: string
|
|
||||||
): FileSystemEntries {
|
|
||||||
const entries = dirEntries.sort((a, b) => {
|
|
||||||
if (a.name < b.name) {
|
|
||||||
return -1;
|
|
||||||
} else if (a.name > b.name) {
|
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const files: string[] = [];
|
|
||||||
const directories: string[] = [];
|
|
||||||
for (const entry of entries) {
|
|
||||||
// This is necessary because on some file system node fails to exclude
|
|
||||||
// "." and "..". See https://github.com/nodejs/node/issues/4002
|
|
||||||
if (entry.name === '.' || entry.name === '..') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entry.isFile()) {
|
|
||||||
files.push(entry.name);
|
|
||||||
} else if (entry.isDirectory()) {
|
|
||||||
directories.push(entry.name);
|
|
||||||
} else if (entry.isSymbolicLink()) {
|
|
||||||
const entryPath = combinePaths(path, entry.name);
|
|
||||||
const stat = tryStat(fs, entryPath);
|
|
||||||
if (stat?.isFile()) {
|
|
||||||
files.push(entry.name);
|
|
||||||
} else if (stat?.isDirectory()) {
|
|
||||||
directories.push(entry.name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return { files, directories };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transforms a relative file spec (one that potentially contains
|
// Transforms a relative file spec (one that potentially contains
|
||||||
@ -803,25 +603,6 @@ export function hasPythonExtension(path: string) {
|
|||||||
return path.endsWith('.py') || path.endsWith('.pyi');
|
return path.endsWith('.py') || path.endsWith('.pyi');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getFileSpec(sp: ServiceProvider, rootPath: string, fileSpec: string): FileSpec {
|
|
||||||
let regExPattern = getWildcardRegexPattern(rootPath, fileSpec);
|
|
||||||
const escapedSeparator = getRegexEscapedSeparator(getPathSeparator(rootPath));
|
|
||||||
regExPattern = `^(${regExPattern})($|${escapedSeparator})`;
|
|
||||||
|
|
||||||
const fs = sp.get(ServiceKeys.fs);
|
|
||||||
const tmp = sp.tryGet(ServiceKeys.tempFile);
|
|
||||||
|
|
||||||
const regExp = new RegExp(regExPattern, isFileSystemCaseSensitive(fs, tmp) ? undefined : 'i');
|
|
||||||
const wildcardRoot = getWildcardRoot(rootPath, fileSpec);
|
|
||||||
const hasDirectoryWildcard = isDirectoryWildcardPatternPresent(fileSpec);
|
|
||||||
|
|
||||||
return {
|
|
||||||
wildcardRoot,
|
|
||||||
regExp,
|
|
||||||
hasDirectoryWildcard,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getRegexEscapedSeparator(pathSep: string = path.sep) {
|
export function getRegexEscapedSeparator(pathSep: string = path.sep) {
|
||||||
// we don't need to escape "/" in typescript regular expression
|
// we don't need to escape "/" in typescript regular expression
|
||||||
return pathSep === '/' ? '/' : '\\\\';
|
return pathSep === '/' ? '/' : '\\\\';
|
||||||
@ -908,168 +689,3 @@ function getPathComponentsRelativeTo(
|
|||||||
}
|
}
|
||||||
return ['', ...relative, ...components];
|
return ['', ...relative, ...components];
|
||||||
}
|
}
|
||||||
|
|
||||||
const enum FileSystemEntryKind {
|
|
||||||
File,
|
|
||||||
Directory,
|
|
||||||
}
|
|
||||||
|
|
||||||
function fileSystemEntryExists(fs: ReadOnlyFileSystem, path: string, entryKind: FileSystemEntryKind): boolean {
|
|
||||||
try {
|
|
||||||
const stat = fs.statSync(path);
|
|
||||||
switch (entryKind) {
|
|
||||||
case FileSystemEntryKind.File:
|
|
||||||
return stat.isFile();
|
|
||||||
case FileSystemEntryKind.Directory:
|
|
||||||
return stat.isDirectory();
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} catch (e: any) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function convertUriToPath(fs: ReadOnlyFileSystem, uriString: string): string {
|
|
||||||
return realCasePath(fs.getMappedFilePath(extractPathFromUri(uriString)), fs);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function extractPathFromUri(uriString: string) {
|
|
||||||
const uri = URI.parse(uriString);
|
|
||||||
|
|
||||||
// Only for file scheme do we actually modify anything. All other uri strings
|
|
||||||
// maintain the same value they started with.
|
|
||||||
if (uri.scheme === 'file' && !uri.fragment) {
|
|
||||||
// When schema is "file", we use fsPath so that we can handle things like UNC paths.
|
|
||||||
let convertedPath = normalizePath(uri.fsPath);
|
|
||||||
|
|
||||||
// If this is a DOS-style path with a drive letter, remove
|
|
||||||
// the leading slash.
|
|
||||||
if (convertedPath.match(/^\\[a-zA-Z]:\\/)) {
|
|
||||||
convertedPath = convertedPath.slice(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return convertedPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
return uriString;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function convertPathToUri(fs: ReadOnlyFileSystem, path: string): string {
|
|
||||||
return fs.getUri(fs.getOriginalFilePath(path));
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setTestingMode(underTest: boolean) {
|
|
||||||
_underTest = underTest;
|
|
||||||
}
|
|
||||||
|
|
||||||
const isFileSystemCaseSensitiveMap = new WeakMap<FileSystem, boolean>();
|
|
||||||
|
|
||||||
export function isFileSystemCaseSensitive(fs: FileSystem, tmp?: TempFile) {
|
|
||||||
if (!tmp) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_underTest && _fsCaseSensitivity !== undefined) {
|
|
||||||
return _fsCaseSensitivity;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isFileSystemCaseSensitiveMap.has(fs)) {
|
|
||||||
_fsCaseSensitivity = isFileSystemCaseSensitiveInternal(fs, tmp);
|
|
||||||
isFileSystemCaseSensitiveMap.set(fs, _fsCaseSensitivity);
|
|
||||||
}
|
|
||||||
return !!isFileSystemCaseSensitiveMap.get(fs);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isFileSystemCaseSensitiveInternal(fs: FileSystem, tmp: TempFile) {
|
|
||||||
let filePath: string | undefined = undefined;
|
|
||||||
try {
|
|
||||||
// Make unique file name.
|
|
||||||
let name: string;
|
|
||||||
let mangledFilePath: string;
|
|
||||||
do {
|
|
||||||
name = `${randomBytesHex(21)}-a`;
|
|
||||||
filePath = path.join(tmp.tmpdir(), name);
|
|
||||||
mangledFilePath = path.join(tmp.tmpdir(), name.toUpperCase());
|
|
||||||
} while (fs.existsSync(filePath) || fs.existsSync(mangledFilePath));
|
|
||||||
|
|
||||||
fs.writeFileSync(filePath, '', 'utf8');
|
|
||||||
|
|
||||||
// If file exists, then it is insensitive.
|
|
||||||
return !fs.existsSync(mangledFilePath);
|
|
||||||
} catch (e: any) {
|
|
||||||
return false;
|
|
||||||
} finally {
|
|
||||||
if (filePath) {
|
|
||||||
// remove temp file created
|
|
||||||
try {
|
|
||||||
fs.unlinkSync(filePath);
|
|
||||||
} catch (e: any) {
|
|
||||||
/* ignored */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getLibraryPathWithoutExtension(libraryFilePath: string) {
|
|
||||||
let filePathWithoutExtension = stripFileExtension(libraryFilePath);
|
|
||||||
|
|
||||||
// Strip off the '/__init__' if it's present.
|
|
||||||
if (filePathWithoutExtension.endsWith('__init__')) {
|
|
||||||
filePathWithoutExtension = filePathWithoutExtension.substr(0, filePathWithoutExtension.length - 9);
|
|
||||||
}
|
|
||||||
|
|
||||||
return filePathWithoutExtension;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getDirectoryChangeKind(
|
|
||||||
fs: ReadOnlyFileSystem,
|
|
||||||
oldDirectory: string,
|
|
||||||
newDirectory: string
|
|
||||||
): 'Same' | 'Renamed' | 'Moved' {
|
|
||||||
if (fs.realCasePath(oldDirectory) === fs.realCasePath(newDirectory)) {
|
|
||||||
return 'Same';
|
|
||||||
}
|
|
||||||
|
|
||||||
const relativePaths = getRelativePathComponentsFromDirectory(oldDirectory, newDirectory, (f) => fs.realCasePath(f));
|
|
||||||
|
|
||||||
// 3 means only last folder name has changed.
|
|
||||||
if (relativePaths.length === 3 && relativePaths[1] === '..' && relativePaths[2] !== '..') {
|
|
||||||
return 'Renamed';
|
|
||||||
}
|
|
||||||
|
|
||||||
return 'Moved';
|
|
||||||
}
|
|
||||||
|
|
||||||
export function deduplicateFolders(listOfFolders: string[][]): string[] {
|
|
||||||
const foldersToWatch = new Set<string>();
|
|
||||||
|
|
||||||
listOfFolders.forEach((folders) => {
|
|
||||||
folders.forEach((p) => {
|
|
||||||
if (foldersToWatch.has(p)) {
|
|
||||||
// Bail out on exact match.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const existing of foldersToWatch) {
|
|
||||||
// ex) p: "/user/test" existing: "/user"
|
|
||||||
if (p.startsWith(existing)) {
|
|
||||||
// We already have the parent folder in the watch list
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ex) p: "/user" folderToWatch: "/user/test"
|
|
||||||
if (existing.startsWith(p)) {
|
|
||||||
// We found better one to watch. replace.
|
|
||||||
foldersToWatch.delete(existing);
|
|
||||||
foldersToWatch.add(p);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foldersToWatch.add(p);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return [...foldersToWatch];
|
|
||||||
}
|
|
||||||
|
@ -8,7 +8,6 @@ import { FakeFS, NativePath, PortablePath, PosixFS, ppath, VirtualFS, ZipFS, Zip
|
|||||||
import { getLibzipSync } from '@yarnpkg/libzip';
|
import { getLibzipSync } from '@yarnpkg/libzip';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as tmp from 'tmp';
|
import * as tmp from 'tmp';
|
||||||
import { URI } from 'vscode-uri';
|
|
||||||
import { isMainThread } from 'worker_threads';
|
import { isMainThread } from 'worker_threads';
|
||||||
|
|
||||||
import { ConsoleInterface, NullConsole } from './console';
|
import { ConsoleInterface, NullConsole } from './console';
|
||||||
@ -21,7 +20,9 @@ import {
|
|||||||
FileWatcherProvider,
|
FileWatcherProvider,
|
||||||
nullFileWatcherProvider,
|
nullFileWatcherProvider,
|
||||||
} from './fileWatcher';
|
} from './fileWatcher';
|
||||||
import { combinePaths, getRootLength, isUri } from './pathUtils';
|
import { getRootLength } from './pathUtils';
|
||||||
|
import { Uri } from './uri/uri';
|
||||||
|
import { getRootUri, isFileSystemCaseSensitive } from './uri/uriUtils';
|
||||||
|
|
||||||
// Automatically remove files created by tmp at process exit.
|
// Automatically remove files created by tmp at process exit.
|
||||||
tmp.setGracefulCleanup();
|
tmp.setGracefulCleanup();
|
||||||
@ -208,9 +209,19 @@ class YarnFS extends PosixFS {
|
|||||||
const yarnFS = new YarnFS();
|
const yarnFS = new YarnFS();
|
||||||
|
|
||||||
class RealFileSystem implements FileSystem {
|
class RealFileSystem implements FileSystem {
|
||||||
constructor(private _fileWatcherProvider: FileWatcherProvider, private _console: ConsoleInterface) {}
|
private _isCaseSensitive = true;
|
||||||
|
constructor(private _fileWatcherProvider: FileWatcherProvider, private _console: ConsoleInterface) {
|
||||||
|
this._isCaseSensitive = isFileSystemCaseSensitive(this, new RealTempFile(/* isCaseSensitive */ true));
|
||||||
|
}
|
||||||
|
|
||||||
existsSync(path: string) {
|
get isCaseSensitive(): boolean {
|
||||||
|
return this._isCaseSensitive;
|
||||||
|
}
|
||||||
|
existsSync(uri: Uri) {
|
||||||
|
if (uri.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const path = uri.getFilePath();
|
||||||
try {
|
try {
|
||||||
// Catch zip open errors. existsSync is assumed to never throw by callers.
|
// Catch zip open errors. existsSync is assumed to never throw by callers.
|
||||||
return yarnFS.existsSync(path);
|
return yarnFS.existsSync(path);
|
||||||
@ -219,11 +230,13 @@ class RealFileSystem implements FileSystem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mkdirSync(path: string, options?: MkDirOptions) {
|
mkdirSync(uri: Uri, options?: MkDirOptions) {
|
||||||
|
const path = uri.getFilePath();
|
||||||
yarnFS.mkdirSync(path, options);
|
yarnFS.mkdirSync(path, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
chdir(path: string) {
|
chdir(uri: Uri) {
|
||||||
|
const path = uri.getFilePath();
|
||||||
// If this file system happens to be running in a worker thread,
|
// If this file system happens to be running in a worker thread,
|
||||||
// then we can't call 'chdir'.
|
// then we can't call 'chdir'.
|
||||||
if (isMainThread) {
|
if (isMainThread) {
|
||||||
@ -231,11 +244,13 @@ class RealFileSystem implements FileSystem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
readdirSync(path: string): string[] {
|
readdirSync(uri: Uri): string[] {
|
||||||
|
const path = uri.getFilePath();
|
||||||
return yarnFS.readdirSync(path);
|
return yarnFS.readdirSync(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
readdirEntriesSync(path: string): fs.Dirent[] {
|
readdirEntriesSync(uri: Uri): fs.Dirent[] {
|
||||||
|
const path = uri.getFilePath();
|
||||||
return yarnFS.readdirSync(path, { withFileTypes: true }).map((entry): fs.Dirent => {
|
return yarnFS.readdirSync(path, { withFileTypes: true }).map((entry): fs.Dirent => {
|
||||||
// Treat zip/egg files as directories.
|
// Treat zip/egg files as directories.
|
||||||
// See: https://github.com/yarnpkg/berry/blob/master/packages/vscode-zipfs/sources/ZipFSProvider.ts
|
// See: https://github.com/yarnpkg/berry/blob/master/packages/vscode-zipfs/sources/ZipFSProvider.ts
|
||||||
@ -257,21 +272,24 @@ class RealFileSystem implements FileSystem {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
readFileSync(path: string, encoding?: null): Buffer;
|
readFileSync(uri: Uri, encoding?: null): Buffer;
|
||||||
readFileSync(path: string, encoding: BufferEncoding): string;
|
readFileSync(uri: Uri, encoding: BufferEncoding): string;
|
||||||
readFileSync(path: string, encoding?: BufferEncoding | null): Buffer | string;
|
readFileSync(uri: Uri, encoding?: BufferEncoding | null): Buffer | string;
|
||||||
readFileSync(path: string, encoding: BufferEncoding | null = null) {
|
readFileSync(uri: Uri, encoding: BufferEncoding | null = null) {
|
||||||
|
const path = uri.getFilePath();
|
||||||
if (encoding === 'utf8' || encoding === 'utf-8') {
|
if (encoding === 'utf8' || encoding === 'utf-8') {
|
||||||
return yarnFS.readFileSync(path, 'utf8');
|
return yarnFS.readFileSync(path, 'utf8');
|
||||||
}
|
}
|
||||||
return yarnFS.readFileSync(path);
|
return yarnFS.readFileSync(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
writeFileSync(path: string, data: string | Buffer, encoding: BufferEncoding | null) {
|
writeFileSync(uri: Uri, data: string | Buffer, encoding: BufferEncoding | null) {
|
||||||
|
const path = uri.getFilePath();
|
||||||
yarnFS.writeFileSync(path, data, encoding || undefined);
|
yarnFS.writeFileSync(path, data, encoding || undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
statSync(path: string) {
|
statSync(uri: Uri) {
|
||||||
|
const path = uri.getFilePath();
|
||||||
const stat = yarnFS.statSync(path);
|
const stat = yarnFS.statSync(path);
|
||||||
// Treat zip/egg files as directories.
|
// Treat zip/egg files as directories.
|
||||||
// See: https://github.com/yarnpkg/berry/blob/master/packages/vscode-zipfs/sources/ZipFSProvider.ts
|
// See: https://github.com/yarnpkg/berry/blob/master/packages/vscode-zipfs/sources/ZipFSProvider.ts
|
||||||
@ -286,53 +304,62 @@ class RealFileSystem implements FileSystem {
|
|||||||
return stat;
|
return stat;
|
||||||
}
|
}
|
||||||
|
|
||||||
rmdirSync(path: string): void {
|
rmdirSync(uri: Uri): void {
|
||||||
|
const path = uri.getFilePath();
|
||||||
yarnFS.rmdirSync(path);
|
yarnFS.rmdirSync(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
unlinkSync(path: string) {
|
unlinkSync(uri: Uri) {
|
||||||
|
const path = uri.getFilePath();
|
||||||
yarnFS.unlinkSync(path);
|
yarnFS.unlinkSync(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
realpathSync(path: string) {
|
realpathSync(uri: Uri) {
|
||||||
try {
|
try {
|
||||||
return yarnFS.realpathSync(path);
|
const path = uri.getFilePath();
|
||||||
|
return Uri.file(yarnFS.realpathSync(path), this._isCaseSensitive);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
return path;
|
return uri;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getModulePath(): string {
|
getModulePath(): Uri {
|
||||||
// The entry point to the tool should have set the __rootDirectory
|
// The entry point to the tool should have set the __rootDirectory
|
||||||
// global variable to point to the directory that contains the
|
// global variable to point to the directory that contains the
|
||||||
// typeshed-fallback directory.
|
// typeshed-fallback directory.
|
||||||
return (global as any).__rootDirectory;
|
return getRootUri(this._isCaseSensitive) || Uri.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
createFileSystemWatcher(paths: string[], listener: FileWatcherEventHandler): FileWatcher {
|
createFileSystemWatcher(paths: Uri[], listener: FileWatcherEventHandler): FileWatcher {
|
||||||
return this._fileWatcherProvider.createFileWatcher(
|
return this._fileWatcherProvider.createFileWatcher(
|
||||||
paths.map((p) => this.realCasePath(p)),
|
paths.map((p) => p.getFilePath()),
|
||||||
listener
|
listener
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
createReadStream(path: string): fs.ReadStream {
|
createReadStream(uri: Uri): fs.ReadStream {
|
||||||
|
const path = uri.getFilePath();
|
||||||
return yarnFS.createReadStream(path);
|
return yarnFS.createReadStream(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
createWriteStream(path: string): fs.WriteStream {
|
createWriteStream(uri: Uri): fs.WriteStream {
|
||||||
|
const path = uri.getFilePath();
|
||||||
return yarnFS.createWriteStream(path);
|
return yarnFS.createWriteStream(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
copyFileSync(src: string, dst: string): void {
|
copyFileSync(src: Uri, dst: Uri): void {
|
||||||
yarnFS.copyFileSync(src, dst);
|
const srcPath = src.getFilePath();
|
||||||
|
const destPath = dst.getFilePath();
|
||||||
|
yarnFS.copyFileSync(srcPath, destPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
readFile(path: string): Promise<Buffer> {
|
readFile(uri: Uri): Promise<Buffer> {
|
||||||
|
const path = uri.getFilePath();
|
||||||
return yarnFS.readFilePromise(path);
|
return yarnFS.readFilePromise(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
async readFileText(path: string, encoding: BufferEncoding): Promise<string> {
|
async readFileText(uri: Uri, encoding: BufferEncoding): Promise<string> {
|
||||||
|
const path = uri.getFilePath();
|
||||||
if (encoding === 'utf8' || encoding === 'utf-8') {
|
if (encoding === 'utf8' || encoding === 'utf-8') {
|
||||||
return yarnFS.readFilePromise(path, 'utf8');
|
return yarnFS.readFilePromise(path, 'utf8');
|
||||||
}
|
}
|
||||||
@ -340,17 +367,18 @@ class RealFileSystem implements FileSystem {
|
|||||||
return buffer.toString(encoding);
|
return buffer.toString(encoding);
|
||||||
}
|
}
|
||||||
|
|
||||||
realCasePath(path: string): string {
|
realCasePath(uri: Uri): Uri {
|
||||||
try {
|
try {
|
||||||
// If it doesn't exist in the real FS, then just use this path.
|
// If it doesn't exist in the real FS, then just use this path.
|
||||||
if (!this.existsSync(path)) {
|
if (!this.existsSync(uri)) {
|
||||||
return this._getNormalizedPath(path);
|
return uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If it does exist, skip this for symlinks.
|
// If it does exist, skip this for symlinks.
|
||||||
|
const path = uri.getFilePath();
|
||||||
const stat = fs.lstatSync(path);
|
const stat = fs.lstatSync(path);
|
||||||
if (stat.isSymbolicLink()) {
|
if (stat.isSymbolicLink()) {
|
||||||
return this._getNormalizedPath(path);
|
return uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
// realpathSync.native will return casing as in OS rather than
|
// realpathSync.native will return casing as in OS rather than
|
||||||
@ -359,53 +387,34 @@ class RealFileSystem implements FileSystem {
|
|||||||
|
|
||||||
// On UNC mapped drives we want to keep the original drive letter.
|
// On UNC mapped drives we want to keep the original drive letter.
|
||||||
if (getRootLength(realCase) !== getRootLength(path)) {
|
if (getRootLength(realCase) !== getRootLength(path)) {
|
||||||
return path;
|
return uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
return realCase;
|
return Uri.file(realCase, this._isCaseSensitive);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
// Return as it is, if anything failed.
|
// Return as it is, if anything failed.
|
||||||
this._console.log(`Failed to get real file system casing for ${path}: ${e}`);
|
this._console.log(`Failed to get real file system casing for ${uri}: ${e}`);
|
||||||
|
|
||||||
return path;
|
return uri;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
isMappedFilePath(filepath: string): boolean {
|
isMappedUri(uri: Uri): boolean {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
getOriginalFilePath(mappedFilePath: string) {
|
getOriginalUri(mappedUri: Uri) {
|
||||||
return mappedFilePath;
|
return mappedUri;
|
||||||
}
|
}
|
||||||
|
|
||||||
getMappedFilePath(originalFilepath: string) {
|
getMappedUri(originalUri: Uri) {
|
||||||
return originalFilepath;
|
return originalUri;
|
||||||
}
|
}
|
||||||
|
|
||||||
getUri(path: string): string {
|
isInZip(uri: Uri): boolean {
|
||||||
// If this is not a file path, just return the original path.
|
const path = uri.getFilePath();
|
||||||
if (isUri(path)) {
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
return URI.file(path).toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
isInZip(path: string): boolean {
|
|
||||||
return /[^\\/]\.(?:egg|zip|jar)[\\/]/.test(path) && yarnFS.isZip(path);
|
return /[^\\/]\.(?:egg|zip|jar)[\\/]/.test(path) && yarnFS.isZip(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getNormalizedPath(path: string) {
|
|
||||||
const driveLength = getRootLength(path);
|
|
||||||
|
|
||||||
if (driveLength === 0) {
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
// `vscode` sometimes uses different casing for drive letter.
|
|
||||||
// Make sure we normalize at least drive letter.
|
|
||||||
return combinePaths(fs.realpathSync.native(path.substring(0, driveLength)), path.substring(driveLength));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface WorkspaceFileWatcher extends FileWatcher {
|
interface WorkspaceFileWatcher extends FileWatcher {
|
||||||
@ -436,15 +445,15 @@ export class WorkspaceFileWatcherProvider implements FileWatcherProvider, FileWa
|
|||||||
return fileWatcher;
|
return fileWatcher;
|
||||||
}
|
}
|
||||||
|
|
||||||
onFileChange(eventType: FileWatcherEventType, filePath: string): void {
|
onFileChange(eventType: FileWatcherEventType, fileUri: Uri): void {
|
||||||
// Since file watcher is a server wide service, we don't know which watcher is
|
// Since file watcher is a server wide service, we don't know which watcher is
|
||||||
// for which workspace (for multi workspace case), also, we don't know which watcher
|
// for which workspace (for multi workspace case), also, we don't know which watcher
|
||||||
// is for source or library. so we need to solely rely on paths that can cause us
|
// is for source or library. so we need to solely rely on paths that can cause us
|
||||||
// to raise events both for source and library if .venv is inside of workspace root
|
// to raise events both for source and library if .venv is inside of workspace root
|
||||||
// for a file change. It is event handler's job to filter those out.
|
// for a file change. It is event handler's job to filter those out.
|
||||||
this._fileWatchers.forEach((watcher) => {
|
this._fileWatchers.forEach((watcher) => {
|
||||||
if (watcher.workspacePaths.some((dirPath) => filePath.startsWith(dirPath))) {
|
if (watcher.workspacePaths.some((dirPath) => fileUri.pathStartsWith(dirPath))) {
|
||||||
watcher.eventHandler(eventType, filePath);
|
watcher.eventHandler(eventType, fileUri);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -453,17 +462,15 @@ export class WorkspaceFileWatcherProvider implements FileWatcherProvider, FileWa
|
|||||||
export class RealTempFile implements TempFile {
|
export class RealTempFile implements TempFile {
|
||||||
private _tmpdir?: tmp.DirResult;
|
private _tmpdir?: tmp.DirResult;
|
||||||
|
|
||||||
tmpdir() {
|
constructor(private readonly _isCaseSensitive: boolean) {}
|
||||||
if (!this._tmpdir) {
|
|
||||||
this._tmpdir = tmp.dirSync({ prefix: 'pyright' });
|
|
||||||
}
|
|
||||||
|
|
||||||
return this._tmpdir.name;
|
tmpdir(): Uri {
|
||||||
|
return Uri.file(this._getTmpDir().name, this._isCaseSensitive);
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpfile(options?: TmpfileOptions): string {
|
tmpfile(options?: TmpfileOptions): Uri {
|
||||||
const f = tmp.fileSync({ dir: this.tmpdir(), discardDescriptor: true, ...options });
|
const f = tmp.fileSync({ dir: this._getTmpDir().name, discardDescriptor: true, ...options });
|
||||||
return f.name;
|
return Uri.file(f.name, this._isCaseSensitive);
|
||||||
}
|
}
|
||||||
|
|
||||||
dispose(): void {
|
dispose(): void {
|
||||||
@ -474,4 +481,12 @@ export class RealTempFile implements TempFile {
|
|||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _getTmpDir(): tmp.DirResult {
|
||||||
|
if (!this._tmpdir) {
|
||||||
|
this._tmpdir = tmp.dirSync({ prefix: 'pyright' });
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._tmpdir;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,20 +11,22 @@ import { IPythonMode, SourceFile, SourceFileEditMode } from '../analyzer/sourceF
|
|||||||
import { SupportPartialStubs } from '../pyrightFileSystem';
|
import { SupportPartialStubs } from '../pyrightFileSystem';
|
||||||
import { ConsoleInterface } from './console';
|
import { ConsoleInterface } from './console';
|
||||||
import {
|
import {
|
||||||
StatusMutationListener,
|
DebugInfoInspector,
|
||||||
ServiceProvider as ReadOnlyServiceProvider,
|
ServiceProvider as ReadOnlyServiceProvider,
|
||||||
|
StatusMutationListener,
|
||||||
SymbolDefinitionProvider,
|
SymbolDefinitionProvider,
|
||||||
SymbolUsageProviderFactory,
|
SymbolUsageProviderFactory,
|
||||||
DebugInfoInspector,
|
|
||||||
} from './extensibility';
|
} from './extensibility';
|
||||||
import { FileSystem, TempFile } from './fileSystem';
|
import { FileSystem, TempFile } from './fileSystem';
|
||||||
import { LogTracker } from './logTracker';
|
import { LogTracker } from './logTracker';
|
||||||
import { GroupServiceKey, ServiceKey, ServiceProvider } from './serviceProvider';
|
import { GroupServiceKey, ServiceKey, ServiceProvider } from './serviceProvider';
|
||||||
|
import { Uri } from './uri/uri';
|
||||||
|
|
||||||
declare module './serviceProvider' {
|
declare module './serviceProvider' {
|
||||||
interface ServiceProvider {
|
interface ServiceProvider {
|
||||||
fs(): FileSystem;
|
fs(): FileSystem;
|
||||||
console(): ConsoleInterface;
|
console(): ConsoleInterface;
|
||||||
|
tmp(): TempFile | undefined;
|
||||||
sourceFileFactory(): ISourceFileFactory;
|
sourceFileFactory(): ISourceFileFactory;
|
||||||
partialStubs(): SupportPartialStubs;
|
partialStubs(): SupportPartialStubs;
|
||||||
}
|
}
|
||||||
@ -79,6 +81,9 @@ ServiceProvider.prototype.console = function () {
|
|||||||
ServiceProvider.prototype.partialStubs = function () {
|
ServiceProvider.prototype.partialStubs = function () {
|
||||||
return this.get(ServiceKeys.partialStubs);
|
return this.get(ServiceKeys.partialStubs);
|
||||||
};
|
};
|
||||||
|
ServiceProvider.prototype.tmp = function () {
|
||||||
|
return this.tryGet(ServiceKeys.tempFile);
|
||||||
|
};
|
||||||
ServiceProvider.prototype.sourceFileFactory = function () {
|
ServiceProvider.prototype.sourceFileFactory = function () {
|
||||||
const result = this.tryGet(ServiceKeys.sourceFileFactory);
|
const result = this.tryGet(ServiceKeys.sourceFileFactory);
|
||||||
return result || DefaultSourceFileFactory;
|
return result || DefaultSourceFileFactory;
|
||||||
@ -87,26 +92,24 @@ ServiceProvider.prototype.sourceFileFactory = function () {
|
|||||||
const DefaultSourceFileFactory: ISourceFileFactory = {
|
const DefaultSourceFileFactory: ISourceFileFactory = {
|
||||||
createSourceFile(
|
createSourceFile(
|
||||||
serviceProvider: ReadOnlyServiceProvider,
|
serviceProvider: ReadOnlyServiceProvider,
|
||||||
filePath: string,
|
fileUri: Uri,
|
||||||
moduleName: string,
|
moduleName: string,
|
||||||
isThirdPartyImport: boolean,
|
isThirdPartyImport: boolean,
|
||||||
isThirdPartyPyTypedPresent: boolean,
|
isThirdPartyPyTypedPresent: boolean,
|
||||||
editMode: SourceFileEditMode,
|
editMode: SourceFileEditMode,
|
||||||
console?: ConsoleInterface,
|
console?: ConsoleInterface,
|
||||||
logTracker?: LogTracker,
|
logTracker?: LogTracker,
|
||||||
realFilePath?: string,
|
|
||||||
ipythonMode?: IPythonMode
|
ipythonMode?: IPythonMode
|
||||||
) {
|
) {
|
||||||
return new SourceFile(
|
return new SourceFile(
|
||||||
serviceProvider,
|
serviceProvider,
|
||||||
filePath,
|
fileUri,
|
||||||
moduleName,
|
moduleName,
|
||||||
isThirdPartyImport,
|
isThirdPartyImport,
|
||||||
isThirdPartyPyTypedPresent,
|
isThirdPartyPyTypedPresent,
|
||||||
editMode,
|
editMode,
|
||||||
console,
|
console,
|
||||||
logTracker,
|
logTracker,
|
||||||
realFilePath,
|
|
||||||
ipythonMode
|
ipythonMode
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -32,11 +32,11 @@ import {
|
|||||||
} from '../parser/parseNodes';
|
} from '../parser/parseNodes';
|
||||||
import { ParseResults } from '../parser/parser';
|
import { ParseResults } from '../parser/parser';
|
||||||
import { appendArray, getOrAdd, removeArrayElements } from './collectionUtils';
|
import { appendArray, getOrAdd, removeArrayElements } from './collectionUtils';
|
||||||
import { isString } from './core';
|
|
||||||
import * as debug from './debug';
|
import * as debug from './debug';
|
||||||
import { FileEditAction } from './editAction';
|
import { FileEditAction } from './editAction';
|
||||||
import { convertOffsetToPosition, convertTextRangeToRange } from './positionUtils';
|
import { convertOffsetToPosition, convertTextRangeToRange } from './positionUtils';
|
||||||
import { doesRangeContain, doRangesIntersect, extendRange, Range, TextRange } from './textRange';
|
import { doesRangeContain, doRangesIntersect, extendRange, Range, TextRange } from './textRange';
|
||||||
|
import { Uri } from './uri/uri';
|
||||||
|
|
||||||
export class TextEditTracker {
|
export class TextEditTracker {
|
||||||
private readonly _nodesRemoved: Map<ParseNode, ParseResults> = new Map<ParseNode, ParseResults>();
|
private readonly _nodesRemoved: Map<ParseNode, ParseResults> = new Map<ParseNode, ParseResults>();
|
||||||
@ -49,11 +49,11 @@ export class TextEditTracker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
addEdits(...edits: FileEditAction[]) {
|
addEdits(...edits: FileEditAction[]) {
|
||||||
edits.forEach((e) => this.addEdit(e.filePath, e.range, e.replacementText));
|
edits.forEach((e) => this.addEdit(e.fileUri, e.range, e.replacementText));
|
||||||
}
|
}
|
||||||
|
|
||||||
addEdit(filePath: string, range: Range, replacementText: string) {
|
addEdit(fileUri: Uri, range: Range, replacementText: string) {
|
||||||
const edits = getOrAdd(this._results, filePath, () => []);
|
const edits = getOrAdd(this._results, fileUri.key, () => []);
|
||||||
|
|
||||||
// If there is any overlapping edit, see whether we can merge edits.
|
// If there is any overlapping edit, see whether we can merge edits.
|
||||||
// We can merge edits, if one of them is 'deletion' or 2 edits has the same
|
// We can merge edits, if one of them is 'deletion' or 2 edits has the same
|
||||||
@ -70,11 +70,11 @@ export class TextEditTracker {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
edits.push({ filePath, range, replacementText });
|
edits.push({ fileUri: fileUri, range, replacementText });
|
||||||
}
|
}
|
||||||
|
|
||||||
addEditWithTextRange(parseResults: ParseResults, range: TextRange, replacementText: string) {
|
addEditWithTextRange(parseResults: ParseResults, range: TextRange, replacementText: string) {
|
||||||
const filePath = getFileInfo(parseResults.parseTree).filePath;
|
const filePath = getFileInfo(parseResults.parseTree).fileUri;
|
||||||
|
|
||||||
const existing = parseResults.text.substr(range.start, range.length);
|
const existing = parseResults.text.substr(range.start, range.length);
|
||||||
if (existing === replacementText) {
|
if (existing === replacementText) {
|
||||||
@ -93,7 +93,7 @@ export class TextEditTracker {
|
|||||||
? (importToDelete.parent as ImportNode).list
|
? (importToDelete.parent as ImportNode).list
|
||||||
: (importToDelete.parent as ImportFromNode).imports;
|
: (importToDelete.parent as ImportFromNode).imports;
|
||||||
|
|
||||||
const filePath = getFileInfo(parseResults.parseTree).filePath;
|
const filePath = getFileInfo(parseResults.parseTree).fileUri;
|
||||||
const ranges = getTextRangeForImportNameDeletion(
|
const ranges = getTextRangeForImportNameDeletion(
|
||||||
parseResults,
|
parseResults,
|
||||||
imports,
|
imports,
|
||||||
@ -183,7 +183,7 @@ export class TextEditTracker {
|
|||||||
importGroup: ImportGroup,
|
importGroup: ImportGroup,
|
||||||
importNameInfo?: ImportNameInfo[]
|
importNameInfo?: ImportNameInfo[]
|
||||||
) {
|
) {
|
||||||
const filePath = getFileInfo(parseResults.parseTree).filePath;
|
const fileUri = getFileInfo(parseResults.parseTree).fileUri;
|
||||||
|
|
||||||
this.addEdits(
|
this.addEdits(
|
||||||
...getTextEditsForAutoImportInsertion(
|
...getTextEditsForAutoImportInsertion(
|
||||||
@ -193,7 +193,7 @@ export class TextEditTracker {
|
|||||||
importGroup,
|
importGroup,
|
||||||
parseResults,
|
parseResults,
|
||||||
convertOffsetToPosition(parseResults.parseTree.length, parseResults.tokenizerOutput.lines)
|
convertOffsetToPosition(parseResults.parseTree.length, parseResults.tokenizerOutput.lines)
|
||||||
).map((e) => ({ filePath, range: e.range, replacementText: e.replacementText }))
|
).map((e) => ({ fileUri, range: e.range, replacementText: e.replacementText }))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -220,7 +220,7 @@ export class TextEditTracker {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const filePath = getFileInfo(parseResults.parseTree).filePath;
|
const fileUri = getFileInfo(parseResults.parseTree).fileUri;
|
||||||
|
|
||||||
const edits = getTextEditsForAutoImportSymbolAddition(importNameInfo, imported, parseResults);
|
const edits = getTextEditsForAutoImportSymbolAddition(importNameInfo, imported, parseResults);
|
||||||
if (imported.node !== updateOptions.currentFromImport) {
|
if (imported.node !== updateOptions.currentFromImport) {
|
||||||
@ -228,7 +228,7 @@ export class TextEditTracker {
|
|||||||
// node we are working on.
|
// node we are working on.
|
||||||
// ex) from xxx import yyy <= we are working on here.
|
// ex) from xxx import yyy <= we are working on here.
|
||||||
// from xxx import zzz <= but we found this.
|
// from xxx import zzz <= but we found this.
|
||||||
this.addEdits(...edits.map((e) => ({ filePath, range: e.range, replacementText: e.replacementText })));
|
this.addEdits(...edits.map((e) => ({ fileUri, range: e.range, replacementText: e.replacementText })));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -247,9 +247,9 @@ export class TextEditTracker {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const deletions = this._getDeletionsForSpan(filePath, edits[0].range);
|
const deletions = this._getDeletionsForSpan(fileUri, edits[0].range);
|
||||||
if (deletions.length === 0) {
|
if (deletions.length === 0) {
|
||||||
this.addEdit(filePath, edits[0].range, edits[0].replacementText);
|
this.addEdit(fileUri, edits[0].range, edits[0].replacementText);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -265,13 +265,13 @@ export class TextEditTracker {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._removeEdits(filePath, deletions);
|
this._removeEdits(fileUri, deletions);
|
||||||
if (importName.alias) {
|
if (importName.alias) {
|
||||||
this._nodesRemoved.delete(importName.alias);
|
this._nodesRemoved.delete(importName.alias);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.addEdit(
|
this.addEdit(
|
||||||
filePath,
|
fileUri,
|
||||||
convertTextRangeToRange(importName.name, parseResults.tokenizerOutput.lines),
|
convertTextRangeToRange(importName.name, parseResults.tokenizerOutput.lines),
|
||||||
newLastModuleName
|
newLastModuleName
|
||||||
);
|
);
|
||||||
@ -279,17 +279,17 @@ export class TextEditTracker {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getDeletionsForSpan(filePathOrEdit: string | FileEditAction[], range: Range) {
|
private _getDeletionsForSpan(fileUriOrEdit: Uri | FileEditAction[], range: Range) {
|
||||||
const edits = this._getOverlappingForSpan(filePathOrEdit, range);
|
const edits = this._getOverlappingForSpan(fileUriOrEdit, range);
|
||||||
return edits.filter((e) => e.replacementText === '');
|
return edits.filter((e) => e.replacementText === '');
|
||||||
}
|
}
|
||||||
|
|
||||||
private _removeEdits(filePathOrEdit: string | FileEditAction[], edits: FileEditAction[]) {
|
private _removeEdits(fileUriOrEdit: Uri | FileEditAction[], edits: FileEditAction[]) {
|
||||||
if (isString(filePathOrEdit)) {
|
if (Uri.isUri(fileUriOrEdit)) {
|
||||||
filePathOrEdit = this._results.get(filePathOrEdit) ?? [];
|
fileUriOrEdit = this._results.get(fileUriOrEdit.key) ?? [];
|
||||||
}
|
}
|
||||||
|
|
||||||
removeArrayElements(filePathOrEdit, (f) => edits.some((e) => FileEditAction.areEqual(f, e)));
|
removeArrayElements(fileUriOrEdit, (f) => edits.some((e) => FileEditAction.areEqual(f, e)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getEditsToMerge(edits: FileEditAction[], range: Range, replacementText: string) {
|
private _getEditsToMerge(edits: FileEditAction[], range: Range, replacementText: string) {
|
||||||
@ -319,12 +319,12 @@ export class TextEditTracker {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getOverlappingForSpan(filePathOrEdit: string | FileEditAction[], range: Range) {
|
private _getOverlappingForSpan(fileUriOrEdit: Uri | FileEditAction[], range: Range) {
|
||||||
if (isString(filePathOrEdit)) {
|
if (Uri.isUri(fileUriOrEdit)) {
|
||||||
filePathOrEdit = this._results.get(filePathOrEdit) ?? [];
|
fileUriOrEdit = this._results.get(fileUriOrEdit.key) ?? [];
|
||||||
}
|
}
|
||||||
|
|
||||||
return filePathOrEdit.filter((e) => doRangesIntersect(e.range, range));
|
return fileUriOrEdit.filter((e) => doRangesIntersect(e.range, range));
|
||||||
}
|
}
|
||||||
|
|
||||||
private _processNodeRemoved(token: CancellationToken) {
|
private _processNodeRemoved(token: CancellationToken) {
|
||||||
@ -343,7 +343,7 @@ export class TextEditTracker {
|
|||||||
this._pendingNodeToRemove.pop();
|
this._pendingNodeToRemove.pop();
|
||||||
|
|
||||||
const info = getFileInfo(peekNodeToRemove.parseResults.parseTree);
|
const info = getFileInfo(peekNodeToRemove.parseResults.parseTree);
|
||||||
this.addEdit(info.filePath, convertTextRangeToRange(peekNodeToRemove.node, info.lines), '');
|
this.addEdit(info.fileUri, convertTextRangeToRange(peekNodeToRemove.node, info.lines), '');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -370,11 +370,7 @@ export class TextEditTracker {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (nameNodes.length === nodesRemoved.length) {
|
if (nameNodes.length === nodesRemoved.length) {
|
||||||
this.addEdit(
|
this.addEdit(info.fileUri, ParseTreeUtils.getFullStatementRange(importNode, nodeToRemove.parseResults), '');
|
||||||
info.filePath,
|
|
||||||
ParseTreeUtils.getFullStatementRange(importNode, nodeToRemove.parseResults),
|
|
||||||
''
|
|
||||||
);
|
|
||||||
|
|
||||||
// Remove nodes that are handled from queue.
|
// Remove nodes that are handled from queue.
|
||||||
this._removeNodesHandled(nodesRemoved);
|
this._removeNodesHandled(nodesRemoved);
|
||||||
@ -397,7 +393,7 @@ export class TextEditTracker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const editSpans = getTextRangeForImportNameDeletion(nodeToRemove.parseResults, nameNodes, ...indices);
|
const editSpans = getTextRangeForImportNameDeletion(nodeToRemove.parseResults, nameNodes, ...indices);
|
||||||
editSpans.forEach((e) => this.addEdit(info.filePath, convertTextRangeToRange(e, info.lines), ''));
|
editSpans.forEach((e) => this.addEdit(info.fileUri, convertTextRangeToRange(e, info.lines), ''));
|
||||||
|
|
||||||
this._removeNodesHandled(nodesRemoved);
|
this._removeNodesHandled(nodesRemoved);
|
||||||
return true;
|
return true;
|
||||||
|
@ -7,6 +7,8 @@
|
|||||||
* Specifies the range of text within a larger string.
|
* Specifies the range of text within a larger string.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { Uri } from './uri/uri';
|
||||||
|
|
||||||
export interface TextRange {
|
export interface TextRange {
|
||||||
readonly start: number;
|
readonly start: number;
|
||||||
readonly length: number;
|
readonly length: number;
|
||||||
@ -131,7 +133,7 @@ export namespace Range {
|
|||||||
|
|
||||||
// Represents a range within a particular document.
|
// Represents a range within a particular document.
|
||||||
export interface DocumentRange {
|
export interface DocumentRange {
|
||||||
path: string;
|
uri: Uri;
|
||||||
range: Range;
|
range: Range;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
269
packages/pyright-internal/src/common/uri/baseUri.ts
Normal file
269
packages/pyright-internal/src/common/uri/baseUri.ts
Normal file
@ -0,0 +1,269 @@
|
|||||||
|
/*
|
||||||
|
* baseUri.ts
|
||||||
|
* Copyright (c) Microsoft Corporation.
|
||||||
|
* Licensed under the MIT license.
|
||||||
|
*
|
||||||
|
* Base URI class for storing and manipulating URIs.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { some } from '../collectionUtils';
|
||||||
|
import { getShortenedFileName, normalizeSlashes } from '../pathUtils';
|
||||||
|
import { Uri } from './uri';
|
||||||
|
|
||||||
|
export abstract class BaseUri implements Uri {
|
||||||
|
protected constructor(private readonly _key: string) {}
|
||||||
|
|
||||||
|
// Unique key for storing in maps.
|
||||||
|
get key() {
|
||||||
|
return this._key;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the scheme of the URI.
|
||||||
|
abstract get scheme(): string;
|
||||||
|
|
||||||
|
// Returns whether the underlying file system is case sensitive or not.
|
||||||
|
abstract get isCaseSensitive(): boolean;
|
||||||
|
|
||||||
|
// Returns the last segment of the URI, similar to the UNIX basename command.
|
||||||
|
abstract get fileName(): string;
|
||||||
|
|
||||||
|
// Returns just the fileName without any extensions
|
||||||
|
get fileNameWithoutExtension(): string {
|
||||||
|
const fileName = this.fileName;
|
||||||
|
const index = fileName.lastIndexOf('.');
|
||||||
|
if (index > 0) {
|
||||||
|
return fileName.slice(0, index);
|
||||||
|
} else {
|
||||||
|
return fileName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the extension of the URI, similar to the UNIX extname command.
|
||||||
|
abstract get lastExtension(): string;
|
||||||
|
|
||||||
|
// Returns a URI where the path just contains the root folder.
|
||||||
|
abstract get root(): Uri;
|
||||||
|
|
||||||
|
// Returns a URI where the path contains the path with .py appended.
|
||||||
|
get packageUri(): Uri {
|
||||||
|
// This is assuming that the current path is a file already.
|
||||||
|
return this.addExtension('.py');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns a URI where the path contains the path with .pyi appended.
|
||||||
|
get packageStubUri(): Uri {
|
||||||
|
// This is assuming that the current path is a file already.
|
||||||
|
return this.addExtension('.pyi');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns a URI where the path has __init__.py appended.
|
||||||
|
get initPyUri(): Uri {
|
||||||
|
// This is assuming that the current path is a directory already.
|
||||||
|
return this.combinePaths('__init__.py');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns a URI where the path has __init__.pyi appended.
|
||||||
|
get initPyiUri(): Uri {
|
||||||
|
// This is assuming that the current path is a directory already.
|
||||||
|
return this.combinePaths('__init__.pyi');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns a URI where the path has py.typed appended.
|
||||||
|
get pytypedUri(): Uri {
|
||||||
|
// This is assuming that the current path is a directory already.
|
||||||
|
return this.combinePaths('py.typed');
|
||||||
|
}
|
||||||
|
|
||||||
|
isEmpty(): boolean {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract toString(): string;
|
||||||
|
|
||||||
|
abstract toUserVisibleString(): string;
|
||||||
|
|
||||||
|
abstract matchesRegex(regex: RegExp): boolean;
|
||||||
|
|
||||||
|
replaceExtension(ext: string): Uri {
|
||||||
|
const dir = this.getDirectory();
|
||||||
|
const base = this.fileName;
|
||||||
|
const newBase = base.slice(0, base.length - this.lastExtension.length) + ext;
|
||||||
|
return dir.combinePaths(newBase);
|
||||||
|
}
|
||||||
|
|
||||||
|
addExtension(ext: string): Uri {
|
||||||
|
return this.addPath(ext);
|
||||||
|
}
|
||||||
|
|
||||||
|
hasExtension(ext: string): boolean {
|
||||||
|
return this.isCaseSensitive
|
||||||
|
? this.lastExtension === ext
|
||||||
|
: this.lastExtension.toLowerCase() === ext.toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract addPath(extra: string): Uri;
|
||||||
|
|
||||||
|
// Returns a URI where the path is the directory name of the original URI, similar to the UNIX dirname command.
|
||||||
|
abstract getDirectory(): Uri;
|
||||||
|
|
||||||
|
getRootPathLength(): number {
|
||||||
|
return this.getRootPath().length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determines whether a path consists only of a path root.
|
||||||
|
abstract isRoot(): boolean;
|
||||||
|
|
||||||
|
// Determines whether a Uri is a child of some parent Uri.
|
||||||
|
abstract isChild(parent: Uri, ignoreCase?: boolean): boolean;
|
||||||
|
|
||||||
|
abstract isLocal(): boolean;
|
||||||
|
|
||||||
|
isUntitled(): boolean {
|
||||||
|
return this.scheme === 'untitled';
|
||||||
|
}
|
||||||
|
|
||||||
|
equals(other: Uri | undefined): boolean {
|
||||||
|
return this.key === other?.key;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract startsWith(other: Uri | undefined, ignoreCase?: boolean): boolean;
|
||||||
|
|
||||||
|
pathStartsWith(name: string): boolean {
|
||||||
|
// ignore path separators.
|
||||||
|
name = normalizeSlashes(name);
|
||||||
|
return this.getComparablePath().startsWith(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
pathEndsWith(name: string): boolean {
|
||||||
|
// ignore path separators.
|
||||||
|
name = normalizeSlashes(name);
|
||||||
|
return this.getComparablePath().endsWith(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
pathIncludes(include: string): boolean {
|
||||||
|
// ignore path separators.
|
||||||
|
include = normalizeSlashes(include);
|
||||||
|
return this.getComparablePath().includes(include);
|
||||||
|
}
|
||||||
|
|
||||||
|
// How long the path for this Uri is.
|
||||||
|
abstract getPathLength(): number;
|
||||||
|
|
||||||
|
// Combines paths to create a new Uri. Any '..' or '.' path components will be normalized.
|
||||||
|
abstract combinePaths(...paths: string[]): Uri;
|
||||||
|
|
||||||
|
getRelativePath(child: Uri): string | undefined {
|
||||||
|
if (this.scheme !== child.scheme) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unlike getRelativePathComponents, this function should not return relative path
|
||||||
|
// markers for non children.
|
||||||
|
if (child.isChild(this)) {
|
||||||
|
const relativeToComponents = this.getRelativePathComponents(child);
|
||||||
|
if (relativeToComponents.length > 0) {
|
||||||
|
return ['.', ...relativeToComponents].join('/');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPathComponents(): readonly string[] {
|
||||||
|
// Make sure to freeze the result so that it can't be modified.
|
||||||
|
return Object.freeze(this.getPathComponentsImpl());
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract getPath(): string;
|
||||||
|
|
||||||
|
abstract getFilePath(): string;
|
||||||
|
|
||||||
|
getRelativePathComponents(to: Uri): readonly string[] {
|
||||||
|
const fromComponents = this.getPathComponents();
|
||||||
|
const toComponents = to.getPathComponents();
|
||||||
|
|
||||||
|
let start: number;
|
||||||
|
for (start = 0; start < fromComponents.length && start < toComponents.length; start++) {
|
||||||
|
const fromComponent = fromComponents[start];
|
||||||
|
const toComponent = toComponents[start];
|
||||||
|
if (fromComponent !== toComponent) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start === 0) {
|
||||||
|
return toComponents;
|
||||||
|
}
|
||||||
|
|
||||||
|
const components = toComponents.slice(start);
|
||||||
|
const relative: string[] = [];
|
||||||
|
for (; start < fromComponents.length; start++) {
|
||||||
|
relative.push('..');
|
||||||
|
}
|
||||||
|
return [...relative, ...components];
|
||||||
|
}
|
||||||
|
|
||||||
|
getShortenedFileName(maxDirLength: number = 15): string {
|
||||||
|
return getShortenedFileName(this.getPath(), maxDirLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
stripExtension(): Uri {
|
||||||
|
const base = this.fileName;
|
||||||
|
const index = base.lastIndexOf('.');
|
||||||
|
if (index > 0) {
|
||||||
|
const stripped = base.slice(0, index);
|
||||||
|
return this.getDirectory().combinePaths(stripped);
|
||||||
|
} else {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stripAllExtensions(): Uri {
|
||||||
|
const base = this.fileName;
|
||||||
|
const stripped = base.split('.')[0];
|
||||||
|
if (stripped === base) {
|
||||||
|
return this;
|
||||||
|
} else {
|
||||||
|
return this.getDirectory().combinePaths(stripped);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract getRootPath(): string;
|
||||||
|
|
||||||
|
protected normalizeSlashes(path: string): string {
|
||||||
|
return path.replace(/\\/g, '/');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected reducePathComponents(components: string[]): string[] {
|
||||||
|
if (!some(components)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reduce the path components by eliminating
|
||||||
|
// any '.' or '..'. We start at 1 because the first component is
|
||||||
|
// always the root.
|
||||||
|
const reduced = [components[0]];
|
||||||
|
for (let i = 1; i < components.length; i++) {
|
||||||
|
const component = components[i];
|
||||||
|
if (!component || component === '.') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (component === '..') {
|
||||||
|
if (reduced.length > 1) {
|
||||||
|
if (reduced[reduced.length - 1] !== '..') {
|
||||||
|
reduced.pop();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else if (reduced[0]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reduced.push(component);
|
||||||
|
}
|
||||||
|
|
||||||
|
return reduced;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract getComparablePath(): string;
|
||||||
|
protected abstract getPathComponentsImpl(): string[];
|
||||||
|
}
|
123
packages/pyright-internal/src/common/uri/emptyUri.ts
Normal file
123
packages/pyright-internal/src/common/uri/emptyUri.ts
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
/*
|
||||||
|
* emptyUri.ts
|
||||||
|
* Copyright (c) Microsoft Corporation.
|
||||||
|
* Licensed under the MIT license.
|
||||||
|
*
|
||||||
|
* URI class that represents an empty URI.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import * as debug from '../debug';
|
||||||
|
import { BaseUri } from './baseUri';
|
||||||
|
import { Uri } from './uri';
|
||||||
|
|
||||||
|
const EmptyKey = '<empty>';
|
||||||
|
|
||||||
|
export class EmptyUri extends BaseUri {
|
||||||
|
private static _instance = new EmptyUri();
|
||||||
|
private constructor() {
|
||||||
|
super(EmptyKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
static get instance() {
|
||||||
|
return EmptyUri._instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
override get scheme(): string {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
override get fileName(): string {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
override get lastExtension(): string {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
override get root(): Uri {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
get isCaseSensitive(): boolean {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
override isEmpty(): boolean {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
override isLocal(): boolean {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
override getPath(): string {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
override getFilePath(): string {
|
||||||
|
debug.fail(`EmptyUri.getFilePath() should not be called.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
override toString(): string {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
override toUserVisibleString(): string {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
override matchesRegex(regex: RegExp): boolean {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
override replaceExtension(ext: string): Uri {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
override addPath(extra: string): Uri {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
override getDirectory(): Uri {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
override isRoot(): boolean {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
override isChild(parent: Uri): boolean {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
override startsWith(other: Uri | undefined): boolean {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
override getPathLength(): number {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
override combinePaths(...paths: string[]): Uri {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
override getShortenedFileName(maxDirLength: number): string {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
override stripExtension(): Uri {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override getPathComponentsImpl(): string[] {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override getRootPath(): string {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override getComparablePath(): string {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
203
packages/pyright-internal/src/common/uri/fileUri.ts
Normal file
203
packages/pyright-internal/src/common/uri/fileUri.ts
Normal file
@ -0,0 +1,203 @@
|
|||||||
|
/*
|
||||||
|
* fileUri.ts
|
||||||
|
* Copyright (c) Microsoft Corporation.
|
||||||
|
* Licensed under the MIT license.
|
||||||
|
*
|
||||||
|
* URI class that represents a file path. These URIs are always 'file' schemed.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { URI } from 'vscode-uri';
|
||||||
|
import {
|
||||||
|
ensureTrailingDirectorySeparator,
|
||||||
|
getDirectoryPath,
|
||||||
|
getFileExtension,
|
||||||
|
getFileName,
|
||||||
|
getPathComponents,
|
||||||
|
getRootLength,
|
||||||
|
hasTrailingDirectorySeparator,
|
||||||
|
isDiskPathRoot,
|
||||||
|
normalizeSlashes,
|
||||||
|
resolvePaths,
|
||||||
|
} from '../pathUtils';
|
||||||
|
import { BaseUri } from './baseUri';
|
||||||
|
import { cacheMethodWithNoArgs, cacheProperty, cacheStaticFunc } from './memoization';
|
||||||
|
import { Uri } from './uri';
|
||||||
|
|
||||||
|
export class FileUri extends BaseUri {
|
||||||
|
private _formattedString: string | undefined;
|
||||||
|
private constructor(
|
||||||
|
key: string,
|
||||||
|
private readonly _filePath: string,
|
||||||
|
private readonly _query: string,
|
||||||
|
private readonly _fragment: string,
|
||||||
|
private readonly _originalString: string | undefined,
|
||||||
|
private readonly _isCaseSensitive: boolean
|
||||||
|
) {
|
||||||
|
super(_isCaseSensitive ? key : key.toLowerCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
override get scheme(): string {
|
||||||
|
return 'file';
|
||||||
|
}
|
||||||
|
|
||||||
|
@cacheProperty()
|
||||||
|
override get fileName(): string {
|
||||||
|
return getFileName(this._filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
@cacheProperty()
|
||||||
|
override get lastExtension(): string {
|
||||||
|
return getFileExtension(this._filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
@cacheProperty()
|
||||||
|
override get root(): Uri {
|
||||||
|
const rootPath = this.getRootPath();
|
||||||
|
if (rootPath !== this._filePath) {
|
||||||
|
return FileUri.createFileUri(rootPath, '', '', undefined, this._isCaseSensitive);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
get isCaseSensitive(): boolean {
|
||||||
|
return this._isCaseSensitive;
|
||||||
|
}
|
||||||
|
|
||||||
|
@cacheStaticFunc()
|
||||||
|
static createFileUri(
|
||||||
|
filePath: string,
|
||||||
|
query: string,
|
||||||
|
fragment: string,
|
||||||
|
originalString: string | undefined,
|
||||||
|
isCaseSensitive: boolean
|
||||||
|
): FileUri {
|
||||||
|
const key = FileUri._createKey(filePath, query, fragment);
|
||||||
|
return new FileUri(key, filePath, query, fragment, originalString, isCaseSensitive);
|
||||||
|
}
|
||||||
|
|
||||||
|
static isFileUri(uri: Uri): uri is FileUri {
|
||||||
|
return uri.scheme === 'file' && (uri as any)._filePath !== undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
override matchesRegex(regex: RegExp): boolean {
|
||||||
|
// Compare the regex to our path but normalize it for comparison.
|
||||||
|
// The regex assumes it's comparing itself to a URI path.
|
||||||
|
const path = this.normalizeSlashes(this._filePath);
|
||||||
|
return regex.test(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
override toString(): string {
|
||||||
|
if (!this._formattedString) {
|
||||||
|
this._formattedString =
|
||||||
|
this._originalString ||
|
||||||
|
URI.file(this._filePath).with({ query: this._query, fragment: this._fragment }).toString();
|
||||||
|
}
|
||||||
|
return this._formattedString;
|
||||||
|
}
|
||||||
|
|
||||||
|
override toUserVisibleString(): string {
|
||||||
|
return this._filePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
override addPath(extra: string): Uri {
|
||||||
|
return FileUri.createFileUri(this._filePath + extra, '', '', undefined, this._isCaseSensitive);
|
||||||
|
}
|
||||||
|
|
||||||
|
override isRoot(): boolean {
|
||||||
|
return isDiskPathRoot(this._filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
override isChild(parent: Uri): boolean {
|
||||||
|
if (!FileUri.isFileUri(parent)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent._filePath.length < this._filePath.length && this.startsWith(parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
override isLocal(): boolean {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
override startsWith(other: Uri | undefined): boolean {
|
||||||
|
if (!other || !FileUri.isFileUri(other)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (other.isEmpty() !== this.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (this._filePath.length >= other._filePath.length) {
|
||||||
|
// Make sure the other ends with a / when comparing longer paths, otherwise we might
|
||||||
|
// say that /a/food is a child of /a/foo.
|
||||||
|
const otherPath =
|
||||||
|
this._filePath.length > other._filePath.length && !hasTrailingDirectorySeparator(other._filePath)
|
||||||
|
? ensureTrailingDirectorySeparator(other._filePath)
|
||||||
|
: other._filePath;
|
||||||
|
|
||||||
|
if (!this.isCaseSensitive) {
|
||||||
|
return this._filePath.toLowerCase().startsWith(otherPath.toLowerCase());
|
||||||
|
}
|
||||||
|
return this._filePath.startsWith(otherPath);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
override getPathLength(): number {
|
||||||
|
return this._filePath.length;
|
||||||
|
}
|
||||||
|
override getPath(): string {
|
||||||
|
return this.normalizeSlashes(this._filePath);
|
||||||
|
}
|
||||||
|
override getFilePath(): string {
|
||||||
|
return this._filePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
override combinePaths(...paths: string[]): Uri {
|
||||||
|
// Resolve and combine paths, never want URIs with '..' in the middle.
|
||||||
|
let combined = resolvePaths(this._filePath, ...paths);
|
||||||
|
|
||||||
|
// Make sure to remove any trailing directory chars.
|
||||||
|
if (hasTrailingDirectorySeparator(combined) && combined.length > 1) {
|
||||||
|
combined = combined.slice(0, combined.length - 1);
|
||||||
|
}
|
||||||
|
if (combined !== this._filePath) {
|
||||||
|
return FileUri.createFileUri(combined, '', '', undefined, this._isCaseSensitive);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@cacheMethodWithNoArgs()
|
||||||
|
override getDirectory(): Uri {
|
||||||
|
const filePath = this._filePath;
|
||||||
|
let dir = getDirectoryPath(filePath);
|
||||||
|
if (hasTrailingDirectorySeparator(dir) && dir.length > 1) {
|
||||||
|
dir = dir.slice(0, -1);
|
||||||
|
}
|
||||||
|
if (dir !== filePath) {
|
||||||
|
return FileUri.createFileUri(dir, '', '', undefined, this._isCaseSensitive);
|
||||||
|
} else {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override getPathComponentsImpl(): string[] {
|
||||||
|
const components = getPathComponents(this._filePath);
|
||||||
|
// Remove the first one if it's empty. The new algorithm doesn't
|
||||||
|
// expect this to be there.
|
||||||
|
if (components.length > 0 && components[0] === '') {
|
||||||
|
components.shift();
|
||||||
|
}
|
||||||
|
return components.map((component) => this.normalizeSlashes(component));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override getRootPath(): string {
|
||||||
|
return this._filePath.slice(0, getRootLength(this._filePath));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override getComparablePath(): string {
|
||||||
|
return normalizeSlashes(this._filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static _createKey(filePath: string, query: string, fragment: string) {
|
||||||
|
return `${filePath}${query ? '?' + query : ''}${fragment ? '#' + fragment : ''}`;
|
||||||
|
}
|
||||||
|
}
|
70
packages/pyright-internal/src/common/uri/memoization.ts
Normal file
70
packages/pyright-internal/src/common/uri/memoization.ts
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* memoization.ts
|
||||||
|
* Copyright (c) Microsoft Corporation.
|
||||||
|
* Licensed under the MIT license.
|
||||||
|
*
|
||||||
|
* Decorators used to memoize the result of a function call.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Cache for static method results.
|
||||||
|
const staticCache = new Map<string, any>();
|
||||||
|
|
||||||
|
// Caches the results of a getter property.
|
||||||
|
export function cacheProperty() {
|
||||||
|
return function (target: any, functionName: string, descriptor: PropertyDescriptor) {
|
||||||
|
const originalMethod = descriptor.get;
|
||||||
|
descriptor.get = function (this: any, ...args: any) {
|
||||||
|
// Call the function once to get the result.
|
||||||
|
const result = originalMethod!.apply(this, args);
|
||||||
|
|
||||||
|
// Then we replace the original function with one that just returns the result.
|
||||||
|
Object.defineProperty(this, functionName, {
|
||||||
|
get() {
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
return descriptor;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Caches the results of method that takes no args.
|
||||||
|
// This situation can be optimized because the parameters are always the same.
|
||||||
|
export function cacheMethodWithNoArgs() {
|
||||||
|
return function (target: any, functionName: string, descriptor: PropertyDescriptor) {
|
||||||
|
const originalMethod = descriptor.value;
|
||||||
|
descriptor.value = function (this: any, ...args: any) {
|
||||||
|
// Call the function once to get the result.
|
||||||
|
const result = originalMethod.apply(this, args);
|
||||||
|
|
||||||
|
// Then we replace the original function with one that just returns the result.
|
||||||
|
this[functionName] = () => {
|
||||||
|
// Note that this poses a risk. The result is passed by reference, so if the caller
|
||||||
|
// modifies the result, it will modify the cached result.
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
return descriptor;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a decorator to cache the results of a static method.
|
||||||
|
export function cacheStaticFunc() {
|
||||||
|
return function cacheStaticFunc_Fast(target: any, functionName: string, descriptor: PropertyDescriptor) {
|
||||||
|
const originalMethod = descriptor.value;
|
||||||
|
descriptor.value = function (...args: any) {
|
||||||
|
const key = `${functionName}+${args.map((a: any) => a?.toString()).join(',')}`;
|
||||||
|
let cachedResult: any;
|
||||||
|
if (!staticCache.has(key)) {
|
||||||
|
cachedResult = originalMethod.apply(this, args);
|
||||||
|
staticCache.set(key, cachedResult);
|
||||||
|
} else {
|
||||||
|
cachedResult = staticCache.get(key);
|
||||||
|
}
|
||||||
|
return cachedResult;
|
||||||
|
};
|
||||||
|
return descriptor;
|
||||||
|
};
|
||||||
|
}
|
215
packages/pyright-internal/src/common/uri/uri.ts
Normal file
215
packages/pyright-internal/src/common/uri/uri.ts
Normal file
@ -0,0 +1,215 @@
|
|||||||
|
/*
|
||||||
|
* uri.ts
|
||||||
|
* Copyright (c) Microsoft Corporation.
|
||||||
|
* Licensed under the MIT license.
|
||||||
|
*
|
||||||
|
* URI namespace for storing and manipulating URIs.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { URI, Utils } from 'vscode-uri';
|
||||||
|
import { combinePaths, isRootedDiskPath } from '../pathUtils';
|
||||||
|
import { EmptyUri } from './emptyUri';
|
||||||
|
import { FileUri } from './fileUri';
|
||||||
|
import { WebUri } from './webUri';
|
||||||
|
|
||||||
|
export interface Uri {
|
||||||
|
// Unique key for storing in maps.
|
||||||
|
readonly key: string;
|
||||||
|
|
||||||
|
// Returns the scheme of the URI.
|
||||||
|
readonly scheme: string;
|
||||||
|
|
||||||
|
// Returns the last segment of the URI, similar to the UNIX basename command.
|
||||||
|
readonly fileName: string;
|
||||||
|
|
||||||
|
// Returns the extension of the URI, similar to the UNIX extname command. This includes '.' on the extension.
|
||||||
|
readonly lastExtension: string;
|
||||||
|
|
||||||
|
// Returns a URI where the path just contains the root folder.
|
||||||
|
readonly root: Uri;
|
||||||
|
|
||||||
|
// Returns a URI where the path contains the directory name with .py appended.
|
||||||
|
readonly packageUri: Uri;
|
||||||
|
|
||||||
|
// Returns a URI where the path contains the directory name with .pyi appended.
|
||||||
|
readonly packageStubUri: Uri;
|
||||||
|
|
||||||
|
// Returns a URI where the path has __init__.py appended.
|
||||||
|
readonly initPyUri: Uri;
|
||||||
|
|
||||||
|
// Returns a URI where the path has __init__.pyi appended.
|
||||||
|
readonly initPyiUri: Uri;
|
||||||
|
|
||||||
|
// Returns a URI where the path has py.typed appended.
|
||||||
|
readonly pytypedUri: Uri;
|
||||||
|
|
||||||
|
// Returns the filename without any extensions
|
||||||
|
readonly fileNameWithoutExtension: string;
|
||||||
|
|
||||||
|
// Indicates if the underlying file system for this URI is case sensitive or not.
|
||||||
|
readonly isCaseSensitive: boolean;
|
||||||
|
|
||||||
|
isEmpty(): boolean;
|
||||||
|
|
||||||
|
toString(): string;
|
||||||
|
|
||||||
|
toUserVisibleString(): string;
|
||||||
|
|
||||||
|
// Determines whether a path consists only of a path root.
|
||||||
|
isRoot(): boolean;
|
||||||
|
|
||||||
|
// Determines whether a Uri is a child of some parent Uri.
|
||||||
|
isChild(parent: Uri): boolean;
|
||||||
|
|
||||||
|
isLocal(): boolean;
|
||||||
|
|
||||||
|
isUntitled(): boolean;
|
||||||
|
|
||||||
|
equals(other: Uri | undefined): boolean;
|
||||||
|
|
||||||
|
startsWith(other: Uri | undefined): boolean;
|
||||||
|
|
||||||
|
pathStartsWith(name: string): boolean;
|
||||||
|
|
||||||
|
pathEndsWith(name: string): boolean;
|
||||||
|
|
||||||
|
pathIncludes(include: string): boolean;
|
||||||
|
matchesRegex(regex: RegExp): boolean;
|
||||||
|
|
||||||
|
addPath(extra: string): Uri;
|
||||||
|
|
||||||
|
// Returns a URI where the path is the directory name of the original URI, similar to the UNIX dirname command.
|
||||||
|
getDirectory(): Uri;
|
||||||
|
|
||||||
|
getRootPathLength(): number;
|
||||||
|
|
||||||
|
// How long the path for this Uri is.
|
||||||
|
getPathLength(): number;
|
||||||
|
|
||||||
|
combinePaths(...paths: string[]): Uri;
|
||||||
|
|
||||||
|
getRelativePath(child: Uri): string | undefined;
|
||||||
|
|
||||||
|
getPathComponents(): readonly string[];
|
||||||
|
|
||||||
|
getPath(): string;
|
||||||
|
|
||||||
|
getFilePath(): string;
|
||||||
|
|
||||||
|
getRelativePathComponents(to: Uri): readonly string[];
|
||||||
|
getShortenedFileName(maxDirLength?: number): string;
|
||||||
|
|
||||||
|
stripExtension(): Uri;
|
||||||
|
|
||||||
|
stripAllExtensions(): Uri;
|
||||||
|
replaceExtension(ext: string): Uri;
|
||||||
|
|
||||||
|
addExtension(ext: string): Uri;
|
||||||
|
hasExtension(ext: string): boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns just the fsPath path portion of a vscode URI.
|
||||||
|
function getFilePath(uri: URI): string {
|
||||||
|
let filePath: string | undefined;
|
||||||
|
|
||||||
|
// Compute the file path ourselves. The vscode.URI class doesn't
|
||||||
|
// treat UNC shares with a single slash as UNC paths.
|
||||||
|
// https://github.com/microsoft/vscode-uri/blob/53e4ca6263f2e4ddc35f5360c62bc1b1d30f27dd/src/uri.ts#L567
|
||||||
|
if (uri.authority && uri.path[0] === '/' && uri.path.length === 1) {
|
||||||
|
filePath = `//${uri.authority}${uri.path}`;
|
||||||
|
} else {
|
||||||
|
// Otherwise use the vscode.URI version
|
||||||
|
filePath = uri.fsPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this is a DOS-style path with a drive letter, remove
|
||||||
|
// the leading slash.
|
||||||
|
if (filePath.match(/^\/[a-zA-Z]:\//)) {
|
||||||
|
filePath = filePath.slice(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// vscode.URI normalizes the path to use the correct path separators.
|
||||||
|
// We need to do the same.
|
||||||
|
if (process.platform === 'win32') {
|
||||||
|
filePath = filePath.replace(/\//g, '\\');
|
||||||
|
}
|
||||||
|
|
||||||
|
return filePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function called to normalize input URIs. This gets rid of '..' and '.' in the path.
|
||||||
|
// It also removes any '/' on the end of the path.
|
||||||
|
// This is slow but should only be called when the URI is first created.
|
||||||
|
function normalizeUri(uri: string | URI): { uri: URI; str: string } {
|
||||||
|
// Make sure the drive letter is lower case. This
|
||||||
|
// is consistent with what VS code does for URIs.
|
||||||
|
let originalString = URI.isUri(uri) ? uri.toString() : uri;
|
||||||
|
const parsed = URI.isUri(uri) ? uri : URI.parse(uri);
|
||||||
|
if (parsed.scheme === 'file') {
|
||||||
|
// The Vscode.URI parser makes sure the drive is lower cased.
|
||||||
|
originalString = parsed.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Original URI may not have resolved all the `..` in the path, so remove them.
|
||||||
|
// Note: this also has the effect of removing any trailing slashes.
|
||||||
|
const finalURI = Utils.resolvePath(parsed);
|
||||||
|
const finalString = finalURI.path.length !== parsed.path.length ? finalURI.toString() : originalString;
|
||||||
|
return { uri: finalURI, str: finalString };
|
||||||
|
}
|
||||||
|
|
||||||
|
export namespace Uri {
|
||||||
|
export function file(path: string, isCaseSensitive = true, checkRelative = false): Uri {
|
||||||
|
// Fix path if we're checking for relative paths and this is not a rooted path.
|
||||||
|
path = checkRelative && !isRootedDiskPath(path) ? combinePaths(process.cwd(), path) : path;
|
||||||
|
|
||||||
|
// If this already starts with 'file:', then we can
|
||||||
|
// parse it normally. It's actually a uri string. Otherwise parse it as a file path.
|
||||||
|
const normalized = path.startsWith('file:') ? normalizeUri(path) : normalizeUri(URI.file(path));
|
||||||
|
|
||||||
|
// Turn the path into a file URI.
|
||||||
|
return FileUri.createFileUri(
|
||||||
|
getFilePath(normalized.uri),
|
||||||
|
normalized.uri.query,
|
||||||
|
normalized.uri.fragment,
|
||||||
|
normalized.str,
|
||||||
|
isCaseSensitive
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function empty(): Uri {
|
||||||
|
return EmptyUri.instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parse(value: string | undefined, isCaseSensitive: boolean): Uri {
|
||||||
|
if (!value) {
|
||||||
|
return Uri.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normalize the value here. This gets rid of '..' and '.' in the path. It also removes any
|
||||||
|
// '/' on the end of the path.
|
||||||
|
const normalized = normalizeUri(value);
|
||||||
|
if (normalized.uri.scheme === 'file') {
|
||||||
|
return FileUri.createFileUri(
|
||||||
|
getFilePath(normalized.uri),
|
||||||
|
normalized.uri.query,
|
||||||
|
normalized.uri.fragment,
|
||||||
|
normalized.str,
|
||||||
|
isCaseSensitive
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Web URIs are always case sensitive.
|
||||||
|
return WebUri.createWebUri(
|
||||||
|
normalized.uri.scheme,
|
||||||
|
normalized.uri.authority,
|
||||||
|
normalized.uri.path,
|
||||||
|
normalized.uri.query,
|
||||||
|
normalized.uri.fragment,
|
||||||
|
normalized.str
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isUri(thing: any): thing is Uri {
|
||||||
|
return !!thing && typeof thing._key === 'string';
|
||||||
|
}
|
||||||
|
}
|
406
packages/pyright-internal/src/common/uri/uriUtils.ts
Normal file
406
packages/pyright-internal/src/common/uri/uriUtils.ts
Normal file
@ -0,0 +1,406 @@
|
|||||||
|
/*
|
||||||
|
* uriUtils.ts
|
||||||
|
* Copyright (c) Microsoft Corporation.
|
||||||
|
* Licensed under the MIT license.
|
||||||
|
*
|
||||||
|
* Utility functions for manipulating URIs.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { Dirent } from 'fs';
|
||||||
|
|
||||||
|
import { randomBytesHex } from '../crypto';
|
||||||
|
import { FileSystem, ReadOnlyFileSystem, Stats, TempFile } from '../fileSystem';
|
||||||
|
import {
|
||||||
|
getRegexEscapedSeparator,
|
||||||
|
isDirectoryWildcardPatternPresent,
|
||||||
|
stripTrailingDirectorySeparator,
|
||||||
|
} from '../pathUtils';
|
||||||
|
import { Uri } from './uri';
|
||||||
|
|
||||||
|
let _fsCaseSensitivity: boolean | undefined = undefined;
|
||||||
|
let _underTest: boolean = false;
|
||||||
|
|
||||||
|
export interface FileSpec {
|
||||||
|
// File specs can contain wildcard characters (**, *, ?). This
|
||||||
|
// specifies the first portion of the file spec that contains
|
||||||
|
// no wildcards.
|
||||||
|
wildcardRoot: Uri;
|
||||||
|
|
||||||
|
// Regular expression that can be used to match against this
|
||||||
|
// file spec.
|
||||||
|
regExp: RegExp;
|
||||||
|
|
||||||
|
// Indicates whether the file spec has a directory wildcard (**).
|
||||||
|
// When present, the search cannot terminate without exploring to
|
||||||
|
// an arbitrary depth.
|
||||||
|
hasDirectoryWildcard: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const _includeFileRegex = /\.pyi?$/;
|
||||||
|
|
||||||
|
export namespace FileSpec {
|
||||||
|
export function is(value: any): value is FileSpec {
|
||||||
|
const candidate: FileSpec = value as FileSpec;
|
||||||
|
return candidate && !!candidate.wildcardRoot && !!candidate.regExp;
|
||||||
|
}
|
||||||
|
export function isInPath(uri: Uri, paths: FileSpec[]) {
|
||||||
|
return !!paths.find((p) => uri.matchesRegex(p.regExp));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function matchesIncludeFileRegex(uri: Uri, isFile = true) {
|
||||||
|
return isFile ? uri.matchesRegex(_includeFileRegex) : true;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function matchIncludeFileSpec(includeRegExp: RegExp, exclude: FileSpec[], uri: Uri, isFile = true) {
|
||||||
|
if (uri.matchesRegex(includeRegExp)) {
|
||||||
|
if (!FileSpec.isInPath(uri, exclude) && FileSpec.matchesIncludeFileRegex(uri, isFile)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FileSystemEntries {
|
||||||
|
files: Uri[];
|
||||||
|
directories: Uri[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function forEachAncestorDirectory(
|
||||||
|
directory: Uri,
|
||||||
|
callback: (directory: Uri) => Uri | undefined
|
||||||
|
): Uri | undefined {
|
||||||
|
while (true) {
|
||||||
|
const result = callback(directory);
|
||||||
|
if (result !== undefined) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
const parentPath = directory.getDirectory();
|
||||||
|
if (parentPath.equals(directory)) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
directory = parentPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates a directory hierarchy for a path, starting from some ancestor path.
|
||||||
|
export function makeDirectories(fs: FileSystem, dir: Uri, startingFrom: Uri) {
|
||||||
|
if (!dir.startsWith(startingFrom)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const pathComponents = dir.getPathComponents();
|
||||||
|
const relativeToComponents = startingFrom.getPathComponents();
|
||||||
|
let curPath = startingFrom;
|
||||||
|
|
||||||
|
for (let i = relativeToComponents.length; i < pathComponents.length; i++) {
|
||||||
|
curPath = curPath.combinePaths(pathComponents[i]);
|
||||||
|
if (!fs.existsSync(curPath)) {
|
||||||
|
fs.mkdirSync(curPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getFileSize(fs: ReadOnlyFileSystem, uri: Uri) {
|
||||||
|
const stat = tryStat(fs, uri);
|
||||||
|
if (stat?.isFile()) {
|
||||||
|
return stat.size;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function fileExists(fs: ReadOnlyFileSystem, uri: Uri): boolean {
|
||||||
|
return fileSystemEntryExists(fs, uri, FileSystemEntryKind.File);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function directoryExists(fs: ReadOnlyFileSystem, uri: Uri): boolean {
|
||||||
|
return fileSystemEntryExists(fs, uri, FileSystemEntryKind.Directory);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isDirectory(fs: ReadOnlyFileSystem, uri: Uri): boolean {
|
||||||
|
return tryStat(fs, uri)?.isDirectory() ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isFile(fs: ReadOnlyFileSystem, uri: Uri, treatZipDirectoryAsFile = false): boolean {
|
||||||
|
const stats = tryStat(fs, uri);
|
||||||
|
if (stats?.isFile()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!treatZipDirectoryAsFile) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return stats?.isZipDirectory?.() ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function tryStat(fs: ReadOnlyFileSystem, uri: Uri): Stats | undefined {
|
||||||
|
try {
|
||||||
|
if (fs.existsSync(uri)) {
|
||||||
|
return fs.statSync(uri);
|
||||||
|
}
|
||||||
|
} catch (e: any) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function tryRealpath(fs: ReadOnlyFileSystem, uri: Uri): Uri | undefined {
|
||||||
|
try {
|
||||||
|
return fs.realCasePath(uri);
|
||||||
|
} catch (e: any) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getFileSystemEntries(fs: ReadOnlyFileSystem, uri: Uri): FileSystemEntries {
|
||||||
|
try {
|
||||||
|
return getFileSystemEntriesFromDirEntries(fs.readdirEntriesSync(uri), fs, uri);
|
||||||
|
} catch (e: any) {
|
||||||
|
return { files: [], directories: [] };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sorts the entires into files and directories, including any symbolic links.
|
||||||
|
export function getFileSystemEntriesFromDirEntries(
|
||||||
|
dirEntries: Dirent[],
|
||||||
|
fs: ReadOnlyFileSystem,
|
||||||
|
uri: Uri
|
||||||
|
): FileSystemEntries {
|
||||||
|
const entries = dirEntries.sort((a, b) => {
|
||||||
|
if (a.name < b.name) {
|
||||||
|
return -1;
|
||||||
|
} else if (a.name > b.name) {
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const files: Uri[] = [];
|
||||||
|
const directories: Uri[] = [];
|
||||||
|
for (const entry of entries) {
|
||||||
|
// This is necessary because on some file system node fails to exclude
|
||||||
|
// "." and "..". See https://github.com/nodejs/node/issues/4002
|
||||||
|
if (entry.name === '.' || entry.name === '..') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const entryUri = uri.combinePaths(entry.name);
|
||||||
|
if (entry.isFile()) {
|
||||||
|
files.push(entryUri);
|
||||||
|
} else if (entry.isDirectory()) {
|
||||||
|
directories.push(entryUri);
|
||||||
|
} else if (entry.isSymbolicLink()) {
|
||||||
|
const stat = tryStat(fs, entryUri);
|
||||||
|
if (stat?.isFile()) {
|
||||||
|
files.push(entryUri);
|
||||||
|
} else if (stat?.isDirectory()) {
|
||||||
|
directories.push(entryUri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { files, directories };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setTestingMode(underTest: boolean) {
|
||||||
|
_underTest = underTest;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transforms a relative file spec (one that potentially contains
|
||||||
|
// escape characters **, * or ?) and returns a regular expression
|
||||||
|
// that can be used for matching against.
|
||||||
|
export function getWildcardRegexPattern(root: Uri, fileSpec: string): string {
|
||||||
|
const absolutePath = root.combinePaths(fileSpec);
|
||||||
|
const pathComponents = Array.from(absolutePath.getPathComponents());
|
||||||
|
const escapedSeparator = getRegexEscapedSeparator('/');
|
||||||
|
const doubleAsteriskRegexFragment = `(${escapedSeparator}[^${escapedSeparator}][^${escapedSeparator}]*)*?`;
|
||||||
|
const reservedCharacterPattern = new RegExp(`[^\\w\\s${escapedSeparator}]`, 'g');
|
||||||
|
|
||||||
|
// Strip the directory separator from the root component.
|
||||||
|
if (pathComponents.length > 0) {
|
||||||
|
pathComponents[0] = stripTrailingDirectorySeparator(pathComponents[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
let regExPattern = '';
|
||||||
|
let firstComponent = true;
|
||||||
|
|
||||||
|
for (let component of pathComponents) {
|
||||||
|
if (component === '**') {
|
||||||
|
regExPattern += doubleAsteriskRegexFragment;
|
||||||
|
} else {
|
||||||
|
if (!firstComponent) {
|
||||||
|
component = escapedSeparator + component;
|
||||||
|
}
|
||||||
|
|
||||||
|
regExPattern += component.replace(reservedCharacterPattern, (match) => {
|
||||||
|
if (match === '*') {
|
||||||
|
return `[^${escapedSeparator}]*`;
|
||||||
|
} else if (match === '?') {
|
||||||
|
return `[^${escapedSeparator}]`;
|
||||||
|
} else {
|
||||||
|
// escaping anything that is not reserved characters - word/space/separator
|
||||||
|
return '\\' + match;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
firstComponent = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return regExPattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the topmost path that contains no wildcard characters.
|
||||||
|
export function getWildcardRoot(root: Uri, fileSpec: string): Uri {
|
||||||
|
const absolutePath = root.combinePaths(fileSpec);
|
||||||
|
// make a copy of the path components so we can modify them.
|
||||||
|
const pathComponents = Array.from(absolutePath.getPathComponents());
|
||||||
|
let wildcardRoot = absolutePath.root;
|
||||||
|
|
||||||
|
// Remove the root component.
|
||||||
|
if (pathComponents.length > 0) {
|
||||||
|
pathComponents.shift();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const component of pathComponents) {
|
||||||
|
if (component === '**') {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
if (/[*?]/.test(component)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
wildcardRoot = wildcardRoot.combinePaths(component);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return wildcardRoot;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function hasPythonExtension(uri: Uri) {
|
||||||
|
return uri.hasExtension('.py') || uri.hasExtension('.pyi');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getFileSpec(root: Uri, fileSpec: string): FileSpec {
|
||||||
|
let regExPattern = getWildcardRegexPattern(root, fileSpec);
|
||||||
|
const escapedSeparator = getRegexEscapedSeparator('/');
|
||||||
|
regExPattern = `^(${regExPattern})($|${escapedSeparator})`;
|
||||||
|
|
||||||
|
const regExp = new RegExp(regExPattern, root.isCaseSensitive ? undefined : 'i');
|
||||||
|
const wildcardRoot = getWildcardRoot(root, fileSpec);
|
||||||
|
const hasDirectoryWildcard = isDirectoryWildcardPatternPresent(fileSpec);
|
||||||
|
|
||||||
|
return {
|
||||||
|
wildcardRoot,
|
||||||
|
regExp,
|
||||||
|
hasDirectoryWildcard,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const enum FileSystemEntryKind {
|
||||||
|
File,
|
||||||
|
Directory,
|
||||||
|
}
|
||||||
|
|
||||||
|
function fileSystemEntryExists(fs: ReadOnlyFileSystem, uri: Uri, entryKind: FileSystemEntryKind): boolean {
|
||||||
|
try {
|
||||||
|
const stat = fs.statSync(uri);
|
||||||
|
switch (entryKind) {
|
||||||
|
case FileSystemEntryKind.File:
|
||||||
|
return stat.isFile();
|
||||||
|
case FileSystemEntryKind.Directory:
|
||||||
|
return stat.isDirectory();
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch (e: any) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const isFileSystemCaseSensitiveMap = new WeakMap<FileSystem, boolean>();
|
||||||
|
|
||||||
|
export function isFileSystemCaseSensitive(fs: FileSystem, tmp: TempFile | undefined) {
|
||||||
|
if (!_underTest && _fsCaseSensitivity !== undefined) {
|
||||||
|
return _fsCaseSensitivity;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isFileSystemCaseSensitiveMap.has(fs)) {
|
||||||
|
_fsCaseSensitivity = tmp ? isFileSystemCaseSensitiveInternal(fs, tmp) : false;
|
||||||
|
isFileSystemCaseSensitiveMap.set(fs, _fsCaseSensitivity);
|
||||||
|
}
|
||||||
|
return !!isFileSystemCaseSensitiveMap.get(fs);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isFileSystemCaseSensitiveInternal(fs: FileSystem, tmp: TempFile) {
|
||||||
|
let filePath: Uri | undefined = undefined;
|
||||||
|
try {
|
||||||
|
// Make unique file name.
|
||||||
|
let name: string;
|
||||||
|
let mangledFilePath: Uri;
|
||||||
|
do {
|
||||||
|
name = `${randomBytesHex(21)}-a`;
|
||||||
|
filePath = tmp.tmpdir().combinePaths(name);
|
||||||
|
mangledFilePath = tmp.tmpdir().combinePaths(name.toUpperCase());
|
||||||
|
} while (fs.existsSync(filePath) || fs.existsSync(mangledFilePath));
|
||||||
|
|
||||||
|
fs.writeFileSync(filePath, '', 'utf8');
|
||||||
|
|
||||||
|
// If file exists, then it is insensitive.
|
||||||
|
return !fs.existsSync(mangledFilePath);
|
||||||
|
} catch (e: any) {
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
if (filePath) {
|
||||||
|
// remove temp file created
|
||||||
|
try {
|
||||||
|
fs.unlinkSync(filePath);
|
||||||
|
} catch (e: any) {
|
||||||
|
/* ignored */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deduplicateFolders(listOfFolders: Uri[][]): Uri[] {
|
||||||
|
const foldersToWatch = new Map<string, Uri>();
|
||||||
|
|
||||||
|
listOfFolders.forEach((folders) => {
|
||||||
|
folders.forEach((p) => {
|
||||||
|
if (foldersToWatch.has(p.key)) {
|
||||||
|
// Bail out on exact match.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const existing of foldersToWatch) {
|
||||||
|
// ex) p: "/user/test" existing: "/user"
|
||||||
|
if (p.startsWith(existing[1])) {
|
||||||
|
// We already have the parent folder in the watch list
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ex) p: "/user" folderToWatch: "/user/test"
|
||||||
|
if (existing[1].startsWith(p)) {
|
||||||
|
// We found better one to watch. replace.
|
||||||
|
foldersToWatch.delete(existing[0]);
|
||||||
|
foldersToWatch.set(p.key, p);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foldersToWatch.set(p.key, p);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return [...foldersToWatch.values()];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getRootUri(isCaseSensitive: boolean): Uri | undefined {
|
||||||
|
if ((global as any).__rootDirectory) {
|
||||||
|
return Uri.file((global as any).__rootDirectory, isCaseSensitive);
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
207
packages/pyright-internal/src/common/uri/webUri.ts
Normal file
207
packages/pyright-internal/src/common/uri/webUri.ts
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
/*
|
||||||
|
* webUri.ts
|
||||||
|
* Copyright (c) Microsoft Corporation.
|
||||||
|
* Licensed under the MIT license.
|
||||||
|
*
|
||||||
|
* URI class that represents a URI that isn't 'file' schemed.
|
||||||
|
* This can be URIs like:
|
||||||
|
* - http://www.microsoft.com/file.txt
|
||||||
|
* - untitled:Untitled-1
|
||||||
|
* - vscode:extension/ms-python.python
|
||||||
|
* - vscode-vfs://github.com/microsoft/debugpy/debugpy/launcher/debugAdapter.py
|
||||||
|
*/
|
||||||
|
|
||||||
|
import * as debug from '../debug';
|
||||||
|
import { getRootLength, hasTrailingDirectorySeparator, normalizeSlashes, resolvePaths } from '../pathUtils';
|
||||||
|
import { BaseUri } from './baseUri';
|
||||||
|
import { cacheMethodWithNoArgs, cacheProperty, cacheStaticFunc } from './memoization';
|
||||||
|
import { Uri } from './uri';
|
||||||
|
|
||||||
|
export class WebUri extends BaseUri {
|
||||||
|
private constructor(
|
||||||
|
key: string,
|
||||||
|
private readonly _scheme: string,
|
||||||
|
private readonly _authority: string,
|
||||||
|
private readonly _path: string,
|
||||||
|
private readonly _query: string,
|
||||||
|
private readonly _fragment: string,
|
||||||
|
private readonly _originalString: string | undefined
|
||||||
|
) {
|
||||||
|
super(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
override get scheme(): string {
|
||||||
|
return this._scheme;
|
||||||
|
}
|
||||||
|
|
||||||
|
get isCaseSensitive(): boolean {
|
||||||
|
// Web URIs are always case sensitive
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@cacheProperty()
|
||||||
|
override get root(): Uri {
|
||||||
|
const rootPath = this.getRootPath();
|
||||||
|
if (rootPath !== this._path) {
|
||||||
|
return WebUri.createWebUri(this._scheme, this._authority, rootPath, '', '', undefined);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@cacheProperty()
|
||||||
|
override get fileName(): string {
|
||||||
|
// Path should already be normalized, just get the last on a split of '/'.
|
||||||
|
const components = this._path.split('/');
|
||||||
|
return components[components.length - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
@cacheProperty()
|
||||||
|
override get lastExtension(): string {
|
||||||
|
const basename = this.fileName;
|
||||||
|
const index = basename.lastIndexOf('.');
|
||||||
|
if (index >= 0) {
|
||||||
|
return basename.slice(index);
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
@cacheStaticFunc()
|
||||||
|
static createWebUri(
|
||||||
|
scheme: string,
|
||||||
|
authority: string,
|
||||||
|
path: string,
|
||||||
|
query: string,
|
||||||
|
fragment: string,
|
||||||
|
originalString: string | undefined
|
||||||
|
): WebUri {
|
||||||
|
const key = WebUri._createKey(scheme, authority, path, query, fragment);
|
||||||
|
return new WebUri(key, scheme, authority, path, query, fragment, originalString);
|
||||||
|
}
|
||||||
|
|
||||||
|
override toString(): string {
|
||||||
|
if (!this._originalString) {
|
||||||
|
return `${this._scheme}://${this._authority}${this._path}${this._query ? '?' + this._query : ''}${
|
||||||
|
this._fragment ? '#' + this._fragment : ''
|
||||||
|
}`;
|
||||||
|
}
|
||||||
|
return this._originalString;
|
||||||
|
}
|
||||||
|
override toUserVisibleString(): string {
|
||||||
|
return this.toString();
|
||||||
|
}
|
||||||
|
static isWebUri(uri: Uri): uri is WebUri {
|
||||||
|
return uri.scheme !== 'file' && (uri as any)._scheme !== undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
override matchesRegex(regex: RegExp): boolean {
|
||||||
|
return regex.test(this._path);
|
||||||
|
}
|
||||||
|
|
||||||
|
override addPath(extra: string): Uri {
|
||||||
|
const newPath = this._path + extra;
|
||||||
|
return WebUri.createWebUri(this._scheme, this._authority, newPath, this._query, this._fragment, undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
override isRoot(): boolean {
|
||||||
|
return this._path === this.getRootPath() && this._path.length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
override isChild(parent: Uri): boolean {
|
||||||
|
if (!WebUri.isWebUri(parent)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent._path.length < this._path.length && this.startsWith(parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
override isLocal(): boolean {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
override startsWith(other: Uri | undefined): boolean {
|
||||||
|
if (!other || !WebUri.isWebUri(other)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (other.isEmpty() !== this.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (this.scheme !== other.scheme) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (this._path.length >= other._path.length) {
|
||||||
|
// Make sure the other ends with a / when comparing longer paths, otherwise we might
|
||||||
|
// say that /a/food is a child of /a/foo.
|
||||||
|
const otherPath =
|
||||||
|
this._path.length > other._path.length && !hasTrailingDirectorySeparator(other._path)
|
||||||
|
? `${other._path}/`
|
||||||
|
: other._path;
|
||||||
|
|
||||||
|
return this._path.startsWith(otherPath);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
override getPathLength(): number {
|
||||||
|
return this._path.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
override getPath(): string {
|
||||||
|
return this._path;
|
||||||
|
}
|
||||||
|
|
||||||
|
override getFilePath(): string {
|
||||||
|
debug.fail(`${this} is not a file based URI.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
override combinePaths(...paths: string[]): Uri {
|
||||||
|
// Resolve and combine paths, never want URIs with '..' in the middle.
|
||||||
|
let combined = this.normalizeSlashes(resolvePaths(this._path, ...paths));
|
||||||
|
|
||||||
|
// Make sure to remove any trailing directory chars.
|
||||||
|
if (hasTrailingDirectorySeparator(combined) && combined.length > 1) {
|
||||||
|
combined = combined.slice(0, combined.length - 1);
|
||||||
|
}
|
||||||
|
if (combined !== this._path) {
|
||||||
|
return WebUri.createWebUri(this._scheme, this._authority, combined, '', '', undefined);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@cacheMethodWithNoArgs()
|
||||||
|
override getDirectory(): Uri {
|
||||||
|
const index = this._path.lastIndexOf('/');
|
||||||
|
if (index > 0) {
|
||||||
|
return WebUri.createWebUri(
|
||||||
|
this._scheme,
|
||||||
|
this._authority,
|
||||||
|
this._path.slice(0, index),
|
||||||
|
this._query,
|
||||||
|
this._fragment,
|
||||||
|
undefined
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override getPathComponentsImpl(): string[] {
|
||||||
|
// Get the root path and the rest of the path components.
|
||||||
|
const rootPath = this.getRootPath();
|
||||||
|
const otherPaths = this._path.slice(rootPath.length).split('/');
|
||||||
|
return this.reducePathComponents([rootPath, ...otherPaths]).map((component) =>
|
||||||
|
this.normalizeSlashes(component)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override getRootPath(): string {
|
||||||
|
const rootLength = getRootLength(this._path, '/');
|
||||||
|
return this._path.slice(0, rootLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override getComparablePath(): string {
|
||||||
|
return normalizeSlashes(this._path);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static _createKey(scheme: string, authority: string, path: string, query: string, fragment: string) {
|
||||||
|
return `${scheme}://${authority}${path}${query ? '?' + query : ''}${fragment ? '#' + fragment : ''}`;
|
||||||
|
}
|
||||||
|
}
|
@ -1,62 +0,0 @@
|
|||||||
/*
|
|
||||||
* uriParser.ts
|
|
||||||
* Copyright (c) Microsoft Corporation.
|
|
||||||
* Licensed under the MIT license.
|
|
||||||
*
|
|
||||||
* URI utility functions.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { Position } from 'vscode-languageserver';
|
|
||||||
import { TextDocumentIdentifier } from 'vscode-languageserver-protocol';
|
|
||||||
import { URI } from 'vscode-uri';
|
|
||||||
|
|
||||||
import { isString } from './core';
|
|
||||||
import { FileSystem } from './fileSystem';
|
|
||||||
import { convertUriToPath } from './pathUtils';
|
|
||||||
|
|
||||||
export interface IUriParser {
|
|
||||||
decodeTextDocumentPosition(
|
|
||||||
textDocument: TextDocumentIdentifier,
|
|
||||||
position: Position
|
|
||||||
): { filePath: string; position: Position };
|
|
||||||
decodeTextDocumentUri(uriString: string): string;
|
|
||||||
isUntitled(uri: URI | string | undefined): boolean;
|
|
||||||
isLocal(uri: URI | string | undefined): boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class UriParser implements IUriParser {
|
|
||||||
constructor(protected readonly fs: FileSystem) {}
|
|
||||||
|
|
||||||
decodeTextDocumentPosition(textDocument: TextDocumentIdentifier, position: Position) {
|
|
||||||
const filePath = this.decodeTextDocumentUri(textDocument.uri);
|
|
||||||
return { filePath, position };
|
|
||||||
}
|
|
||||||
|
|
||||||
decodeTextDocumentUri(uriString: string) {
|
|
||||||
return convertUriToPath(this.fs, uriString);
|
|
||||||
}
|
|
||||||
|
|
||||||
isUntitled(uri: URI | string | undefined) {
|
|
||||||
if (!uri) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isString(uri)) {
|
|
||||||
uri = URI.parse(uri);
|
|
||||||
}
|
|
||||||
|
|
||||||
return uri.scheme === 'untitled';
|
|
||||||
}
|
|
||||||
|
|
||||||
isLocal(uri: URI | string | undefined) {
|
|
||||||
if (!uri) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isString(uri)) {
|
|
||||||
uri = URI.parse(uri);
|
|
||||||
}
|
|
||||||
|
|
||||||
return uri.scheme === 'file';
|
|
||||||
}
|
|
||||||
}
|
|
@ -19,15 +19,14 @@ import {
|
|||||||
import { TextDocument } from 'vscode-languageserver-textdocument';
|
import { TextDocument } from 'vscode-languageserver-textdocument';
|
||||||
import { AnalyzerService } from '../analyzer/service';
|
import { AnalyzerService } from '../analyzer/service';
|
||||||
import { FileEditAction, FileEditActions, TextEditAction } from '../common/editAction';
|
import { FileEditAction, FileEditActions, TextEditAction } from '../common/editAction';
|
||||||
import { convertPathToUri, convertUriToPath } from '../common/pathUtils';
|
|
||||||
import { createMapFromItems } from './collectionUtils';
|
import { createMapFromItems } from './collectionUtils';
|
||||||
import { isArray } from './core';
|
import { isArray } from './core';
|
||||||
import { assertNever } from './debug';
|
import { assertNever } from './debug';
|
||||||
import { EditableProgram, SourceFileInfo } from './extensibility';
|
import { EditableProgram, SourceFileInfo } from './extensibility';
|
||||||
import { ReadOnlyFileSystem } from './fileSystem';
|
|
||||||
import { convertRangeToTextRange, convertTextRangeToRange } from './positionUtils';
|
import { convertRangeToTextRange, convertTextRangeToRange } from './positionUtils';
|
||||||
import { TextRange } from './textRange';
|
import { TextRange } from './textRange';
|
||||||
import { TextRangeCollection } from './textRangeCollection';
|
import { TextRangeCollection } from './textRangeCollection';
|
||||||
|
import { Uri } from './uri/uri';
|
||||||
|
|
||||||
export function convertToTextEdits(editActions: TextEditAction[]): TextEdit[] {
|
export function convertToTextEdits(editActions: TextEditAction[]): TextEdit[] {
|
||||||
return editActions.map((editAction) => ({
|
return editActions.map((editAction) => ({
|
||||||
@ -36,14 +35,13 @@ export function convertToTextEdits(editActions: TextEditAction[]): TextEdit[] {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function convertToFileTextEdits(filePath: string, editActions: TextEditAction[]): FileEditAction[] {
|
export function convertToFileTextEdits(fileUri: Uri, editActions: TextEditAction[]): FileEditAction[] {
|
||||||
return editActions.map((a) => ({ filePath, ...a }));
|
return editActions.map((a) => ({ fileUri, ...a }));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function convertToWorkspaceEdit(fs: ReadOnlyFileSystem, edits: FileEditAction[]): WorkspaceEdit;
|
export function convertToWorkspaceEdit(edits: FileEditAction[]): WorkspaceEdit;
|
||||||
export function convertToWorkspaceEdit(fs: ReadOnlyFileSystem, edits: FileEditActions): WorkspaceEdit;
|
export function convertToWorkspaceEdit(edits: FileEditActions): WorkspaceEdit;
|
||||||
export function convertToWorkspaceEdit(
|
export function convertToWorkspaceEdit(
|
||||||
fs: ReadOnlyFileSystem,
|
|
||||||
edits: FileEditActions,
|
edits: FileEditActions,
|
||||||
changeAnnotations: {
|
changeAnnotations: {
|
||||||
[id: string]: ChangeAnnotation;
|
[id: string]: ChangeAnnotation;
|
||||||
@ -51,7 +49,6 @@ export function convertToWorkspaceEdit(
|
|||||||
defaultAnnotationId: string
|
defaultAnnotationId: string
|
||||||
): WorkspaceEdit;
|
): WorkspaceEdit;
|
||||||
export function convertToWorkspaceEdit(
|
export function convertToWorkspaceEdit(
|
||||||
fs: ReadOnlyFileSystem,
|
|
||||||
edits: FileEditActions | FileEditAction[],
|
edits: FileEditActions | FileEditAction[],
|
||||||
changeAnnotations?: {
|
changeAnnotations?: {
|
||||||
[id: string]: ChangeAnnotation;
|
[id: string]: ChangeAnnotation;
|
||||||
@ -59,17 +56,17 @@ export function convertToWorkspaceEdit(
|
|||||||
defaultAnnotationId = 'default'
|
defaultAnnotationId = 'default'
|
||||||
): WorkspaceEdit {
|
): WorkspaceEdit {
|
||||||
if (isArray(edits)) {
|
if (isArray(edits)) {
|
||||||
return _convertToWorkspaceEditWithChanges(fs, edits);
|
return _convertToWorkspaceEditWithChanges(edits);
|
||||||
}
|
}
|
||||||
|
|
||||||
return _convertToWorkspaceEditWithDocumentChanges(fs, edits, changeAnnotations, defaultAnnotationId);
|
return _convertToWorkspaceEditWithDocumentChanges(edits, changeAnnotations, defaultAnnotationId);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function appendToWorkspaceEdit(fs: ReadOnlyFileSystem, edits: FileEditAction[], workspaceEdit: WorkspaceEdit) {
|
export function appendToWorkspaceEdit(edits: FileEditAction[], workspaceEdit: WorkspaceEdit) {
|
||||||
edits.forEach((edit) => {
|
edits.forEach((edit) => {
|
||||||
const uri = convertPathToUri(fs, edit.filePath);
|
const uri = edit.fileUri;
|
||||||
workspaceEdit.changes![uri] = workspaceEdit.changes![uri] || [];
|
workspaceEdit.changes![uri.toString()] = workspaceEdit.changes![uri.toString()] || [];
|
||||||
workspaceEdit.changes![uri].push({ range: edit.range, newText: edit.replacementText });
|
workspaceEdit.changes![uri.toString()].push({ range: edit.range, newText: edit.replacementText });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,18 +98,18 @@ export function applyTextEditsToString(
|
|||||||
return current;
|
return current;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function applyWorkspaceEdit(program: EditableProgram, edits: WorkspaceEdit, filesChanged: Set<string>) {
|
export function applyWorkspaceEdit(program: EditableProgram, edits: WorkspaceEdit, filesChanged: Map<string, Uri>) {
|
||||||
if (edits.changes) {
|
if (edits.changes) {
|
||||||
for (const kv of Object.entries(edits.changes)) {
|
for (const kv of Object.entries(edits.changes)) {
|
||||||
const filePath = convertUriToPath(program.fileSystem, kv[0]);
|
const fileUri = Uri.parse(kv[0], program.configOptions.projectRoot.isCaseSensitive);
|
||||||
const fileInfo = program.getSourceFileInfo(filePath);
|
const fileInfo = program.getSourceFileInfo(fileUri);
|
||||||
if (!fileInfo || !fileInfo.isTracked) {
|
if (!fileInfo || !fileInfo.isTracked) {
|
||||||
// We don't allow non user file being modified.
|
// We don't allow non user file being modified.
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
applyDocumentChanges(program, fileInfo, kv[1]);
|
applyDocumentChanges(program, fileInfo, kv[1]);
|
||||||
filesChanged.add(filePath);
|
filesChanged.set(fileUri.key, fileUri);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,15 +117,15 @@ export function applyWorkspaceEdit(program: EditableProgram, edits: WorkspaceEdi
|
|||||||
if (edits.documentChanges) {
|
if (edits.documentChanges) {
|
||||||
for (const change of edits.documentChanges) {
|
for (const change of edits.documentChanges) {
|
||||||
if (TextDocumentEdit.is(change)) {
|
if (TextDocumentEdit.is(change)) {
|
||||||
const filePath = convertUriToPath(program.fileSystem, change.textDocument.uri);
|
const fileUri = Uri.parse(change.textDocument.uri, program.configOptions.projectRoot.isCaseSensitive);
|
||||||
const fileInfo = program.getSourceFileInfo(filePath);
|
const fileInfo = program.getSourceFileInfo(fileUri);
|
||||||
if (!fileInfo || !fileInfo.isTracked) {
|
if (!fileInfo || !fileInfo.isTracked) {
|
||||||
// We don't allow non user file being modified.
|
// We don't allow non user file being modified.
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
applyDocumentChanges(program, fileInfo, change.edits);
|
applyDocumentChanges(program, fileInfo, change.edits);
|
||||||
filesChanged.add(filePath);
|
filesChanged.set(fileUri.key, fileUri);
|
||||||
}
|
}
|
||||||
|
|
||||||
// For now, we don't support other kinds of text changes.
|
// For now, we don't support other kinds of text changes.
|
||||||
@ -140,30 +137,29 @@ export function applyWorkspaceEdit(program: EditableProgram, edits: WorkspaceEdi
|
|||||||
export function applyDocumentChanges(program: EditableProgram, fileInfo: SourceFileInfo, edits: TextEdit[]) {
|
export function applyDocumentChanges(program: EditableProgram, fileInfo: SourceFileInfo, edits: TextEdit[]) {
|
||||||
if (!fileInfo.isOpenByClient) {
|
if (!fileInfo.isOpenByClient) {
|
||||||
const fileContent = fileInfo.sourceFile.getFileContent();
|
const fileContent = fileInfo.sourceFile.getFileContent();
|
||||||
program.setFileOpened(fileInfo.sourceFile.getFilePath(), 0, fileContent ?? '', {
|
program.setFileOpened(fileInfo.sourceFile.getUri(), 0, fileContent ?? '', {
|
||||||
isTracked: fileInfo.isTracked,
|
isTracked: fileInfo.isTracked,
|
||||||
ipythonMode: fileInfo.sourceFile.getIPythonMode(),
|
ipythonMode: fileInfo.sourceFile.getIPythonMode(),
|
||||||
chainedFilePath: fileInfo.chainedSourceFile?.sourceFile.getFilePath(),
|
chainedFileUri: fileInfo.chainedSourceFile?.sourceFile.getUri(),
|
||||||
realFilePath: fileInfo.sourceFile.getRealFilePath(),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const version = fileInfo.sourceFile.getClientVersion() ?? 0;
|
const version = fileInfo.sourceFile.getClientVersion() ?? 0;
|
||||||
const filePath = fileInfo.sourceFile.getFilePath();
|
const fileUri = fileInfo.sourceFile.getUri();
|
||||||
|
const filePath = fileUri.getFilePath();
|
||||||
const sourceDoc = TextDocument.create(filePath, 'python', version, fileInfo.sourceFile.getOpenFileContents() ?? '');
|
const sourceDoc = TextDocument.create(filePath, 'python', version, fileInfo.sourceFile.getOpenFileContents() ?? '');
|
||||||
|
|
||||||
program.setFileOpened(filePath, version + 1, TextDocument.applyEdits(sourceDoc, edits), {
|
program.setFileOpened(fileUri, version + 1, TextDocument.applyEdits(sourceDoc, edits), {
|
||||||
isTracked: fileInfo.isTracked,
|
isTracked: fileInfo.isTracked,
|
||||||
ipythonMode: fileInfo.sourceFile.getIPythonMode(),
|
ipythonMode: fileInfo.sourceFile.getIPythonMode(),
|
||||||
chainedFilePath: fileInfo.chainedSourceFile?.sourceFile.getFilePath(),
|
chainedFileUri: fileInfo.chainedSourceFile?.sourceFile.getUri(),
|
||||||
realFilePath: fileInfo.sourceFile.getRealFilePath(),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function generateWorkspaceEdit(
|
export function generateWorkspaceEdit(
|
||||||
originalService: AnalyzerService,
|
originalService: AnalyzerService,
|
||||||
clonedService: AnalyzerService,
|
clonedService: AnalyzerService,
|
||||||
filesChanged: Set<string>
|
filesChanged: Map<string, Uri>
|
||||||
) {
|
) {
|
||||||
// For now, we won't do text diff to find out minimal text changes. instead, we will
|
// For now, we won't do text diff to find out minimal text changes. instead, we will
|
||||||
// consider whole text of the files are changed. In future, we could consider
|
// consider whole text of the files are changed. In future, we could consider
|
||||||
@ -171,9 +167,9 @@ export function generateWorkspaceEdit(
|
|||||||
// to support annotation.
|
// to support annotation.
|
||||||
const edits: WorkspaceEdit = { changes: {} };
|
const edits: WorkspaceEdit = { changes: {} };
|
||||||
|
|
||||||
for (const filePath of filesChanged) {
|
for (const uri of filesChanged.values()) {
|
||||||
const original = originalService.backgroundAnalysisProgram.program.getBoundSourceFile(filePath);
|
const original = originalService.backgroundAnalysisProgram.program.getBoundSourceFile(uri);
|
||||||
const final = clonedService.backgroundAnalysisProgram.program.getBoundSourceFile(filePath);
|
const final = clonedService.backgroundAnalysisProgram.program.getBoundSourceFile(uri);
|
||||||
if (!original || !final) {
|
if (!original || !final) {
|
||||||
// Both must exist.
|
// Both must exist.
|
||||||
continue;
|
continue;
|
||||||
@ -184,7 +180,7 @@ export function generateWorkspaceEdit(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
edits.changes![convertPathToUri(originalService.fs, filePath)] = [
|
edits.changes![uri.toString()] = [
|
||||||
{
|
{
|
||||||
range: convertTextRangeToRange(parseResults.parseTree, parseResults.tokenizerOutput.lines),
|
range: convertTextRangeToRange(parseResults.parseTree, parseResults.tokenizerOutput.lines),
|
||||||
newText: final.getFileContent() ?? '',
|
newText: final.getFileContent() ?? '',
|
||||||
@ -195,17 +191,16 @@ export function generateWorkspaceEdit(
|
|||||||
return edits;
|
return edits;
|
||||||
}
|
}
|
||||||
|
|
||||||
function _convertToWorkspaceEditWithChanges(fs: ReadOnlyFileSystem, edits: FileEditAction[]) {
|
function _convertToWorkspaceEditWithChanges(edits: FileEditAction[]) {
|
||||||
const workspaceEdit: WorkspaceEdit = {
|
const workspaceEdit: WorkspaceEdit = {
|
||||||
changes: {},
|
changes: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
appendToWorkspaceEdit(fs, edits, workspaceEdit);
|
appendToWorkspaceEdit(edits, workspaceEdit);
|
||||||
return workspaceEdit;
|
return workspaceEdit;
|
||||||
}
|
}
|
||||||
|
|
||||||
function _convertToWorkspaceEditWithDocumentChanges(
|
function _convertToWorkspaceEditWithDocumentChanges(
|
||||||
fs: ReadOnlyFileSystem,
|
|
||||||
editActions: FileEditActions,
|
editActions: FileEditActions,
|
||||||
changeAnnotations?: {
|
changeAnnotations?: {
|
||||||
[id: string]: ChangeAnnotation;
|
[id: string]: ChangeAnnotation;
|
||||||
@ -223,11 +218,7 @@ function _convertToWorkspaceEditWithDocumentChanges(
|
|||||||
switch (operation.kind) {
|
switch (operation.kind) {
|
||||||
case 'create':
|
case 'create':
|
||||||
workspaceEdit.documentChanges!.push(
|
workspaceEdit.documentChanges!.push(
|
||||||
CreateFile.create(
|
CreateFile.create(operation.fileUri.toString(), /* options */ undefined, defaultAnnotationId)
|
||||||
convertPathToUri(fs, operation.filePath),
|
|
||||||
/* options */ undefined,
|
|
||||||
defaultAnnotationId
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case 'rename':
|
case 'rename':
|
||||||
@ -239,11 +230,11 @@ function _convertToWorkspaceEditWithDocumentChanges(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Text edit's file path must refer to original file paths unless it is a new file just created.
|
// Text edit's file path must refer to original file paths unless it is a new file just created.
|
||||||
const mapPerFile = createMapFromItems(editActions.edits, (e) => e.filePath);
|
const mapPerFile = createMapFromItems(editActions.edits, (e) => e.fileUri.key);
|
||||||
for (const [key, value] of mapPerFile) {
|
for (const [key, value] of mapPerFile) {
|
||||||
workspaceEdit.documentChanges!.push(
|
workspaceEdit.documentChanges!.push(
|
||||||
TextDocumentEdit.create(
|
TextDocumentEdit.create(
|
||||||
{ uri: convertPathToUri(fs, key), version: null },
|
{ uri: key, version: null },
|
||||||
Array.from(
|
Array.from(
|
||||||
value.map((v) => ({
|
value.map((v) => ({
|
||||||
range: v.range,
|
range: v.range,
|
||||||
@ -262,8 +253,8 @@ function _convertToWorkspaceEditWithDocumentChanges(
|
|||||||
case 'rename':
|
case 'rename':
|
||||||
workspaceEdit.documentChanges!.push(
|
workspaceEdit.documentChanges!.push(
|
||||||
RenameFile.create(
|
RenameFile.create(
|
||||||
convertPathToUri(fs, operation.oldFilePath),
|
operation.oldFileUri.toString(),
|
||||||
convertPathToUri(fs, operation.newFilePath),
|
operation.newFileUri.toString(),
|
||||||
/* options */ undefined,
|
/* options */ undefined,
|
||||||
defaultAnnotationId
|
defaultAnnotationId
|
||||||
)
|
)
|
||||||
@ -271,11 +262,7 @@ function _convertToWorkspaceEditWithDocumentChanges(
|
|||||||
break;
|
break;
|
||||||
case 'delete':
|
case 'delete':
|
||||||
workspaceEdit.documentChanges!.push(
|
workspaceEdit.documentChanges!.push(
|
||||||
DeleteFile.create(
|
DeleteFile.create(operation.fileUri.toString(), /* options */ undefined, defaultAnnotationId)
|
||||||
convertPathToUri(fs, operation.filePath),
|
|
||||||
/* options */ undefined,
|
|
||||||
defaultAnnotationId
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -107,18 +107,11 @@ import { FileSystem, ReadOnlyFileSystem } from './common/fileSystem';
|
|||||||
import { FileWatcherEventType, FileWatcherHandler } from './common/fileWatcher';
|
import { FileWatcherEventType, FileWatcherHandler } from './common/fileWatcher';
|
||||||
import { Host } from './common/host';
|
import { Host } from './common/host';
|
||||||
import { fromLSPAny } from './common/lspUtils';
|
import { fromLSPAny } from './common/lspUtils';
|
||||||
import {
|
|
||||||
convertPathToUri,
|
|
||||||
convertUriToPath,
|
|
||||||
deduplicateFolders,
|
|
||||||
getDirectoryPath,
|
|
||||||
getFileName,
|
|
||||||
isFile,
|
|
||||||
} from './common/pathUtils';
|
|
||||||
import { ProgressReportTracker, ProgressReporter } from './common/progressReporter';
|
import { ProgressReportTracker, ProgressReporter } from './common/progressReporter';
|
||||||
import { ServiceProvider } from './common/serviceProvider';
|
import { ServiceProvider } from './common/serviceProvider';
|
||||||
import { DocumentRange, Position, Range } from './common/textRange';
|
import { DocumentRange, Position, Range } from './common/textRange';
|
||||||
import { UriParser } from './common/uriParser';
|
import { Uri } from './common/uri/uri';
|
||||||
|
import { deduplicateFolders, isFile } from './common/uri/uriUtils';
|
||||||
import { AnalyzerServiceExecutor } from './languageService/analyzerServiceExecutor';
|
import { AnalyzerServiceExecutor } from './languageService/analyzerServiceExecutor';
|
||||||
import { CallHierarchyProvider } from './languageService/callHierarchyProvider';
|
import { CallHierarchyProvider } from './languageService/callHierarchyProvider';
|
||||||
import { CompletionItemData, CompletionProvider } from './languageService/completionProvider';
|
import { CompletionItemData, CompletionProvider } from './languageService/completionProvider';
|
||||||
@ -137,17 +130,17 @@ import { ParseResults } from './parser/parser';
|
|||||||
import { InitStatus, WellKnownWorkspaceKinds, Workspace, WorkspaceFactory } from './workspaceFactory';
|
import { InitStatus, WellKnownWorkspaceKinds, Workspace, WorkspaceFactory } from './workspaceFactory';
|
||||||
|
|
||||||
export interface ServerSettings {
|
export interface ServerSettings {
|
||||||
venvPath?: string | undefined;
|
venvPath?: Uri | undefined;
|
||||||
pythonPath?: string | undefined;
|
pythonPath?: Uri | undefined;
|
||||||
typeshedPath?: string | undefined;
|
typeshedPath?: Uri | undefined;
|
||||||
stubPath?: string | undefined;
|
stubPath?: Uri | undefined;
|
||||||
openFilesOnly?: boolean | undefined;
|
openFilesOnly?: boolean | undefined;
|
||||||
typeCheckingMode?: string | undefined;
|
typeCheckingMode?: string | undefined;
|
||||||
useLibraryCodeForTypes?: boolean | undefined;
|
useLibraryCodeForTypes?: boolean | undefined;
|
||||||
disableLanguageServices?: boolean | undefined;
|
disableLanguageServices?: boolean | undefined;
|
||||||
disableOrganizeImports?: boolean | undefined;
|
disableOrganizeImports?: boolean | undefined;
|
||||||
autoSearchPaths?: boolean | undefined;
|
autoSearchPaths?: boolean | undefined;
|
||||||
extraPaths?: string[] | undefined;
|
extraPaths?: Uri[] | undefined;
|
||||||
watchForSourceChanges?: boolean | undefined;
|
watchForSourceChanges?: boolean | undefined;
|
||||||
watchForLibraryChanges?: boolean | undefined;
|
watchForLibraryChanges?: boolean | undefined;
|
||||||
watchForConfigChanges?: boolean | undefined;
|
watchForConfigChanges?: boolean | undefined;
|
||||||
@ -181,23 +174,22 @@ export interface WindowInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface LanguageServerInterface {
|
export interface LanguageServerInterface {
|
||||||
readonly rootPath: string;
|
readonly rootUri: Uri;
|
||||||
readonly console: ConsoleInterface;
|
readonly console: ConsoleInterface;
|
||||||
readonly window: WindowInterface;
|
readonly window: WindowInterface;
|
||||||
readonly supportAdvancedEdits: boolean;
|
readonly supportAdvancedEdits: boolean;
|
||||||
|
|
||||||
getWorkspaces(): Promise<Workspace[]>;
|
getWorkspaces(): Promise<Workspace[]>;
|
||||||
getWorkspaceForFile(filePath: string): Promise<Workspace>;
|
getWorkspaceForFile(fileUri: Uri): Promise<Workspace>;
|
||||||
getSettings(workspace: Workspace): Promise<ServerSettings>;
|
getSettings(workspace: Workspace): Promise<ServerSettings>;
|
||||||
createBackgroundAnalysis(serviceId: string): BackgroundAnalysisBase | undefined;
|
createBackgroundAnalysis(serviceId: string): BackgroundAnalysisBase | undefined;
|
||||||
reanalyze(): void;
|
reanalyze(): void;
|
||||||
restart(): void;
|
restart(): void;
|
||||||
decodeTextDocumentUri(uriString: string): string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ServerOptions {
|
export interface ServerOptions {
|
||||||
productName: string;
|
productName: string;
|
||||||
rootDirectory: string;
|
rootDirectory: Uri;
|
||||||
version: string;
|
version: string;
|
||||||
cancellationProvider: CancellationProvider;
|
cancellationProvider: CancellationProvider;
|
||||||
serviceProvider: ServiceProvider;
|
serviceProvider: ServiceProvider;
|
||||||
@ -323,7 +315,7 @@ export abstract class LanguageServerBase implements LanguageServerInterface, Dis
|
|||||||
private _workspaceFoldersChangedDisposable: Disposable | undefined;
|
private _workspaceFoldersChangedDisposable: Disposable | undefined;
|
||||||
|
|
||||||
// Global root path - the basis for all global settings.
|
// Global root path - the basis for all global settings.
|
||||||
rootPath = '';
|
rootUri = Uri.empty();
|
||||||
|
|
||||||
protected client: ClientCapabilities = {
|
protected client: ClientCapabilities = {
|
||||||
hasConfigurationCapability: false,
|
hasConfigurationCapability: false,
|
||||||
@ -358,16 +350,10 @@ export abstract class LanguageServerBase implements LanguageServerInterface, Dis
|
|||||||
// The URIs for which diagnostics are reported
|
// The URIs for which diagnostics are reported
|
||||||
protected readonly documentsWithDiagnostics = new Set<string>();
|
protected readonly documentsWithDiagnostics = new Set<string>();
|
||||||
|
|
||||||
readonly uriParser: UriParser;
|
constructor(protected serverOptions: ServerOptions, protected connection: Connection) {
|
||||||
|
|
||||||
constructor(
|
|
||||||
protected serverOptions: ServerOptions,
|
|
||||||
protected connection: Connection,
|
|
||||||
uriParserFactory = (fs: FileSystem) => new UriParser(fs)
|
|
||||||
) {
|
|
||||||
// Stash the base directory into a global variable.
|
// Stash the base directory into a global variable.
|
||||||
// This must happen before fs.getModulePath().
|
// This must happen before fs.getModulePath().
|
||||||
(global as any).__rootDirectory = serverOptions.rootDirectory;
|
(global as any).__rootDirectory = serverOptions.rootDirectory.getFilePath();
|
||||||
|
|
||||||
this.console.info(
|
this.console.info(
|
||||||
`${serverOptions.productName} language server ${
|
`${serverOptions.productName} language server ${
|
||||||
@ -379,16 +365,14 @@ export abstract class LanguageServerBase implements LanguageServerInterface, Dis
|
|||||||
|
|
||||||
this.fs = this.serverOptions.serviceProvider.fs();
|
this.fs = this.serverOptions.serviceProvider.fs();
|
||||||
|
|
||||||
this.uriParser = uriParserFactory(this.fs);
|
|
||||||
|
|
||||||
this.workspaceFactory = new WorkspaceFactory(
|
this.workspaceFactory = new WorkspaceFactory(
|
||||||
this.console,
|
this.console,
|
||||||
this.uriParser,
|
|
||||||
/* isWeb */ false,
|
/* isWeb */ false,
|
||||||
this.createAnalyzerServiceForWorkspace.bind(this),
|
this.createAnalyzerServiceForWorkspace.bind(this),
|
||||||
this.isPythonPathImmutable.bind(this),
|
this.isPythonPathImmutable.bind(this),
|
||||||
this.onWorkspaceCreated.bind(this),
|
this.onWorkspaceCreated.bind(this),
|
||||||
this.onWorkspaceRemoved.bind(this)
|
this.onWorkspaceRemoved.bind(this),
|
||||||
|
this.fs.isCaseSensitive
|
||||||
);
|
);
|
||||||
|
|
||||||
// Set the working directory to a known location within
|
// Set the working directory to a known location within
|
||||||
@ -427,11 +411,6 @@ export abstract class LanguageServerBase implements LanguageServerInterface, Dis
|
|||||||
this._workspaceFoldersChangedDisposable?.dispose();
|
this._workspaceFoldersChangedDisposable?.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert uri to path
|
|
||||||
decodeTextDocumentUri(uriString: string): string {
|
|
||||||
return this.uriParser.decodeTextDocumentUri(uriString);
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract createBackgroundAnalysis(serviceId: string): BackgroundAnalysisBase | undefined;
|
abstract createBackgroundAnalysis(serviceId: string): BackgroundAnalysisBase | undefined;
|
||||||
|
|
||||||
abstract getSettings(workspace: Workspace): Promise<ServerSettings>;
|
abstract getSettings(workspace: Workspace): Promise<ServerSettings>;
|
||||||
@ -472,12 +451,12 @@ export abstract class LanguageServerBase implements LanguageServerInterface, Dis
|
|||||||
return workspaces;
|
return workspaces;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getWorkspaceForFile(filePath: string, pythonPath?: string): Promise<Workspace> {
|
async getWorkspaceForFile(fileUri: Uri, pythonPath?: Uri): Promise<Workspace> {
|
||||||
return this.workspaceFactory.getWorkspaceForFile(filePath, pythonPath);
|
return this.workspaceFactory.getWorkspaceForFile(fileUri, pythonPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getContainingWorkspacesForFile(filePath: string): Promise<Workspace[]> {
|
async getContainingWorkspacesForFile(fileUri: Uri): Promise<Workspace[]> {
|
||||||
return this.workspaceFactory.getContainingWorkspacesForFile(filePath);
|
return this.workspaceFactory.getContainingWorkspacesForFile(fileUri);
|
||||||
}
|
}
|
||||||
|
|
||||||
reanalyze() {
|
reanalyze() {
|
||||||
@ -521,7 +500,10 @@ export abstract class LanguageServerBase implements LanguageServerInterface, Dis
|
|||||||
(this.console as ConsoleWithLogLevel).level = serverSettings.logLevel ?? LogLevel.Info;
|
(this.console as ConsoleWithLogLevel).level = serverSettings.logLevel ?? LogLevel.Info;
|
||||||
|
|
||||||
// Apply the new path to the workspace (before restarting the service).
|
// Apply the new path to the workspace (before restarting the service).
|
||||||
serverSettings.pythonPath = this.workspaceFactory.applyPythonPath(workspace, serverSettings.pythonPath);
|
serverSettings.pythonPath = this.workspaceFactory.applyPythonPath(
|
||||||
|
workspace,
|
||||||
|
serverSettings.pythonPath ? serverSettings.pythonPath : undefined
|
||||||
|
);
|
||||||
|
|
||||||
// Then use the updated settings to restart the service.
|
// Then use the updated settings to restart the service.
|
||||||
this.updateOptionsAndRestartService(workspace, serverSettings);
|
this.updateOptionsAndRestartService(workspace, serverSettings);
|
||||||
@ -540,8 +522,8 @@ export abstract class LanguageServerBase implements LanguageServerInterface, Dis
|
|||||||
serverSettings: ServerSettings,
|
serverSettings: ServerSettings,
|
||||||
typeStubTargetImportName?: string
|
typeStubTargetImportName?: string
|
||||||
) {
|
) {
|
||||||
AnalyzerServiceExecutor.runWithOptions(this.rootPath, workspace, serverSettings, typeStubTargetImportName);
|
AnalyzerServiceExecutor.runWithOptions(this.rootUri, workspace, serverSettings, typeStubTargetImportName);
|
||||||
workspace.searchPathsToWatch = workspace.service.librarySearchPathsToWatch ?? [];
|
workspace.searchPathsToWatch = workspace.service.librarySearchUrisToWatch ?? [];
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract executeCommand(params: ExecuteCommandParams, token: CancellationToken): Promise<any>;
|
protected abstract executeCommand(params: ExecuteCommandParams, token: CancellationToken): Promise<any>;
|
||||||
@ -553,18 +535,18 @@ export abstract class LanguageServerBase implements LanguageServerInterface, Dis
|
|||||||
token: CancellationToken
|
token: CancellationToken
|
||||||
): Promise<(Command | CodeAction)[] | undefined | null>;
|
): Promise<(Command | CodeAction)[] | undefined | null>;
|
||||||
|
|
||||||
protected isPythonPathImmutable(filePath: string): boolean {
|
protected isPythonPathImmutable(fileUri: Uri): boolean {
|
||||||
// This function is called to determine if the file is using
|
// This function is called to determine if the file is using
|
||||||
// a special pythonPath separate from a workspace or not.
|
// a special pythonPath separate from a workspace or not.
|
||||||
// The default is no.
|
// The default is no.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async getConfiguration(scopeUri: string | undefined, section: string) {
|
protected async getConfiguration(scopeUri: Uri | undefined, section: string) {
|
||||||
if (this.client.hasConfigurationCapability) {
|
if (this.client.hasConfigurationCapability) {
|
||||||
const item: ConfigurationItem = {};
|
const item: ConfigurationItem = {};
|
||||||
if (scopeUri !== undefined) {
|
if (scopeUri !== undefined) {
|
||||||
item.scopeUri = scopeUri;
|
item.scopeUri = scopeUri.toString();
|
||||||
}
|
}
|
||||||
if (section !== undefined) {
|
if (section !== undefined) {
|
||||||
item.section = section;
|
item.section = section;
|
||||||
@ -691,7 +673,7 @@ export abstract class LanguageServerBase implements LanguageServerInterface, Dis
|
|||||||
setLocaleOverride(params.locale);
|
setLocaleOverride(params.locale);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.rootPath = params.rootPath || '';
|
this.rootUri = params.rootUri ? Uri.parse(params.rootUri, this.fs.isCaseSensitive) : Uri.empty();
|
||||||
|
|
||||||
const capabilities = params.capabilities;
|
const capabilities = params.capabilities;
|
||||||
this.client.hasConfigurationCapability = !!capabilities.workspace?.configuration;
|
this.client.hasConfigurationCapability = !!capabilities.workspace?.configuration;
|
||||||
@ -862,7 +844,7 @@ export abstract class LanguageServerBase implements LanguageServerInterface, Dis
|
|||||||
filter: DefinitionFilter,
|
filter: DefinitionFilter,
|
||||||
getDefinitionsFunc: (
|
getDefinitionsFunc: (
|
||||||
workspace: Workspace,
|
workspace: Workspace,
|
||||||
filePath: string,
|
fileUri: Uri,
|
||||||
position: Position,
|
position: Position,
|
||||||
filter: DefinitionFilter,
|
filter: DefinitionFilter,
|
||||||
token: CancellationToken
|
token: CancellationToken
|
||||||
@ -870,20 +852,20 @@ export abstract class LanguageServerBase implements LanguageServerInterface, Dis
|
|||||||
) {
|
) {
|
||||||
this.recordUserInteractionTime();
|
this.recordUserInteractionTime();
|
||||||
|
|
||||||
const { filePath, position } = this.uriParser.decodeTextDocumentPosition(params.textDocument, params.position);
|
const uri = Uri.parse(params.textDocument.uri, this.fs.isCaseSensitive);
|
||||||
|
|
||||||
const workspace = await this.getWorkspaceForFile(filePath);
|
const workspace = await this.getWorkspaceForFile(uri);
|
||||||
if (workspace.disableLanguageServices) {
|
if (workspace.disableLanguageServices) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const locations = getDefinitionsFunc(workspace, filePath, position, filter, token);
|
const locations = getDefinitionsFunc(workspace, uri, params.position, filter, token);
|
||||||
if (!locations) {
|
if (!locations) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
return locations
|
return locations
|
||||||
.filter((loc) => this.canNavigateToFile(loc.path, workspace.service.fs))
|
.filter((loc) => this.canNavigateToFile(loc.uri, workspace.service.fs))
|
||||||
.map((loc) => Location.create(convertPathToUri(workspace.service.fs, loc.path), loc.range));
|
.map((loc) => Location.create(loc.uri.toString(), loc.range));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async onReferences(
|
protected async onReferences(
|
||||||
@ -891,7 +873,7 @@ export abstract class LanguageServerBase implements LanguageServerInterface, Dis
|
|||||||
token: CancellationToken,
|
token: CancellationToken,
|
||||||
workDoneReporter: WorkDoneProgressReporter,
|
workDoneReporter: WorkDoneProgressReporter,
|
||||||
resultReporter: ResultProgressReporter<Location[]> | undefined,
|
resultReporter: ResultProgressReporter<Location[]> | undefined,
|
||||||
createDocumentRange?: (filePath: string, result: CollectionResult, parseResults: ParseResults) => DocumentRange,
|
createDocumentRange?: (uri: Uri, result: CollectionResult, parseResults: ParseResults) => DocumentRange,
|
||||||
convertToLocation?: (fs: ReadOnlyFileSystem, ranges: DocumentRange) => Location | undefined
|
convertToLocation?: (fs: ReadOnlyFileSystem, ranges: DocumentRange) => Location | undefined
|
||||||
): Promise<Location[] | null | undefined> {
|
): Promise<Location[] | null | undefined> {
|
||||||
if (this._pendingFindAllRefsCancellationSource) {
|
if (this._pendingFindAllRefsCancellationSource) {
|
||||||
@ -912,12 +894,9 @@ export abstract class LanguageServerBase implements LanguageServerInterface, Dis
|
|||||||
this._pendingFindAllRefsCancellationSource = source;
|
this._pendingFindAllRefsCancellationSource = source;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { filePath, position } = this.uriParser.decodeTextDocumentPosition(
|
const uri = Uri.parse(params.textDocument.uri, this.fs.isCaseSensitive);
|
||||||
params.textDocument,
|
|
||||||
params.position
|
|
||||||
);
|
|
||||||
|
|
||||||
const workspace = await this.getWorkspaceForFile(filePath);
|
const workspace = await this.getWorkspaceForFile(uri);
|
||||||
if (workspace.disableLanguageServices) {
|
if (workspace.disableLanguageServices) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -928,7 +907,7 @@ export abstract class LanguageServerBase implements LanguageServerInterface, Dis
|
|||||||
source.token,
|
source.token,
|
||||||
createDocumentRange,
|
createDocumentRange,
|
||||||
convertToLocation
|
convertToLocation
|
||||||
).reportReferences(filePath, position, params.context.includeDeclaration, resultReporter);
|
).reportReferences(uri, params.position, params.context.includeDeclaration, resultReporter);
|
||||||
}, token);
|
}, token);
|
||||||
} finally {
|
} finally {
|
||||||
progress.reporter.done();
|
progress.reporter.done();
|
||||||
@ -942,8 +921,8 @@ export abstract class LanguageServerBase implements LanguageServerInterface, Dis
|
|||||||
): Promise<DocumentSymbol[] | SymbolInformation[] | null | undefined> {
|
): Promise<DocumentSymbol[] | SymbolInformation[] | null | undefined> {
|
||||||
this.recordUserInteractionTime();
|
this.recordUserInteractionTime();
|
||||||
|
|
||||||
const filePath = this.uriParser.decodeTextDocumentUri(params.textDocument.uri);
|
const uri = Uri.parse(params.textDocument.uri, this.fs.isCaseSensitive);
|
||||||
const workspace = await this.getWorkspaceForFile(filePath);
|
const workspace = await this.getWorkspaceForFile(uri);
|
||||||
if (workspace.disableLanguageServices) {
|
if (workspace.disableLanguageServices) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
@ -951,7 +930,7 @@ export abstract class LanguageServerBase implements LanguageServerInterface, Dis
|
|||||||
return workspace.service.run((program) => {
|
return workspace.service.run((program) => {
|
||||||
return new DocumentSymbolProvider(
|
return new DocumentSymbolProvider(
|
||||||
program,
|
program,
|
||||||
filePath,
|
uri,
|
||||||
this.client.hasHierarchicalDocumentSymbolCapability,
|
this.client.hasHierarchicalDocumentSymbolCapability,
|
||||||
token
|
token
|
||||||
).getSymbols();
|
).getSymbols();
|
||||||
@ -974,11 +953,11 @@ export abstract class LanguageServerBase implements LanguageServerInterface, Dis
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected async onHover(params: HoverParams, token: CancellationToken) {
|
protected async onHover(params: HoverParams, token: CancellationToken) {
|
||||||
const { filePath, position } = this.uriParser.decodeTextDocumentPosition(params.textDocument, params.position);
|
const uri = Uri.parse(params.textDocument.uri, this.fs.isCaseSensitive);
|
||||||
const workspace = await this.getWorkspaceForFile(filePath);
|
const workspace = await this.getWorkspaceForFile(uri);
|
||||||
|
|
||||||
return workspace.service.run((program) => {
|
return workspace.service.run((program) => {
|
||||||
return new HoverProvider(program, filePath, position, this.client.hoverContentFormat, token).getHover();
|
return new HoverProvider(program, uri, params.position, this.client.hoverContentFormat, token).getHover();
|
||||||
}, token);
|
}, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -986,11 +965,11 @@ export abstract class LanguageServerBase implements LanguageServerInterface, Dis
|
|||||||
params: DocumentHighlightParams,
|
params: DocumentHighlightParams,
|
||||||
token: CancellationToken
|
token: CancellationToken
|
||||||
): Promise<DocumentHighlight[] | null | undefined> {
|
): Promise<DocumentHighlight[] | null | undefined> {
|
||||||
const { filePath, position } = this.uriParser.decodeTextDocumentPosition(params.textDocument, params.position);
|
const uri = Uri.parse(params.textDocument.uri, this.fs.isCaseSensitive);
|
||||||
const workspace = await this.getWorkspaceForFile(filePath);
|
const workspace = await this.getWorkspaceForFile(uri);
|
||||||
|
|
||||||
return workspace.service.run((program) => {
|
return workspace.service.run((program) => {
|
||||||
return new DocumentHighlightProvider(program, filePath, position, token).getDocumentHighlight();
|
return new DocumentHighlightProvider(program, uri, params.position, token).getDocumentHighlight();
|
||||||
}, token);
|
}, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -998,9 +977,9 @@ export abstract class LanguageServerBase implements LanguageServerInterface, Dis
|
|||||||
params: SignatureHelpParams,
|
params: SignatureHelpParams,
|
||||||
token: CancellationToken
|
token: CancellationToken
|
||||||
): Promise<SignatureHelp | undefined | null> {
|
): Promise<SignatureHelp | undefined | null> {
|
||||||
const { filePath, position } = this.uriParser.decodeTextDocumentPosition(params.textDocument, params.position);
|
const uri = Uri.parse(params.textDocument.uri, this.fs.isCaseSensitive);
|
||||||
|
|
||||||
const workspace = await this.getWorkspaceForFile(filePath);
|
const workspace = await this.getWorkspaceForFile(uri);
|
||||||
if (workspace.disableLanguageServices) {
|
if (workspace.disableLanguageServices) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1008,8 +987,8 @@ export abstract class LanguageServerBase implements LanguageServerInterface, Dis
|
|||||||
return workspace.service.run((program) => {
|
return workspace.service.run((program) => {
|
||||||
return new SignatureHelpProvider(
|
return new SignatureHelpProvider(
|
||||||
program,
|
program,
|
||||||
filePath,
|
uri,
|
||||||
position,
|
params.position,
|
||||||
this.client.signatureDocFormat,
|
this.client.signatureDocFormat,
|
||||||
this.client.hasSignatureLabelOffsetCapability,
|
this.client.hasSignatureLabelOffsetCapability,
|
||||||
this.client.hasActiveParameterCapability,
|
this.client.hasActiveParameterCapability,
|
||||||
@ -1040,8 +1019,8 @@ export abstract class LanguageServerBase implements LanguageServerInterface, Dis
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected async onCompletion(params: CompletionParams, token: CancellationToken): Promise<CompletionList | null> {
|
protected async onCompletion(params: CompletionParams, token: CancellationToken): Promise<CompletionList | null> {
|
||||||
const { filePath, position } = this.uriParser.decodeTextDocumentPosition(params.textDocument, params.position);
|
const uri = Uri.parse(params.textDocument.uri, this.fs.isCaseSensitive);
|
||||||
const workspace = await this.getWorkspaceForFile(filePath);
|
const workspace = await this.getWorkspaceForFile(uri);
|
||||||
if (workspace.disableLanguageServices) {
|
if (workspace.disableLanguageServices) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -1049,9 +1028,9 @@ export abstract class LanguageServerBase implements LanguageServerInterface, Dis
|
|||||||
return workspace.service.run((program) => {
|
return workspace.service.run((program) => {
|
||||||
const completions = new CompletionProvider(
|
const completions = new CompletionProvider(
|
||||||
program,
|
program,
|
||||||
workspace.rootPath,
|
workspace.rootUri,
|
||||||
filePath,
|
uri,
|
||||||
position,
|
params.position,
|
||||||
{
|
{
|
||||||
format: this.client.completionDocFormat,
|
format: this.client.completionDocFormat,
|
||||||
snippet: this.client.completionSupportsSnippet,
|
snippet: this.client.completionSupportsSnippet,
|
||||||
@ -1074,13 +1053,14 @@ export abstract class LanguageServerBase implements LanguageServerInterface, Dis
|
|||||||
// cache that result and never call us back.
|
// cache that result and never call us back.
|
||||||
protected async onCompletionResolve(params: CompletionItem, token: CancellationToken): Promise<CompletionItem> {
|
protected async onCompletionResolve(params: CompletionItem, token: CancellationToken): Promise<CompletionItem> {
|
||||||
const completionItemData = fromLSPAny<CompletionItemData>(params.data);
|
const completionItemData = fromLSPAny<CompletionItemData>(params.data);
|
||||||
if (completionItemData && completionItemData.filePath) {
|
if (completionItemData && completionItemData.uri) {
|
||||||
const workspace = await this.getWorkspaceForFile(completionItemData.filePath);
|
const uri = Uri.parse(completionItemData.uri, this.fs.isCaseSensitive);
|
||||||
|
const workspace = await this.getWorkspaceForFile(uri);
|
||||||
workspace.service.run((program) => {
|
workspace.service.run((program) => {
|
||||||
return new CompletionProvider(
|
return new CompletionProvider(
|
||||||
program,
|
program,
|
||||||
workspace.rootPath,
|
workspace.rootUri,
|
||||||
completionItemData.filePath,
|
uri,
|
||||||
completionItemData.position,
|
completionItemData.position,
|
||||||
{
|
{
|
||||||
format: this.client.completionDocFormat,
|
format: this.client.completionDocFormat,
|
||||||
@ -1098,16 +1078,16 @@ export abstract class LanguageServerBase implements LanguageServerInterface, Dis
|
|||||||
params: PrepareRenameParams,
|
params: PrepareRenameParams,
|
||||||
token: CancellationToken
|
token: CancellationToken
|
||||||
): Promise<Range | { range: Range; placeholder: string } | null> {
|
): Promise<Range | { range: Range; placeholder: string } | null> {
|
||||||
const { filePath, position } = this.uriParser.decodeTextDocumentPosition(params.textDocument, params.position);
|
const uri = Uri.parse(params.textDocument.uri, this.fs.isCaseSensitive);
|
||||||
const isUntitled = this.uriParser.isUntitled(params.textDocument.uri);
|
const isUntitled = uri.isUntitled();
|
||||||
|
|
||||||
const workspace = await this.getWorkspaceForFile(filePath);
|
const workspace = await this.getWorkspaceForFile(uri);
|
||||||
if (workspace.disableLanguageServices) {
|
if (workspace.disableLanguageServices) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return workspace.service.run((program) => {
|
return workspace.service.run((program) => {
|
||||||
return new RenameProvider(program, filePath, position, token).canRenameSymbol(
|
return new RenameProvider(program, uri, params.position, token).canRenameSymbol(
|
||||||
workspace.kinds.includes(WellKnownWorkspaceKinds.Default),
|
workspace.kinds.includes(WellKnownWorkspaceKinds.Default),
|
||||||
isUntitled
|
isUntitled
|
||||||
);
|
);
|
||||||
@ -1118,16 +1098,16 @@ export abstract class LanguageServerBase implements LanguageServerInterface, Dis
|
|||||||
params: RenameParams,
|
params: RenameParams,
|
||||||
token: CancellationToken
|
token: CancellationToken
|
||||||
): Promise<WorkspaceEdit | null | undefined> {
|
): Promise<WorkspaceEdit | null | undefined> {
|
||||||
const { filePath, position } = this.uriParser.decodeTextDocumentPosition(params.textDocument, params.position);
|
const uri = Uri.parse(params.textDocument.uri, this.fs.isCaseSensitive);
|
||||||
const isUntitled = this.uriParser.isUntitled(params.textDocument.uri);
|
const isUntitled = uri.isUntitled();
|
||||||
|
|
||||||
const workspace = await this.getWorkspaceForFile(filePath);
|
const workspace = await this.getWorkspaceForFile(uri);
|
||||||
if (workspace.disableLanguageServices) {
|
if (workspace.disableLanguageServices) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return workspace.service.run((program) => {
|
return workspace.service.run((program) => {
|
||||||
return new RenameProvider(program, filePath, position, token).renameSymbol(
|
return new RenameProvider(program, uri, params.position, token).renameSymbol(
|
||||||
params.newName,
|
params.newName,
|
||||||
workspace.kinds.includes(WellKnownWorkspaceKinds.Default),
|
workspace.kinds.includes(WellKnownWorkspaceKinds.Default),
|
||||||
isUntitled
|
isUntitled
|
||||||
@ -1139,28 +1119,28 @@ export abstract class LanguageServerBase implements LanguageServerInterface, Dis
|
|||||||
params: CallHierarchyPrepareParams,
|
params: CallHierarchyPrepareParams,
|
||||||
token: CancellationToken
|
token: CancellationToken
|
||||||
): Promise<CallHierarchyItem[] | null> {
|
): Promise<CallHierarchyItem[] | null> {
|
||||||
const { filePath, position } = this.uriParser.decodeTextDocumentPosition(params.textDocument, params.position);
|
const uri = Uri.parse(params.textDocument.uri, this.fs.isCaseSensitive);
|
||||||
|
|
||||||
const workspace = await this.getWorkspaceForFile(filePath);
|
const workspace = await this.getWorkspaceForFile(uri);
|
||||||
if (workspace.disableLanguageServices) {
|
if (workspace.disableLanguageServices) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return workspace.service.run((program) => {
|
return workspace.service.run((program) => {
|
||||||
return new CallHierarchyProvider(program, filePath, position, token).onPrepare();
|
return new CallHierarchyProvider(program, uri, params.position, token).onPrepare();
|
||||||
}, token);
|
}, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async onCallHierarchyIncomingCalls(params: CallHierarchyIncomingCallsParams, token: CancellationToken) {
|
protected async onCallHierarchyIncomingCalls(params: CallHierarchyIncomingCallsParams, token: CancellationToken) {
|
||||||
const { filePath, position } = this.uriParser.decodeTextDocumentPosition(params.item, params.item.range.start);
|
const uri = Uri.parse(params.item.uri, this.fs.isCaseSensitive);
|
||||||
|
|
||||||
const workspace = await this.getWorkspaceForFile(filePath);
|
const workspace = await this.getWorkspaceForFile(uri);
|
||||||
if (workspace.disableLanguageServices) {
|
if (workspace.disableLanguageServices) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return workspace.service.run((program) => {
|
return workspace.service.run((program) => {
|
||||||
return new CallHierarchyProvider(program, filePath, position, token).getIncomingCalls();
|
return new CallHierarchyProvider(program, uri, params.item.range.start, token).getIncomingCalls();
|
||||||
}, token);
|
}, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1168,46 +1148,51 @@ export abstract class LanguageServerBase implements LanguageServerInterface, Dis
|
|||||||
params: CallHierarchyOutgoingCallsParams,
|
params: CallHierarchyOutgoingCallsParams,
|
||||||
token: CancellationToken
|
token: CancellationToken
|
||||||
): Promise<CallHierarchyOutgoingCall[] | null> {
|
): Promise<CallHierarchyOutgoingCall[] | null> {
|
||||||
const { filePath, position } = this.uriParser.decodeTextDocumentPosition(params.item, params.item.range.start);
|
const uri = Uri.parse(params.item.uri, this.fs.isCaseSensitive);
|
||||||
|
|
||||||
const workspace = await this.getWorkspaceForFile(filePath);
|
const workspace = await this.getWorkspaceForFile(uri);
|
||||||
if (workspace.disableLanguageServices) {
|
if (workspace.disableLanguageServices) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return workspace.service.run((program) => {
|
return workspace.service.run((program) => {
|
||||||
return new CallHierarchyProvider(program, filePath, position, token).getOutgoingCalls();
|
return new CallHierarchyProvider(program, uri, params.item.range.start, token).getOutgoingCalls();
|
||||||
}, token);
|
}, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async onDidOpenTextDocument(params: DidOpenTextDocumentParams, ipythonMode = IPythonMode.None) {
|
protected async onDidOpenTextDocument(params: DidOpenTextDocumentParams, ipythonMode = IPythonMode.None) {
|
||||||
const filePath = this.uriParser.decodeTextDocumentUri(params.textDocument.uri);
|
const uri = Uri.parse(params.textDocument.uri, this.fs.isCaseSensitive);
|
||||||
|
|
||||||
let doc = this.openFileMap.get(filePath);
|
let doc = this.openFileMap.get(uri.key);
|
||||||
if (doc) {
|
if (doc) {
|
||||||
// We shouldn't get an open text document request for an already-opened doc.
|
// We shouldn't get an open text document request for an already-opened doc.
|
||||||
this.console.error(`Received redundant open text document command for ${filePath}`);
|
this.console.error(`Received redundant open text document command for ${uri}`);
|
||||||
TextDocument.update(doc, [{ text: params.textDocument.text }], params.textDocument.version);
|
TextDocument.update(doc, [{ text: params.textDocument.text }], params.textDocument.version);
|
||||||
} else {
|
} else {
|
||||||
doc = TextDocument.create(filePath, 'python', params.textDocument.version, params.textDocument.text);
|
doc = TextDocument.create(
|
||||||
|
params.textDocument.uri,
|
||||||
|
'python',
|
||||||
|
params.textDocument.version,
|
||||||
|
params.textDocument.text
|
||||||
|
);
|
||||||
}
|
}
|
||||||
this.openFileMap.set(filePath, doc);
|
this.openFileMap.set(uri.key, doc);
|
||||||
|
|
||||||
// Send this open to all the workspaces that might contain this file.
|
// Send this open to all the workspaces that might contain this file.
|
||||||
const workspaces = await this.getContainingWorkspacesForFile(filePath);
|
const workspaces = await this.getContainingWorkspacesForFile(uri);
|
||||||
workspaces.forEach((w) => {
|
workspaces.forEach((w) => {
|
||||||
w.service.setFileOpened(filePath, params.textDocument.version, params.textDocument.text, ipythonMode);
|
w.service.setFileOpened(uri, params.textDocument.version, params.textDocument.text, ipythonMode);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async onDidChangeTextDocument(params: DidChangeTextDocumentParams, ipythonMode = IPythonMode.None) {
|
protected async onDidChangeTextDocument(params: DidChangeTextDocumentParams, ipythonMode = IPythonMode.None) {
|
||||||
this.recordUserInteractionTime();
|
this.recordUserInteractionTime();
|
||||||
|
|
||||||
const filePath = this.uriParser.decodeTextDocumentUri(params.textDocument.uri);
|
const uri = Uri.parse(params.textDocument.uri, this.fs.isCaseSensitive);
|
||||||
const doc = this.openFileMap.get(filePath);
|
const doc = this.openFileMap.get(uri.key);
|
||||||
if (!doc) {
|
if (!doc) {
|
||||||
// We shouldn't get a change text request for a closed doc.
|
// We shouldn't get a change text request for a closed doc.
|
||||||
this.console.error(`Received change text document command for closed file ${filePath}`);
|
this.console.error(`Received change text document command for closed file ${uri}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1215,27 +1200,27 @@ export abstract class LanguageServerBase implements LanguageServerInterface, Dis
|
|||||||
const newContents = doc.getText();
|
const newContents = doc.getText();
|
||||||
|
|
||||||
// Send this change to all the workspaces that might contain this file.
|
// Send this change to all the workspaces that might contain this file.
|
||||||
const workspaces = await this.getContainingWorkspacesForFile(filePath);
|
const workspaces = await this.getContainingWorkspacesForFile(uri);
|
||||||
workspaces.forEach((w) => {
|
workspaces.forEach((w) => {
|
||||||
w.service.updateOpenFileContents(filePath, params.textDocument.version, newContents, ipythonMode);
|
w.service.updateOpenFileContents(uri, params.textDocument.version, newContents, ipythonMode);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async onDidCloseTextDocument(params: DidCloseTextDocumentParams) {
|
protected async onDidCloseTextDocument(params: DidCloseTextDocumentParams) {
|
||||||
const filePath = this.uriParser.decodeTextDocumentUri(params.textDocument.uri);
|
const uri = Uri.parse(params.textDocument.uri, this.fs.isCaseSensitive);
|
||||||
|
|
||||||
// Send this close to all the workspaces that might contain this file.
|
// Send this close to all the workspaces that might contain this file.
|
||||||
const workspaces = await this.getContainingWorkspacesForFile(filePath);
|
const workspaces = await this.getContainingWorkspacesForFile(uri);
|
||||||
workspaces.forEach((w) => {
|
workspaces.forEach((w) => {
|
||||||
w.service.setFileClosed(filePath);
|
w.service.setFileClosed(uri);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.openFileMap.delete(filePath);
|
this.openFileMap.delete(uri.key);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected onDidChangeWatchedFiles(params: DidChangeWatchedFilesParams) {
|
protected onDidChangeWatchedFiles(params: DidChangeWatchedFilesParams) {
|
||||||
params.changes.forEach((change) => {
|
params.changes.forEach((change) => {
|
||||||
const filePath = this.fs.realCasePath(this.uriParser.decodeTextDocumentUri(change.uri));
|
const filePath = this.fs.realCasePath(Uri.parse(change.uri, this.fs.isCaseSensitive));
|
||||||
const eventType: FileWatcherEventType = change.type === 1 ? 'add' : 'change';
|
const eventType: FileWatcherEventType = change.type === 1 ? 'add' : 'change';
|
||||||
this.serverOptions.fileWatcherHandler.onFileChange(eventType, filePath);
|
this.serverOptions.fileWatcherHandler.onFileChange(eventType, filePath);
|
||||||
});
|
});
|
||||||
@ -1302,7 +1287,7 @@ export abstract class LanguageServerBase implements LanguageServerInterface, Dis
|
|||||||
protected convertDiagnostics(fs: FileSystem, fileDiagnostics: FileDiagnostics): PublishDiagnosticsParams[] {
|
protected convertDiagnostics(fs: FileSystem, fileDiagnostics: FileDiagnostics): PublishDiagnosticsParams[] {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
uri: convertPathToUri(fs, fileDiagnostics.filePath),
|
uri: fileDiagnostics.fileUri.toString(),
|
||||||
version: fileDiagnostics.version,
|
version: fileDiagnostics.version,
|
||||||
diagnostics: this._convertDiagnostics(fs, fileDiagnostics.diagnostics),
|
diagnostics: this._convertDiagnostics(fs, fileDiagnostics.diagnostics),
|
||||||
},
|
},
|
||||||
@ -1316,7 +1301,7 @@ export abstract class LanguageServerBase implements LanguageServerInterface, Dis
|
|||||||
protected onAnalysisCompletedHandler(fs: FileSystem, results: AnalysisResults): void {
|
protected onAnalysisCompletedHandler(fs: FileSystem, results: AnalysisResults): void {
|
||||||
// Send the computed diagnostics to the client.
|
// Send the computed diagnostics to the client.
|
||||||
results.diagnostics.forEach((fileDiag) => {
|
results.diagnostics.forEach((fileDiag) => {
|
||||||
if (!this.canNavigateToFile(fileDiag.filePath, fs)) {
|
if (!this.canNavigateToFile(fileDiag.fileUri, fs)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1361,11 +1346,11 @@ export abstract class LanguageServerBase implements LanguageServerInterface, Dis
|
|||||||
const otherWorkspaces = this.workspaceFactory.items().filter((w) => w !== workspace);
|
const otherWorkspaces = this.workspaceFactory.items().filter((w) => w !== workspace);
|
||||||
|
|
||||||
for (const uri of documentsWithDiagnosticsList) {
|
for (const uri of documentsWithDiagnosticsList) {
|
||||||
const filePath = convertUriToPath(workspace.service.fs, uri);
|
const fileUri = Uri.parse(uri, this.fs.isCaseSensitive);
|
||||||
|
|
||||||
if (workspace.service.isTracked(filePath)) {
|
if (workspace.service.isTracked(fileUri)) {
|
||||||
// Do not clean up diagnostics for files tracked by multiple workspaces
|
// Do not clean up diagnostics for files tracked by multiple workspaces
|
||||||
if (otherWorkspaces.some((w) => w.service.isTracked(filePath))) {
|
if (otherWorkspaces.some((w) => w.service.isTracked(fileUri))) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
this.sendDiagnostics([
|
this.sendDiagnostics([
|
||||||
@ -1380,8 +1365,7 @@ export abstract class LanguageServerBase implements LanguageServerInterface, Dis
|
|||||||
|
|
||||||
protected createAnalyzerServiceForWorkspace(
|
protected createAnalyzerServiceForWorkspace(
|
||||||
name: string,
|
name: string,
|
||||||
_rootPath: string,
|
uri: Uri,
|
||||||
_uri: string,
|
|
||||||
kinds: string[],
|
kinds: string[],
|
||||||
services?: WorkspaceServices
|
services?: WorkspaceServices
|
||||||
): AnalyzerService {
|
): AnalyzerService {
|
||||||
@ -1422,7 +1406,7 @@ export abstract class LanguageServerBase implements LanguageServerInterface, Dis
|
|||||||
|
|
||||||
protected abstract createProgressReporter(): ProgressReporter;
|
protected abstract createProgressReporter(): ProgressReporter;
|
||||||
|
|
||||||
protected canNavigateToFile(path: string, fs: FileSystem): boolean {
|
protected canNavigateToFile(path: Uri, fs: FileSystem): boolean {
|
||||||
return canNavigateToFile(fs, path);
|
return canNavigateToFile(fs, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1481,13 +1465,13 @@ export abstract class LanguageServerBase implements LanguageServerInterface, Dis
|
|||||||
const foldersToWatch = deduplicateFolders(
|
const foldersToWatch = deduplicateFolders(
|
||||||
this.workspaceFactory
|
this.workspaceFactory
|
||||||
.getNonDefaultWorkspaces()
|
.getNonDefaultWorkspaces()
|
||||||
.map((w) => w.searchPathsToWatch.filter((p) => !p.startsWith(w.rootPath)))
|
.map((w) => w.searchPathsToWatch.filter((p) => !p.startsWith(w.rootUri)))
|
||||||
);
|
);
|
||||||
|
|
||||||
foldersToWatch.forEach((p) => {
|
foldersToWatch.forEach((p) => {
|
||||||
const globPattern = isFile(this.fs, p, /* treatZipDirectoryAsFile */ true)
|
const globPattern = isFile(this.fs, p, /* treatZipDirectoryAsFile */ true)
|
||||||
? { baseUri: convertPathToUri(this.fs, getDirectoryPath(p)), pattern: getFileName(p) }
|
? { baseUri: p.getDirectory().toString(), pattern: p.fileName }
|
||||||
: { baseUri: convertPathToUri(this.fs, p), pattern: '**' };
|
: { baseUri: p.toString(), pattern: '**' };
|
||||||
|
|
||||||
watchers.push({ globPattern, kind: watchKind });
|
watchers.push({ globPattern, kind: watchKind });
|
||||||
});
|
});
|
||||||
@ -1582,10 +1566,10 @@ export abstract class LanguageServerBase implements LanguageServerInterface, Dis
|
|||||||
const relatedInfo = diag.getRelatedInfo();
|
const relatedInfo = diag.getRelatedInfo();
|
||||||
if (relatedInfo.length > 0) {
|
if (relatedInfo.length > 0) {
|
||||||
vsDiag.relatedInformation = relatedInfo
|
vsDiag.relatedInformation = relatedInfo
|
||||||
.filter((info) => this.canNavigateToFile(info.filePath, fs))
|
.filter((info) => this.canNavigateToFile(info.uri, fs))
|
||||||
.map((info) =>
|
.map((info) =>
|
||||||
DiagnosticRelatedInformation.create(
|
DiagnosticRelatedInformation.create(
|
||||||
Location.create(convertPathToUri(fs, info.filePath), info.range),
|
Location.create(info.uri.toString(), info.range),
|
||||||
info.message
|
info.message
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
@ -13,7 +13,7 @@ import { AnalyzerService, getNextServiceId } from '../analyzer/service';
|
|||||||
import { CommandLineOptions } from '../common/commandLineOptions';
|
import { CommandLineOptions } from '../common/commandLineOptions';
|
||||||
import { LogLevel } from '../common/console';
|
import { LogLevel } from '../common/console';
|
||||||
import { FileSystem } from '../common/fileSystem';
|
import { FileSystem } from '../common/fileSystem';
|
||||||
import { combinePaths } from '../common/pathUtils';
|
import { Uri } from '../common/uri/uri';
|
||||||
import { LanguageServerInterface, ServerSettings } from '../languageServerBase';
|
import { LanguageServerInterface, ServerSettings } from '../languageServerBase';
|
||||||
import { WellKnownWorkspaceKinds, Workspace, createInitStatus } from '../workspaceFactory';
|
import { WellKnownWorkspaceKinds, Workspace, createInitStatus } from '../workspaceFactory';
|
||||||
|
|
||||||
@ -25,15 +25,15 @@ export interface CloneOptions {
|
|||||||
|
|
||||||
export class AnalyzerServiceExecutor {
|
export class AnalyzerServiceExecutor {
|
||||||
static runWithOptions(
|
static runWithOptions(
|
||||||
languageServiceRootPath: string,
|
languageServiceRootUri: Uri,
|
||||||
workspace: Workspace,
|
workspace: Workspace,
|
||||||
serverSettings: ServerSettings,
|
serverSettings: ServerSettings,
|
||||||
typeStubTargetImportName?: string,
|
typeStubTargetImportName?: string,
|
||||||
trackFiles = true
|
trackFiles = true
|
||||||
): void {
|
): void {
|
||||||
const commandLineOptions = getEffectiveCommandLineOptions(
|
const commandLineOptions = getEffectiveCommandLineOptions(
|
||||||
languageServiceRootPath,
|
languageServiceRootUri.getFilePath(),
|
||||||
workspace.rootPath,
|
workspace.rootUri.getFilePath(),
|
||||||
serverSettings,
|
serverSettings,
|
||||||
trackFiles,
|
trackFiles,
|
||||||
typeStubTargetImportName,
|
typeStubTargetImportName,
|
||||||
@ -58,8 +58,7 @@ export class AnalyzerServiceExecutor {
|
|||||||
const tempWorkspace: Workspace = {
|
const tempWorkspace: Workspace = {
|
||||||
...workspace,
|
...workspace,
|
||||||
workspaceName: `temp workspace for cloned service`,
|
workspaceName: `temp workspace for cloned service`,
|
||||||
rootPath: workspace.rootPath,
|
rootUri: workspace.rootUri,
|
||||||
uri: workspace.uri,
|
|
||||||
pythonPath: workspace.pythonPath,
|
pythonPath: workspace.pythonPath,
|
||||||
pythonPathKind: workspace.pythonPathKind,
|
pythonPathKind: workspace.pythonPathKind,
|
||||||
kinds: [...workspace.kinds, WellKnownWorkspaceKinds.Cloned],
|
kinds: [...workspace.kinds, WellKnownWorkspaceKinds.Cloned],
|
||||||
@ -78,7 +77,7 @@ export class AnalyzerServiceExecutor {
|
|||||||
|
|
||||||
const serverSettings = await ls.getSettings(workspace);
|
const serverSettings = await ls.getSettings(workspace);
|
||||||
AnalyzerServiceExecutor.runWithOptions(
|
AnalyzerServiceExecutor.runWithOptions(
|
||||||
ls.rootPath,
|
ls.rootUri,
|
||||||
tempWorkspace,
|
tempWorkspace,
|
||||||
serverSettings,
|
serverSettings,
|
||||||
options.typeStubTargetImportName,
|
options.typeStubTargetImportName,
|
||||||
@ -120,21 +119,15 @@ function getEffectiveCommandLineOptions(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (serverSettings.venvPath) {
|
if (serverSettings.venvPath) {
|
||||||
commandLineOptions.venvPath = combinePaths(
|
commandLineOptions.venvPath = serverSettings.venvPath.getFilePath();
|
||||||
workspaceRootPath || languageServiceRootPath,
|
|
||||||
serverSettings.venvPath
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (serverSettings.pythonPath) {
|
if (serverSettings.pythonPath) {
|
||||||
// The Python VS Code extension treats the value "python" specially. This means
|
// The Python VS Code extension treats the value "python" specially. This means
|
||||||
// the local python interpreter should be used rather than interpreting the
|
// the local python interpreter should be used rather than interpreting the
|
||||||
// setting value as a path to the interpreter. We'll simply ignore it in this case.
|
// setting value as a path to the interpreter. We'll simply ignore it in this case.
|
||||||
if (!isPythonBinary(serverSettings.pythonPath)) {
|
if (!isPythonBinary(serverSettings.pythonPath.getFilePath())) {
|
||||||
commandLineOptions.pythonPath = combinePaths(
|
commandLineOptions.pythonPath = serverSettings.pythonPath.getFilePath();
|
||||||
workspaceRootPath || languageServiceRootPath,
|
|
||||||
serverSettings.pythonPath
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,11 +135,11 @@ function getEffectiveCommandLineOptions(
|
|||||||
// Pyright supports only one typeshed path currently, whereas the
|
// Pyright supports only one typeshed path currently, whereas the
|
||||||
// official VS Code Python extension supports multiple typeshed paths.
|
// official VS Code Python extension supports multiple typeshed paths.
|
||||||
// We'll use the first one specified and ignore the rest.
|
// We'll use the first one specified and ignore the rest.
|
||||||
commandLineOptions.typeshedPath = serverSettings.typeshedPath;
|
commandLineOptions.typeshedPath = serverSettings.typeshedPath.getFilePath();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (serverSettings.stubPath) {
|
if (serverSettings.stubPath) {
|
||||||
commandLineOptions.stubPath = serverSettings.stubPath;
|
commandLineOptions.stubPath = serverSettings.stubPath.getFilePath();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (serverSettings.logLevel === LogLevel.Log) {
|
if (serverSettings.logLevel === LogLevel.Log) {
|
||||||
@ -160,7 +153,7 @@ function getEffectiveCommandLineOptions(
|
|||||||
}
|
}
|
||||||
|
|
||||||
commandLineOptions.autoSearchPaths = serverSettings.autoSearchPaths;
|
commandLineOptions.autoSearchPaths = serverSettings.autoSearchPaths;
|
||||||
commandLineOptions.extraPaths = serverSettings.extraPaths;
|
commandLineOptions.extraPaths = serverSettings.extraPaths?.map((e) => e.getFilePath()) ?? [];
|
||||||
commandLineOptions.diagnosticSeverityOverrides = serverSettings.diagnosticSeverityOverrides;
|
commandLineOptions.diagnosticSeverityOverrides = serverSettings.diagnosticSeverityOverrides;
|
||||||
|
|
||||||
commandLineOptions.includeFileSpecs = serverSettings.includeFileSpecs ?? [];
|
commandLineOptions.includeFileSpecs = serverSettings.includeFileSpecs ?? [];
|
||||||
|
@ -12,15 +12,15 @@ import { DeclarationType } from '../analyzer/declaration';
|
|||||||
import { ImportResolver, ModuleNameAndType } from '../analyzer/importResolver';
|
import { ImportResolver, ModuleNameAndType } from '../analyzer/importResolver';
|
||||||
import { ImportType } from '../analyzer/importResult';
|
import { ImportType } from '../analyzer/importResult';
|
||||||
import {
|
import {
|
||||||
|
ImportGroup,
|
||||||
|
ImportNameInfo,
|
||||||
|
ImportStatements,
|
||||||
|
ModuleNameInfo,
|
||||||
getImportGroup,
|
getImportGroup,
|
||||||
getImportGroupFromModuleNameAndType,
|
getImportGroupFromModuleNameAndType,
|
||||||
getTextEditsForAutoImportInsertion,
|
getTextEditsForAutoImportInsertion,
|
||||||
getTextEditsForAutoImportSymbolAddition,
|
getTextEditsForAutoImportSymbolAddition,
|
||||||
getTopLevelImports,
|
getTopLevelImports,
|
||||||
ImportGroup,
|
|
||||||
ImportNameInfo,
|
|
||||||
ImportStatements,
|
|
||||||
ModuleNameInfo,
|
|
||||||
} from '../analyzer/importStatementUtils';
|
} from '../analyzer/importStatementUtils';
|
||||||
import { isUserCode } from '../analyzer/sourceFileInfoUtils';
|
import { isUserCode } from '../analyzer/sourceFileInfoUtils';
|
||||||
import { Symbol } from '../analyzer/symbol';
|
import { Symbol } from '../analyzer/symbol';
|
||||||
@ -30,13 +30,14 @@ import { throwIfCancellationRequested } from '../common/cancellationUtils';
|
|||||||
import { appendArray } from '../common/collectionUtils';
|
import { appendArray } from '../common/collectionUtils';
|
||||||
import { ExecutionEnvironment } from '../common/configOptions';
|
import { ExecutionEnvironment } from '../common/configOptions';
|
||||||
import { TextEditAction } from '../common/editAction';
|
import { TextEditAction } from '../common/editAction';
|
||||||
import { combinePaths, getDirectoryPath, getFileName, stripFileExtension } from '../common/pathUtils';
|
import { SourceFileInfo } from '../common/extensibility';
|
||||||
|
import { stripFileExtension } from '../common/pathUtils';
|
||||||
import * as StringUtils from '../common/stringUtils';
|
import * as StringUtils from '../common/stringUtils';
|
||||||
import { Position } from '../common/textRange';
|
import { Position } from '../common/textRange';
|
||||||
|
import { Uri } from '../common/uri/uri';
|
||||||
import { ParseNodeType } from '../parser/parseNodes';
|
import { ParseNodeType } from '../parser/parseNodes';
|
||||||
import { ParseResults } from '../parser/parser';
|
import { ParseResults } from '../parser/parser';
|
||||||
import { CompletionMap } from './completionProvider';
|
import { CompletionMap } from './completionProvider';
|
||||||
import { SourceFileInfo } from '../common/extensibility';
|
|
||||||
import { IndexAliasData } from './symbolIndexer';
|
import { IndexAliasData } from './symbolIndexer';
|
||||||
|
|
||||||
export interface AutoImportSymbol {
|
export interface AutoImportSymbol {
|
||||||
@ -47,6 +48,7 @@ export interface AutoImportSymbol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface ModuleSymbolTable {
|
export interface ModuleSymbolTable {
|
||||||
|
uri: Uri;
|
||||||
forEach(callbackfn: (symbol: AutoImportSymbol, name: string, library: boolean) => void): void;
|
forEach(callbackfn: (symbol: AutoImportSymbol, name: string, library: boolean) => void): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,8 +56,8 @@ export type ModuleSymbolMap = Map<string, ModuleSymbolTable>;
|
|||||||
|
|
||||||
export interface AutoImportResult {
|
export interface AutoImportResult {
|
||||||
readonly name: string;
|
readonly name: string;
|
||||||
readonly declPath: string;
|
readonly declUri: Uri;
|
||||||
readonly originalDeclPath: string;
|
readonly originalDeclUri: Uri;
|
||||||
readonly insertionText: string;
|
readonly insertionText: string;
|
||||||
readonly symbol?: Symbol;
|
readonly symbol?: Symbol;
|
||||||
readonly source?: string;
|
readonly source?: string;
|
||||||
@ -74,7 +76,7 @@ export interface ImportParts {
|
|||||||
readonly importName: string;
|
readonly importName: string;
|
||||||
readonly symbolName?: string;
|
readonly symbolName?: string;
|
||||||
readonly importFrom?: string;
|
readonly importFrom?: string;
|
||||||
readonly filePath: string;
|
readonly fileUri: Uri;
|
||||||
readonly dotCount: number;
|
readonly dotCount: number;
|
||||||
readonly moduleNameAndType: ModuleNameAndType;
|
readonly moduleNameAndType: ModuleNameAndType;
|
||||||
}
|
}
|
||||||
@ -85,7 +87,7 @@ export interface ImportAliasData {
|
|||||||
readonly symbol?: Symbol;
|
readonly symbol?: Symbol;
|
||||||
readonly kind?: SymbolKind;
|
readonly kind?: SymbolKind;
|
||||||
readonly itemKind?: CompletionItemKind;
|
readonly itemKind?: CompletionItemKind;
|
||||||
readonly filePath: string;
|
readonly fileUri: Uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type AutoImportResultMap = Map<string, AutoImportResult[]>;
|
export type AutoImportResultMap = Map<string, AutoImportResult[]>;
|
||||||
@ -106,13 +108,13 @@ export function addModuleSymbolsMap(files: readonly SourceFileInfo[], moduleSymb
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const filePath = file.sourceFile.getFilePath();
|
const uri = file.sourceFile.getUri();
|
||||||
const symbolTable = file.sourceFile.getModuleSymbolTable();
|
const symbolTable = file.sourceFile.getModuleSymbolTable();
|
||||||
if (!symbolTable) {
|
if (!symbolTable) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const fileName = stripFileExtension(getFileName(filePath));
|
const fileName = stripFileExtension(uri.fileName);
|
||||||
|
|
||||||
// Don't offer imports from files that are named with private
|
// Don't offer imports from files that are named with private
|
||||||
// naming semantics like "_ast.py".
|
// naming semantics like "_ast.py".
|
||||||
@ -120,7 +122,8 @@ export function addModuleSymbolsMap(files: readonly SourceFileInfo[], moduleSymb
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
moduleSymbolMap.set(filePath, {
|
moduleSymbolMap.set(uri.key, {
|
||||||
|
uri,
|
||||||
forEach(callbackfn: (value: AutoImportSymbol, key: string, library: boolean) => void): void {
|
forEach(callbackfn: (value: AutoImportSymbol, key: string, library: boolean) => void): void {
|
||||||
symbolTable.forEach((symbol, name) => {
|
symbolTable.forEach((symbol, name) => {
|
||||||
if (!isVisibleExternally(symbol)) {
|
if (!isVisibleExternally(symbol)) {
|
||||||
@ -206,12 +209,12 @@ export class AutoImporter {
|
|||||||
results: AutoImportResultMap,
|
results: AutoImportResultMap,
|
||||||
token: CancellationToken
|
token: CancellationToken
|
||||||
) {
|
) {
|
||||||
this.moduleSymbolMap.forEach((topLevelSymbols, filePath) => {
|
this.moduleSymbolMap.forEach((topLevelSymbols, key) => {
|
||||||
// See if this file should be offered as an implicit import.
|
// See if this file should be offered as an implicit import.
|
||||||
const isStubFileOrHasInit = this.isStubFileOrHasInit(this.moduleSymbolMap!, filePath);
|
const isStubFileOrHasInit = this.isStubFileOrHasInit(this.moduleSymbolMap!, topLevelSymbols.uri);
|
||||||
this.processModuleSymbolTable(
|
this.processModuleSymbolTable(
|
||||||
topLevelSymbols,
|
topLevelSymbols,
|
||||||
filePath,
|
topLevelSymbols.uri,
|
||||||
word,
|
word,
|
||||||
similarityLimit,
|
similarityLimit,
|
||||||
isStubFileOrHasInit,
|
isStubFileOrHasInit,
|
||||||
@ -244,7 +247,7 @@ export class AutoImporter {
|
|||||||
|
|
||||||
// If import statement for the module already exist, then bail out.
|
// If import statement for the module already exist, then bail out.
|
||||||
// ex) import module[.submodule] or from module[.submodule] import symbol
|
// ex) import module[.submodule] or from module[.submodule] import symbol
|
||||||
if (this._importStatements.mapByFilePath.has(importAliasData.importParts.filePath)) {
|
if (this._importStatements.mapByFilePath.has(importAliasData.importParts.fileUri.key)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -281,7 +284,7 @@ export class AutoImporter {
|
|||||||
},
|
},
|
||||||
importAliasData.importParts.importName,
|
importAliasData.importParts.importName,
|
||||||
importAliasData.importGroup,
|
importAliasData.importGroup,
|
||||||
importAliasData.importParts.filePath
|
importAliasData.importParts.fileUri
|
||||||
);
|
);
|
||||||
|
|
||||||
this._addResult(results, {
|
this._addResult(results, {
|
||||||
@ -292,8 +295,8 @@ export class AutoImporter {
|
|||||||
source: importAliasData.importParts.importFrom,
|
source: importAliasData.importParts.importFrom,
|
||||||
insertionText: autoImportTextEdits.insertionText,
|
insertionText: autoImportTextEdits.insertionText,
|
||||||
edits: autoImportTextEdits.edits,
|
edits: autoImportTextEdits.edits,
|
||||||
declPath: importAliasData.importParts.filePath,
|
declUri: importAliasData.importParts.fileUri,
|
||||||
originalDeclPath: importAliasData.filePath,
|
originalDeclUri: importAliasData.fileUri,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -301,7 +304,7 @@ export class AutoImporter {
|
|||||||
|
|
||||||
protected processModuleSymbolTable(
|
protected processModuleSymbolTable(
|
||||||
topLevelSymbols: ModuleSymbolTable,
|
topLevelSymbols: ModuleSymbolTable,
|
||||||
moduleFilePath: string,
|
moduleUri: Uri,
|
||||||
word: string,
|
word: string,
|
||||||
similarityLimit: number,
|
similarityLimit: number,
|
||||||
isStubOrHasInit: { isStub: boolean; hasInit: boolean },
|
isStubOrHasInit: { isStub: boolean; hasInit: boolean },
|
||||||
@ -312,7 +315,7 @@ export class AutoImporter {
|
|||||||
) {
|
) {
|
||||||
throwIfCancellationRequested(token);
|
throwIfCancellationRequested(token);
|
||||||
|
|
||||||
const [importSource, importGroup, moduleNameAndType] = this._getImportPartsForSymbols(moduleFilePath);
|
const [importSource, importGroup, moduleNameAndType] = this._getImportPartsForSymbols(moduleUri);
|
||||||
if (!importSource) {
|
if (!importSource) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -345,7 +348,7 @@ export class AutoImporter {
|
|||||||
symbolName: name,
|
symbolName: name,
|
||||||
importName: name,
|
importName: name,
|
||||||
importFrom: importSource,
|
importFrom: importSource,
|
||||||
filePath: moduleFilePath,
|
fileUri: moduleUri,
|
||||||
dotCount,
|
dotCount,
|
||||||
moduleNameAndType,
|
moduleNameAndType,
|
||||||
},
|
},
|
||||||
@ -353,20 +356,20 @@ export class AutoImporter {
|
|||||||
symbol: autoImportSymbol.symbol,
|
symbol: autoImportSymbol.symbol,
|
||||||
kind: autoImportSymbol.importAlias.kind,
|
kind: autoImportSymbol.importAlias.kind,
|
||||||
itemKind: autoImportSymbol.importAlias.itemKind,
|
itemKind: autoImportSymbol.importAlias.itemKind,
|
||||||
filePath: autoImportSymbol.importAlias.modulePath,
|
fileUri: autoImportSymbol.importAlias.moduleUri,
|
||||||
},
|
},
|
||||||
importAliasMap
|
importAliasMap
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const nameForImportFrom = this.getNameForImportFrom(library, moduleFilePath);
|
const nameForImportFrom = this.getNameForImportFrom(library, moduleUri);
|
||||||
const autoImportTextEdits = this._getTextEditsForAutoImportByFilePath(
|
const autoImportTextEdits = this._getTextEditsForAutoImportByFilePath(
|
||||||
{ name, alias: abbrFromUsers },
|
{ name, alias: abbrFromUsers },
|
||||||
{ name: importSource, nameForImportFrom },
|
{ name: importSource, nameForImportFrom },
|
||||||
name,
|
name,
|
||||||
importGroup,
|
importGroup,
|
||||||
moduleFilePath
|
moduleUri
|
||||||
);
|
);
|
||||||
|
|
||||||
this._addResult(results, {
|
this._addResult(results, {
|
||||||
@ -377,8 +380,8 @@ export class AutoImporter {
|
|||||||
kind: autoImportSymbol.itemKind ?? convertSymbolKindToCompletionItemKind(autoImportSymbol.kind),
|
kind: autoImportSymbol.itemKind ?? convertSymbolKindToCompletionItemKind(autoImportSymbol.kind),
|
||||||
insertionText: autoImportTextEdits.insertionText,
|
insertionText: autoImportTextEdits.insertionText,
|
||||||
edits: autoImportTextEdits.edits,
|
edits: autoImportTextEdits.edits,
|
||||||
declPath: moduleFilePath,
|
declUri: moduleUri,
|
||||||
originalDeclPath: moduleFilePath,
|
originalDeclUri: moduleUri,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -389,7 +392,7 @@ export class AutoImporter {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const importParts = this._getImportParts(moduleFilePath);
|
const importParts = this._getImportParts(moduleUri);
|
||||||
if (!importParts) {
|
if (!importParts) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -406,7 +409,7 @@ export class AutoImporter {
|
|||||||
|
|
||||||
this._addToImportAliasMap(
|
this._addToImportAliasMap(
|
||||||
{
|
{
|
||||||
modulePath: moduleFilePath,
|
moduleUri,
|
||||||
originalName: importParts.importName,
|
originalName: importParts.importName,
|
||||||
kind: SymbolKind.Module,
|
kind: SymbolKind.Module,
|
||||||
itemKind: CompletionItemKind.Module,
|
itemKind: CompletionItemKind.Module,
|
||||||
@ -416,22 +419,22 @@ export class AutoImporter {
|
|||||||
importGroup,
|
importGroup,
|
||||||
kind: SymbolKind.Module,
|
kind: SymbolKind.Module,
|
||||||
itemKind: CompletionItemKind.Module,
|
itemKind: CompletionItemKind.Module,
|
||||||
filePath: moduleFilePath,
|
fileUri: moduleUri,
|
||||||
},
|
},
|
||||||
importAliasMap
|
importAliasMap
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected getNameForImportFrom(library: boolean, moduleFilePath: string): string | undefined {
|
protected getNameForImportFrom(library: boolean, moduleUri: Uri): string | undefined {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected isStubFileOrHasInit<T>(map: Map<string, T>, filePath: string) {
|
protected isStubFileOrHasInit<T>(map: Map<string, T>, uri: Uri) {
|
||||||
const fileDir = getDirectoryPath(filePath);
|
const fileDir = uri.getDirectory();
|
||||||
const initPathPy = combinePaths(fileDir, '__init__.py');
|
const initPathPy = fileDir.combinePaths('__init__.py');
|
||||||
const initPathPyi = initPathPy + 'i';
|
const initPathPyi = initPathPy.addPath('i');
|
||||||
const isStub = filePath.endsWith('.pyi');
|
const isStub = uri.pathEndsWith('.pyi');
|
||||||
const hasInit = map.has(initPathPy) || map.has(initPathPyi);
|
const hasInit = map.has(initPathPy.key) || map.has(initPathPyi.key);
|
||||||
return { isStub, hasInit };
|
return { isStub, hasInit };
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -462,14 +465,14 @@ export class AutoImporter {
|
|||||||
// Since we don't resolve alias declaration using type evaluator, there is still a chance
|
// Since we don't resolve alias declaration using type evaluator, there is still a chance
|
||||||
// where we show multiple aliases for same symbols. but this should still reduce number of
|
// where we show multiple aliases for same symbols. but this should still reduce number of
|
||||||
// such cases.
|
// such cases.
|
||||||
if (!importAliasMap.has(alias.modulePath)) {
|
if (!importAliasMap.has(alias.moduleUri.key)) {
|
||||||
const map = new Map<string, ImportAliasData>();
|
const map = new Map<string, ImportAliasData>();
|
||||||
map.set(alias.originalName, data);
|
map.set(alias.originalName, data);
|
||||||
importAliasMap.set(alias.modulePath, map);
|
importAliasMap.set(alias.moduleUri.key, map);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const map = importAliasMap.get(alias.modulePath)!;
|
const map = importAliasMap.get(alias.moduleUri.key)!;
|
||||||
if (!map.has(alias.originalName)) {
|
if (!map.has(alias.originalName)) {
|
||||||
map.set(alias.originalName, data);
|
map.set(alias.originalName, data);
|
||||||
return;
|
return;
|
||||||
@ -508,8 +511,8 @@ export class AutoImporter {
|
|||||||
return StringUtils.getStringComparer()(left.importParts.importName, right.importParts.importName);
|
return StringUtils.getStringComparer()(left.importParts.importName, right.importParts.importName);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getImportPartsForSymbols(filePath: string): [string | undefined, ImportGroup, ModuleNameAndType] {
|
private _getImportPartsForSymbols(uri: Uri): [string | undefined, ImportGroup, ModuleNameAndType] {
|
||||||
const localImport = this._importStatements.mapByFilePath.get(filePath);
|
const localImport = this._importStatements.mapByFilePath.get(uri.key);
|
||||||
if (localImport) {
|
if (localImport) {
|
||||||
return [
|
return [
|
||||||
localImport.moduleName,
|
localImport.moduleName,
|
||||||
@ -521,7 +524,7 @@ export class AutoImporter {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
const moduleNameAndType = this._getModuleNameAndTypeFromFilePath(filePath);
|
const moduleNameAndType = this._getModuleNameAndTypeFromFilePath(uri);
|
||||||
return [
|
return [
|
||||||
moduleNameAndType.moduleName,
|
moduleNameAndType.moduleName,
|
||||||
getImportGroupFromModuleNameAndType(moduleNameAndType),
|
getImportGroupFromModuleNameAndType(moduleNameAndType),
|
||||||
@ -530,15 +533,15 @@ export class AutoImporter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getImportParts(filePath: string) {
|
private _getImportParts(uri: Uri) {
|
||||||
const name = stripFileExtension(getFileName(filePath));
|
const name = stripFileExtension(uri.fileName);
|
||||||
|
|
||||||
// See if we can import module as "import xxx"
|
// See if we can import module as "import xxx"
|
||||||
if (name === '__init__') {
|
if (name === '__init__') {
|
||||||
return createImportParts(this._getModuleNameAndTypeFromFilePath(getDirectoryPath(filePath)));
|
return createImportParts(this._getModuleNameAndTypeFromFilePath(uri.getDirectory()));
|
||||||
}
|
}
|
||||||
|
|
||||||
return createImportParts(this._getModuleNameAndTypeFromFilePath(filePath));
|
return createImportParts(this._getModuleNameAndTypeFromFilePath(uri));
|
||||||
|
|
||||||
function createImportParts(module: ModuleNameAndType): ImportParts | undefined {
|
function createImportParts(module: ModuleNameAndType): ImportParts | undefined {
|
||||||
const moduleName = module.moduleName;
|
const moduleName = module.moduleName;
|
||||||
@ -553,7 +556,7 @@ export class AutoImporter {
|
|||||||
symbolName: importNamePart,
|
symbolName: importNamePart,
|
||||||
importName: importNamePart ?? moduleName,
|
importName: importNamePart ?? moduleName,
|
||||||
importFrom,
|
importFrom,
|
||||||
filePath,
|
fileUri: uri,
|
||||||
dotCount: StringUtils.getCharacterCount(moduleName, '.'),
|
dotCount: StringUtils.getCharacterCount(moduleName, '.'),
|
||||||
moduleNameAndType: module,
|
moduleNameAndType: module,
|
||||||
};
|
};
|
||||||
@ -601,8 +604,8 @@ export class AutoImporter {
|
|||||||
// Given the file path of a module that we want to import,
|
// Given the file path of a module that we want to import,
|
||||||
// convert to a module name that can be used in an
|
// convert to a module name that can be used in an
|
||||||
// 'import from' statement.
|
// 'import from' statement.
|
||||||
private _getModuleNameAndTypeFromFilePath(filePath: string): ModuleNameAndType {
|
private _getModuleNameAndTypeFromFilePath(uri: Uri): ModuleNameAndType {
|
||||||
return this.importResolver.getModuleNameForImport(filePath, this.execEnvironment);
|
return this.importResolver.getModuleNameForImport(uri, this.execEnvironment);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getTextEditsForAutoImportByFilePath(
|
private _getTextEditsForAutoImportByFilePath(
|
||||||
@ -610,10 +613,10 @@ export class AutoImporter {
|
|||||||
moduleNameInfo: ModuleNameInfo,
|
moduleNameInfo: ModuleNameInfo,
|
||||||
insertionText: string,
|
insertionText: string,
|
||||||
importGroup: ImportGroup,
|
importGroup: ImportGroup,
|
||||||
filePath: string
|
fileUri: Uri
|
||||||
): { insertionText: string; edits?: TextEditAction[] | undefined } {
|
): { insertionText: string; edits?: TextEditAction[] | undefined } {
|
||||||
// If there is no symbolName, there can't be existing import statement.
|
// If there is no symbolName, there can't be existing import statement.
|
||||||
const importStatement = this._importStatements.mapByFilePath.get(filePath);
|
const importStatement = this._importStatements.mapByFilePath.get(fileUri.key);
|
||||||
if (importStatement) {
|
if (importStatement) {
|
||||||
// Found import for given module. See whether we can use the module as it is.
|
// Found import for given module. See whether we can use the module as it is.
|
||||||
if (importStatement.node.nodeType === ParseNodeType.Import) {
|
if (importStatement.node.nodeType === ParseNodeType.Import) {
|
||||||
@ -699,7 +702,7 @@ export class AutoImporter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check whether it is one of implicit imports
|
// Check whether it is one of implicit imports
|
||||||
const importFrom = this._importStatements.implicitImports?.get(filePath);
|
const importFrom = this._importStatements.implicitImports?.get(fileUri.key);
|
||||||
if (importFrom) {
|
if (importFrom) {
|
||||||
// For now, we don't check whether alias or moduleName got overwritten at
|
// For now, we don't check whether alias or moduleName got overwritten at
|
||||||
// given position
|
// given position
|
||||||
|
@ -29,10 +29,10 @@ import { appendArray } from '../common/collectionUtils';
|
|||||||
import { isDefined } from '../common/core';
|
import { isDefined } from '../common/core';
|
||||||
import { ProgramView, ReferenceUseCase, SymbolUsageProvider } from '../common/extensibility';
|
import { ProgramView, ReferenceUseCase, SymbolUsageProvider } from '../common/extensibility';
|
||||||
import { getSymbolKind } from '../common/lspUtils';
|
import { getSymbolKind } from '../common/lspUtils';
|
||||||
import { convertPathToUri, getFileName } from '../common/pathUtils';
|
|
||||||
import { convertOffsetsToRange } from '../common/positionUtils';
|
import { convertOffsetsToRange } from '../common/positionUtils';
|
||||||
import { ServiceKeys } from '../common/serviceProviderExtensions';
|
import { ServiceKeys } from '../common/serviceProviderExtensions';
|
||||||
import { Position, rangesAreEqual } from '../common/textRange';
|
import { Position, rangesAreEqual } from '../common/textRange';
|
||||||
|
import { Uri } from '../common/uri/uri';
|
||||||
import { ReferencesProvider, ReferencesResult } from '../languageService/referencesProvider';
|
import { ReferencesProvider, ReferencesResult } from '../languageService/referencesProvider';
|
||||||
import { CallNode, MemberAccessNode, NameNode, ParseNode, ParseNodeType } from '../parser/parseNodes';
|
import { CallNode, MemberAccessNode, NameNode, ParseNode, ParseNodeType } from '../parser/parseNodes';
|
||||||
import { ParseResults } from '../parser/parser';
|
import { ParseResults } from '../parser/parser';
|
||||||
@ -44,11 +44,11 @@ export class CallHierarchyProvider {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private _program: ProgramView,
|
private _program: ProgramView,
|
||||||
private _filePath: string,
|
private _fileUri: Uri,
|
||||||
private _position: Position,
|
private _position: Position,
|
||||||
private _token: CancellationToken
|
private _token: CancellationToken
|
||||||
) {
|
) {
|
||||||
this._parseResults = this._program.getParseResults(this._filePath);
|
this._parseResults = this._program.getParseResults(this._fileUri);
|
||||||
}
|
}
|
||||||
|
|
||||||
onPrepare(): CallHierarchyItem[] | null {
|
onPrepare(): CallHierarchyItem[] | null {
|
||||||
@ -86,18 +86,15 @@ export class CallHierarchyProvider {
|
|||||||
const callItem: CallHierarchyItem = {
|
const callItem: CallHierarchyItem = {
|
||||||
name: symbolName,
|
name: symbolName,
|
||||||
kind: getSymbolKind(targetDecl, this._evaluator, symbolName) ?? SymbolKind.Module,
|
kind: getSymbolKind(targetDecl, this._evaluator, symbolName) ?? SymbolKind.Module,
|
||||||
uri: callItemUri,
|
uri: callItemUri.toString(),
|
||||||
range: targetDecl.range,
|
range: targetDecl.range,
|
||||||
selectionRange: targetDecl.range,
|
selectionRange: targetDecl.range,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!canNavigateToFile(this._program.fileSystem, callItem.uri)) {
|
if (!canNavigateToFile(this._program.fileSystem, Uri.parse(callItem.uri, this._fileUri.isCaseSensitive))) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert the file path in the item to proper URI.
|
|
||||||
callItem.uri = convertPathToUri(this._program.fileSystem, callItem.uri);
|
|
||||||
|
|
||||||
return [callItem];
|
return [callItem];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,11 +114,11 @@ export class CallHierarchyProvider {
|
|||||||
const items: CallHierarchyIncomingCall[] = [];
|
const items: CallHierarchyIncomingCall[] = [];
|
||||||
const sourceFiles =
|
const sourceFiles =
|
||||||
targetDecl.type === DeclarationType.Alias
|
targetDecl.type === DeclarationType.Alias
|
||||||
? [this._program.getSourceFileInfo(this._filePath)!]
|
? [this._program.getSourceFileInfo(this._fileUri)!]
|
||||||
: this._program.getSourceFileInfoList();
|
: this._program.getSourceFileInfoList();
|
||||||
for (const curSourceFileInfo of sourceFiles) {
|
for (const curSourceFileInfo of sourceFiles) {
|
||||||
if (isUserCode(curSourceFileInfo) || curSourceFileInfo.isOpenByClient) {
|
if (isUserCode(curSourceFileInfo) || curSourceFileInfo.isOpenByClient) {
|
||||||
const filePath = curSourceFileInfo.sourceFile.getFilePath();
|
const filePath = curSourceFileInfo.sourceFile.getUri();
|
||||||
const itemsToAdd = this._getIncomingCallsForDeclaration(filePath, symbolName, targetDecl);
|
const itemsToAdd = this._getIncomingCallsForDeclaration(filePath, symbolName, targetDecl);
|
||||||
|
|
||||||
if (itemsToAdd) {
|
if (itemsToAdd) {
|
||||||
@ -138,14 +135,9 @@ export class CallHierarchyProvider {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const callItems = items.filter((item) => canNavigateToFile(this._program.fileSystem, item.from.uri));
|
return items.filter((item) =>
|
||||||
|
canNavigateToFile(this._program.fileSystem, Uri.parse(item.from.uri, this._fileUri.isCaseSensitive))
|
||||||
// Convert the file paths in the items to proper URIs.
|
);
|
||||||
callItems.forEach((item) => {
|
|
||||||
item.from.uri = convertPathToUri(this._program.fileSystem, item.from.uri);
|
|
||||||
});
|
|
||||||
|
|
||||||
return callItems;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getOutgoingCalls(): CallHierarchyOutgoingCall[] | null {
|
getOutgoingCalls(): CallHierarchyOutgoingCall[] | null {
|
||||||
@ -209,14 +201,9 @@ export class CallHierarchyProvider {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const callItems = outgoingCalls.filter((item) => canNavigateToFile(this._program.fileSystem, item.to.uri));
|
return outgoingCalls.filter((item) =>
|
||||||
|
canNavigateToFile(this._program.fileSystem, Uri.parse(item.to.uri, this._fileUri.isCaseSensitive))
|
||||||
// Convert the file paths in the items to proper URIs.
|
);
|
||||||
callItems.forEach((item) => {
|
|
||||||
item.to.uri = convertPathToUri(this._program.fileSystem, item.to.uri);
|
|
||||||
});
|
|
||||||
|
|
||||||
return callItems;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private get _evaluator(): TypeEvaluator {
|
private get _evaluator(): TypeEvaluator {
|
||||||
@ -225,7 +212,7 @@ export class CallHierarchyProvider {
|
|||||||
|
|
||||||
private _getTargetDeclaration(referencesResult: ReferencesResult): {
|
private _getTargetDeclaration(referencesResult: ReferencesResult): {
|
||||||
targetDecl: Declaration;
|
targetDecl: Declaration;
|
||||||
callItemUri: string;
|
callItemUri: Uri;
|
||||||
symbolName: string;
|
symbolName: string;
|
||||||
} {
|
} {
|
||||||
// If there's more than one declaration, pick the target one.
|
// If there's more than one declaration, pick the target one.
|
||||||
@ -253,32 +240,26 @@ export class CallHierarchyProvider {
|
|||||||
// Although the LSP specification requires a URI, we are using a file path
|
// Although the LSP specification requires a URI, we are using a file path
|
||||||
// here because it is converted to the proper URI by the caller.
|
// here because it is converted to the proper URI by the caller.
|
||||||
// This simplifies our code and ensures compatibility with the LSP specification.
|
// This simplifies our code and ensures compatibility with the LSP specification.
|
||||||
let callItemUri;
|
let callItemUri: Uri;
|
||||||
if (targetDecl.type === DeclarationType.Alias) {
|
if (targetDecl.type === DeclarationType.Alias) {
|
||||||
symbolName = (referencesResult.nodeAtOffset as NameNode).value;
|
symbolName = (referencesResult.nodeAtOffset as NameNode).value;
|
||||||
callItemUri = this._filePath;
|
callItemUri = this._fileUri;
|
||||||
} else {
|
} else {
|
||||||
symbolName = DeclarationUtils.getNameFromDeclaration(targetDecl) || referencesResult.symbolNames[0];
|
symbolName = DeclarationUtils.getNameFromDeclaration(targetDecl) || referencesResult.symbolNames[0];
|
||||||
callItemUri = targetDecl.path;
|
callItemUri = targetDecl.uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
return { targetDecl, callItemUri, symbolName };
|
return { targetDecl, callItemUri, symbolName };
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getIncomingCallsForDeclaration(
|
private _getIncomingCallsForDeclaration(
|
||||||
filePath: string,
|
fileUri: Uri,
|
||||||
symbolName: string,
|
symbolName: string,
|
||||||
declaration: Declaration
|
declaration: Declaration
|
||||||
): CallHierarchyIncomingCall[] | undefined {
|
): CallHierarchyIncomingCall[] | undefined {
|
||||||
throwIfCancellationRequested(this._token);
|
throwIfCancellationRequested(this._token);
|
||||||
|
|
||||||
const callFinder = new FindIncomingCallTreeWalker(
|
const callFinder = new FindIncomingCallTreeWalker(this._program, fileUri, symbolName, declaration, this._token);
|
||||||
this._program,
|
|
||||||
filePath,
|
|
||||||
symbolName,
|
|
||||||
declaration,
|
|
||||||
this._token
|
|
||||||
);
|
|
||||||
|
|
||||||
const incomingCalls = callFinder.findCalls();
|
const incomingCalls = callFinder.findCalls();
|
||||||
return incomingCalls.length > 0 ? incomingCalls : undefined;
|
return incomingCalls.length > 0 ? incomingCalls : undefined;
|
||||||
@ -287,7 +268,7 @@ export class CallHierarchyProvider {
|
|||||||
private _getDeclaration(): ReferencesResult | undefined {
|
private _getDeclaration(): ReferencesResult | undefined {
|
||||||
return ReferencesProvider.getDeclarationForPosition(
|
return ReferencesProvider.getDeclarationForPosition(
|
||||||
this._program,
|
this._program,
|
||||||
this._filePath,
|
this._fileUri,
|
||||||
this._position,
|
this._position,
|
||||||
/* reporter */ undefined,
|
/* reporter */ undefined,
|
||||||
ReferenceUseCase.References,
|
ReferenceUseCase.References,
|
||||||
@ -394,7 +375,7 @@ class FindOutgoingCallTreeWalker extends ParseTreeWalker {
|
|||||||
const callDest: CallHierarchyItem = {
|
const callDest: CallHierarchyItem = {
|
||||||
name: nameNode.value,
|
name: nameNode.value,
|
||||||
kind: getSymbolKind(resolvedDecl, this._evaluator, nameNode.value) ?? SymbolKind.Module,
|
kind: getSymbolKind(resolvedDecl, this._evaluator, nameNode.value) ?? SymbolKind.Module,
|
||||||
uri: resolvedDecl.path,
|
uri: resolvedDecl.uri.toString(),
|
||||||
range: resolvedDecl.range,
|
range: resolvedDecl.range,
|
||||||
selectionRange: resolvedDecl.range,
|
selectionRange: resolvedDecl.range,
|
||||||
};
|
};
|
||||||
@ -437,14 +418,14 @@ class FindIncomingCallTreeWalker extends ParseTreeWalker {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _program: ProgramView,
|
private readonly _program: ProgramView,
|
||||||
private readonly _filePath: string,
|
private readonly _fileUri: Uri,
|
||||||
private readonly _symbolName: string,
|
private readonly _symbolName: string,
|
||||||
private readonly _targetDeclaration: Declaration,
|
private readonly _targetDeclaration: Declaration,
|
||||||
private readonly _cancellationToken: CancellationToken
|
private readonly _cancellationToken: CancellationToken
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this._parseResults = this._program.getParseResults(this._filePath)!;
|
this._parseResults = this._program.getParseResults(this._fileUri)!;
|
||||||
this._usageProviders = (this._program.serviceProvider.tryGet(ServiceKeys.symbolUsageProviderFactory) ?? [])
|
this._usageProviders = (this._program.serviceProvider.tryGet(ServiceKeys.symbolUsageProviderFactory) ?? [])
|
||||||
.map((f) =>
|
.map((f) =>
|
||||||
f.tryCreateProvider(ReferenceUseCase.References, [this._targetDeclaration], this._cancellationToken)
|
f.tryCreateProvider(ReferenceUseCase.References, [this._targetDeclaration], this._cancellationToken)
|
||||||
@ -570,12 +551,12 @@ class FindIncomingCallTreeWalker extends ParseTreeWalker {
|
|||||||
let callSource: CallHierarchyItem;
|
let callSource: CallHierarchyItem;
|
||||||
if (executionNode.nodeType === ParseNodeType.Module) {
|
if (executionNode.nodeType === ParseNodeType.Module) {
|
||||||
const moduleRange = convertOffsetsToRange(0, 0, this._parseResults.tokenizerOutput.lines);
|
const moduleRange = convertOffsetsToRange(0, 0, this._parseResults.tokenizerOutput.lines);
|
||||||
const fileName = getFileName(this._filePath);
|
const fileName = this._fileUri.fileName;
|
||||||
|
|
||||||
callSource = {
|
callSource = {
|
||||||
name: `(module) ${fileName}`,
|
name: `(module) ${fileName}`,
|
||||||
kind: SymbolKind.Module,
|
kind: SymbolKind.Module,
|
||||||
uri: this._filePath,
|
uri: this._fileUri.toString(),
|
||||||
range: moduleRange,
|
range: moduleRange,
|
||||||
selectionRange: moduleRange,
|
selectionRange: moduleRange,
|
||||||
};
|
};
|
||||||
@ -589,7 +570,7 @@ class FindIncomingCallTreeWalker extends ParseTreeWalker {
|
|||||||
callSource = {
|
callSource = {
|
||||||
name: '(lambda)',
|
name: '(lambda)',
|
||||||
kind: SymbolKind.Function,
|
kind: SymbolKind.Function,
|
||||||
uri: this._filePath,
|
uri: this._fileUri.toString(),
|
||||||
range: lambdaRange,
|
range: lambdaRange,
|
||||||
selectionRange: lambdaRange,
|
selectionRange: lambdaRange,
|
||||||
};
|
};
|
||||||
@ -603,7 +584,7 @@ class FindIncomingCallTreeWalker extends ParseTreeWalker {
|
|||||||
callSource = {
|
callSource = {
|
||||||
name: executionNode.name.value,
|
name: executionNode.name.value,
|
||||||
kind: SymbolKind.Function,
|
kind: SymbolKind.Function,
|
||||||
uri: this._filePath,
|
uri: this._fileUri.toString(),
|
||||||
range: functionRange,
|
range: functionRange,
|
||||||
selectionRange: functionRange,
|
selectionRange: functionRange,
|
||||||
};
|
};
|
||||||
|
@ -12,8 +12,8 @@ import { Commands } from '../commands/commands';
|
|||||||
import { throwIfCancellationRequested } from '../common/cancellationUtils';
|
import { throwIfCancellationRequested } from '../common/cancellationUtils';
|
||||||
import { ActionKind, CreateTypeStubFileAction, RenameShadowedFileAction } from '../common/diagnostic';
|
import { ActionKind, CreateTypeStubFileAction, RenameShadowedFileAction } from '../common/diagnostic';
|
||||||
import { FileEditActions } from '../common/editAction';
|
import { FileEditActions } from '../common/editAction';
|
||||||
import { getShortenedFileName } from '../common/pathUtils';
|
|
||||||
import { Range } from '../common/textRange';
|
import { Range } from '../common/textRange';
|
||||||
|
import { Uri } from '../common/uri/uri';
|
||||||
import { convertToWorkspaceEdit } from '../common/workspaceEditUtils';
|
import { convertToWorkspaceEdit } from '../common/workspaceEditUtils';
|
||||||
import { Localizer } from '../localization/localize';
|
import { Localizer } from '../localization/localize';
|
||||||
import { Workspace } from '../workspaceFactory';
|
import { Workspace } from '../workspaceFactory';
|
||||||
@ -29,7 +29,7 @@ export class CodeActionProvider {
|
|||||||
}
|
}
|
||||||
static async getCodeActionsForPosition(
|
static async getCodeActionsForPosition(
|
||||||
workspace: Workspace,
|
workspace: Workspace,
|
||||||
filePath: string,
|
fileUri: Uri,
|
||||||
range: Range,
|
range: Range,
|
||||||
kinds: CodeActionKind[] | undefined,
|
kinds: CodeActionKind[] | undefined,
|
||||||
token: CancellationToken
|
token: CancellationToken
|
||||||
@ -44,7 +44,7 @@ export class CodeActionProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!workspace.disableLanguageServices) {
|
if (!workspace.disableLanguageServices) {
|
||||||
const diags = await workspace.service.getDiagnosticsForRange(filePath, range, token);
|
const diags = await workspace.service.getDiagnosticsForRange(fileUri, range, token);
|
||||||
const typeStubDiag = diags.find((d) => {
|
const typeStubDiag = diags.find((d) => {
|
||||||
const actions = d.getActions();
|
const actions = d.getActions();
|
||||||
return actions && actions.find((a) => a.action === Commands.createTypeStub);
|
return actions && actions.find((a) => a.action === Commands.createTypeStub);
|
||||||
@ -60,9 +60,9 @@ export class CodeActionProvider {
|
|||||||
Command.create(
|
Command.create(
|
||||||
Localizer.CodeAction.createTypeStub(),
|
Localizer.CodeAction.createTypeStub(),
|
||||||
Commands.createTypeStub,
|
Commands.createTypeStub,
|
||||||
workspace.rootPath,
|
workspace.rootUri.toString(),
|
||||||
action.moduleName,
|
action.moduleName,
|
||||||
filePath
|
fileUri.toString()
|
||||||
),
|
),
|
||||||
CodeActionKind.QuickFix
|
CodeActionKind.QuickFix
|
||||||
);
|
);
|
||||||
@ -80,21 +80,20 @@ export class CodeActionProvider {
|
|||||||
.find((a) => a.action === ActionKind.RenameShadowedFileAction) as RenameShadowedFileAction;
|
.find((a) => a.action === ActionKind.RenameShadowedFileAction) as RenameShadowedFileAction;
|
||||||
if (action) {
|
if (action) {
|
||||||
const title = Localizer.CodeAction.renameShadowedFile().format({
|
const title = Localizer.CodeAction.renameShadowedFile().format({
|
||||||
oldFile: getShortenedFileName(action.oldFile),
|
oldFile: action.oldUri.getShortenedFileName(),
|
||||||
newFile: getShortenedFileName(action.newFile),
|
newFile: action.newUri.getShortenedFileName(),
|
||||||
});
|
});
|
||||||
const fs = workspace.service.getImportResolver().fileSystem;
|
|
||||||
const editActions: FileEditActions = {
|
const editActions: FileEditActions = {
|
||||||
edits: [],
|
edits: [],
|
||||||
fileOperations: [
|
fileOperations: [
|
||||||
{
|
{
|
||||||
kind: 'rename',
|
kind: 'rename',
|
||||||
oldFilePath: action.oldFile,
|
oldFileUri: action.oldUri,
|
||||||
newFilePath: action.newFile,
|
newFileUri: action.newUri,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
const workspaceEdit = convertToWorkspaceEdit(fs, editActions);
|
const workspaceEdit = convertToWorkspaceEdit(editActions);
|
||||||
const renameAction = CodeAction.create(title, workspaceEdit, CodeActionKind.QuickFix);
|
const renameAction = CodeAction.create(title, workspaceEdit, CodeActionKind.QuickFix);
|
||||||
codeActions.push(renameAction);
|
codeActions.push(renameAction);
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ import { Symbol, SymbolTable } from '../analyzer/symbol';
|
|||||||
import * as SymbolNameUtils from '../analyzer/symbolNameUtils';
|
import * as SymbolNameUtils from '../analyzer/symbolNameUtils';
|
||||||
import { getLastTypedDeclaredForSymbol, isVisibleExternally } from '../analyzer/symbolUtils';
|
import { getLastTypedDeclaredForSymbol, isVisibleExternally } from '../analyzer/symbolUtils';
|
||||||
import { getTypedDictMembersForClass } from '../analyzer/typedDicts';
|
import { getTypedDictMembersForClass } from '../analyzer/typedDicts';
|
||||||
import { getModuleDocStringFromPaths } from '../analyzer/typeDocStringUtils';
|
import { getModuleDocStringFromUris } from '../analyzer/typeDocStringUtils';
|
||||||
import { CallSignatureInfo, TypeEvaluator } from '../analyzer/typeEvaluatorTypes';
|
import { CallSignatureInfo, TypeEvaluator } from '../analyzer/typeEvaluatorTypes';
|
||||||
import { printLiteralValue } from '../analyzer/typePrinter';
|
import { printLiteralValue } from '../analyzer/typePrinter';
|
||||||
import {
|
import {
|
||||||
@ -83,6 +83,7 @@ import { PythonVersion } from '../common/pythonVersion';
|
|||||||
import * as StringUtils from '../common/stringUtils';
|
import * as StringUtils from '../common/stringUtils';
|
||||||
import { comparePositions, Position, TextRange } from '../common/textRange';
|
import { comparePositions, Position, TextRange } from '../common/textRange';
|
||||||
import { TextRangeCollection } from '../common/textRangeCollection';
|
import { TextRangeCollection } from '../common/textRangeCollection';
|
||||||
|
import { Uri } from '../common/uri/uri';
|
||||||
import { convertToTextEdits } from '../common/workspaceEditUtils';
|
import { convertToTextEdits } from '../common/workspaceEditUtils';
|
||||||
import { Localizer } from '../localization/localize';
|
import { Localizer } from '../localization/localize';
|
||||||
import {
|
import {
|
||||||
@ -231,13 +232,13 @@ enum SortCategory {
|
|||||||
// This data allows the resolve handling to disambiguate
|
// This data allows the resolve handling to disambiguate
|
||||||
// which item was selected.
|
// which item was selected.
|
||||||
export interface CompletionItemData {
|
export interface CompletionItemData {
|
||||||
filePath: string;
|
uri: string; // Have to be strings because this data is passed across the LSP boundary.
|
||||||
workspacePath: string;
|
workspaceUri: string;
|
||||||
position: Position;
|
position: Position;
|
||||||
autoImportText?: string;
|
autoImportText?: string;
|
||||||
symbolLabel?: string;
|
symbolLabel?: string;
|
||||||
funcParensDisabled?: boolean;
|
funcParensDisabled?: boolean;
|
||||||
modulePath?: string;
|
moduleUri?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CompletionOptions {
|
export interface CompletionOptions {
|
||||||
@ -287,20 +288,20 @@ export class CompletionProvider {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected readonly program: ProgramView,
|
protected readonly program: ProgramView,
|
||||||
private readonly _workspacePath: string,
|
private readonly _workspaceRootUri: Uri,
|
||||||
protected readonly filePath: string,
|
protected readonly fileUri: Uri,
|
||||||
protected readonly position: Position,
|
protected readonly position: Position,
|
||||||
protected readonly options: CompletionOptions,
|
protected readonly options: CompletionOptions,
|
||||||
protected readonly cancellationToken: CancellationToken
|
protected readonly cancellationToken: CancellationToken
|
||||||
) {
|
) {
|
||||||
this.execEnv = this.configOptions.findExecEnvironment(this.filePath);
|
this.execEnv = this.configOptions.findExecEnvironment(this.fileUri);
|
||||||
|
|
||||||
this.parseResults = this.program.getParseResults(this.filePath)!;
|
this.parseResults = this.program.getParseResults(this.fileUri)!;
|
||||||
this.sourceMapper = this.program.getSourceMapper(this.filePath, this.cancellationToken, /* mapCompiled */ true);
|
this.sourceMapper = this.program.getSourceMapper(this.fileUri, this.cancellationToken, /* mapCompiled */ true);
|
||||||
}
|
}
|
||||||
|
|
||||||
getCompletions(): CompletionList | null {
|
getCompletions(): CompletionList | null {
|
||||||
if (!this.program.getSourceFileInfo(this.filePath)) {
|
if (!this.program.getSourceFileInfo(this.fileUri)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -347,10 +348,15 @@ export class CompletionProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
completionItemData.modulePath &&
|
completionItemData.moduleUri &&
|
||||||
ImportResolver.isSupportedImportSourceFile(completionItemData.modulePath)
|
ImportResolver.isSupportedImportSourceFile(
|
||||||
|
Uri.parse(completionItemData.moduleUri, this.importResolver.fileSystem.isCaseSensitive)
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
const documentation = getModuleDocStringFromPaths([completionItemData.modulePath], this.sourceMapper);
|
const documentation = getModuleDocStringFromUris(
|
||||||
|
[Uri.parse(completionItemData.moduleUri, this.importResolver.fileSystem.isCaseSensitive)],
|
||||||
|
this.sourceMapper
|
||||||
|
);
|
||||||
if (!documentation) {
|
if (!documentation) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -499,7 +505,7 @@ export class CompletionProvider {
|
|||||||
const methodSignature = this._printMethodSignature(classResults.classType, decl);
|
const methodSignature = this._printMethodSignature(classResults.classType, decl);
|
||||||
|
|
||||||
let text: string;
|
let text: string;
|
||||||
if (isStubFile(this.filePath)) {
|
if (isStubFile(this.fileUri)) {
|
||||||
text = `${methodSignature}: ...`;
|
text = `${methodSignature}: ...`;
|
||||||
} else {
|
} else {
|
||||||
const methodBody = this.printOverriddenMethodBody(
|
const methodBody = this.printOverriddenMethodBody(
|
||||||
@ -826,7 +832,7 @@ export class CompletionProvider {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentFile = this.program.getSourceFileInfo(this.filePath);
|
const currentFile = this.program.getSourceFileInfo(this.fileUri);
|
||||||
const moduleSymbolMap = buildModuleSymbolsMap(
|
const moduleSymbolMap = buildModuleSymbolsMap(
|
||||||
this.program.getSourceFileInfoList().filter((s) => s !== currentFile)
|
this.program.getSourceFileInfoList().filter((s) => s !== currentFile)
|
||||||
);
|
);
|
||||||
@ -923,8 +929,8 @@ export class CompletionProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const completionItemData: CompletionItemData = {
|
const completionItemData: CompletionItemData = {
|
||||||
workspacePath: this._workspacePath,
|
workspaceUri: this._workspaceRootUri.toString(),
|
||||||
filePath: this.filePath,
|
uri: this.fileUri.toString(),
|
||||||
position: this.position,
|
position: this.position,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -932,8 +938,8 @@ export class CompletionProvider {
|
|||||||
completionItemData.funcParensDisabled = true;
|
completionItemData.funcParensDisabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (detail?.modulePath) {
|
if (detail?.moduleUri) {
|
||||||
completionItemData.modulePath = detail.modulePath;
|
completionItemData.moduleUri = detail.moduleUri.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
completionItem.data = toLSPAny(completionItemData);
|
completionItem.data = toLSPAny(completionItemData);
|
||||||
@ -1689,7 +1695,7 @@ export class CompletionProvider {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const printFlags = isStubFile(this.filePath)
|
const printFlags = isStubFile(this.fileUri)
|
||||||
? ParseTreeUtils.PrintExpressionFlags.ForwardDeclarations |
|
? ParseTreeUtils.PrintExpressionFlags.ForwardDeclarations |
|
||||||
ParseTreeUtils.PrintExpressionFlags.DoNotLimitStringLength
|
ParseTreeUtils.PrintExpressionFlags.DoNotLimitStringLength
|
||||||
: ParseTreeUtils.PrintExpressionFlags.DoNotLimitStringLength;
|
: ParseTreeUtils.PrintExpressionFlags.DoNotLimitStringLength;
|
||||||
@ -1822,7 +1828,7 @@ export class CompletionProvider {
|
|||||||
const node = decl.node;
|
const node = decl.node;
|
||||||
|
|
||||||
let ellipsisForDefault: boolean | undefined;
|
let ellipsisForDefault: boolean | undefined;
|
||||||
if (isStubFile(this.filePath)) {
|
if (isStubFile(this.fileUri)) {
|
||||||
// In stubs, always use "...".
|
// In stubs, always use "...".
|
||||||
ellipsisForDefault = true;
|
ellipsisForDefault = true;
|
||||||
} else if (classType.details.moduleName === decl.moduleName) {
|
} else if (classType.details.moduleName === decl.moduleName) {
|
||||||
@ -1830,7 +1836,7 @@ export class CompletionProvider {
|
|||||||
ellipsisForDefault = false;
|
ellipsisForDefault = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const printFlags = isStubFile(this.filePath)
|
const printFlags = isStubFile(this.fileUri)
|
||||||
? ParseTreeUtils.PrintExpressionFlags.ForwardDeclarations |
|
? ParseTreeUtils.PrintExpressionFlags.ForwardDeclarations |
|
||||||
ParseTreeUtils.PrintExpressionFlags.DoNotLimitStringLength
|
ParseTreeUtils.PrintExpressionFlags.DoNotLimitStringLength
|
||||||
: ParseTreeUtils.PrintExpressionFlags.DoNotLimitStringLength;
|
: ParseTreeUtils.PrintExpressionFlags.DoNotLimitStringLength;
|
||||||
@ -2190,7 +2196,7 @@ export class CompletionProvider {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (declaration.path !== this.filePath) {
|
if (!declaration.uri.equals(this.fileUri)) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2200,8 +2206,8 @@ export class CompletionProvider {
|
|||||||
|
|
||||||
// Find the lowest tree to search the symbol.
|
// Find the lowest tree to search the symbol.
|
||||||
if (
|
if (
|
||||||
ParseTreeUtils.getFileInfoFromNode(startingNode)?.filePath ===
|
ParseTreeUtils.getFileInfoFromNode(startingNode)?.fileUri ===
|
||||||
ParseTreeUtils.getFileInfoFromNode(scopeRoot)?.filePath
|
ParseTreeUtils.getFileInfoFromNode(scopeRoot)?.fileUri
|
||||||
) {
|
) {
|
||||||
startingNode = scopeRoot;
|
startingNode = scopeRoot;
|
||||||
}
|
}
|
||||||
@ -2731,7 +2737,9 @@ export class CompletionProvider {
|
|||||||
|
|
||||||
const completionMap = new CompletionMap();
|
const completionMap = new CompletionMap();
|
||||||
const resolvedPath =
|
const resolvedPath =
|
||||||
importInfo.resolvedPaths.length > 0 ? importInfo.resolvedPaths[importInfo.resolvedPaths.length - 1] : '';
|
importInfo.resolvedUris.length > 0
|
||||||
|
? importInfo.resolvedUris[importInfo.resolvedUris.length - 1]
|
||||||
|
: Uri.empty();
|
||||||
|
|
||||||
const parseResults = this.program.getParseResults(resolvedPath);
|
const parseResults = this.program.getParseResults(resolvedPath);
|
||||||
if (!parseResults) {
|
if (!parseResults) {
|
||||||
@ -2775,7 +2783,7 @@ export class CompletionProvider {
|
|||||||
importInfo.implicitImports.forEach((implImport) => {
|
importInfo.implicitImports.forEach((implImport) => {
|
||||||
if (!importFromNode.imports.find((imp) => imp.name.value === implImport.name)) {
|
if (!importFromNode.imports.find((imp) => imp.name.value === implImport.name)) {
|
||||||
this.addNameToCompletions(implImport.name, CompletionItemKind.Module, priorWord, completionMap, {
|
this.addNameToCompletions(implImport.name, CompletionItemKind.Module, priorWord, completionMap, {
|
||||||
modulePath: implImport.path,
|
moduleUri: implImport.uri,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -2817,8 +2825,8 @@ export class CompletionProvider {
|
|||||||
completionItem.kind = CompletionItemKind.Variable;
|
completionItem.kind = CompletionItemKind.Variable;
|
||||||
|
|
||||||
const completionItemData: CompletionItemData = {
|
const completionItemData: CompletionItemData = {
|
||||||
workspacePath: this._workspacePath,
|
workspaceUri: this._workspaceRootUri.toString(),
|
||||||
filePath: this.filePath,
|
uri: this.fileUri.toString(),
|
||||||
position: this.position,
|
position: this.position,
|
||||||
};
|
};
|
||||||
completionItem.data = toLSPAny(completionItemData);
|
completionItem.data = toLSPAny(completionItemData);
|
||||||
@ -2914,8 +2922,7 @@ export class CompletionProvider {
|
|||||||
// exported from this scope, don't include it in the
|
// exported from this scope, don't include it in the
|
||||||
// suggestion list unless we are in the same file.
|
// suggestion list unless we are in the same file.
|
||||||
const hidden =
|
const hidden =
|
||||||
!isVisibleExternally(symbol) &&
|
!isVisibleExternally(symbol) && !symbol.getDeclarations().some((d) => isDefinedInFile(d, this.fileUri));
|
||||||
!symbol.getDeclarations().some((d) => isDefinedInFile(d, this.filePath));
|
|
||||||
if (!hidden && includeSymbolCallback(symbol, name)) {
|
if (!hidden && includeSymbolCallback(symbol, name)) {
|
||||||
// Don't add a symbol more than once. It may have already been
|
// Don't add a symbol more than once. It may have already been
|
||||||
// added from an inner scope's symbol table.
|
// added from an inner scope's symbol table.
|
||||||
@ -3098,7 +3105,7 @@ export class CompletionProvider {
|
|||||||
importedSymbols: new Set<string>(),
|
importedSymbols: new Set<string>(),
|
||||||
};
|
};
|
||||||
|
|
||||||
const completions = this.importResolver.getCompletionSuggestions(this.filePath, this.execEnv, moduleDescriptor);
|
const completions = this.importResolver.getCompletionSuggestions(this.fileUri, this.execEnv, moduleDescriptor);
|
||||||
|
|
||||||
const completionMap = new CompletionMap();
|
const completionMap = new CompletionMap();
|
||||||
|
|
||||||
@ -3120,7 +3127,7 @@ export class CompletionProvider {
|
|||||||
completions.forEach((modulePath, completionName) => {
|
completions.forEach((modulePath, completionName) => {
|
||||||
this.addNameToCompletions(completionName, CompletionItemKind.Module, '', completionMap, {
|
this.addNameToCompletions(completionName, CompletionItemKind.Module, '', completionMap, {
|
||||||
sortText: this._makeSortText(SortCategory.ImportModuleName, completionName),
|
sortText: this._makeSortText(SortCategory.ImportModuleName, completionName),
|
||||||
modulePath,
|
moduleUri: modulePath,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -11,21 +11,22 @@ import { InsertTextFormat, MarkupContent, MarkupKind, TextEdit } from 'vscode-la
|
|||||||
import { Declaration, DeclarationType } from '../analyzer/declaration';
|
import { Declaration, DeclarationType } from '../analyzer/declaration';
|
||||||
import { convertDocStringToMarkdown, convertDocStringToPlainText } from '../analyzer/docStringConversion';
|
import { convertDocStringToMarkdown, convertDocStringToPlainText } from '../analyzer/docStringConversion';
|
||||||
import { TypeEvaluator } from '../analyzer/typeEvaluatorTypes';
|
import { TypeEvaluator } from '../analyzer/typeEvaluatorTypes';
|
||||||
|
import { isProperty } from '../analyzer/typeUtils';
|
||||||
import {
|
import {
|
||||||
ClassType,
|
ClassType,
|
||||||
|
Type,
|
||||||
|
TypeBase,
|
||||||
|
TypeCategory,
|
||||||
|
UnknownType,
|
||||||
getTypeAliasInfo,
|
getTypeAliasInfo,
|
||||||
isClassInstance,
|
isClassInstance,
|
||||||
isFunction,
|
isFunction,
|
||||||
isModule,
|
isModule,
|
||||||
isOverloadedFunction,
|
isOverloadedFunction,
|
||||||
Type,
|
|
||||||
TypeBase,
|
|
||||||
TypeCategory,
|
|
||||||
UnknownType,
|
|
||||||
} from '../analyzer/types';
|
} from '../analyzer/types';
|
||||||
import { isProperty } from '../analyzer/typeUtils';
|
|
||||||
import { SignatureDisplayType } from '../common/configOptions';
|
import { SignatureDisplayType } from '../common/configOptions';
|
||||||
import { TextEditAction } from '../common/editAction';
|
import { TextEditAction } from '../common/editAction';
|
||||||
|
import { Uri } from '../common/uri/uri';
|
||||||
import { getToolTipForType } from './tooltipUtils';
|
import { getToolTipForType } from './tooltipUtils';
|
||||||
|
|
||||||
export interface Edits {
|
export interface Edits {
|
||||||
@ -55,7 +56,7 @@ export interface CompletionDetail extends CommonDetail {
|
|||||||
};
|
};
|
||||||
sortText?: string;
|
sortText?: string;
|
||||||
itemDetail?: string;
|
itemDetail?: string;
|
||||||
modulePath?: string;
|
moduleUri?: Uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getTypeDetail(
|
export function getTypeDetail(
|
||||||
|
@ -24,10 +24,11 @@ import { appendArray } from '../common/collectionUtils';
|
|||||||
import { isDefined } from '../common/core';
|
import { isDefined } from '../common/core';
|
||||||
import { ProgramView, ServiceProvider } from '../common/extensibility';
|
import { ProgramView, ServiceProvider } from '../common/extensibility';
|
||||||
import { convertPositionToOffset } from '../common/positionUtils';
|
import { convertPositionToOffset } from '../common/positionUtils';
|
||||||
|
import { ServiceKeys } from '../common/serviceProviderExtensions';
|
||||||
import { DocumentRange, Position, rangesAreEqual } from '../common/textRange';
|
import { DocumentRange, Position, rangesAreEqual } from '../common/textRange';
|
||||||
|
import { Uri } from '../common/uri/uri';
|
||||||
import { ParseNode, ParseNodeType } from '../parser/parseNodes';
|
import { ParseNode, ParseNodeType } from '../parser/parseNodes';
|
||||||
import { ParseResults } from '../parser/parser';
|
import { ParseResults } from '../parser/parser';
|
||||||
import { ServiceKeys } from '../common/serviceProviderExtensions';
|
|
||||||
|
|
||||||
export enum DefinitionFilter {
|
export enum DefinitionFilter {
|
||||||
All = 'all',
|
All = 'all',
|
||||||
@ -50,7 +51,7 @@ export function addDeclarationsToDefinitions(
|
|||||||
allowExternallyHiddenAccess: true,
|
allowExternallyHiddenAccess: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!resolvedDecl || !resolvedDecl.path) {
|
if (!resolvedDecl || resolvedDecl.uri.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,13 +67,13 @@ export function addDeclarationsToDefinitions(
|
|||||||
resolvedDecl.type === DeclarationType.Alias &&
|
resolvedDecl.type === DeclarationType.Alias &&
|
||||||
resolvedDecl.symbolName &&
|
resolvedDecl.symbolName &&
|
||||||
resolvedDecl.submoduleFallback &&
|
resolvedDecl.submoduleFallback &&
|
||||||
resolvedDecl.submoduleFallback.path
|
!resolvedDecl.submoduleFallback.uri.isEmpty()
|
||||||
) {
|
) {
|
||||||
resolvedDecl = resolvedDecl.submoduleFallback;
|
resolvedDecl = resolvedDecl.submoduleFallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
_addIfUnique(definitions, {
|
_addIfUnique(definitions, {
|
||||||
path: resolvedDecl.path,
|
uri: resolvedDecl.uri,
|
||||||
range: resolvedDecl.range,
|
range: resolvedDecl.range,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -82,22 +83,22 @@ export function addDeclarationsToDefinitions(
|
|||||||
if (functionType && isOverloadedFunction(functionType)) {
|
if (functionType && isOverloadedFunction(functionType)) {
|
||||||
for (const overloadDecl of functionType.overloads.map((o) => o.details.declaration).filter(isDefined)) {
|
for (const overloadDecl of functionType.overloads.map((o) => o.details.declaration).filter(isDefined)) {
|
||||||
_addIfUnique(definitions, {
|
_addIfUnique(definitions, {
|
||||||
path: overloadDecl.path,
|
uri: overloadDecl.uri,
|
||||||
range: overloadDecl.range,
|
range: overloadDecl.range,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isStubFile(resolvedDecl.path)) {
|
if (!isStubFile(resolvedDecl.uri)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resolvedDecl.type === DeclarationType.Alias) {
|
if (resolvedDecl.type === DeclarationType.Alias) {
|
||||||
// Add matching source module
|
// Add matching source module
|
||||||
sourceMapper
|
sourceMapper
|
||||||
.findModules(resolvedDecl.path)
|
.findModules(resolvedDecl.uri)
|
||||||
.map((m) => getFileInfo(m)?.filePath)
|
.map((m) => getFileInfo(m)?.fileUri)
|
||||||
.filter(isDefined)
|
.filter(isDefined)
|
||||||
.forEach((f) => _addIfUnique(definitions, _createModuleEntry(f)));
|
.forEach((f) => _addIfUnique(definitions, _createModuleEntry(f)));
|
||||||
return;
|
return;
|
||||||
@ -105,9 +106,9 @@ export function addDeclarationsToDefinitions(
|
|||||||
|
|
||||||
const implDecls = sourceMapper.findDeclarations(resolvedDecl);
|
const implDecls = sourceMapper.findDeclarations(resolvedDecl);
|
||||||
for (const implDecl of implDecls) {
|
for (const implDecl of implDecls) {
|
||||||
if (implDecl && implDecl.path) {
|
if (implDecl && !implDecl.uri.isEmpty()) {
|
||||||
_addIfUnique(definitions, {
|
_addIfUnique(definitions, {
|
||||||
path: implDecl.path,
|
uri: implDecl.uri,
|
||||||
range: implDecl.range,
|
range: implDecl.range,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -123,7 +124,7 @@ export function filterDefinitions(filter: DefinitionFilter, definitions: Documen
|
|||||||
// If go-to-declaration is supported, attempt to only show only pyi files in go-to-declaration
|
// If go-to-declaration is supported, attempt to only show only pyi files in go-to-declaration
|
||||||
// and none in go-to-definition, unless filtering would produce an empty list.
|
// and none in go-to-definition, unless filtering would produce an empty list.
|
||||||
const preferStubs = filter === DefinitionFilter.PreferStubs;
|
const preferStubs = filter === DefinitionFilter.PreferStubs;
|
||||||
const wantedFile = (v: DocumentRange) => preferStubs === isStubFile(v.path);
|
const wantedFile = (v: DocumentRange) => preferStubs === isStubFile(v.uri);
|
||||||
if (definitions.find(wantedFile)) {
|
if (definitions.find(wantedFile)) {
|
||||||
return definitions.filter(wantedFile);
|
return definitions.filter(wantedFile);
|
||||||
}
|
}
|
||||||
@ -181,13 +182,13 @@ class DefinitionProviderBase {
|
|||||||
export class DefinitionProvider extends DefinitionProviderBase {
|
export class DefinitionProvider extends DefinitionProviderBase {
|
||||||
constructor(
|
constructor(
|
||||||
program: ProgramView,
|
program: ProgramView,
|
||||||
filePath: string,
|
fileUri: Uri,
|
||||||
position: Position,
|
position: Position,
|
||||||
filter: DefinitionFilter,
|
filter: DefinitionFilter,
|
||||||
token: CancellationToken
|
token: CancellationToken
|
||||||
) {
|
) {
|
||||||
const sourceMapper = program.getSourceMapper(filePath, token);
|
const sourceMapper = program.getSourceMapper(fileUri, token);
|
||||||
const parseResults = program.getParseResults(filePath);
|
const parseResults = program.getParseResults(fileUri);
|
||||||
const { node, offset } = _tryGetNode(parseResults, position);
|
const { node, offset } = _tryGetNode(parseResults, position);
|
||||||
|
|
||||||
super(sourceMapper, program.evaluator!, program.serviceProvider, node, offset, filter, token);
|
super(sourceMapper, program.evaluator!, program.serviceProvider, node, offset, filter, token);
|
||||||
@ -222,15 +223,15 @@ export class DefinitionProvider extends DefinitionProviderBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class TypeDefinitionProvider extends DefinitionProviderBase {
|
export class TypeDefinitionProvider extends DefinitionProviderBase {
|
||||||
private readonly _filePath: string;
|
private readonly _fileUri: Uri;
|
||||||
|
|
||||||
constructor(program: ProgramView, filePath: string, position: Position, token: CancellationToken) {
|
constructor(program: ProgramView, fileUri: Uri, position: Position, token: CancellationToken) {
|
||||||
const sourceMapper = program.getSourceMapper(filePath, token, /*mapCompiled*/ false, /*preferStubs*/ true);
|
const sourceMapper = program.getSourceMapper(fileUri, token, /*mapCompiled*/ false, /*preferStubs*/ true);
|
||||||
const parseResults = program.getParseResults(filePath);
|
const parseResults = program.getParseResults(fileUri);
|
||||||
const { node, offset } = _tryGetNode(parseResults, position);
|
const { node, offset } = _tryGetNode(parseResults, position);
|
||||||
|
|
||||||
super(sourceMapper, program.evaluator!, program.serviceProvider, node, offset, DefinitionFilter.All, token);
|
super(sourceMapper, program.evaluator!, program.serviceProvider, node, offset, DefinitionFilter.All, token);
|
||||||
this._filePath = filePath;
|
this._fileUri = fileUri;
|
||||||
}
|
}
|
||||||
|
|
||||||
getDefinitions(): DocumentRange[] | undefined {
|
getDefinitions(): DocumentRange[] | undefined {
|
||||||
@ -251,7 +252,7 @@ export class TypeDefinitionProvider extends DefinitionProviderBase {
|
|||||||
if (subtype?.category === TypeCategory.Class) {
|
if (subtype?.category === TypeCategory.Class) {
|
||||||
appendArray(
|
appendArray(
|
||||||
declarations,
|
declarations,
|
||||||
this.sourceMapper.findClassDeclarationsByType(this._filePath, subtype)
|
this.sourceMapper.findClassDeclarationsByType(this._fileUri, subtype)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -290,9 +291,9 @@ function _tryGetNode(parseResults: ParseResults | undefined, position: Position)
|
|||||||
return { node: ParseTreeUtils.findNodeByOffset(parseResults.parseTree, offset), offset };
|
return { node: ParseTreeUtils.findNodeByOffset(parseResults.parseTree, offset), offset };
|
||||||
}
|
}
|
||||||
|
|
||||||
function _createModuleEntry(filePath: string): DocumentRange {
|
function _createModuleEntry(uri: Uri): DocumentRange {
|
||||||
return {
|
return {
|
||||||
path: filePath,
|
uri,
|
||||||
range: {
|
range: {
|
||||||
start: { line: 0, character: 0 },
|
start: { line: 0, character: 0 },
|
||||||
end: { line: 0, character: 0 },
|
end: { line: 0, character: 0 },
|
||||||
@ -302,7 +303,7 @@ function _createModuleEntry(filePath: string): DocumentRange {
|
|||||||
|
|
||||||
function _addIfUnique(definitions: DocumentRange[], itemToAdd: DocumentRange) {
|
function _addIfUnique(definitions: DocumentRange[], itemToAdd: DocumentRange) {
|
||||||
for (const def of definitions) {
|
for (const def of definitions) {
|
||||||
if (def.path === itemToAdd.path && rangesAreEqual(def.range, itemToAdd.range)) {
|
if (def.uri.equals(itemToAdd.uri) && rangesAreEqual(def.range, itemToAdd.range)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ import { throwIfCancellationRequested } from '../common/cancellationUtils';
|
|||||||
import { ProgramView, ReferenceUseCase } from '../common/extensibility';
|
import { ProgramView, ReferenceUseCase } from '../common/extensibility';
|
||||||
import { convertOffsetsToRange, convertPositionToOffset } from '../common/positionUtils';
|
import { convertOffsetsToRange, convertPositionToOffset } from '../common/positionUtils';
|
||||||
import { Position, TextRange } from '../common/textRange';
|
import { Position, TextRange } from '../common/textRange';
|
||||||
|
import { Uri } from '../common/uri/uri';
|
||||||
import { ParseNodeType } from '../parser/parseNodes';
|
import { ParseNodeType } from '../parser/parseNodes';
|
||||||
import { ParseResults } from '../parser/parser';
|
import { ParseResults } from '../parser/parser';
|
||||||
import { DocumentSymbolCollector } from './documentSymbolCollector';
|
import { DocumentSymbolCollector } from './documentSymbolCollector';
|
||||||
@ -24,11 +25,11 @@ export class DocumentHighlightProvider {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private _program: ProgramView,
|
private _program: ProgramView,
|
||||||
private _filePath: string,
|
private _fileUri: Uri,
|
||||||
private _position: Position,
|
private _position: Position,
|
||||||
private _token: CancellationToken
|
private _token: CancellationToken
|
||||||
) {
|
) {
|
||||||
this._parseResults = this._program.getParseResults(this._filePath);
|
this._parseResults = this._program.getParseResults(this._fileUri);
|
||||||
}
|
}
|
||||||
|
|
||||||
getDocumentHighlight(): DocumentHighlight[] | undefined {
|
getDocumentHighlight(): DocumentHighlight[] | undefined {
|
||||||
|
@ -29,12 +29,12 @@ import { TypeEvaluator } from '../analyzer/typeEvaluatorTypes';
|
|||||||
import { TypeCategory } from '../analyzer/types';
|
import { TypeCategory } from '../analyzer/types';
|
||||||
import { throwIfCancellationRequested } from '../common/cancellationUtils';
|
import { throwIfCancellationRequested } from '../common/cancellationUtils';
|
||||||
import { appendArray } from '../common/collectionUtils';
|
import { appendArray } from '../common/collectionUtils';
|
||||||
|
import { isDefined } from '../common/core';
|
||||||
import { assert } from '../common/debug';
|
import { assert } from '../common/debug';
|
||||||
import { ProgramView, ReferenceUseCase, SymbolUsageProvider } from '../common/extensibility';
|
import { ProgramView, ReferenceUseCase, SymbolUsageProvider } from '../common/extensibility';
|
||||||
|
import { ServiceKeys } from '../common/serviceProviderExtensions';
|
||||||
import { TextRange } from '../common/textRange';
|
import { TextRange } from '../common/textRange';
|
||||||
import { ImportAsNode, NameNode, ParseNode, ParseNodeType, StringListNode, StringNode } from '../parser/parseNodes';
|
import { ImportAsNode, NameNode, ParseNode, ParseNodeType, StringListNode, StringNode } from '../parser/parseNodes';
|
||||||
import { ServiceKeys } from '../common/serviceProviderExtensions';
|
|
||||||
import { isDefined } from '../common/core';
|
|
||||||
|
|
||||||
export type CollectionResult = {
|
export type CollectionResult = {
|
||||||
node: NameNode | StringNode;
|
node: NameNode | StringNode;
|
||||||
@ -184,17 +184,18 @@ export class DocumentSymbolCollector extends ParseTreeWalker {
|
|||||||
|
|
||||||
const declarations = getDeclarationsForNameNode(evaluator, node, /* skipUnreachableCode */ false);
|
const declarations = getDeclarationsForNameNode(evaluator, node, /* skipUnreachableCode */ false);
|
||||||
const fileInfo = AnalyzerNodeInfo.getFileInfo(node);
|
const fileInfo = AnalyzerNodeInfo.getFileInfo(node);
|
||||||
|
const fileUri = fileInfo.fileUri;
|
||||||
|
|
||||||
const resolvedDeclarations: Declaration[] = [];
|
const resolvedDeclarations: Declaration[] = [];
|
||||||
const sourceMapper = program.getSourceMapper(fileInfo.filePath, token);
|
const sourceMapper = program.getSourceMapper(fileUri, token);
|
||||||
declarations.forEach((decl) => {
|
declarations.forEach((decl) => {
|
||||||
const resolvedDecl = evaluator.resolveAliasDeclaration(decl, resolveLocalName);
|
const resolvedDecl = evaluator.resolveAliasDeclaration(decl, resolveLocalName);
|
||||||
if (resolvedDecl) {
|
if (resolvedDecl) {
|
||||||
addDeclarationIfUnique(resolvedDeclarations, resolvedDecl);
|
addDeclarationIfUnique(resolvedDeclarations, resolvedDecl);
|
||||||
if (sourceMapper && isStubFile(resolvedDecl.path)) {
|
if (sourceMapper && isStubFile(resolvedDecl.uri)) {
|
||||||
const implDecls = sourceMapper.findDeclarations(resolvedDecl);
|
const implDecls = sourceMapper.findDeclarations(resolvedDecl);
|
||||||
for (const implDecl of implDecls) {
|
for (const implDecl of implDecls) {
|
||||||
if (implDecl && implDecl.path) {
|
if (implDecl && !implDecl.uri.isEmpty()) {
|
||||||
addDeclarationIfUnique(resolvedDeclarations, implDecl);
|
addDeclarationIfUnique(resolvedDeclarations, implDecl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -202,7 +203,7 @@ export class DocumentSymbolCollector extends ParseTreeWalker {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const sourceFileInfo = program.getSourceFileInfo(fileInfo.filePath);
|
const sourceFileInfo = program.getSourceFileInfo(fileUri);
|
||||||
if (sourceFileInfo && sourceFileInfo.sourceFile.getIPythonMode() === IPythonMode.CellDocs) {
|
if (sourceFileInfo && sourceFileInfo.sourceFile.getIPythonMode() === IPythonMode.CellDocs) {
|
||||||
// Add declarations from chained source files
|
// Add declarations from chained source files
|
||||||
let builtinsScope = fileInfo.builtinsScope;
|
let builtinsScope = fileInfo.builtinsScope;
|
||||||
@ -215,7 +216,7 @@ export class DocumentSymbolCollector extends ParseTreeWalker {
|
|||||||
// Add declarations from files that implicitly import the target file.
|
// Add declarations from files that implicitly import the target file.
|
||||||
const implicitlyImportedBy = collectImportedByCells(program, sourceFileInfo);
|
const implicitlyImportedBy = collectImportedByCells(program, sourceFileInfo);
|
||||||
implicitlyImportedBy.forEach((implicitImport) => {
|
implicitlyImportedBy.forEach((implicitImport) => {
|
||||||
const parseTree = program.getParseResults(implicitImport.sourceFile.getFilePath())?.parseTree;
|
const parseTree = program.getParseResults(implicitImport.sourceFile.getUri())?.parseTree;
|
||||||
if (parseTree) {
|
if (parseTree) {
|
||||||
const scope = AnalyzerNodeInfo.getScope(parseTree);
|
const scope = AnalyzerNodeInfo.getScope(parseTree);
|
||||||
const symbol = scope?.lookUpSymbol(node.value);
|
const symbol = scope?.lookUpSymbol(node.value);
|
||||||
@ -449,7 +450,7 @@ function _getDeclarationsForNonModuleNameNode(
|
|||||||
const type = evaluator.getType(node);
|
const type = evaluator.getType(node);
|
||||||
if (type?.category === TypeCategory.Module) {
|
if (type?.category === TypeCategory.Module) {
|
||||||
// Synthesize decl for the module.
|
// Synthesize decl for the module.
|
||||||
return [createSynthesizedAliasDeclaration(type.filePath)];
|
return [createSynthesizedAliasDeclaration(type.fileUri)];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,23 +10,22 @@
|
|||||||
|
|
||||||
import { CancellationToken, DocumentSymbol, Location, SymbolInformation } from 'vscode-languageserver';
|
import { CancellationToken, DocumentSymbol, Location, SymbolInformation } from 'vscode-languageserver';
|
||||||
|
|
||||||
|
import { getFileInfo } from '../analyzer/analyzerNodeInfo';
|
||||||
import { throwIfCancellationRequested } from '../common/cancellationUtils';
|
import { throwIfCancellationRequested } from '../common/cancellationUtils';
|
||||||
|
import { ProgramView } from '../common/extensibility';
|
||||||
|
import { Uri } from '../common/uri/uri';
|
||||||
import { ParseResults } from '../parser/parser';
|
import { ParseResults } from '../parser/parser';
|
||||||
import { IndexSymbolData, SymbolIndexer } from './symbolIndexer';
|
import { IndexSymbolData, SymbolIndexer } from './symbolIndexer';
|
||||||
import { ProgramView } from '../common/extensibility';
|
|
||||||
import { getFileInfo } from '../analyzer/analyzerNodeInfo';
|
|
||||||
import { convertPathToUri } from '../common/pathUtils';
|
|
||||||
|
|
||||||
export function convertToFlatSymbols(
|
export function convertToFlatSymbols(
|
||||||
program: ProgramView,
|
program: ProgramView,
|
||||||
filePath: string,
|
uri: Uri,
|
||||||
symbolList: DocumentSymbol[]
|
symbolList: DocumentSymbol[]
|
||||||
): SymbolInformation[] {
|
): SymbolInformation[] {
|
||||||
const flatSymbols: SymbolInformation[] = [];
|
const flatSymbols: SymbolInformation[] = [];
|
||||||
const documentUri = convertPathToUri(program.fileSystem, filePath);
|
|
||||||
|
|
||||||
for (const symbol of symbolList) {
|
for (const symbol of symbolList) {
|
||||||
_appendToFlatSymbolsRecursive(flatSymbols, documentUri, symbol);
|
_appendToFlatSymbolsRecursive(flatSymbols, uri, symbol);
|
||||||
}
|
}
|
||||||
|
|
||||||
return flatSymbols;
|
return flatSymbols;
|
||||||
@ -37,11 +36,11 @@ export class DocumentSymbolProvider {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected readonly program: ProgramView,
|
protected readonly program: ProgramView,
|
||||||
protected readonly filePath: string,
|
protected readonly uri: Uri,
|
||||||
private readonly _supportHierarchicalDocumentSymbol: boolean,
|
private readonly _supportHierarchicalDocumentSymbol: boolean,
|
||||||
private readonly _token: CancellationToken
|
private readonly _token: CancellationToken
|
||||||
) {
|
) {
|
||||||
this._parseResults = this.program.getParseResults(this.filePath);
|
this._parseResults = this.program.getParseResults(this.uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
getSymbols(): DocumentSymbol[] | SymbolInformation[] {
|
getSymbols(): DocumentSymbol[] | SymbolInformation[] {
|
||||||
@ -54,12 +53,12 @@ export class DocumentSymbolProvider {
|
|||||||
return symbolList;
|
return symbolList;
|
||||||
}
|
}
|
||||||
|
|
||||||
return convertToFlatSymbols(this.program, this.filePath, symbolList);
|
return convertToFlatSymbols(this.program, this.uri, symbolList);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected getHierarchicalSymbols() {
|
protected getHierarchicalSymbols() {
|
||||||
const symbolList: DocumentSymbol[] = [];
|
const symbolList: DocumentSymbol[] = [];
|
||||||
const parseResults = this.program.getParseResults(this.filePath);
|
const parseResults = this.program.getParseResults(this.uri);
|
||||||
if (!parseResults) {
|
if (!parseResults) {
|
||||||
return symbolList;
|
return symbolList;
|
||||||
}
|
}
|
||||||
@ -115,14 +114,14 @@ export class DocumentSymbolProvider {
|
|||||||
|
|
||||||
function _appendToFlatSymbolsRecursive(
|
function _appendToFlatSymbolsRecursive(
|
||||||
flatSymbols: SymbolInformation[],
|
flatSymbols: SymbolInformation[],
|
||||||
documentUri: string,
|
documentUri: Uri,
|
||||||
symbol: DocumentSymbol,
|
symbol: DocumentSymbol,
|
||||||
parent?: DocumentSymbol
|
parent?: DocumentSymbol
|
||||||
) {
|
) {
|
||||||
const flatSymbol: SymbolInformation = {
|
const flatSymbol: SymbolInformation = {
|
||||||
name: symbol.name,
|
name: symbol.name,
|
||||||
kind: symbol.kind,
|
kind: symbol.kind,
|
||||||
location: Location.create(documentUri, symbol.range),
|
location: Location.create(documentUri.toString(), symbol.range),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (symbol.tags) {
|
if (symbol.tags) {
|
||||||
|
@ -30,10 +30,12 @@ import {
|
|||||||
isTypeVar,
|
isTypeVar,
|
||||||
} from '../analyzer/types';
|
} from '../analyzer/types';
|
||||||
import { throwIfCancellationRequested } from '../common/cancellationUtils';
|
import { throwIfCancellationRequested } from '../common/cancellationUtils';
|
||||||
|
import { SignatureDisplayType } from '../common/configOptions';
|
||||||
import { assertNever, fail } from '../common/debug';
|
import { assertNever, fail } from '../common/debug';
|
||||||
import { ProgramView } from '../common/extensibility';
|
import { ProgramView } from '../common/extensibility';
|
||||||
import { convertOffsetToPosition, convertPositionToOffset } from '../common/positionUtils';
|
import { convertOffsetToPosition, convertPositionToOffset } from '../common/positionUtils';
|
||||||
import { Position, Range, TextRange } from '../common/textRange';
|
import { Position, Range, TextRange } from '../common/textRange';
|
||||||
|
import { Uri } from '../common/uri/uri';
|
||||||
import { ExpressionNode, NameNode, ParseNode, ParseNodeType, StringNode } from '../parser/parseNodes';
|
import { ExpressionNode, NameNode, ParseNode, ParseNodeType, StringNode } from '../parser/parseNodes';
|
||||||
import { ParseResults } from '../parser/parser';
|
import { ParseResults } from '../parser/parser';
|
||||||
import {
|
import {
|
||||||
@ -43,7 +45,6 @@ import {
|
|||||||
getToolTipForType,
|
getToolTipForType,
|
||||||
getTypeForToolTip,
|
getTypeForToolTip,
|
||||||
} from './tooltipUtils';
|
} from './tooltipUtils';
|
||||||
import { SignatureDisplayType } from '../common/configOptions';
|
|
||||||
|
|
||||||
export interface HoverTextPart {
|
export interface HoverTextPart {
|
||||||
python?: boolean;
|
python?: boolean;
|
||||||
@ -151,13 +152,13 @@ export class HoverProvider {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _program: ProgramView,
|
private readonly _program: ProgramView,
|
||||||
private readonly _filePath: string,
|
private readonly _fileUri: Uri,
|
||||||
private readonly _position: Position,
|
private readonly _position: Position,
|
||||||
private readonly _format: MarkupKind,
|
private readonly _format: MarkupKind,
|
||||||
private readonly _token: CancellationToken
|
private readonly _token: CancellationToken
|
||||||
) {
|
) {
|
||||||
this._parseResults = this._program.getParseResults(this._filePath);
|
this._parseResults = this._program.getParseResults(this._fileUri);
|
||||||
this._sourceMapper = this._program.getSourceMapper(this._filePath, this._token, /* mapCompiled */ true);
|
this._sourceMapper = this._program.getSourceMapper(this._fileUri, this._token, /* mapCompiled */ true);
|
||||||
}
|
}
|
||||||
|
|
||||||
getHover(): Hover | null {
|
getHover(): Hover | null {
|
||||||
|
@ -7,10 +7,10 @@
|
|||||||
*/
|
*/
|
||||||
import { Location } from 'vscode-languageserver-types';
|
import { Location } from 'vscode-languageserver-types';
|
||||||
import { ReadOnlyFileSystem } from '../common/fileSystem';
|
import { ReadOnlyFileSystem } from '../common/fileSystem';
|
||||||
import { convertPathToUri } from '../common/pathUtils';
|
|
||||||
import { DocumentRange } from '../common/textRange';
|
import { DocumentRange } from '../common/textRange';
|
||||||
|
import { Uri } from '../common/uri/uri';
|
||||||
|
|
||||||
export function canNavigateToFile(fs: ReadOnlyFileSystem, path: string): boolean {
|
export function canNavigateToFile(fs: ReadOnlyFileSystem, path: Uri): boolean {
|
||||||
return !fs.isInZip(path);
|
return !fs.isInZip(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -23,9 +23,9 @@ export function convertDocumentRangesToLocation(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function convertDocumentRangeToLocation(fs: ReadOnlyFileSystem, range: DocumentRange): Location | undefined {
|
export function convertDocumentRangeToLocation(fs: ReadOnlyFileSystem, range: DocumentRange): Location | undefined {
|
||||||
if (!canNavigateToFile(fs, range.path)) {
|
if (!canNavigateToFile(fs, range.uri)) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Location.create(convertPathToUri(fs, range.path), range.range);
|
return Location.create(range.uri.toString(), range.range);
|
||||||
}
|
}
|
||||||
|
@ -11,16 +11,17 @@ import { CancellationToken } from 'vscode-languageserver';
|
|||||||
|
|
||||||
import { Commands } from '../commands/commands';
|
import { Commands } from '../commands/commands';
|
||||||
import { ProgramView } from '../common/extensibility';
|
import { ProgramView } from '../common/extensibility';
|
||||||
|
import { Uri } from '../common/uri/uri';
|
||||||
import { ImportSorter } from './importSorter';
|
import { ImportSorter } from './importSorter';
|
||||||
|
|
||||||
export function performQuickAction(
|
export function performQuickAction(
|
||||||
programView: ProgramView,
|
programView: ProgramView,
|
||||||
filePath: string,
|
uri: Uri,
|
||||||
command: string,
|
command: string,
|
||||||
args: any[],
|
args: any[],
|
||||||
token: CancellationToken
|
token: CancellationToken
|
||||||
) {
|
) {
|
||||||
const sourceFileInfo = programView.getSourceFileInfo(filePath);
|
const sourceFileInfo = programView.getSourceFileInfo(uri);
|
||||||
|
|
||||||
// This command should be called only for open files, in which
|
// This command should be called only for open files, in which
|
||||||
// case we should have the file contents already loaded.
|
// case we should have the file contents already loaded.
|
||||||
@ -29,7 +30,7 @@ export function performQuickAction(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If we have no completed analysis job, there's nothing to do.
|
// If we have no completed analysis job, there's nothing to do.
|
||||||
const parseResults = programView.getParseResults(filePath);
|
const parseResults = programView.getParseResults(uri);
|
||||||
if (!parseResults) {
|
if (!parseResults) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
@ -19,18 +19,19 @@ import { isVisibleExternally } from '../analyzer/symbolUtils';
|
|||||||
import { TypeEvaluator } from '../analyzer/typeEvaluatorTypes';
|
import { TypeEvaluator } from '../analyzer/typeEvaluatorTypes';
|
||||||
import { maxTypeRecursionCount } from '../analyzer/types';
|
import { maxTypeRecursionCount } from '../analyzer/types';
|
||||||
import { throwIfCancellationRequested } from '../common/cancellationUtils';
|
import { throwIfCancellationRequested } from '../common/cancellationUtils';
|
||||||
import { isDefined } from '../common/core';
|
|
||||||
import { appendArray } from '../common/collectionUtils';
|
import { appendArray } from '../common/collectionUtils';
|
||||||
|
import { isDefined } from '../common/core';
|
||||||
import { assertNever } from '../common/debug';
|
import { assertNever } from '../common/debug';
|
||||||
import { ProgramView, ReferenceUseCase, SymbolUsageProvider } from '../common/extensibility';
|
import { ProgramView, ReferenceUseCase, SymbolUsageProvider } from '../common/extensibility';
|
||||||
|
import { ReadOnlyFileSystem } from '../common/fileSystem';
|
||||||
import { convertOffsetToPosition, convertPositionToOffset } from '../common/positionUtils';
|
import { convertOffsetToPosition, convertPositionToOffset } from '../common/positionUtils';
|
||||||
|
import { ServiceKeys } from '../common/serviceProviderExtensions';
|
||||||
import { DocumentRange, Position, TextRange, doesRangeContain } from '../common/textRange';
|
import { DocumentRange, Position, TextRange, doesRangeContain } from '../common/textRange';
|
||||||
|
import { Uri } from '../common/uri/uri';
|
||||||
import { NameNode, ParseNode, ParseNodeType } from '../parser/parseNodes';
|
import { NameNode, ParseNode, ParseNodeType } from '../parser/parseNodes';
|
||||||
import { ParseResults } from '../parser/parser';
|
import { ParseResults } from '../parser/parser';
|
||||||
import { CollectionResult, DocumentSymbolCollector } from './documentSymbolCollector';
|
import { CollectionResult, DocumentSymbolCollector } from './documentSymbolCollector';
|
||||||
import { ReadOnlyFileSystem } from '../common/fileSystem';
|
|
||||||
import { convertDocumentRangesToLocation } from './navigationUtils';
|
import { convertDocumentRangesToLocation } from './navigationUtils';
|
||||||
import { ServiceKeys } from '../common/serviceProviderExtensions';
|
|
||||||
|
|
||||||
export type ReferenceCallback = (locations: DocumentRange[]) => void;
|
export type ReferenceCallback = (locations: DocumentRange[]) => void;
|
||||||
|
|
||||||
@ -103,17 +104,17 @@ export class FindReferencesTreeWalker {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private _program: ProgramView,
|
private _program: ProgramView,
|
||||||
private _filePath: string,
|
private _fileUri: Uri,
|
||||||
private _referencesResult: ReferencesResult,
|
private _referencesResult: ReferencesResult,
|
||||||
private _includeDeclaration: boolean,
|
private _includeDeclaration: boolean,
|
||||||
private _cancellationToken: CancellationToken,
|
private _cancellationToken: CancellationToken,
|
||||||
private readonly _createDocumentRange: (
|
private readonly _createDocumentRange: (
|
||||||
filePath: string,
|
fileUri: Uri,
|
||||||
result: CollectionResult,
|
result: CollectionResult,
|
||||||
parseResults: ParseResults
|
parseResults: ParseResults
|
||||||
) => DocumentRange = FindReferencesTreeWalker.createDocumentRange
|
) => DocumentRange = FindReferencesTreeWalker.createDocumentRange
|
||||||
) {
|
) {
|
||||||
this._parseResults = this._program.getParseResults(this._filePath);
|
this._parseResults = this._program.getParseResults(this._fileUri);
|
||||||
}
|
}
|
||||||
|
|
||||||
findReferences(rootNode = this._parseResults?.parseTree) {
|
findReferences(rootNode = this._parseResults?.parseTree) {
|
||||||
@ -139,16 +140,16 @@ export class FindReferencesTreeWalker {
|
|||||||
for (const result of collector.collect()) {
|
for (const result of collector.collect()) {
|
||||||
// Is it the same symbol?
|
// Is it the same symbol?
|
||||||
if (this._includeDeclaration || result.node !== this._referencesResult.nodeAtOffset) {
|
if (this._includeDeclaration || result.node !== this._referencesResult.nodeAtOffset) {
|
||||||
results.push(this._createDocumentRange(this._filePath, result, this._parseResults));
|
results.push(this._createDocumentRange(this._fileUri, result, this._parseResults));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
static createDocumentRange(filePath: string, result: CollectionResult, parseResults: ParseResults): DocumentRange {
|
static createDocumentRange(fileUri: Uri, result: CollectionResult, parseResults: ParseResults): DocumentRange {
|
||||||
return {
|
return {
|
||||||
path: filePath,
|
uri: fileUri,
|
||||||
range: {
|
range: {
|
||||||
start: convertOffsetToPosition(result.range.start, parseResults.tokenizerOutput.lines),
|
start: convertOffsetToPosition(result.range.start, parseResults.tokenizerOutput.lines),
|
||||||
end: convertOffsetToPosition(TextRange.getEnd(result.range), parseResults.tokenizerOutput.lines),
|
end: convertOffsetToPosition(TextRange.getEnd(result.range), parseResults.tokenizerOutput.lines),
|
||||||
@ -162,7 +163,7 @@ export class ReferencesProvider {
|
|||||||
private _program: ProgramView,
|
private _program: ProgramView,
|
||||||
private _token: CancellationToken,
|
private _token: CancellationToken,
|
||||||
private readonly _createDocumentRange?: (
|
private readonly _createDocumentRange?: (
|
||||||
filePath: string,
|
fileUri: Uri,
|
||||||
result: CollectionResult,
|
result: CollectionResult,
|
||||||
parseResults: ParseResults
|
parseResults: ParseResults
|
||||||
) => DocumentRange,
|
) => DocumentRange,
|
||||||
@ -172,17 +173,17 @@ export class ReferencesProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
reportReferences(
|
reportReferences(
|
||||||
filePath: string,
|
fileUri: Uri,
|
||||||
position: Position,
|
position: Position,
|
||||||
includeDeclaration: boolean,
|
includeDeclaration: boolean,
|
||||||
resultReporter?: ResultProgressReporter<Location[]>
|
resultReporter?: ResultProgressReporter<Location[]>
|
||||||
) {
|
) {
|
||||||
const sourceFileInfo = this._program.getSourceFileInfo(filePath);
|
const sourceFileInfo = this._program.getSourceFileInfo(fileUri);
|
||||||
if (!sourceFileInfo) {
|
if (!sourceFileInfo) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const parseResults = this._program.getParseResults(filePath);
|
const parseResults = this._program.getParseResults(fileUri);
|
||||||
if (!parseResults) {
|
if (!parseResults) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -202,7 +203,7 @@ export class ReferencesProvider {
|
|||||||
const invokedFromUserFile = isUserCode(sourceFileInfo);
|
const invokedFromUserFile = isUserCode(sourceFileInfo);
|
||||||
const referencesResult = ReferencesProvider.getDeclarationForPosition(
|
const referencesResult = ReferencesProvider.getDeclarationForPosition(
|
||||||
this._program,
|
this._program,
|
||||||
filePath,
|
fileUri,
|
||||||
position,
|
position,
|
||||||
reporter,
|
reporter,
|
||||||
ReferenceUseCase.References,
|
ReferenceUseCase.References,
|
||||||
@ -214,7 +215,7 @@ export class ReferencesProvider {
|
|||||||
|
|
||||||
// Do we need to do a global search as well?
|
// Do we need to do a global search as well?
|
||||||
if (!referencesResult.requiresGlobalSearch) {
|
if (!referencesResult.requiresGlobalSearch) {
|
||||||
this.addReferencesToResult(sourceFileInfo.sourceFile.getFilePath(), includeDeclaration, referencesResult);
|
this.addReferencesToResult(sourceFileInfo.sourceFile.getUri(), includeDeclaration, referencesResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const curSourceFileInfo of this._program.getSourceFileInfoList()) {
|
for (const curSourceFileInfo of this._program.getSourceFileInfoList()) {
|
||||||
@ -228,7 +229,7 @@ export class ReferencesProvider {
|
|||||||
const fileContents = curSourceFileInfo.sourceFile.getFileContent();
|
const fileContents = curSourceFileInfo.sourceFile.getFileContent();
|
||||||
if (!fileContents || referencesResult.symbolNames.some((s) => fileContents.search(s) >= 0)) {
|
if (!fileContents || referencesResult.symbolNames.some((s) => fileContents.search(s) >= 0)) {
|
||||||
this.addReferencesToResult(
|
this.addReferencesToResult(
|
||||||
curSourceFileInfo.sourceFile.getFilePath(),
|
curSourceFileInfo.sourceFile.getUri(),
|
||||||
includeDeclaration,
|
includeDeclaration,
|
||||||
referencesResult
|
referencesResult
|
||||||
);
|
);
|
||||||
@ -246,12 +247,12 @@ export class ReferencesProvider {
|
|||||||
for (const decl of referencesResult.declarations) {
|
for (const decl of referencesResult.declarations) {
|
||||||
throwIfCancellationRequested(this._token);
|
throwIfCancellationRequested(this._token);
|
||||||
|
|
||||||
if (referencesResult.locations.some((l) => l.path === decl.path)) {
|
if (referencesResult.locations.some((l) => l.uri.equals(decl.uri))) {
|
||||||
// Already included.
|
// Already included.
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const declFileInfo = this._program.getSourceFileInfo(decl.path);
|
const declFileInfo = this._program.getSourceFileInfo(decl.uri);
|
||||||
if (!declFileInfo) {
|
if (!declFileInfo) {
|
||||||
// The file the declaration belongs to doesn't belong to the program.
|
// The file the declaration belongs to doesn't belong to the program.
|
||||||
continue;
|
continue;
|
||||||
@ -266,10 +267,10 @@ export class ReferencesProvider {
|
|||||||
referencesResult.providers
|
referencesResult.providers
|
||||||
);
|
);
|
||||||
|
|
||||||
this.addReferencesToResult(declFileInfo.sourceFile.getFilePath(), includeDeclaration, tempResult);
|
this.addReferencesToResult(declFileInfo.sourceFile.getUri(), includeDeclaration, tempResult);
|
||||||
for (const loc of tempResult.locations) {
|
for (const loc of tempResult.locations) {
|
||||||
// Include declarations only. And throw away any references
|
// Include declarations only. And throw away any references
|
||||||
if (loc.path === decl.path && doesRangeContain(decl.range, loc.range)) {
|
if (loc.uri.equals(decl.uri) && doesRangeContain(decl.range, loc.range)) {
|
||||||
referencesResult.addLocations(loc);
|
referencesResult.addLocations(loc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -279,15 +280,15 @@ export class ReferencesProvider {
|
|||||||
return locations;
|
return locations;
|
||||||
}
|
}
|
||||||
|
|
||||||
addReferencesToResult(filePath: string, includeDeclaration: boolean, referencesResult: ReferencesResult): void {
|
addReferencesToResult(fileUri: Uri, includeDeclaration: boolean, referencesResult: ReferencesResult): void {
|
||||||
const parseResults = this._program.getParseResults(filePath);
|
const parseResults = this._program.getParseResults(fileUri);
|
||||||
if (!parseResults) {
|
if (!parseResults) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const refTreeWalker = new FindReferencesTreeWalker(
|
const refTreeWalker = new FindReferencesTreeWalker(
|
||||||
this._program,
|
this._program,
|
||||||
filePath,
|
fileUri,
|
||||||
referencesResult,
|
referencesResult,
|
||||||
includeDeclaration,
|
includeDeclaration,
|
||||||
this._token,
|
this._token,
|
||||||
@ -299,7 +300,7 @@ export class ReferencesProvider {
|
|||||||
|
|
||||||
static getDeclarationForNode(
|
static getDeclarationForNode(
|
||||||
program: ProgramView,
|
program: ProgramView,
|
||||||
filePath: string,
|
fileUri: Uri,
|
||||||
node: NameNode,
|
node: NameNode,
|
||||||
reporter: ReferenceCallback | undefined,
|
reporter: ReferenceCallback | undefined,
|
||||||
useCase: ReferenceUseCase,
|
useCase: ReferenceUseCase,
|
||||||
@ -318,7 +319,7 @@ export class ReferencesProvider {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const requiresGlobalSearch = isVisibleOutside(program.evaluator!, filePath, node, declarations);
|
const requiresGlobalSearch = isVisibleOutside(program.evaluator!, fileUri, node, declarations);
|
||||||
const symbolNames = new Set<string>(declarations.map((d) => getNameFromDeclaration(d)!).filter((n) => !!n));
|
const symbolNames = new Set<string>(declarations.map((d) => getNameFromDeclaration(d)!).filter((n) => !!n));
|
||||||
symbolNames.add(node.value);
|
symbolNames.add(node.value);
|
||||||
|
|
||||||
@ -345,14 +346,14 @@ export class ReferencesProvider {
|
|||||||
|
|
||||||
static getDeclarationForPosition(
|
static getDeclarationForPosition(
|
||||||
program: ProgramView,
|
program: ProgramView,
|
||||||
filePath: string,
|
fileUri: Uri,
|
||||||
position: Position,
|
position: Position,
|
||||||
reporter: ReferenceCallback | undefined,
|
reporter: ReferenceCallback | undefined,
|
||||||
useCase: ReferenceUseCase,
|
useCase: ReferenceUseCase,
|
||||||
token: CancellationToken
|
token: CancellationToken
|
||||||
): ReferencesResult | undefined {
|
): ReferencesResult | undefined {
|
||||||
throwIfCancellationRequested(token);
|
throwIfCancellationRequested(token);
|
||||||
const parseResults = program.getParseResults(filePath);
|
const parseResults = program.getParseResults(fileUri);
|
||||||
if (!parseResults) {
|
if (!parseResults) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
@ -372,16 +373,11 @@ export class ReferencesProvider {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.getDeclarationForNode(program, filePath, node, reporter, useCase, token);
|
return this.getDeclarationForNode(program, fileUri, node, reporter, useCase, token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function isVisibleOutside(
|
function isVisibleOutside(evaluator: TypeEvaluator, currentUri: Uri, node: NameNode, declarations: Declaration[]) {
|
||||||
evaluator: TypeEvaluator,
|
|
||||||
currentFilePath: string,
|
|
||||||
node: NameNode,
|
|
||||||
declarations: Declaration[]
|
|
||||||
) {
|
|
||||||
const result = evaluator.lookUpSymbolRecursive(node, node.value, /* honorCodeFlow */ false);
|
const result = evaluator.lookUpSymbolRecursive(node, node.value, /* honorCodeFlow */ false);
|
||||||
if (result && !isExternallyVisible(result.symbol)) {
|
if (result && !isExternallyVisible(result.symbol)) {
|
||||||
return false;
|
return false;
|
||||||
@ -394,7 +390,7 @@ function isVisibleOutside(
|
|||||||
// that is within the current file and cannot be imported directly from other modules.
|
// that is within the current file and cannot be imported directly from other modules.
|
||||||
return declarations.some((decl) => {
|
return declarations.some((decl) => {
|
||||||
// If the declaration is outside of this file, a global search is needed.
|
// If the declaration is outside of this file, a global search is needed.
|
||||||
if (decl.path !== currentFilePath) {
|
if (!decl.uri.equals(currentUri)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,27 +9,28 @@
|
|||||||
|
|
||||||
import { CancellationToken, WorkspaceEdit } from 'vscode-languageserver';
|
import { CancellationToken, WorkspaceEdit } from 'vscode-languageserver';
|
||||||
|
|
||||||
|
import { isUserCode } from '../analyzer/sourceFileInfoUtils';
|
||||||
|
import { throwIfCancellationRequested } from '../common/cancellationUtils';
|
||||||
import { assertNever } from '../common/debug';
|
import { assertNever } from '../common/debug';
|
||||||
import { FileEditAction } from '../common/editAction';
|
import { FileEditAction } from '../common/editAction';
|
||||||
import { ProgramView, ReferenceUseCase } from '../common/extensibility';
|
import { ProgramView, ReferenceUseCase } from '../common/extensibility';
|
||||||
import { convertTextRangeToRange } from '../common/positionUtils';
|
import { convertTextRangeToRange } from '../common/positionUtils';
|
||||||
import { Position, Range } from '../common/textRange';
|
import { Position, Range } from '../common/textRange';
|
||||||
import { ReferencesProvider, ReferencesResult } from '../languageService/referencesProvider';
|
import { Uri } from '../common/uri/uri';
|
||||||
import { isUserCode } from '../analyzer/sourceFileInfoUtils';
|
|
||||||
import { throwIfCancellationRequested } from '../common/cancellationUtils';
|
|
||||||
import { ParseResults } from '../parser/parser';
|
|
||||||
import { convertToWorkspaceEdit } from '../common/workspaceEditUtils';
|
import { convertToWorkspaceEdit } from '../common/workspaceEditUtils';
|
||||||
|
import { ReferencesProvider, ReferencesResult } from '../languageService/referencesProvider';
|
||||||
|
import { ParseResults } from '../parser/parser';
|
||||||
|
|
||||||
export class RenameProvider {
|
export class RenameProvider {
|
||||||
private readonly _parseResults: ParseResults | undefined;
|
private readonly _parseResults: ParseResults | undefined;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private _program: ProgramView,
|
private _program: ProgramView,
|
||||||
private _filePath: string,
|
private _fileUri: Uri,
|
||||||
private _position: Position,
|
private _position: Position,
|
||||||
private _token: CancellationToken
|
private _token: CancellationToken
|
||||||
) {
|
) {
|
||||||
this._parseResults = this._program.getParseResults(this._filePath);
|
this._parseResults = this._program.getParseResults(this._fileUri);
|
||||||
}
|
}
|
||||||
|
|
||||||
canRenameSymbol(isDefaultWorkspace: boolean, isUntitled: boolean): Range | null {
|
canRenameSymbol(isDefaultWorkspace: boolean, isUntitled: boolean): Range | null {
|
||||||
@ -45,7 +46,7 @@ export class RenameProvider {
|
|||||||
|
|
||||||
const renameMode = RenameProvider.getRenameSymbolMode(
|
const renameMode = RenameProvider.getRenameSymbolMode(
|
||||||
this._program,
|
this._program,
|
||||||
this._filePath,
|
this._fileUri,
|
||||||
referencesResult,
|
referencesResult,
|
||||||
isDefaultWorkspace,
|
isDefaultWorkspace,
|
||||||
isUntitled
|
isUntitled
|
||||||
@ -72,7 +73,7 @@ export class RenameProvider {
|
|||||||
const referenceProvider = new ReferencesProvider(this._program, this._token);
|
const referenceProvider = new ReferencesProvider(this._program, this._token);
|
||||||
const renameMode = RenameProvider.getRenameSymbolMode(
|
const renameMode = RenameProvider.getRenameSymbolMode(
|
||||||
this._program,
|
this._program,
|
||||||
this._filePath,
|
this._fileUri,
|
||||||
referencesResult,
|
referencesResult,
|
||||||
isDefaultWorkspace,
|
isDefaultWorkspace,
|
||||||
isUntitled
|
isUntitled
|
||||||
@ -80,11 +81,7 @@ export class RenameProvider {
|
|||||||
|
|
||||||
switch (renameMode) {
|
switch (renameMode) {
|
||||||
case 'singleFileMode':
|
case 'singleFileMode':
|
||||||
referenceProvider.addReferencesToResult(
|
referenceProvider.addReferencesToResult(this._fileUri, /* includeDeclaration */ true, referencesResult);
|
||||||
this._filePath,
|
|
||||||
/* includeDeclaration */ true,
|
|
||||||
referencesResult
|
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'multiFileMode': {
|
case 'multiFileMode': {
|
||||||
@ -99,7 +96,7 @@ export class RenameProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
referenceProvider.addReferencesToResult(
|
referenceProvider.addReferencesToResult(
|
||||||
curSourceFileInfo.sourceFile.getFilePath(),
|
curSourceFileInfo.sourceFile.getUri(),
|
||||||
/* includeDeclaration */ true,
|
/* includeDeclaration */ true,
|
||||||
referencesResult
|
referencesResult
|
||||||
);
|
);
|
||||||
@ -124,23 +121,23 @@ export class RenameProvider {
|
|||||||
const edits: FileEditAction[] = [];
|
const edits: FileEditAction[] = [];
|
||||||
referencesResult.locations.forEach((loc) => {
|
referencesResult.locations.forEach((loc) => {
|
||||||
edits.push({
|
edits.push({
|
||||||
filePath: loc.path,
|
fileUri: loc.uri,
|
||||||
range: loc.range,
|
range: loc.range,
|
||||||
replacementText: newName,
|
replacementText: newName,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
return convertToWorkspaceEdit(this._program.fileSystem, { edits, fileOperations: [] });
|
return convertToWorkspaceEdit({ edits, fileOperations: [] });
|
||||||
}
|
}
|
||||||
|
|
||||||
static getRenameSymbolMode(
|
static getRenameSymbolMode(
|
||||||
program: ProgramView,
|
program: ProgramView,
|
||||||
filePath: string,
|
fileUri: Uri,
|
||||||
referencesResult: ReferencesResult,
|
referencesResult: ReferencesResult,
|
||||||
isDefaultWorkspace: boolean,
|
isDefaultWorkspace: boolean,
|
||||||
isUntitled: boolean
|
isUntitled: boolean
|
||||||
) {
|
) {
|
||||||
const sourceFileInfo = program.getSourceFileInfo(filePath)!;
|
const sourceFileInfo = program.getSourceFileInfo(fileUri)!;
|
||||||
|
|
||||||
// We have 2 different cases
|
// We have 2 different cases
|
||||||
// Single file mode.
|
// Single file mode.
|
||||||
@ -156,12 +153,12 @@ export class RenameProvider {
|
|||||||
(userFile && !referencesResult.requiresGlobalSearch) ||
|
(userFile && !referencesResult.requiresGlobalSearch) ||
|
||||||
(!userFile &&
|
(!userFile &&
|
||||||
sourceFileInfo.isOpenByClient &&
|
sourceFileInfo.isOpenByClient &&
|
||||||
referencesResult.declarations.every((d) => program.getSourceFileInfo(d.path) === sourceFileInfo))
|
referencesResult.declarations.every((d) => program.getSourceFileInfo(d.uri) === sourceFileInfo))
|
||||||
) {
|
) {
|
||||||
return 'singleFileMode';
|
return 'singleFileMode';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isUntitled && referencesResult.declarations.every((d) => isUserCode(program.getSourceFileInfo(d.path)))) {
|
if (!isUntitled && referencesResult.declarations.every((d) => isUserCode(program.getSourceFileInfo(d.uri)))) {
|
||||||
return 'multiFileMode';
|
return 'multiFileMode';
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,7 +170,7 @@ export class RenameProvider {
|
|||||||
private _getReferenceResult() {
|
private _getReferenceResult() {
|
||||||
const referencesResult = ReferencesProvider.getDeclarationForPosition(
|
const referencesResult = ReferencesProvider.getDeclarationForPosition(
|
||||||
this._program,
|
this._program,
|
||||||
this._filePath,
|
this._fileUri,
|
||||||
this._position,
|
this._position,
|
||||||
/* reporter */ undefined,
|
/* reporter */ undefined,
|
||||||
ReferenceUseCase.Rename,
|
ReferenceUseCase.Rename,
|
||||||
|
@ -31,6 +31,7 @@ import { throwIfCancellationRequested } from '../common/cancellationUtils';
|
|||||||
import { ProgramView } from '../common/extensibility';
|
import { ProgramView } from '../common/extensibility';
|
||||||
import { convertPositionToOffset } from '../common/positionUtils';
|
import { convertPositionToOffset } from '../common/positionUtils';
|
||||||
import { Position } from '../common/textRange';
|
import { Position } from '../common/textRange';
|
||||||
|
import { Uri } from '../common/uri/uri';
|
||||||
import { CallNode, NameNode, ParseNodeType } from '../parser/parseNodes';
|
import { CallNode, NameNode, ParseNodeType } from '../parser/parseNodes';
|
||||||
import { ParseResults } from '../parser/parser';
|
import { ParseResults } from '../parser/parser';
|
||||||
import { getDocumentationPartsForTypeAndDecl, getFunctionDocStringFromType } from './tooltipUtils';
|
import { getDocumentationPartsForTypeAndDecl, getFunctionDocStringFromType } from './tooltipUtils';
|
||||||
@ -41,7 +42,7 @@ export class SignatureHelpProvider {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private _program: ProgramView,
|
private _program: ProgramView,
|
||||||
private _filePath: string,
|
private _fileUri: Uri,
|
||||||
private _position: Position,
|
private _position: Position,
|
||||||
private _format: MarkupKind,
|
private _format: MarkupKind,
|
||||||
private _hasSignatureLabelOffsetCapability: boolean,
|
private _hasSignatureLabelOffsetCapability: boolean,
|
||||||
@ -49,8 +50,8 @@ export class SignatureHelpProvider {
|
|||||||
private _context: SignatureHelpContext | undefined,
|
private _context: SignatureHelpContext | undefined,
|
||||||
private _token: CancellationToken
|
private _token: CancellationToken
|
||||||
) {
|
) {
|
||||||
this._parseResults = this._program.getParseResults(this._filePath);
|
this._parseResults = this._program.getParseResults(this._fileUri);
|
||||||
this._sourceMapper = this._program.getSourceMapper(this._filePath, this._token, /* mapCompiled */ true);
|
this._sourceMapper = this._program.getSourceMapper(this._fileUri, this._token, /* mapCompiled */ true);
|
||||||
}
|
}
|
||||||
|
|
||||||
getSignatureHelp(): SignatureHelp | undefined {
|
getSignatureHelp(): SignatureHelp | undefined {
|
||||||
|
@ -13,15 +13,16 @@ import * as AnalyzerNodeInfo from '../analyzer/analyzerNodeInfo';
|
|||||||
import { Declaration, DeclarationType } from '../analyzer/declaration';
|
import { Declaration, DeclarationType } from '../analyzer/declaration';
|
||||||
import { getLastTypedDeclaredForSymbol, isVisibleExternally } from '../analyzer/symbolUtils';
|
import { getLastTypedDeclaredForSymbol, isVisibleExternally } from '../analyzer/symbolUtils';
|
||||||
import { throwIfCancellationRequested } from '../common/cancellationUtils';
|
import { throwIfCancellationRequested } from '../common/cancellationUtils';
|
||||||
|
import { getSymbolKind } from '../common/lspUtils';
|
||||||
import { convertOffsetsToRange } from '../common/positionUtils';
|
import { convertOffsetsToRange } from '../common/positionUtils';
|
||||||
import { Range } from '../common/textRange';
|
import { Range } from '../common/textRange';
|
||||||
|
import { Uri } from '../common/uri/uri';
|
||||||
import { ParseResults } from '../parser/parser';
|
import { ParseResults } from '../parser/parser';
|
||||||
import { convertSymbolKindToCompletionItemKind } from './autoImporter';
|
import { convertSymbolKindToCompletionItemKind } from './autoImporter';
|
||||||
import { getSymbolKind } from '../common/lspUtils';
|
|
||||||
|
|
||||||
export interface IndexAliasData {
|
export interface IndexAliasData {
|
||||||
readonly originalName: string;
|
readonly originalName: string;
|
||||||
readonly modulePath: string;
|
readonly moduleUri: Uri;
|
||||||
readonly kind: SymbolKind;
|
readonly kind: SymbolKind;
|
||||||
readonly itemKind?: CompletionItemKind | undefined;
|
readonly itemKind?: CompletionItemKind | undefined;
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ import {
|
|||||||
getClassDocString,
|
getClassDocString,
|
||||||
getFunctionDocStringInherited,
|
getFunctionDocStringInherited,
|
||||||
getModuleDocString,
|
getModuleDocString,
|
||||||
getModuleDocStringFromPaths,
|
getModuleDocStringFromUris,
|
||||||
getOverloadedFunctionDocStringsInherited,
|
getOverloadedFunctionDocStringsInherited,
|
||||||
getPropertyDocStringInherited,
|
getPropertyDocStringInherited,
|
||||||
getVariableDocString,
|
getVariableDocString,
|
||||||
@ -323,7 +323,7 @@ export function getDocumentationPartsForTypeAndDecl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
typeDoc = getModuleDocStringFromPaths([resolvedDecl.path], sourceMapper);
|
typeDoc = getModuleDocStringFromUris([resolvedDecl.uri], sourceMapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
typeDoc =
|
typeDoc =
|
||||||
|
@ -7,15 +7,15 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { CancellationToken, Location, ResultProgressReporter, SymbolInformation } from 'vscode-languageserver';
|
import { CancellationToken, Location, ResultProgressReporter, SymbolInformation } from 'vscode-languageserver';
|
||||||
import { throwIfCancellationRequested } from '../common/cancellationUtils';
|
|
||||||
import * as StringUtils from '../common/stringUtils';
|
|
||||||
import { IndexSymbolData, SymbolIndexer } from './symbolIndexer';
|
|
||||||
import { ProgramView } from '../common/extensibility';
|
|
||||||
import { isUserCode } from '../analyzer/sourceFileInfoUtils';
|
|
||||||
import { getFileInfo } from '../analyzer/analyzerNodeInfo';
|
import { getFileInfo } from '../analyzer/analyzerNodeInfo';
|
||||||
import { convertPathToUri } from '../common/pathUtils';
|
import { isUserCode } from '../analyzer/sourceFileInfoUtils';
|
||||||
import { Workspace } from '../workspaceFactory';
|
import { throwIfCancellationRequested } from '../common/cancellationUtils';
|
||||||
import { appendArray } from '../common/collectionUtils';
|
import { appendArray } from '../common/collectionUtils';
|
||||||
|
import { ProgramView } from '../common/extensibility';
|
||||||
|
import * as StringUtils from '../common/stringUtils';
|
||||||
|
import { Uri } from '../common/uri/uri';
|
||||||
|
import { Workspace } from '../workspaceFactory';
|
||||||
|
import { IndexSymbolData, SymbolIndexer } from './symbolIndexer';
|
||||||
|
|
||||||
type WorkspaceSymbolCallback = (symbols: SymbolInformation[]) => void;
|
type WorkspaceSymbolCallback = (symbols: SymbolInformation[]) => void;
|
||||||
|
|
||||||
@ -55,10 +55,10 @@ export class WorkspaceSymbolProvider {
|
|||||||
return this._allSymbols;
|
return this._allSymbols;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected getSymbolsForDocument(program: ProgramView, filePath: string): SymbolInformation[] {
|
protected getSymbolsForDocument(program: ProgramView, fileUri: Uri): SymbolInformation[] {
|
||||||
const symbolList: SymbolInformation[] = [];
|
const symbolList: SymbolInformation[] = [];
|
||||||
|
|
||||||
const parseResults = program.getParseResults(filePath);
|
const parseResults = program.getParseResults(fileUri);
|
||||||
if (!parseResults) {
|
if (!parseResults) {
|
||||||
return symbolList;
|
return symbolList;
|
||||||
}
|
}
|
||||||
@ -69,7 +69,7 @@ export class WorkspaceSymbolProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const indexSymbolData = SymbolIndexer.indexSymbols(fileInfo, parseResults, this._token);
|
const indexSymbolData = SymbolIndexer.indexSymbols(fileInfo, parseResults, this._token);
|
||||||
this.appendWorkspaceSymbolsRecursive(indexSymbolData, program, filePath, '', symbolList);
|
this.appendWorkspaceSymbolsRecursive(indexSymbolData, program, fileUri, '', symbolList);
|
||||||
|
|
||||||
return symbolList;
|
return symbolList;
|
||||||
}
|
}
|
||||||
@ -77,7 +77,7 @@ export class WorkspaceSymbolProvider {
|
|||||||
protected appendWorkspaceSymbolsRecursive(
|
protected appendWorkspaceSymbolsRecursive(
|
||||||
indexSymbolData: IndexSymbolData[] | undefined,
|
indexSymbolData: IndexSymbolData[] | undefined,
|
||||||
program: ProgramView,
|
program: ProgramView,
|
||||||
filePath: string,
|
fileUri: Uri,
|
||||||
container: string,
|
container: string,
|
||||||
symbolList: SymbolInformation[]
|
symbolList: SymbolInformation[]
|
||||||
) {
|
) {
|
||||||
@ -94,7 +94,7 @@ export class WorkspaceSymbolProvider {
|
|||||||
|
|
||||||
if (StringUtils.isPatternInSymbol(this._query, symbolData.name)) {
|
if (StringUtils.isPatternInSymbol(this._query, symbolData.name)) {
|
||||||
const location: Location = {
|
const location: Location = {
|
||||||
uri: convertPathToUri(program.fileSystem, filePath),
|
uri: fileUri.toString(),
|
||||||
range: symbolData.selectionRange!,
|
range: symbolData.selectionRange!,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -114,7 +114,7 @@ export class WorkspaceSymbolProvider {
|
|||||||
this.appendWorkspaceSymbolsRecursive(
|
this.appendWorkspaceSymbolsRecursive(
|
||||||
symbolData.children,
|
symbolData.children,
|
||||||
program,
|
program,
|
||||||
filePath,
|
fileUri,
|
||||||
this._getContainerName(container, symbolData.name),
|
this._getContainerName(container, symbolData.name),
|
||||||
symbolList
|
symbolList
|
||||||
);
|
);
|
||||||
@ -134,7 +134,7 @@ export class WorkspaceSymbolProvider {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const symbolList = this.getSymbolsForDocument(program, sourceFileInfo.sourceFile.getFilePath());
|
const symbolList = this.getSymbolsForDocument(program, sourceFileInfo.sourceFile.getUri());
|
||||||
if (symbolList.length > 0) {
|
if (symbolList.length > 0) {
|
||||||
this._reporter(symbolList);
|
this._reporter(symbolList);
|
||||||
}
|
}
|
||||||
|
@ -28,12 +28,14 @@ import { createDeferred } from './common/deferred';
|
|||||||
import { Diagnostic, DiagnosticCategory } from './common/diagnostic';
|
import { Diagnostic, DiagnosticCategory } from './common/diagnostic';
|
||||||
import { FileDiagnostics } from './common/diagnosticSink';
|
import { FileDiagnostics } from './common/diagnosticSink';
|
||||||
import { FullAccessHost } from './common/fullAccessHost';
|
import { FullAccessHost } from './common/fullAccessHost';
|
||||||
import { combinePaths, getFileSpec, normalizePath, tryStat } from './common/pathUtils';
|
import { combinePaths, normalizePath } from './common/pathUtils';
|
||||||
import { versionFromString } from './common/pythonVersion';
|
import { versionFromString } from './common/pythonVersion';
|
||||||
import { RealTempFile, createFromRealFileSystem } from './common/realFileSystem';
|
import { RealTempFile, createFromRealFileSystem } from './common/realFileSystem';
|
||||||
import { ServiceProvider } from './common/serviceProvider';
|
import { ServiceProvider } from './common/serviceProvider';
|
||||||
import { createServiceProvider } from './common/serviceProviderExtensions';
|
import { createServiceProvider } from './common/serviceProviderExtensions';
|
||||||
import { Range, isEmptyRange } from './common/textRange';
|
import { Range, isEmptyRange } from './common/textRange';
|
||||||
|
import { Uri } from './common/uri/uri';
|
||||||
|
import { getFileSpec, tryStat } from './common/uri/uriUtils';
|
||||||
import { PyrightFileSystem } from './pyrightFileSystem';
|
import { PyrightFileSystem } from './pyrightFileSystem';
|
||||||
|
|
||||||
const toolName = 'pyright';
|
const toolName = 'pyright';
|
||||||
@ -64,11 +66,11 @@ interface PyrightSymbolCount {
|
|||||||
|
|
||||||
interface PyrightTypeCompletenessReport {
|
interface PyrightTypeCompletenessReport {
|
||||||
packageName: string;
|
packageName: string;
|
||||||
packageRootDirectory?: string | undefined;
|
packageRootDirectory?: Uri | undefined;
|
||||||
moduleName: string;
|
moduleName: string;
|
||||||
moduleRootDirectory?: string | undefined;
|
moduleRootDirectory?: Uri | undefined;
|
||||||
ignoreUnknownTypesFromImports: boolean;
|
ignoreUnknownTypesFromImports: boolean;
|
||||||
pyTypedPath?: string | undefined;
|
pyTypedPath?: Uri | undefined;
|
||||||
exportedSymbolCounts: PyrightSymbolCount;
|
exportedSymbolCounts: PyrightSymbolCount;
|
||||||
otherSymbolCounts: PyrightSymbolCount;
|
otherSymbolCounts: PyrightSymbolCount;
|
||||||
missingFunctionDocStringCount: number;
|
missingFunctionDocStringCount: number;
|
||||||
@ -95,7 +97,7 @@ interface PyrightPublicSymbolReport {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface PyrightJsonDiagnostic {
|
interface PyrightJsonDiagnostic {
|
||||||
file: string;
|
uri: Uri;
|
||||||
severity: SeverityLevel;
|
severity: SeverityLevel;
|
||||||
message: string;
|
message: string;
|
||||||
range?: Range | undefined;
|
range?: Range | undefined;
|
||||||
@ -244,10 +246,9 @@ async function processArgs(): Promise<ExitStatus> {
|
|||||||
|
|
||||||
// Verify the specified file specs to make sure their wildcard roots exist.
|
// Verify the specified file specs to make sure their wildcard roots exist.
|
||||||
const tempFileSystem = new PyrightFileSystem(createFromRealFileSystem());
|
const tempFileSystem = new PyrightFileSystem(createFromRealFileSystem());
|
||||||
const tempServiceProvider = createServiceProvider(tempFileSystem, console);
|
|
||||||
|
|
||||||
for (const fileDesc of options.includeFileSpecsOverride) {
|
for (const fileDesc of options.includeFileSpecsOverride) {
|
||||||
const includeSpec = getFileSpec(tempServiceProvider, '', fileDesc);
|
const includeSpec = getFileSpec(Uri.file(process.cwd(), tempFileSystem.isCaseSensitive), fileDesc);
|
||||||
try {
|
try {
|
||||||
const stat = tryStat(tempFileSystem, includeSpec.wildcardRoot);
|
const stat = tryStat(tempFileSystem, includeSpec.wildcardRoot);
|
||||||
if (!stat) {
|
if (!stat) {
|
||||||
@ -361,7 +362,7 @@ async function processArgs(): Promise<ExitStatus> {
|
|||||||
// up the JSON output, which goes to stdout.
|
// up the JSON output, which goes to stdout.
|
||||||
const output = args.outputjson ? new StderrConsole(logLevel) : new StandardConsole(logLevel);
|
const output = args.outputjson ? new StderrConsole(logLevel) : new StandardConsole(logLevel);
|
||||||
const fileSystem = new PyrightFileSystem(createFromRealFileSystem(output, new ChokidarFileWatcherProvider(output)));
|
const fileSystem = new PyrightFileSystem(createFromRealFileSystem(output, new ChokidarFileWatcherProvider(output)));
|
||||||
const tempFile = new RealTempFile();
|
const tempFile = new RealTempFile(fileSystem.isCaseSensitive);
|
||||||
const serviceProvider = createServiceProvider(fileSystem, output, tempFile);
|
const serviceProvider = createServiceProvider(fileSystem, output, tempFile);
|
||||||
|
|
||||||
// The package type verification uses a different path.
|
// The package type verification uses a different path.
|
||||||
@ -386,7 +387,7 @@ async function processArgs(): Promise<ExitStatus> {
|
|||||||
// Refresh service after 2 seconds after the last library file change is detected.
|
// Refresh service after 2 seconds after the last library file change is detected.
|
||||||
const service = new AnalyzerService('<default>', serviceProvider, {
|
const service = new AnalyzerService('<default>', serviceProvider, {
|
||||||
console: output,
|
console: output,
|
||||||
hostFactory: () => new FullAccessHost(fileSystem),
|
hostFactory: () => new FullAccessHost(serviceProvider),
|
||||||
libraryReanalysisTimeProvider: () => 2 * 1000,
|
libraryReanalysisTimeProvider: () => 2 * 1000,
|
||||||
});
|
});
|
||||||
const exitStatus = createDeferred<ExitStatus>();
|
const exitStatus = createDeferred<ExitStatus>();
|
||||||
@ -540,7 +541,7 @@ function buildTypeCompletenessReport(
|
|||||||
|
|
||||||
// Add the general diagnostics.
|
// Add the general diagnostics.
|
||||||
completenessReport.generalDiagnostics.forEach((diag) => {
|
completenessReport.generalDiagnostics.forEach((diag) => {
|
||||||
const jsonDiag = convertDiagnosticToJson('', diag);
|
const jsonDiag = convertDiagnosticToJson(Uri.empty(), diag);
|
||||||
if (isDiagnosticIncluded(jsonDiag.severity, minSeverityLevel)) {
|
if (isDiagnosticIncluded(jsonDiag.severity, minSeverityLevel)) {
|
||||||
report.generalDiagnostics.push(jsonDiag);
|
report.generalDiagnostics.push(jsonDiag);
|
||||||
}
|
}
|
||||||
@ -549,11 +550,11 @@ function buildTypeCompletenessReport(
|
|||||||
|
|
||||||
report.typeCompleteness = {
|
report.typeCompleteness = {
|
||||||
packageName,
|
packageName,
|
||||||
packageRootDirectory: completenessReport.packageRootDirectory,
|
packageRootDirectory: completenessReport.packageRootDirectoryUri,
|
||||||
moduleName: completenessReport.moduleName,
|
moduleName: completenessReport.moduleName,
|
||||||
moduleRootDirectory: completenessReport.moduleRootDirectory,
|
moduleRootDirectory: completenessReport.moduleRootDirectoryUri,
|
||||||
ignoreUnknownTypesFromImports: completenessReport.ignoreExternal,
|
ignoreUnknownTypesFromImports: completenessReport.ignoreExternal,
|
||||||
pyTypedPath: completenessReport.pyTypedPath,
|
pyTypedPath: completenessReport.pyTypedPathUri,
|
||||||
exportedSymbolCounts: {
|
exportedSymbolCounts: {
|
||||||
withKnownType: 0,
|
withKnownType: 0,
|
||||||
withAmbiguousType: 0,
|
withAmbiguousType: 0,
|
||||||
@ -587,7 +588,7 @@ function buildTypeCompletenessReport(
|
|||||||
|
|
||||||
// Convert and filter the diagnostics.
|
// Convert and filter the diagnostics.
|
||||||
symbol.diagnostics.forEach((diag) => {
|
symbol.diagnostics.forEach((diag) => {
|
||||||
const jsonDiag = convertDiagnosticToJson(diag.filePath, diag.diagnostic);
|
const jsonDiag = convertDiagnosticToJson(diag.uri, diag.diagnostic);
|
||||||
if (isDiagnosticIncluded(jsonDiag.severity, minSeverityLevel)) {
|
if (isDiagnosticIncluded(jsonDiag.severity, minSeverityLevel)) {
|
||||||
diagnostics.push(jsonDiag);
|
diagnostics.push(jsonDiag);
|
||||||
}
|
}
|
||||||
@ -811,7 +812,7 @@ function reportDiagnosticsAsJson(
|
|||||||
diag.category === DiagnosticCategory.Warning ||
|
diag.category === DiagnosticCategory.Warning ||
|
||||||
diag.category === DiagnosticCategory.Information
|
diag.category === DiagnosticCategory.Information
|
||||||
) {
|
) {
|
||||||
const jsonDiag = convertDiagnosticToJson(fileDiag.filePath, diag);
|
const jsonDiag = convertDiagnosticToJson(fileDiag.fileUri, diag);
|
||||||
if (isDiagnosticIncluded(jsonDiag.severity, minSeverityLevel)) {
|
if (isDiagnosticIncluded(jsonDiag.severity, minSeverityLevel)) {
|
||||||
report.generalDiagnostics.push(jsonDiag);
|
report.generalDiagnostics.push(jsonDiag);
|
||||||
}
|
}
|
||||||
@ -862,9 +863,9 @@ function convertDiagnosticCategoryToSeverity(category: DiagnosticCategory): Seve
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function convertDiagnosticToJson(filePath: string, diag: Diagnostic): PyrightJsonDiagnostic {
|
function convertDiagnosticToJson(uri: Uri, diag: Diagnostic): PyrightJsonDiagnostic {
|
||||||
return {
|
return {
|
||||||
file: filePath,
|
uri,
|
||||||
severity: convertDiagnosticCategoryToSeverity(diag.category),
|
severity: convertDiagnosticCategoryToSeverity(diag.category),
|
||||||
message: diag.message,
|
message: diag.message,
|
||||||
range: isEmptyRange(diag.range) ? undefined : diag.range,
|
range: isEmptyRange(diag.range) ? undefined : diag.range,
|
||||||
@ -891,9 +892,9 @@ function reportDiagnosticsAsText(
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (fileErrorsAndWarnings.length > 0) {
|
if (fileErrorsAndWarnings.length > 0) {
|
||||||
console.info(`${fileDiagnostics.filePath}`);
|
console.info(`${fileDiagnostics.fileUri.toUserVisibleString()}`);
|
||||||
fileErrorsAndWarnings.forEach((diag) => {
|
fileErrorsAndWarnings.forEach((diag) => {
|
||||||
const jsonDiag = convertDiagnosticToJson(fileDiagnostics.filePath, diag);
|
const jsonDiag = convertDiagnosticToJson(fileDiagnostics.fileUri, diag);
|
||||||
logDiagnosticToConsole(jsonDiag);
|
logDiagnosticToConsole(jsonDiag);
|
||||||
|
|
||||||
if (diag.category === DiagnosticCategory.Error) {
|
if (diag.category === DiagnosticCategory.Error) {
|
||||||
@ -923,8 +924,8 @@ function reportDiagnosticsAsText(
|
|||||||
|
|
||||||
function logDiagnosticToConsole(diag: PyrightJsonDiagnostic, prefix = ' ') {
|
function logDiagnosticToConsole(diag: PyrightJsonDiagnostic, prefix = ' ') {
|
||||||
let message = prefix;
|
let message = prefix;
|
||||||
if (diag.file) {
|
if (!diag.uri.isEmpty()) {
|
||||||
message += `${diag.file}:`;
|
message += `${diag.uri.toUserVisibleString()}:`;
|
||||||
}
|
}
|
||||||
if (diag.range && !isEmptyRange(diag.range)) {
|
if (diag.range && !isEmptyRange(diag.range)) {
|
||||||
message +=
|
message +=
|
||||||
|
@ -15,13 +15,14 @@ import { getPyTypedInfo } from './analyzer/pyTypedUtils';
|
|||||||
import { ExecutionEnvironment } from './common/configOptions';
|
import { ExecutionEnvironment } from './common/configOptions';
|
||||||
import { FileSystem, MkDirOptions } from './common/fileSystem';
|
import { FileSystem, MkDirOptions } from './common/fileSystem';
|
||||||
import { stubsSuffix } from './common/pathConsts';
|
import { stubsSuffix } from './common/pathConsts';
|
||||||
import { combinePaths, ensureTrailingDirectorySeparator, isDirectory, tryStat } from './common/pathUtils';
|
import { Uri } from './common/uri/uri';
|
||||||
|
import { isDirectory, tryStat } from './common/uri/uriUtils';
|
||||||
import { ReadOnlyAugmentedFileSystem } from './readonlyAugmentedFileSystem';
|
import { ReadOnlyAugmentedFileSystem } from './readonlyAugmentedFileSystem';
|
||||||
|
|
||||||
export interface SupportPartialStubs {
|
export interface SupportPartialStubs {
|
||||||
isPartialStubPackagesScanned(execEnv: ExecutionEnvironment): boolean;
|
isPartialStubPackagesScanned(execEnv: ExecutionEnvironment): boolean;
|
||||||
isPathScanned(path: string): boolean;
|
isPathScanned(path: Uri): boolean;
|
||||||
processPartialStubPackages(paths: string[], roots: string[], bundledStubPath?: string): void;
|
processPartialStubPackages(paths: Uri[], roots: Uri[], bundledStubPath?: Uri): void;
|
||||||
clearPartialStubs(): void;
|
clearPartialStubs(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,49 +50,45 @@ export class PyrightFileSystem extends ReadOnlyAugmentedFileSystem implements IP
|
|||||||
super(realFS);
|
super(realFS);
|
||||||
}
|
}
|
||||||
|
|
||||||
override mkdirSync(path: string, options?: MkDirOptions): void {
|
override mkdirSync(uri: Uri, options?: MkDirOptions): void {
|
||||||
this.realFS.mkdirSync(path, options);
|
this.realFS.mkdirSync(uri, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
override chdir(path: string): void {
|
override chdir(uri: Uri): void {
|
||||||
this.realFS.chdir(path);
|
this.realFS.chdir(uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
override writeFileSync(path: string, data: string | Buffer, encoding: BufferEncoding | null): void {
|
override writeFileSync(uri: Uri, data: string | Buffer, encoding: BufferEncoding | null): void {
|
||||||
this.realFS.writeFileSync(this.getOriginalPath(path), data, encoding);
|
this.realFS.writeFileSync(this.getOriginalPath(uri), data, encoding);
|
||||||
}
|
}
|
||||||
|
|
||||||
override rmdirSync(path: string): void {
|
override rmdirSync(uri: Uri): void {
|
||||||
this.realFS.rmdirSync(this.getOriginalPath(path));
|
this.realFS.rmdirSync(this.getOriginalPath(uri));
|
||||||
}
|
}
|
||||||
|
|
||||||
override unlinkSync(path: string): void {
|
override unlinkSync(uri: Uri): void {
|
||||||
this.realFS.unlinkSync(this.getOriginalPath(path));
|
this.realFS.unlinkSync(this.getOriginalPath(uri));
|
||||||
}
|
}
|
||||||
|
|
||||||
override createWriteStream(path: string): fs.WriteStream {
|
override createWriteStream(uri: Uri): fs.WriteStream {
|
||||||
return this.realFS.createWriteStream(this.getOriginalPath(path));
|
return this.realFS.createWriteStream(this.getOriginalPath(uri));
|
||||||
}
|
}
|
||||||
|
|
||||||
override copyFileSync(src: string, dst: string): void {
|
override copyFileSync(src: Uri, dst: Uri): void {
|
||||||
this.realFS.copyFileSync(this.getOriginalPath(src), this.getOriginalPath(dst));
|
this.realFS.copyFileSync(this.getOriginalPath(src), this.getOriginalPath(dst));
|
||||||
}
|
}
|
||||||
|
|
||||||
override getUri(originalPath: string): string {
|
|
||||||
return this.realFS.getUri(originalPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
isPartialStubPackagesScanned(execEnv: ExecutionEnvironment): boolean {
|
isPartialStubPackagesScanned(execEnv: ExecutionEnvironment): boolean {
|
||||||
return this.isPathScanned(execEnv.root ?? '');
|
return execEnv.root ? this.isPathScanned(execEnv.root) : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
isPathScanned(path: string): boolean {
|
isPathScanned(uri: Uri): boolean {
|
||||||
return this._rootSearched.has(path);
|
return this._rootSearched.has(uri.key);
|
||||||
}
|
}
|
||||||
|
|
||||||
processPartialStubPackages(paths: string[], roots: string[], bundledStubPath?: string) {
|
processPartialStubPackages(paths: Uri[], roots: Uri[], bundledStubPath?: Uri) {
|
||||||
for (const path of paths) {
|
for (const path of paths) {
|
||||||
this._rootSearched.add(path);
|
this._rootSearched.add(path.key);
|
||||||
|
|
||||||
if (!this.realFS.existsSync(path) || !isDirectory(this.realFS, path)) {
|
if (!this.realFS.existsSync(path) || !isDirectory(this.realFS, path)) {
|
||||||
continue;
|
continue;
|
||||||
@ -105,9 +102,9 @@ export class PyrightFileSystem extends ReadOnlyAugmentedFileSystem implements IP
|
|||||||
// Leave empty set of dir entries to process.
|
// Leave empty set of dir entries to process.
|
||||||
}
|
}
|
||||||
|
|
||||||
const isBundledStub = path === bundledStubPath;
|
const isBundledStub = path.equals(bundledStubPath);
|
||||||
for (const entry of dirEntries) {
|
for (const entry of dirEntries) {
|
||||||
const partialStubPackagePath = combinePaths(path, entry.name);
|
const partialStubPackagePath = path.combinePaths(entry.name);
|
||||||
const isDirectory = !entry.isSymbolicLink()
|
const isDirectory = !entry.isSymbolicLink()
|
||||||
? entry.isDirectory()
|
? entry.isDirectory()
|
||||||
: !!tryStat(this.realFS, partialStubPackagePath)?.isDirectory();
|
: !!tryStat(this.realFS, partialStubPackagePath)?.isDirectory();
|
||||||
@ -123,13 +120,13 @@ export class PyrightFileSystem extends ReadOnlyAugmentedFileSystem implements IP
|
|||||||
}
|
}
|
||||||
|
|
||||||
// We found partially typed stub-packages.
|
// We found partially typed stub-packages.
|
||||||
this._partialStubPackagePaths.add(partialStubPackagePath);
|
this._partialStubPackagePaths.add(partialStubPackagePath.key);
|
||||||
|
|
||||||
// Search the root to see whether we have matching package installed.
|
// Search the root to see whether we have matching package installed.
|
||||||
let partialStubs: string[] | undefined;
|
let partialStubs: string[] | undefined;
|
||||||
const packageName = entry.name.substr(0, entry.name.length - stubsSuffix.length);
|
const packageName = entry.name.substr(0, entry.name.length - stubsSuffix.length);
|
||||||
for (const root of roots) {
|
for (const root of roots) {
|
||||||
const packagePath = combinePaths(root, packageName);
|
const packagePath = root.combinePaths(packageName);
|
||||||
try {
|
try {
|
||||||
const stat = tryStat(this.realFS, packagePath);
|
const stat = tryStat(this.realFS, packagePath);
|
||||||
if (!stat?.isDirectory()) {
|
if (!stat?.isDirectory()) {
|
||||||
@ -149,8 +146,8 @@ export class PyrightFileSystem extends ReadOnlyAugmentedFileSystem implements IP
|
|||||||
// Merge partial stub packages to the library.
|
// Merge partial stub packages to the library.
|
||||||
partialStubs = partialStubs ?? this._getRelativePathPartialStubs(partialStubPackagePath);
|
partialStubs = partialStubs ?? this._getRelativePathPartialStubs(partialStubPackagePath);
|
||||||
for (const partialStub of partialStubs) {
|
for (const partialStub of partialStubs) {
|
||||||
const originalPyiFile = combinePaths(partialStubPackagePath, partialStub);
|
const originalPyiFile = partialStubPackagePath.combinePaths(partialStub);
|
||||||
const mappedPyiFile = combinePaths(packagePath, partialStub);
|
const mappedPyiFile = packagePath.combinePaths(partialStub);
|
||||||
this.recordMovedEntry(mappedPyiFile, originalPyiFile, packagePath);
|
this.recordMovedEntry(mappedPyiFile, originalPyiFile, packagePath);
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
@ -168,17 +165,15 @@ export class PyrightFileSystem extends ReadOnlyAugmentedFileSystem implements IP
|
|||||||
this._partialStubPackagePaths.clear();
|
this._partialStubPackagePaths.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override isMovedEntry(path: string) {
|
protected override isMovedEntry(uri: Uri) {
|
||||||
return this._partialStubPackagePaths.has(path) || super.isMovedEntry(path);
|
return this._partialStubPackagePaths.has(uri.key) || super.isMovedEntry(uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getRelativePathPartialStubs(path: string) {
|
private _getRelativePathPartialStubs(partialStubPath: Uri) {
|
||||||
const paths: string[] = [];
|
const relativePaths: string[] = [];
|
||||||
|
const searchAllStubs = (uri: Uri) => {
|
||||||
const partialStubPathLength = ensureTrailingDirectorySeparator(path).length;
|
for (const entry of this.realFS.readdirEntriesSync(uri)) {
|
||||||
const searchAllStubs = (path: string) => {
|
const filePath = uri.combinePaths(entry.name);
|
||||||
for (const entry of this.realFS.readdirEntriesSync(path)) {
|
|
||||||
const filePath = combinePaths(path, entry.name);
|
|
||||||
|
|
||||||
let isDirectory = entry.isDirectory();
|
let isDirectory = entry.isDirectory();
|
||||||
let isFile = entry.isFile();
|
let isFile = entry.isFile();
|
||||||
@ -195,15 +190,15 @@ export class PyrightFileSystem extends ReadOnlyAugmentedFileSystem implements IP
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isFile && entry.name.endsWith('.pyi')) {
|
if (isFile && entry.name.endsWith('.pyi')) {
|
||||||
const relative = filePath.substring(partialStubPathLength);
|
const relative = partialStubPath.getRelativePathComponents(filePath).join('/');
|
||||||
if (relative) {
|
if (relative) {
|
||||||
paths.push(relative);
|
relativePaths.push(relative);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
searchAllStubs(path);
|
searchAllStubs(partialStubPath);
|
||||||
return paths;
|
return relativePaths;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,57 +12,53 @@ import type * as fs from 'fs';
|
|||||||
import { appendArray, getOrAdd } from './common/collectionUtils';
|
import { appendArray, getOrAdd } from './common/collectionUtils';
|
||||||
import { FileSystem, MkDirOptions, Stats, VirtualDirent } from './common/fileSystem';
|
import { FileSystem, MkDirOptions, Stats, VirtualDirent } from './common/fileSystem';
|
||||||
import { FileWatcher, FileWatcherEventHandler } from './common/fileWatcher';
|
import { FileWatcher, FileWatcherEventHandler } from './common/fileWatcher';
|
||||||
import {
|
import { Uri } from './common/uri/uri';
|
||||||
combinePaths,
|
|
||||||
ensureTrailingDirectorySeparator,
|
|
||||||
getDirectoryPath,
|
|
||||||
getFileName,
|
|
||||||
getRelativePathComponentsFromDirectory,
|
|
||||||
} from './common/pathUtils';
|
|
||||||
|
|
||||||
export class ReadOnlyAugmentedFileSystem implements FileSystem {
|
export class ReadOnlyAugmentedFileSystem implements FileSystem {
|
||||||
// Mapped file to original file map
|
// Mapped file to original file map
|
||||||
private readonly _entryMap = new Map<string, string>();
|
private readonly _entryMap = new Map<string, Uri>();
|
||||||
|
|
||||||
// Original file to mapped file map
|
// Original file to mapped file map
|
||||||
private readonly _reverseEntryMap = new Map<string, string>();
|
private readonly _reverseEntryMap = new Map<string, Uri>();
|
||||||
|
|
||||||
// Mapped files per a containing folder map
|
// Mapped files per a containing folder map
|
||||||
private readonly _folderMap = new Map<string, { name: string; isFile: boolean }[]>();
|
private readonly _folderMap = new Map<string, { name: string; isFile: boolean }[]>();
|
||||||
|
|
||||||
constructor(protected realFS: FileSystem) {}
|
constructor(protected realFS: FileSystem) {}
|
||||||
|
|
||||||
existsSync(path: string): boolean {
|
get isCaseSensitive(): boolean {
|
||||||
if (this.isMovedEntry(path)) {
|
return this.realFS.isCaseSensitive;
|
||||||
|
}
|
||||||
|
|
||||||
|
existsSync(uri: Uri): boolean {
|
||||||
|
if (this.isMovedEntry(uri)) {
|
||||||
// Pretend partial stub folder and its files not exist
|
// Pretend partial stub folder and its files not exist
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.realFS.existsSync(this.getOriginalPath(path));
|
return this.realFS.existsSync(this.getOriginalPath(uri));
|
||||||
}
|
}
|
||||||
|
|
||||||
mkdirSync(path: string, options?: MkDirOptions): void {
|
mkdirSync(uri: Uri, options?: MkDirOptions): void {
|
||||||
throw new Error('Operation is not allowed.');
|
throw new Error('Operation is not allowed.');
|
||||||
}
|
}
|
||||||
|
|
||||||
chdir(path: string): void {
|
chdir(uri: Uri): void {
|
||||||
throw new Error('Operation is not allowed.');
|
throw new Error('Operation is not allowed.');
|
||||||
}
|
}
|
||||||
|
|
||||||
readdirEntriesSync(path: string): fs.Dirent[] {
|
readdirEntriesSync(uri: Uri): fs.Dirent[] {
|
||||||
const maybeDirectory = ensureTrailingDirectorySeparator(path);
|
|
||||||
|
|
||||||
const entries: fs.Dirent[] = [];
|
const entries: fs.Dirent[] = [];
|
||||||
const movedEntries = this._folderMap.get(maybeDirectory);
|
const movedEntries = this._folderMap.get(uri.key);
|
||||||
if (!movedEntries || this.realFS.existsSync(path)) {
|
if (!movedEntries || this.realFS.existsSync(uri)) {
|
||||||
appendArray(
|
appendArray(
|
||||||
entries,
|
entries,
|
||||||
this.realFS.readdirEntriesSync(path).filter((item) => {
|
this.realFS.readdirEntriesSync(uri).filter((item) => {
|
||||||
// Filter out the stub package directory and any
|
// Filter out the stub package directory and any
|
||||||
// entries that will be overwritten by stub package
|
// entries that will be overwritten by stub package
|
||||||
// virtual items.
|
// virtual items.
|
||||||
return (
|
return (
|
||||||
!this.isMovedEntry(combinePaths(path, item.name)) &&
|
!this.isMovedEntry(uri.combinePaths(item.name)) &&
|
||||||
!movedEntries?.some((movedEntry) => movedEntry.name === item.name)
|
!movedEntries?.some((movedEntry) => movedEntry.name === item.name)
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
@ -76,129 +72,124 @@ export class ReadOnlyAugmentedFileSystem implements FileSystem {
|
|||||||
return entries.concat(movedEntries.map((e) => new VirtualDirent(e.name, e.isFile)));
|
return entries.concat(movedEntries.map((e) => new VirtualDirent(e.name, e.isFile)));
|
||||||
}
|
}
|
||||||
|
|
||||||
readdirSync(path: string): string[] {
|
readdirSync(uri: Uri): string[] {
|
||||||
return this.readdirEntriesSync(path).map((p) => p.name);
|
return this.readdirEntriesSync(uri).map((p) => p.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
readFileSync(path: string, encoding?: null): Buffer;
|
readFileSync(uri: Uri, encoding?: null): Buffer;
|
||||||
readFileSync(path: string, encoding: BufferEncoding): string;
|
readFileSync(uri: Uri, encoding: BufferEncoding): string;
|
||||||
readFileSync(path: string, encoding?: BufferEncoding | null): string | Buffer {
|
readFileSync(uri: Uri, encoding?: BufferEncoding | null): string | Buffer {
|
||||||
return this.realFS.readFileSync(this.getOriginalPath(path), encoding);
|
return this.realFS.readFileSync(this.getOriginalPath(uri), encoding);
|
||||||
}
|
}
|
||||||
|
|
||||||
writeFileSync(path: string, data: string | Buffer, encoding: BufferEncoding | null): void {
|
writeFileSync(uri: Uri, data: string | Buffer, encoding: BufferEncoding | null): void {
|
||||||
throw new Error('Operation is not allowed.');
|
throw new Error('Operation is not allowed.');
|
||||||
}
|
}
|
||||||
|
|
||||||
statSync(path: string): Stats {
|
statSync(uri: Uri): Stats {
|
||||||
return this.realFS.statSync(this.getOriginalPath(path));
|
return this.realFS.statSync(this.getOriginalPath(uri));
|
||||||
}
|
}
|
||||||
|
|
||||||
rmdirSync(path: string): void {
|
rmdirSync(uri: Uri): void {
|
||||||
throw new Error('Operation is not allowed.');
|
throw new Error('Operation is not allowed.');
|
||||||
}
|
}
|
||||||
|
|
||||||
unlinkSync(path: string): void {
|
unlinkSync(uri: Uri): void {
|
||||||
throw new Error('Operation is not allowed.');
|
throw new Error('Operation is not allowed.');
|
||||||
}
|
}
|
||||||
|
|
||||||
realpathSync(path: string): string {
|
realpathSync(uri: Uri): Uri {
|
||||||
if (this._entryMap.has(path)) {
|
if (this._entryMap.has(uri.key)) {
|
||||||
return path;
|
return uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.realFS.realpathSync(path);
|
return this.realFS.realpathSync(uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
getModulePath(): string {
|
getModulePath(): Uri {
|
||||||
return this.realFS.getModulePath();
|
return this.realFS.getModulePath();
|
||||||
}
|
}
|
||||||
|
|
||||||
createFileSystemWatcher(paths: string[], listener: FileWatcherEventHandler): FileWatcher {
|
createFileSystemWatcher(paths: Uri[], listener: FileWatcherEventHandler): FileWatcher {
|
||||||
return this.realFS.createFileSystemWatcher(paths, listener);
|
return this.realFS.createFileSystemWatcher(paths, listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
createReadStream(path: string): fs.ReadStream {
|
createReadStream(uri: Uri): fs.ReadStream {
|
||||||
return this.realFS.createReadStream(this.getOriginalPath(path));
|
return this.realFS.createReadStream(this.getOriginalPath(uri));
|
||||||
}
|
}
|
||||||
|
|
||||||
createWriteStream(path: string): fs.WriteStream {
|
createWriteStream(uri: Uri): fs.WriteStream {
|
||||||
throw new Error('Operation is not allowed.');
|
throw new Error('Operation is not allowed.');
|
||||||
}
|
}
|
||||||
|
|
||||||
copyFileSync(src: string, dst: string): void {
|
copyFileSync(src: Uri, dst: Uri): void {
|
||||||
throw new Error('Operation is not allowed.');
|
throw new Error('Operation is not allowed.');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Async I/O
|
// Async I/O
|
||||||
readFile(path: string): Promise<Buffer> {
|
readFile(uri: Uri): Promise<Buffer> {
|
||||||
return this.realFS.readFile(this.getOriginalPath(path));
|
return this.realFS.readFile(this.getOriginalPath(uri));
|
||||||
}
|
}
|
||||||
|
|
||||||
readFileText(path: string, encoding?: BufferEncoding): Promise<string> {
|
readFileText(uri: Uri, encoding?: BufferEncoding): Promise<string> {
|
||||||
return this.realFS.readFileText(this.getOriginalPath(path), encoding);
|
return this.realFS.readFileText(this.getOriginalPath(uri), encoding);
|
||||||
}
|
}
|
||||||
|
|
||||||
realCasePath(path: string): string {
|
realCasePath(uri: Uri): Uri {
|
||||||
return this.realFS.realCasePath(path);
|
return this.realFS.realCasePath(uri);
|
||||||
}
|
|
||||||
|
|
||||||
getUri(originalPath: string): string {
|
|
||||||
return this.realFS.getUri(originalPath);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// See whether the file is mapped to another location.
|
// See whether the file is mapped to another location.
|
||||||
isMappedFilePath(filepath: string): boolean {
|
isMappedUri(fileUri: Uri): boolean {
|
||||||
return this._entryMap.has(filepath) || this.realFS.isMappedFilePath(filepath);
|
return this._entryMap.has(fileUri.key) || this.realFS.isMappedUri(fileUri);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get original filepath if the given filepath is mapped.
|
// Get original filepath if the given filepath is mapped.
|
||||||
getOriginalFilePath(mappedFilePath: string) {
|
getOriginalUri(mappedFileUri: Uri) {
|
||||||
return this.realFS.getOriginalFilePath(this.getOriginalPath(mappedFilePath));
|
return this.realFS.getOriginalUri(this.getOriginalPath(mappedFileUri));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get mapped filepath if the given filepath is mapped.
|
// Get mapped filepath if the given filepath is mapped.
|
||||||
getMappedFilePath(originalFilepath: string) {
|
getMappedUri(originalFileUri: Uri) {
|
||||||
const mappedFilePath = this.realFS.getMappedFilePath(originalFilepath);
|
const mappedFileUri = this.realFS.getMappedUri(originalFileUri);
|
||||||
return this._reverseEntryMap.get(mappedFilePath) ?? mappedFilePath;
|
return this._reverseEntryMap.get(mappedFileUri.key) ?? mappedFileUri;
|
||||||
}
|
}
|
||||||
|
|
||||||
isInZip(path: string): boolean {
|
isInZip(uri: Uri): boolean {
|
||||||
return this.realFS.isInZip(path);
|
return this.realFS.isInZip(uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected recordMovedEntry(mappedPath: string, originalPath: string, rootPath: string) {
|
protected recordMovedEntry(mappedUri: Uri, originalUri: Uri, rootPath: Uri) {
|
||||||
this._entryMap.set(mappedPath, originalPath);
|
this._entryMap.set(mappedUri.key, originalUri);
|
||||||
this._reverseEntryMap.set(originalPath, mappedPath);
|
this._reverseEntryMap.set(originalUri.key, mappedUri);
|
||||||
|
|
||||||
const directory = ensureTrailingDirectorySeparator(getDirectoryPath(mappedPath));
|
const directory = mappedUri.getDirectory();
|
||||||
const folderInfo = getOrAdd(this._folderMap, directory, () => []);
|
const folderInfo = getOrAdd(this._folderMap, directory.key, () => []);
|
||||||
|
|
||||||
const name = getFileName(mappedPath);
|
const name = mappedUri.fileName;
|
||||||
if (!folderInfo.some((entry) => entry.name === name)) {
|
if (!folderInfo.some((entry) => entry.name === name)) {
|
||||||
folderInfo.push({ name, isFile: true });
|
folderInfo.push({ name, isFile: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the directory entries for the sub paths as well. We should ignoreCase here because
|
// Add the directory entries for the sub paths as well.
|
||||||
// the paths are just combining of already known paths.
|
const subPathEntries = rootPath.getRelativePathComponents(directory);
|
||||||
const subPathEntries = getRelativePathComponentsFromDirectory(rootPath, directory, /* ignoreCase */ false);
|
for (let i = 0; i < subPathEntries.length; i++) {
|
||||||
for (let i = 1; i < subPathEntries.length; i++) {
|
const subdir = rootPath.combinePaths(...subPathEntries.slice(0, i + 1));
|
||||||
const subdir = combinePaths(rootPath, ...subPathEntries.slice(1, i + 1));
|
const parent = subdir.getDirectory().key;
|
||||||
const parent = ensureTrailingDirectorySeparator(getDirectoryPath(subdir));
|
|
||||||
const dirInfo = getOrAdd(this._folderMap, parent, () => []);
|
const dirInfo = getOrAdd(this._folderMap, parent, () => []);
|
||||||
const dirName = getFileName(subdir);
|
const dirName = subdir.fileName;
|
||||||
if (!dirInfo.some((entry) => entry.name === dirName)) {
|
if (!dirInfo.some((entry) => entry.name === dirName)) {
|
||||||
dirInfo.push({ name: dirName, isFile: false });
|
dirInfo.push({ name: dirName, isFile: false });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected getOriginalPath(mappedFilePath: string) {
|
protected getOriginalPath(mappedFileUri: Uri) {
|
||||||
return this._entryMap.get(mappedFilePath) ?? mappedFilePath;
|
return this._entryMap.get(mappedFileUri.key) ?? mappedFileUri;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected isMovedEntry(path: string) {
|
protected isMovedEntry(uri: Uri) {
|
||||||
return this._reverseEntryMap.has(path);
|
return this._reverseEntryMap.has(uri.key);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected clear() {
|
protected clear() {
|
||||||
|
@ -30,11 +30,12 @@ import { expandPathVariables } from './common/envVarUtils';
|
|||||||
import { FileBasedCancellationProvider } from './common/fileBasedCancellationUtils';
|
import { FileBasedCancellationProvider } from './common/fileBasedCancellationUtils';
|
||||||
import { FullAccessHost } from './common/fullAccessHost';
|
import { FullAccessHost } from './common/fullAccessHost';
|
||||||
import { Host } from './common/host';
|
import { Host } from './common/host';
|
||||||
import { realCasePath, resolvePaths } from './common/pathUtils';
|
|
||||||
import { ProgressReporter } from './common/progressReporter';
|
import { ProgressReporter } from './common/progressReporter';
|
||||||
import { RealTempFile, WorkspaceFileWatcherProvider, createFromRealFileSystem } from './common/realFileSystem';
|
import { RealTempFile, WorkspaceFileWatcherProvider, createFromRealFileSystem } from './common/realFileSystem';
|
||||||
import { ServiceProvider } from './common/serviceProvider';
|
import { ServiceProvider } from './common/serviceProvider';
|
||||||
import { createServiceProvider } from './common/serviceProviderExtensions';
|
import { createServiceProvider } from './common/serviceProviderExtensions';
|
||||||
|
import { Uri } from './common/uri/uri';
|
||||||
|
import { getRootUri } from './common/uri/uriUtils';
|
||||||
import { LanguageServerBase, ServerSettings } from './languageServerBase';
|
import { LanguageServerBase, ServerSettings } from './languageServerBase';
|
||||||
import { CodeActionProvider } from './languageService/codeActionProvider';
|
import { CodeActionProvider } from './languageService/codeActionProvider';
|
||||||
import { PyrightFileSystem } from './pyrightFileSystem';
|
import { PyrightFileSystem } from './pyrightFileSystem';
|
||||||
@ -49,20 +50,21 @@ export class PyrightServer extends LanguageServerBase {
|
|||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
const version = require('../package.json').version || '';
|
const version = require('../package.json').version || '';
|
||||||
|
|
||||||
// When executed from CLI command (pyright-langserver), __rootDirectory is
|
|
||||||
// already defined. When executed from VSCode extension, rootDirectory should
|
|
||||||
// be __dirname.
|
|
||||||
const rootDirectory = (global as any).__rootDirectory || __dirname;
|
|
||||||
|
|
||||||
const console = new ConsoleWithLogLevel(connection.console);
|
const console = new ConsoleWithLogLevel(connection.console);
|
||||||
const fileWatcherProvider = new WorkspaceFileWatcherProvider();
|
const fileWatcherProvider = new WorkspaceFileWatcherProvider();
|
||||||
const fileSystem = createFromRealFileSystem(console, fileWatcherProvider);
|
const fileSystem = createFromRealFileSystem(console, fileWatcherProvider);
|
||||||
const pyrightFs = new PyrightFileSystem(fileSystem);
|
const pyrightFs = new PyrightFileSystem(fileSystem);
|
||||||
const tempFile = new RealTempFile();
|
const tempFile = new RealTempFile(pyrightFs.isCaseSensitive);
|
||||||
const cacheManager = new CacheManager();
|
const cacheManager = new CacheManager();
|
||||||
|
|
||||||
const serviceProvider = createServiceProvider(pyrightFs, tempFile, console, cacheManager);
|
const serviceProvider = createServiceProvider(pyrightFs, tempFile, console, cacheManager);
|
||||||
const realPathRoot = realCasePath(rootDirectory, pyrightFs);
|
|
||||||
|
// When executed from CLI command (pyright-langserver), __rootDirectory is
|
||||||
|
// already defined. When executed from VSCode extension, rootDirectory should
|
||||||
|
// be __dirname.
|
||||||
|
const rootDirectory: Uri =
|
||||||
|
getRootUri(pyrightFs.isCaseSensitive) || Uri.file(__dirname, pyrightFs.isCaseSensitive);
|
||||||
|
const realPathRoot = pyrightFs.realCasePath(rootDirectory);
|
||||||
|
|
||||||
super(
|
super(
|
||||||
{
|
{
|
||||||
@ -98,44 +100,40 @@ export class PyrightServer extends LanguageServerBase {
|
|||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const pythonSection = await this.getConfiguration(workspace.uri, 'python');
|
const pythonSection = await this.getConfiguration(workspace.rootUri, 'python');
|
||||||
if (pythonSection) {
|
if (pythonSection) {
|
||||||
const pythonPath = pythonSection.pythonPath;
|
const pythonPath = pythonSection.pythonPath;
|
||||||
if (pythonPath && isString(pythonPath) && !isPythonBinary(pythonPath)) {
|
if (pythonPath && isString(pythonPath) && !isPythonBinary(pythonPath)) {
|
||||||
serverSettings.pythonPath = resolvePaths(
|
serverSettings.pythonPath = workspace.rootUri.combinePaths(
|
||||||
workspace.rootPath,
|
expandPathVariables(workspace.rootUri, pythonPath)
|
||||||
expandPathVariables(workspace.rootPath, pythonPath)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const venvPath = pythonSection.venvPath;
|
const venvPath = pythonSection.venvPath;
|
||||||
|
|
||||||
if (venvPath && isString(venvPath)) {
|
if (venvPath && isString(venvPath)) {
|
||||||
serverSettings.venvPath = resolvePaths(
|
serverSettings.venvPath = workspace.rootUri.combinePaths(
|
||||||
workspace.rootPath,
|
expandPathVariables(workspace.rootUri, venvPath)
|
||||||
expandPathVariables(workspace.rootPath, venvPath)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const pythonAnalysisSection = await this.getConfiguration(workspace.uri, 'python.analysis');
|
const pythonAnalysisSection = await this.getConfiguration(workspace.rootUri, 'python.analysis');
|
||||||
if (pythonAnalysisSection) {
|
if (pythonAnalysisSection) {
|
||||||
const typeshedPaths = pythonAnalysisSection.typeshedPaths;
|
const typeshedPaths = pythonAnalysisSection.typeshedPaths;
|
||||||
if (typeshedPaths && Array.isArray(typeshedPaths) && typeshedPaths.length > 0) {
|
if (typeshedPaths && Array.isArray(typeshedPaths) && typeshedPaths.length > 0) {
|
||||||
const typeshedPath = typeshedPaths[0];
|
const typeshedPath = typeshedPaths[0];
|
||||||
if (typeshedPath && isString(typeshedPath)) {
|
if (typeshedPath && isString(typeshedPath)) {
|
||||||
serverSettings.typeshedPath = resolvePaths(
|
serverSettings.typeshedPath = workspace.rootUri.combinePaths(
|
||||||
workspace.rootPath,
|
expandPathVariables(workspace.rootUri, typeshedPath)
|
||||||
expandPathVariables(workspace.rootPath, typeshedPath)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const stubPath = pythonAnalysisSection.stubPath;
|
const stubPath = pythonAnalysisSection.stubPath;
|
||||||
if (stubPath && isString(stubPath)) {
|
if (stubPath && isString(stubPath)) {
|
||||||
serverSettings.stubPath = resolvePaths(
|
serverSettings.stubPath = workspace.rootUri.combinePaths(
|
||||||
workspace.rootPath,
|
expandPathVariables(workspace.rootUri, stubPath)
|
||||||
expandPathVariables(workspace.rootPath, stubPath)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,7 +165,7 @@ export class PyrightServer extends LanguageServerBase {
|
|||||||
if (extraPaths && Array.isArray(extraPaths) && extraPaths.length > 0) {
|
if (extraPaths && Array.isArray(extraPaths) && extraPaths.length > 0) {
|
||||||
serverSettings.extraPaths = extraPaths
|
serverSettings.extraPaths = extraPaths
|
||||||
.filter((p) => p && isString(p))
|
.filter((p) => p && isString(p))
|
||||||
.map((p) => resolvePaths(workspace.rootPath, expandPathVariables(workspace.rootPath, p)));
|
.map((p) => workspace.rootUri.combinePaths(expandPathVariables(workspace.rootUri, p)));
|
||||||
}
|
}
|
||||||
|
|
||||||
serverSettings.includeFileSpecs = this._getStringValues(pythonAnalysisSection.include);
|
serverSettings.includeFileSpecs = this._getStringValues(pythonAnalysisSection.include);
|
||||||
@ -196,7 +194,7 @@ export class PyrightServer extends LanguageServerBase {
|
|||||||
serverSettings.autoSearchPaths = true;
|
serverSettings.autoSearchPaths = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const pyrightSection = await this.getConfiguration(workspace.uri, 'pyright');
|
const pyrightSection = await this.getConfiguration(workspace.rootUri, 'pyright');
|
||||||
if (pyrightSection) {
|
if (pyrightSection) {
|
||||||
if (pyrightSection.openFilesOnly !== undefined) {
|
if (pyrightSection.openFilesOnly !== undefined) {
|
||||||
serverSettings.openFilesOnly = !!pyrightSection.openFilesOnly;
|
serverSettings.openFilesOnly = !!pyrightSection.openFilesOnly;
|
||||||
@ -227,11 +225,11 @@ export class PyrightServer extends LanguageServerBase {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new BackgroundAnalysis(this.console);
|
return new BackgroundAnalysis(this.serverOptions.serviceProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override createHost() {
|
protected override createHost() {
|
||||||
return new FullAccessHost(this.fs);
|
return new FullAccessHost(this.serverOptions.serviceProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override createImportResolver(
|
protected override createImportResolver(
|
||||||
@ -262,15 +260,9 @@ export class PyrightServer extends LanguageServerBase {
|
|||||||
): Promise<(Command | CodeAction)[] | undefined | null> {
|
): Promise<(Command | CodeAction)[] | undefined | null> {
|
||||||
this.recordUserInteractionTime();
|
this.recordUserInteractionTime();
|
||||||
|
|
||||||
const filePath = this.uriParser.decodeTextDocumentUri(params.textDocument.uri);
|
const uri = Uri.parse(params.textDocument.uri, this.serverOptions.serviceProvider.fs().isCaseSensitive);
|
||||||
const workspace = await this.getWorkspaceForFile(filePath);
|
const workspace = await this.getWorkspaceForFile(uri);
|
||||||
return CodeActionProvider.getCodeActionsForPosition(
|
return CodeActionProvider.getCodeActionsForPosition(workspace, uri, params.range, params.context.only, token);
|
||||||
workspace,
|
|
||||||
filePath,
|
|
||||||
params.range,
|
|
||||||
params.context.only,
|
|
||||||
token
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected createProgressReporter(): ProgressReporter {
|
protected createProgressReporter(): ProgressReporter {
|
||||||
|
@ -17,13 +17,14 @@ import { ConfigOptions } from '../common/configOptions';
|
|||||||
import { NullConsole } from '../common/console';
|
import { NullConsole } from '../common/console';
|
||||||
import { normalizeSlashes } from '../common/pathUtils';
|
import { normalizeSlashes } from '../common/pathUtils';
|
||||||
import { convertOffsetsToRange, convertOffsetToPosition } from '../common/positionUtils';
|
import { convertOffsetsToRange, convertOffsetToPosition } from '../common/positionUtils';
|
||||||
|
import { ServiceProvider } from '../common/serviceProvider';
|
||||||
|
import { Uri } from '../common/uri/uri';
|
||||||
|
import { CompletionProvider } from '../languageService/completionProvider';
|
||||||
import { parseTestData } from './harness/fourslash/fourSlashParser';
|
import { parseTestData } from './harness/fourslash/fourSlashParser';
|
||||||
import { TestAccessHost } from './harness/testAccessHost';
|
import { TestAccessHost } from './harness/testAccessHost';
|
||||||
import * as host from './harness/testHost';
|
import * as host from './harness/testHost';
|
||||||
import { createFromFileSystem, distlibFolder, libFolder } from './harness/vfs/factory';
|
import { createFromFileSystem, distlibFolder, libFolder } from './harness/vfs/factory';
|
||||||
import * as vfs from './harness/vfs/filesystem';
|
import * as vfs from './harness/vfs/filesystem';
|
||||||
import { CompletionProvider } from '../languageService/completionProvider';
|
|
||||||
import { ServiceProvider } from '../common/serviceProvider';
|
|
||||||
|
|
||||||
test('check chained files', () => {
|
test('check chained files', () => {
|
||||||
const code = `
|
const code = `
|
||||||
@ -40,16 +41,17 @@ test('check chained files', () => {
|
|||||||
//// [|foo/*marker*/|]
|
//// [|foo/*marker*/|]
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const basePath = normalizeSlashes('/');
|
const basePath = Uri.file(normalizeSlashes('/'));
|
||||||
const { data, service } = createServiceWithChainedSourceFiles(basePath, code);
|
const { data, service } = createServiceWithChainedSourceFiles(basePath, code);
|
||||||
|
|
||||||
const marker = data.markerPositions.get('marker')!;
|
const marker = data.markerPositions.get('marker')!;
|
||||||
|
const markerUri = Uri.file(marker.fileName);
|
||||||
|
|
||||||
const parseResult = service.getParseResult(marker.fileName)!;
|
const parseResult = service.getParseResult(markerUri)!;
|
||||||
const result = new CompletionProvider(
|
const result = new CompletionProvider(
|
||||||
service.test_program,
|
service.test_program,
|
||||||
basePath,
|
basePath,
|
||||||
marker.fileName,
|
markerUri,
|
||||||
convertOffsetToPosition(marker.position, parseResult.tokenizerOutput.lines),
|
convertOffsetToPosition(marker.position, parseResult.tokenizerOutput.lines),
|
||||||
{
|
{
|
||||||
format: MarkupKind.Markdown,
|
format: MarkupKind.Markdown,
|
||||||
@ -80,15 +82,16 @@ test('modify chained files', () => {
|
|||||||
//// [|foo/*marker*/|]
|
//// [|foo/*marker*/|]
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const basePath = normalizeSlashes('/');
|
const basePath = Uri.file(normalizeSlashes('/'));
|
||||||
const { data, service } = createServiceWithChainedSourceFiles(basePath, code);
|
const { data, service } = createServiceWithChainedSourceFiles(basePath, code);
|
||||||
|
|
||||||
// Make sure files are all realized.
|
// Make sure files are all realized.
|
||||||
const marker = data.markerPositions.get('marker')!;
|
const marker = data.markerPositions.get('marker')!;
|
||||||
const parseResult = service.getParseResult(marker.fileName)!;
|
const markerUri = Uri.file(marker.fileName);
|
||||||
|
const parseResult = service.getParseResult(markerUri)!;
|
||||||
|
|
||||||
// Close file in the middle of the chain
|
// Close file in the middle of the chain
|
||||||
service.setFileClosed(data.markerPositions.get('delete')!.fileName);
|
service.setFileClosed(Uri.file(data.markerPositions.get('delete')!.fileName));
|
||||||
|
|
||||||
// Make sure we don't get suggestion from auto import but from chained files.
|
// Make sure we don't get suggestion from auto import but from chained files.
|
||||||
service.test_program.configOptions.autoImportCompletions = false;
|
service.test_program.configOptions.autoImportCompletions = false;
|
||||||
@ -96,7 +99,7 @@ test('modify chained files', () => {
|
|||||||
const result = new CompletionProvider(
|
const result = new CompletionProvider(
|
||||||
service.test_program,
|
service.test_program,
|
||||||
basePath,
|
basePath,
|
||||||
marker.fileName,
|
markerUri,
|
||||||
convertOffsetToPosition(marker.position, parseResult.tokenizerOutput.lines),
|
convertOffsetToPosition(marker.position, parseResult.tokenizerOutput.lines),
|
||||||
{
|
{
|
||||||
format: MarkupKind.Markdown,
|
format: MarkupKind.Markdown,
|
||||||
@ -129,18 +132,19 @@ test('modify chained files', async () => {
|
|||||||
//// [|/*marker*/foo1()|]
|
//// [|/*marker*/foo1()|]
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const basePath = normalizeSlashes('/');
|
const basePath = Uri.file(normalizeSlashes('/'));
|
||||||
const { data, service } = createServiceWithChainedSourceFiles(basePath, code);
|
const { data, service } = createServiceWithChainedSourceFiles(basePath, code);
|
||||||
|
|
||||||
const marker = data.markerPositions.get('marker')!;
|
const marker = data.markerPositions.get('marker')!;
|
||||||
|
const markerUri = Uri.file(marker.fileName);
|
||||||
const range = data.ranges.find((r) => r.marker === marker)!;
|
const range = data.ranges.find((r) => r.marker === marker)!;
|
||||||
|
|
||||||
const parseResults = service.getParseResult(marker.fileName)!;
|
const parseResults = service.getParseResult(markerUri)!;
|
||||||
analyze(service.test_program);
|
analyze(service.test_program);
|
||||||
|
|
||||||
// Initially, there should be no error.
|
// Initially, there should be no error.
|
||||||
const initialDiags = await service.getDiagnosticsForRange(
|
const initialDiags = await service.getDiagnosticsForRange(
|
||||||
marker.fileName,
|
markerUri,
|
||||||
convertOffsetsToRange(range.pos, range.end, parseResults.tokenizerOutput.lines),
|
convertOffsetsToRange(range.pos, range.end, parseResults.tokenizerOutput.lines),
|
||||||
CancellationToken.None
|
CancellationToken.None
|
||||||
);
|
);
|
||||||
@ -148,11 +152,11 @@ test('modify chained files', async () => {
|
|||||||
assert.strictEqual(initialDiags.length, 0);
|
assert.strictEqual(initialDiags.length, 0);
|
||||||
|
|
||||||
// Change test1 content
|
// Change test1 content
|
||||||
service.updateOpenFileContents(data.markerPositions.get('changed')!.fileName, 2, 'def foo5(): pass');
|
service.updateOpenFileContents(Uri.file(data.markerPositions.get('changed')!.fileName), 2, 'def foo5(): pass');
|
||||||
analyze(service.test_program);
|
analyze(service.test_program);
|
||||||
|
|
||||||
const finalDiags = await service.getDiagnosticsForRange(
|
const finalDiags = await service.getDiagnosticsForRange(
|
||||||
marker.fileName,
|
markerUri,
|
||||||
convertOffsetsToRange(range.pos, range.end, parseResults.tokenizerOutput.lines),
|
convertOffsetsToRange(range.pos, range.end, parseResults.tokenizerOutput.lines),
|
||||||
CancellationToken.None
|
CancellationToken.None
|
||||||
);
|
);
|
||||||
@ -178,17 +182,18 @@ test('chained files with 1000s of files', async () => {
|
|||||||
//// [|/*marker*/foo1()|]
|
//// [|/*marker*/foo1()|]
|
||||||
`;
|
`;
|
||||||
const code = generateChainedFiles(1000, lastFile);
|
const code = generateChainedFiles(1000, lastFile);
|
||||||
const basePath = normalizeSlashes('/');
|
const basePath = Uri.file(normalizeSlashes('/'));
|
||||||
const { data, service } = createServiceWithChainedSourceFiles(basePath, code);
|
const { data, service } = createServiceWithChainedSourceFiles(basePath, code);
|
||||||
const marker = data.markerPositions.get('marker')!;
|
const marker = data.markerPositions.get('marker')!;
|
||||||
|
const markerUri = Uri.file(marker.fileName);
|
||||||
const range = data.ranges.find((r) => r.marker === marker)!;
|
const range = data.ranges.find((r) => r.marker === marker)!;
|
||||||
|
|
||||||
const parseResults = service.getParseResult(marker.fileName)!;
|
const parseResults = service.getParseResult(markerUri)!;
|
||||||
analyze(service.test_program);
|
analyze(service.test_program);
|
||||||
|
|
||||||
// There should be no error as it should find the foo1 in the first chained file.
|
// There should be no error as it should find the foo1 in the first chained file.
|
||||||
const initialDiags = await service.getDiagnosticsForRange(
|
const initialDiags = await service.getDiagnosticsForRange(
|
||||||
marker.fileName,
|
markerUri,
|
||||||
convertOffsetsToRange(range.pos, range.end, parseResults.tokenizerOutput.lines),
|
convertOffsetsToRange(range.pos, range.end, parseResults.tokenizerOutput.lines),
|
||||||
CancellationToken.None
|
CancellationToken.None
|
||||||
);
|
);
|
||||||
@ -205,16 +210,17 @@ test('imported by files', async () => {
|
|||||||
//// os.path.join()
|
//// os.path.join()
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const basePath = normalizeSlashes('/');
|
const basePath = Uri.file(normalizeSlashes('/'));
|
||||||
const { data, service } = createServiceWithChainedSourceFiles(basePath, code);
|
const { data, service } = createServiceWithChainedSourceFiles(basePath, code);
|
||||||
analyze(service.test_program);
|
analyze(service.test_program);
|
||||||
|
|
||||||
const marker = data.markerPositions.get('marker')!;
|
const marker = data.markerPositions.get('marker')!;
|
||||||
|
const markerUri = Uri.file(marker.fileName);
|
||||||
const range = data.ranges.find((r) => r.marker === marker)!;
|
const range = data.ranges.find((r) => r.marker === marker)!;
|
||||||
|
|
||||||
const parseResults = service.getParseResult(marker.fileName)!;
|
const parseResults = service.getParseResult(markerUri)!;
|
||||||
const diagnostics = await service.getDiagnosticsForRange(
|
const diagnostics = await service.getDiagnosticsForRange(
|
||||||
marker.fileName,
|
markerUri,
|
||||||
convertOffsetsToRange(range.pos, range.end, parseResults.tokenizerOutput.lines),
|
convertOffsetsToRange(range.pos, range.end, parseResults.tokenizerOutput.lines),
|
||||||
CancellationToken.None
|
CancellationToken.None
|
||||||
);
|
);
|
||||||
@ -231,22 +237,24 @@ test('re ordering cells', async () => {
|
|||||||
//// /*bottom*/os.path.join()
|
//// /*bottom*/os.path.join()
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const basePath = normalizeSlashes('/');
|
const basePath = Uri.file(normalizeSlashes('/'));
|
||||||
const { data, service } = createServiceWithChainedSourceFiles(basePath, code);
|
const { data, service } = createServiceWithChainedSourceFiles(basePath, code);
|
||||||
analyze(service.test_program);
|
analyze(service.test_program);
|
||||||
|
|
||||||
const marker = data.markerPositions.get('marker')!;
|
const marker = data.markerPositions.get('marker')!;
|
||||||
|
const markerUri = Uri.file(marker.fileName);
|
||||||
const range = data.ranges.find((r) => r.marker === marker)!;
|
const range = data.ranges.find((r) => r.marker === marker)!;
|
||||||
|
|
||||||
const bottom = data.markerPositions.get('bottom')!;
|
const bottom = data.markerPositions.get('bottom')!;
|
||||||
|
const bottomUri = Uri.file(bottom.fileName);
|
||||||
|
|
||||||
service.updateChainedFilePath(bottom.fileName, undefined);
|
service.updateChainedUri(bottomUri, undefined);
|
||||||
service.updateChainedFilePath(marker.fileName, bottom.fileName);
|
service.updateChainedUri(markerUri, bottomUri);
|
||||||
analyze(service.test_program);
|
analyze(service.test_program);
|
||||||
|
|
||||||
const parseResults = service.getParseResult(marker.fileName)!;
|
const parseResults = service.getParseResult(markerUri)!;
|
||||||
const diagnostics = await service.getDiagnosticsForRange(
|
const diagnostics = await service.getDiagnosticsForRange(
|
||||||
marker.fileName,
|
markerUri,
|
||||||
convertOffsetsToRange(range.pos, range.end, parseResults.tokenizerOutput.lines),
|
convertOffsetsToRange(range.pos, range.end, parseResults.tokenizerOutput.lines),
|
||||||
CancellationToken.None
|
CancellationToken.None
|
||||||
);
|
);
|
||||||
@ -254,22 +262,23 @@ test('re ordering cells', async () => {
|
|||||||
assert.strictEqual(diagnostics.length, 1);
|
assert.strictEqual(diagnostics.length, 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
function createServiceWithChainedSourceFiles(basePath: string, code: string) {
|
function createServiceWithChainedSourceFiles(basePath: Uri, code: string) {
|
||||||
const fs = createFromFileSystem(host.HOST, /*ignoreCase*/ false, { cwd: basePath });
|
const fs = createFromFileSystem(host.HOST, /*ignoreCase*/ false, { cwd: basePath.getFilePath() });
|
||||||
const service = new AnalyzerService('test service', new ServiceProvider(), {
|
const service = new AnalyzerService('test service', new ServiceProvider(), {
|
||||||
console: new NullConsole(),
|
console: new NullConsole(),
|
||||||
hostFactory: () => new TestAccessHost(vfs.MODULE_PATH, [libFolder, distlibFolder]),
|
hostFactory: () => new TestAccessHost(Uri.file(vfs.MODULE_PATH), [libFolder, distlibFolder]),
|
||||||
importResolverFactory: AnalyzerService.createImportResolver,
|
importResolverFactory: AnalyzerService.createImportResolver,
|
||||||
configOptions: new ConfigOptions(basePath),
|
configOptions: new ConfigOptions(basePath),
|
||||||
fileSystem: fs,
|
fileSystem: fs,
|
||||||
});
|
});
|
||||||
|
|
||||||
const data = parseTestData(basePath, code, '');
|
const data = parseTestData(basePath.getFilePath(), code, '');
|
||||||
|
|
||||||
let chainedFilePath: string | undefined;
|
let chainedFilePath: Uri | undefined;
|
||||||
for (const file of data.files) {
|
for (const file of data.files) {
|
||||||
service.setFileOpened(file.fileName, 1, file.content, IPythonMode.CellDocs, chainedFilePath);
|
const uri = Uri.file(file.fileName);
|
||||||
chainedFilePath = file.fileName;
|
service.setFileOpened(uri, 1, file.content, IPythonMode.CellDocs, chainedFilePath);
|
||||||
|
chainedFilePath = uri;
|
||||||
}
|
}
|
||||||
return { data, service };
|
return { data, service };
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
import { ConfigOptions } from '../common/configOptions';
|
import { ConfigOptions } from '../common/configOptions';
|
||||||
import { PythonVersion } from '../common/pythonVersion';
|
import { PythonVersion } from '../common/pythonVersion';
|
||||||
|
import { Uri } from '../common/uri/uri';
|
||||||
import * as TestUtils from './testUtils';
|
import * as TestUtils from './testUtils';
|
||||||
|
|
||||||
test('BadToken1', () => {
|
test('BadToken1', () => {
|
||||||
@ -34,7 +35,7 @@ test('CircularBaseClass', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('Private1', () => {
|
test('Private1', () => {
|
||||||
const configOptions = new ConfigOptions('.');
|
const configOptions = new ConfigOptions(Uri.empty());
|
||||||
|
|
||||||
// By default, optional diagnostics are ignored.
|
// By default, optional diagnostics are ignored.
|
||||||
let analysisResults = TestUtils.typeAnalyzeSampleFiles(['private1.py'], configOptions);
|
let analysisResults = TestUtils.typeAnalyzeSampleFiles(['private1.py'], configOptions);
|
||||||
@ -47,7 +48,7 @@ test('Private1', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('Constant1', () => {
|
test('Constant1', () => {
|
||||||
const configOptions = new ConfigOptions('.');
|
const configOptions = new ConfigOptions(Uri.empty());
|
||||||
|
|
||||||
// By default, optional diagnostics are ignored.
|
// By default, optional diagnostics are ignored.
|
||||||
let analysisResults = TestUtils.typeAnalyzeSampleFiles(['constant1.py'], configOptions);
|
let analysisResults = TestUtils.typeAnalyzeSampleFiles(['constant1.py'], configOptions);
|
||||||
@ -168,7 +169,7 @@ test('With3', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('With4', () => {
|
test('With4', () => {
|
||||||
const configOptions = new ConfigOptions('.');
|
const configOptions = new ConfigOptions(Uri.empty());
|
||||||
|
|
||||||
configOptions.defaultPythonVersion = PythonVersion.V3_8;
|
configOptions.defaultPythonVersion = PythonVersion.V3_8;
|
||||||
const analysisResults1 = TestUtils.typeAnalyzeSampleFiles(['with4.py'], configOptions);
|
const analysisResults1 = TestUtils.typeAnalyzeSampleFiles(['with4.py'], configOptions);
|
||||||
@ -216,7 +217,7 @@ test('Mro4', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('DefaultInitializer1', () => {
|
test('DefaultInitializer1', () => {
|
||||||
const configOptions = new ConfigOptions('.');
|
const configOptions = new ConfigOptions(Uri.empty());
|
||||||
|
|
||||||
// By default, the reportCallInDefaultInitializer is disabled.
|
// By default, the reportCallInDefaultInitializer is disabled.
|
||||||
let analysisResults = TestUtils.typeAnalyzeSampleFiles(['defaultInitializer1.py'], configOptions);
|
let analysisResults = TestUtils.typeAnalyzeSampleFiles(['defaultInitializer1.py'], configOptions);
|
||||||
@ -229,7 +230,7 @@ test('DefaultInitializer1', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('UnnecessaryIsInstance1', () => {
|
test('UnnecessaryIsInstance1', () => {
|
||||||
const configOptions = new ConfigOptions('.');
|
const configOptions = new ConfigOptions(Uri.empty());
|
||||||
|
|
||||||
let analysisResults = TestUtils.typeAnalyzeSampleFiles(['unnecessaryIsInstance1.py'], configOptions);
|
let analysisResults = TestUtils.typeAnalyzeSampleFiles(['unnecessaryIsInstance1.py'], configOptions);
|
||||||
TestUtils.validateResults(analysisResults, 1);
|
TestUtils.validateResults(analysisResults, 1);
|
||||||
@ -241,7 +242,7 @@ test('UnnecessaryIsInstance1', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('UnnecessaryIsSubclass1', () => {
|
test('UnnecessaryIsSubclass1', () => {
|
||||||
const configOptions = new ConfigOptions('.');
|
const configOptions = new ConfigOptions(Uri.empty());
|
||||||
|
|
||||||
let analysisResults = TestUtils.typeAnalyzeSampleFiles(['unnecessaryIsSubclass1.py'], configOptions);
|
let analysisResults = TestUtils.typeAnalyzeSampleFiles(['unnecessaryIsSubclass1.py'], configOptions);
|
||||||
TestUtils.validateResults(analysisResults, 0);
|
TestUtils.validateResults(analysisResults, 0);
|
||||||
@ -253,7 +254,7 @@ test('UnnecessaryIsSubclass1', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('UnnecessaryCast1', () => {
|
test('UnnecessaryCast1', () => {
|
||||||
const configOptions = new ConfigOptions('.');
|
const configOptions = new ConfigOptions(Uri.empty());
|
||||||
|
|
||||||
let analysisResults = TestUtils.typeAnalyzeSampleFiles(['unnecessaryCast1.py'], configOptions);
|
let analysisResults = TestUtils.typeAnalyzeSampleFiles(['unnecessaryCast1.py'], configOptions);
|
||||||
TestUtils.validateResults(analysisResults, 0);
|
TestUtils.validateResults(analysisResults, 0);
|
||||||
@ -265,7 +266,7 @@ test('UnnecessaryCast1', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('UnnecessaryContains1', () => {
|
test('UnnecessaryContains1', () => {
|
||||||
const configOptions = new ConfigOptions('.');
|
const configOptions = new ConfigOptions(Uri.empty());
|
||||||
|
|
||||||
let analysisResults = TestUtils.typeAnalyzeSampleFiles(['unnecessaryContains1.py'], configOptions);
|
let analysisResults = TestUtils.typeAnalyzeSampleFiles(['unnecessaryContains1.py'], configOptions);
|
||||||
TestUtils.validateResults(analysisResults, 0);
|
TestUtils.validateResults(analysisResults, 0);
|
||||||
@ -277,7 +278,7 @@ test('UnnecessaryContains1', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('TypeIgnore1', () => {
|
test('TypeIgnore1', () => {
|
||||||
const configOptions = new ConfigOptions('.');
|
const configOptions = new ConfigOptions(Uri.empty());
|
||||||
|
|
||||||
let analysisResults = TestUtils.typeAnalyzeSampleFiles(['typeIgnore1.py'], configOptions);
|
let analysisResults = TestUtils.typeAnalyzeSampleFiles(['typeIgnore1.py'], configOptions);
|
||||||
TestUtils.validateResults(analysisResults, 0);
|
TestUtils.validateResults(analysisResults, 0);
|
||||||
@ -289,7 +290,7 @@ test('TypeIgnore1', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('TypeIgnore2', () => {
|
test('TypeIgnore2', () => {
|
||||||
const configOptions = new ConfigOptions('.');
|
const configOptions = new ConfigOptions(Uri.empty());
|
||||||
|
|
||||||
let analysisResults = TestUtils.typeAnalyzeSampleFiles(['typeIgnore2.py'], configOptions);
|
let analysisResults = TestUtils.typeAnalyzeSampleFiles(['typeIgnore2.py'], configOptions);
|
||||||
TestUtils.validateResults(analysisResults, 0);
|
TestUtils.validateResults(analysisResults, 0);
|
||||||
@ -301,7 +302,7 @@ test('TypeIgnore2', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('TypeIgnore3', () => {
|
test('TypeIgnore3', () => {
|
||||||
const configOptions = new ConfigOptions('.');
|
const configOptions = new ConfigOptions(Uri.empty());
|
||||||
|
|
||||||
let analysisResults = TestUtils.typeAnalyzeSampleFiles(['typeIgnore3.py'], configOptions);
|
let analysisResults = TestUtils.typeAnalyzeSampleFiles(['typeIgnore3.py'], configOptions);
|
||||||
TestUtils.validateResults(analysisResults, 0);
|
TestUtils.validateResults(analysisResults, 0);
|
||||||
@ -313,7 +314,7 @@ test('TypeIgnore3', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('TypeIgnore4', () => {
|
test('TypeIgnore4', () => {
|
||||||
const configOptions = new ConfigOptions('.');
|
const configOptions = new ConfigOptions(Uri.empty());
|
||||||
|
|
||||||
let analysisResults = TestUtils.typeAnalyzeSampleFiles(['typeIgnore4.py'], configOptions);
|
let analysisResults = TestUtils.typeAnalyzeSampleFiles(['typeIgnore4.py'], configOptions);
|
||||||
TestUtils.validateResults(analysisResults, 0);
|
TestUtils.validateResults(analysisResults, 0);
|
||||||
@ -324,7 +325,7 @@ test('TypeIgnore4', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('TypeIgnore5', () => {
|
test('TypeIgnore5', () => {
|
||||||
const configOptions = new ConfigOptions('.');
|
const configOptions = new ConfigOptions(Uri.empty());
|
||||||
|
|
||||||
let analysisResults = TestUtils.typeAnalyzeSampleFiles(['typeIgnore5.py'], configOptions);
|
let analysisResults = TestUtils.typeAnalyzeSampleFiles(['typeIgnore5.py'], configOptions);
|
||||||
TestUtils.validateResults(analysisResults, 0);
|
TestUtils.validateResults(analysisResults, 0);
|
||||||
@ -335,14 +336,14 @@ test('TypeIgnore5', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('PyrightIgnore1', () => {
|
test('PyrightIgnore1', () => {
|
||||||
const configOptions = new ConfigOptions('.');
|
const configOptions = new ConfigOptions(Uri.empty());
|
||||||
|
|
||||||
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['pyrightIgnore1.py'], configOptions);
|
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['pyrightIgnore1.py'], configOptions);
|
||||||
TestUtils.validateResults(analysisResults, 1);
|
TestUtils.validateResults(analysisResults, 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('PyrightIgnore2', () => {
|
test('PyrightIgnore2', () => {
|
||||||
const configOptions = new ConfigOptions('.');
|
const configOptions = new ConfigOptions(Uri.empty());
|
||||||
|
|
||||||
let analysisResults = TestUtils.typeAnalyzeSampleFiles(['pyrightIgnore2.py'], configOptions);
|
let analysisResults = TestUtils.typeAnalyzeSampleFiles(['pyrightIgnore2.py'], configOptions);
|
||||||
TestUtils.validateResults(analysisResults, 2);
|
TestUtils.validateResults(analysisResults, 2);
|
||||||
@ -353,14 +354,14 @@ test('PyrightIgnore2', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('PyrightComment1', () => {
|
test('PyrightComment1', () => {
|
||||||
const configOptions = new ConfigOptions('.');
|
const configOptions = new ConfigOptions(Uri.empty());
|
||||||
|
|
||||||
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['pyrightComment1.py'], configOptions);
|
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['pyrightComment1.py'], configOptions);
|
||||||
TestUtils.validateResults(analysisResults, 9);
|
TestUtils.validateResults(analysisResults, 9);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('DuplicateImports1', () => {
|
test('DuplicateImports1', () => {
|
||||||
const configOptions = new ConfigOptions('.');
|
const configOptions = new ConfigOptions(Uri.empty());
|
||||||
|
|
||||||
// By default, optional diagnostics are ignored.
|
// By default, optional diagnostics are ignored.
|
||||||
let analysisResults = TestUtils.typeAnalyzeSampleFiles(['duplicateImports1.py'], configOptions);
|
let analysisResults = TestUtils.typeAnalyzeSampleFiles(['duplicateImports1.py'], configOptions);
|
||||||
@ -373,7 +374,7 @@ test('DuplicateImports1', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('ParamNames1', () => {
|
test('ParamNames1', () => {
|
||||||
const configOptions = new ConfigOptions('.');
|
const configOptions = new ConfigOptions(Uri.empty());
|
||||||
|
|
||||||
let analysisResults = TestUtils.typeAnalyzeSampleFiles(['paramNames1.py'], configOptions);
|
let analysisResults = TestUtils.typeAnalyzeSampleFiles(['paramNames1.py'], configOptions);
|
||||||
TestUtils.validateResults(analysisResults, 0, 7);
|
TestUtils.validateResults(analysisResults, 0, 7);
|
||||||
@ -423,7 +424,7 @@ test('DuplicateDeclaration2', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('Strings1', () => {
|
test('Strings1', () => {
|
||||||
const configOptions = new ConfigOptions('.');
|
const configOptions = new ConfigOptions(Uri.empty());
|
||||||
const analysisResults1 = TestUtils.typeAnalyzeSampleFiles(['strings1.py'], configOptions);
|
const analysisResults1 = TestUtils.typeAnalyzeSampleFiles(['strings1.py'], configOptions);
|
||||||
TestUtils.validateResults(analysisResults1, 0);
|
TestUtils.validateResults(analysisResults1, 0);
|
||||||
|
|
||||||
@ -433,7 +434,7 @@ test('Strings1', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('UnusedExpression1', () => {
|
test('UnusedExpression1', () => {
|
||||||
const configOptions = new ConfigOptions('.');
|
const configOptions = new ConfigOptions(Uri.empty());
|
||||||
|
|
||||||
// By default, this is a warning.
|
// By default, this is a warning.
|
||||||
let analysisResults = TestUtils.typeAnalyzeSampleFiles(['unusedExpression1.py'], configOptions);
|
let analysisResults = TestUtils.typeAnalyzeSampleFiles(['unusedExpression1.py'], configOptions);
|
||||||
@ -451,7 +452,7 @@ test('UnusedExpression1', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('UnusedImport1', () => {
|
test('UnusedImport1', () => {
|
||||||
const configOptions = new ConfigOptions('.');
|
const configOptions = new ConfigOptions(Uri.empty());
|
||||||
|
|
||||||
// Enabled it
|
// Enabled it
|
||||||
configOptions.diagnosticRuleSet.reportUnusedImport = 'warning';
|
configOptions.diagnosticRuleSet.reportUnusedImport = 'warning';
|
||||||
@ -470,7 +471,7 @@ test('UnusedImport1', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('UninitializedVariable1', () => {
|
test('UninitializedVariable1', () => {
|
||||||
const configOptions = new ConfigOptions('.');
|
const configOptions = new ConfigOptions(Uri.empty());
|
||||||
|
|
||||||
// By default, this is off.
|
// By default, this is off.
|
||||||
let analysisResults = TestUtils.typeAnalyzeSampleFiles(['uninitializedVariable1.py'], configOptions);
|
let analysisResults = TestUtils.typeAnalyzeSampleFiles(['uninitializedVariable1.py'], configOptions);
|
||||||
@ -483,7 +484,7 @@ test('UninitializedVariable1', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('UninitializedVariable2', () => {
|
test('UninitializedVariable2', () => {
|
||||||
const configOptions = new ConfigOptions('.');
|
const configOptions = new ConfigOptions(Uri.empty());
|
||||||
|
|
||||||
// By default, this is off.
|
// By default, this is off.
|
||||||
let analysisResults = TestUtils.typeAnalyzeSampleFiles(['uninitializedVariable2.py'], configOptions);
|
let analysisResults = TestUtils.typeAnalyzeSampleFiles(['uninitializedVariable2.py'], configOptions);
|
||||||
@ -502,7 +503,7 @@ test('RegionComments1', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('Deprecated1', () => {
|
test('Deprecated1', () => {
|
||||||
const configOptions = new ConfigOptions('.');
|
const configOptions = new ConfigOptions(Uri.empty());
|
||||||
|
|
||||||
configOptions.defaultPythonVersion = PythonVersion.V3_8;
|
configOptions.defaultPythonVersion = PythonVersion.V3_8;
|
||||||
const analysisResults1 = TestUtils.typeAnalyzeSampleFiles(['deprecated1.py'], configOptions);
|
const analysisResults1 = TestUtils.typeAnalyzeSampleFiles(['deprecated1.py'], configOptions);
|
||||||
@ -548,7 +549,7 @@ test('Deprecated1', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('Deprecated2', () => {
|
test('Deprecated2', () => {
|
||||||
const configOptions = new ConfigOptions('.');
|
const configOptions = new ConfigOptions(Uri.empty());
|
||||||
|
|
||||||
const analysisResults1 = TestUtils.typeAnalyzeSampleFiles(['deprecated2.py'], configOptions);
|
const analysisResults1 = TestUtils.typeAnalyzeSampleFiles(['deprecated2.py'], configOptions);
|
||||||
TestUtils.validateResults(analysisResults1, 0, 0, 0, undefined, undefined, 12);
|
TestUtils.validateResults(analysisResults1, 0, 0, 0, undefined, undefined, 12);
|
||||||
@ -559,7 +560,7 @@ test('Deprecated2', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('Deprecated3', () => {
|
test('Deprecated3', () => {
|
||||||
const configOptions = new ConfigOptions('.');
|
const configOptions = new ConfigOptions(Uri.empty());
|
||||||
|
|
||||||
const analysisResults1 = TestUtils.typeAnalyzeSampleFiles(['deprecated3.py'], configOptions);
|
const analysisResults1 = TestUtils.typeAnalyzeSampleFiles(['deprecated3.py'], configOptions);
|
||||||
TestUtils.validateResults(analysisResults1, 0, 0, 0, undefined, undefined, 5);
|
TestUtils.validateResults(analysisResults1, 0, 0, 0, undefined, undefined, 5);
|
||||||
@ -570,7 +571,7 @@ test('Deprecated3', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('Deprecated4', () => {
|
test('Deprecated4', () => {
|
||||||
const configOptions = new ConfigOptions('.');
|
const configOptions = new ConfigOptions(Uri.empty());
|
||||||
|
|
||||||
const analysisResults1 = TestUtils.typeAnalyzeSampleFiles(['deprecated4.py'], configOptions);
|
const analysisResults1 = TestUtils.typeAnalyzeSampleFiles(['deprecated4.py'], configOptions);
|
||||||
TestUtils.validateResults(analysisResults1, 0, 0, 0, undefined, undefined, 6);
|
TestUtils.validateResults(analysisResults1, 0, 0, 0, undefined, undefined, 6);
|
||||||
@ -581,7 +582,7 @@ test('Deprecated4', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('Deprecated5', () => {
|
test('Deprecated5', () => {
|
||||||
const configOptions = new ConfigOptions('.');
|
const configOptions = new ConfigOptions(Uri.empty());
|
||||||
|
|
||||||
const analysisResults1 = TestUtils.typeAnalyzeSampleFiles(['deprecated5.py'], configOptions);
|
const analysisResults1 = TestUtils.typeAnalyzeSampleFiles(['deprecated5.py'], configOptions);
|
||||||
TestUtils.validateResults(analysisResults1, 0, 0, 0, undefined, undefined, 2);
|
TestUtils.validateResults(analysisResults1, 0, 0, 0, undefined, undefined, 2);
|
||||||
|
@ -8,6 +8,7 @@ import assert from 'assert';
|
|||||||
import { CancellationToken } from 'vscode-languageserver';
|
import { CancellationToken } from 'vscode-languageserver';
|
||||||
import { CompletionItemKind, MarkupKind } from 'vscode-languageserver-types';
|
import { CompletionItemKind, MarkupKind } from 'vscode-languageserver-types';
|
||||||
|
|
||||||
|
import { Uri } from '../common/uri/uri';
|
||||||
import { CompletionOptions, CompletionProvider } from '../languageService/completionProvider';
|
import { CompletionOptions, CompletionProvider } from '../languageService/completionProvider';
|
||||||
import { parseAndGetTestState } from './harness/fourslash/testState';
|
import { parseAndGetTestState } from './harness/fourslash/testState';
|
||||||
|
|
||||||
@ -798,6 +799,7 @@ test('completion quote trigger', async () => {
|
|||||||
const state = parseAndGetTestState(code).state;
|
const state = parseAndGetTestState(code).state;
|
||||||
const marker = state.getMarkerByName('marker');
|
const marker = state.getMarkerByName('marker');
|
||||||
const filePath = marker.fileName;
|
const filePath = marker.fileName;
|
||||||
|
const uri = Uri.file(filePath);
|
||||||
const position = state.convertOffsetToPosition(filePath, marker.position);
|
const position = state.convertOffsetToPosition(filePath, marker.position);
|
||||||
|
|
||||||
const options: CompletionOptions = {
|
const options: CompletionOptions = {
|
||||||
@ -809,8 +811,8 @@ test('completion quote trigger', async () => {
|
|||||||
|
|
||||||
const result = new CompletionProvider(
|
const result = new CompletionProvider(
|
||||||
state.program,
|
state.program,
|
||||||
state.workspace.rootPath,
|
state.workspace.rootUri,
|
||||||
filePath,
|
uri,
|
||||||
position,
|
position,
|
||||||
options,
|
options,
|
||||||
CancellationToken.None
|
CancellationToken.None
|
||||||
@ -836,6 +838,7 @@ test('completion quote trigger - middle', async () => {
|
|||||||
const state = parseAndGetTestState(code).state;
|
const state = parseAndGetTestState(code).state;
|
||||||
const marker = state.getMarkerByName('marker');
|
const marker = state.getMarkerByName('marker');
|
||||||
const filePath = marker.fileName;
|
const filePath = marker.fileName;
|
||||||
|
const uri = Uri.file(filePath);
|
||||||
const position = state.convertOffsetToPosition(filePath, marker.position);
|
const position = state.convertOffsetToPosition(filePath, marker.position);
|
||||||
|
|
||||||
const options: CompletionOptions = {
|
const options: CompletionOptions = {
|
||||||
@ -847,8 +850,8 @@ test('completion quote trigger - middle', async () => {
|
|||||||
|
|
||||||
const result = new CompletionProvider(
|
const result = new CompletionProvider(
|
||||||
state.program,
|
state.program,
|
||||||
state.workspace.rootPath,
|
state.workspace.rootUri,
|
||||||
filePath,
|
uri,
|
||||||
position,
|
position,
|
||||||
options,
|
options,
|
||||||
CancellationToken.None
|
CancellationToken.None
|
||||||
@ -882,6 +885,7 @@ test('auto import sort text', async () => {
|
|||||||
while (state.workspace.service.test_program.analyze());
|
while (state.workspace.service.test_program.analyze());
|
||||||
|
|
||||||
const filePath = marker.fileName;
|
const filePath = marker.fileName;
|
||||||
|
const uri = Uri.file(filePath);
|
||||||
const position = state.convertOffsetToPosition(filePath, marker.position);
|
const position = state.convertOffsetToPosition(filePath, marker.position);
|
||||||
|
|
||||||
const options: CompletionOptions = {
|
const options: CompletionOptions = {
|
||||||
@ -892,8 +896,8 @@ test('auto import sort text', async () => {
|
|||||||
|
|
||||||
const result = new CompletionProvider(
|
const result = new CompletionProvider(
|
||||||
state.program,
|
state.program,
|
||||||
state.workspace.rootPath,
|
state.workspace.rootUri,
|
||||||
filePath,
|
uri,
|
||||||
position,
|
position,
|
||||||
options,
|
options,
|
||||||
CancellationToken.None
|
CancellationToken.None
|
||||||
|
@ -15,10 +15,11 @@ import { CommandLineOptions } from '../common/commandLineOptions';
|
|||||||
import { ConfigOptions, ExecutionEnvironment } from '../common/configOptions';
|
import { ConfigOptions, ExecutionEnvironment } from '../common/configOptions';
|
||||||
import { ConsoleInterface, NullConsole } from '../common/console';
|
import { ConsoleInterface, NullConsole } from '../common/console';
|
||||||
import { NoAccessHost } from '../common/host';
|
import { NoAccessHost } from '../common/host';
|
||||||
import { combinePaths, getBaseFileName, normalizePath, normalizeSlashes, realCasePath } from '../common/pathUtils';
|
import { combinePaths, normalizePath, normalizeSlashes } from '../common/pathUtils';
|
||||||
import { PythonVersion } from '../common/pythonVersion';
|
import { PythonVersion } from '../common/pythonVersion';
|
||||||
import { createFromRealFileSystem } from '../common/realFileSystem';
|
import { createFromRealFileSystem } from '../common/realFileSystem';
|
||||||
import { createServiceProvider } from '../common/serviceProviderExtensions';
|
import { createServiceProvider } from '../common/serviceProviderExtensions';
|
||||||
|
import { Uri } from '../common/uri/uri';
|
||||||
import { TestAccessHost } from './harness/testAccessHost';
|
import { TestAccessHost } from './harness/testAccessHost';
|
||||||
import { TestFileSystem } from './harness/vfs/filesystem';
|
import { TestFileSystem } from './harness/vfs/filesystem';
|
||||||
|
|
||||||
@ -41,8 +42,8 @@ test('FindFilesWithConfigFile', () => {
|
|||||||
// The config file specifies a single file spec (a directory).
|
// The config file specifies a single file spec (a directory).
|
||||||
assert.strictEqual(configOptions.include.length, 1, `failed creating options from ${cwd}`);
|
assert.strictEqual(configOptions.include.length, 1, `failed creating options from ${cwd}`);
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
normalizeSlashes(configOptions.projectRoot),
|
configOptions.projectRoot.key,
|
||||||
realCasePath(combinePaths(cwd, commandLineOptions.configFilePath), service.fs)
|
service.fs.realCasePath(Uri.file(combinePaths(cwd, commandLineOptions.configFilePath))).key
|
||||||
);
|
);
|
||||||
|
|
||||||
const fileList = service.test_getFileNamesFromFileSpecs();
|
const fileList = service.test_getFileNamesFromFileSpecs();
|
||||||
@ -67,7 +68,7 @@ test('FindFilesVirtualEnvAutoDetectExclude', () => {
|
|||||||
|
|
||||||
// There are 3 python files in the workspace, outside of myvenv
|
// There are 3 python files in the workspace, outside of myvenv
|
||||||
// There is 1 python file in myvenv, which should be excluded
|
// There is 1 python file in myvenv, which should be excluded
|
||||||
const fileNames = fileList.map((p) => getBaseFileName(p)).sort();
|
const fileNames = fileList.map((p) => p.fileName).sort();
|
||||||
assert.deepStrictEqual(fileNames, ['sample1.py', 'sample2.py', 'sample3.py']);
|
assert.deepStrictEqual(fileNames, ['sample1.py', 'sample2.py', 'sample3.py']);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -85,7 +86,7 @@ test('FindFilesVirtualEnvAutoDetectInclude', () => {
|
|||||||
// There are 3 python files in the workspace, outside of myvenv
|
// There are 3 python files in the workspace, outside of myvenv
|
||||||
// There is 1 more python file in excluded folder
|
// There is 1 more python file in excluded folder
|
||||||
// There is 1 python file in myvenv, which should be included
|
// There is 1 python file in myvenv, which should be included
|
||||||
const fileNames = fileList.map((p) => getBaseFileName(p)).sort();
|
const fileNames = fileList.map((p) => p.fileName).sort();
|
||||||
assert.deepStrictEqual(fileNames, ['library1.py', 'sample1.py', 'sample2.py', 'sample3.py']);
|
assert.deepStrictEqual(fileNames, ['library1.py', 'sample1.py', 'sample2.py', 'sample3.py']);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -132,8 +133,8 @@ test('SomeFileSpecsAreInvalid', () => {
|
|||||||
assert.strictEqual(configOptions.include.length, 4, `failed creating options from ${cwd}`);
|
assert.strictEqual(configOptions.include.length, 4, `failed creating options from ${cwd}`);
|
||||||
assert.strictEqual(configOptions.exclude.length, 1);
|
assert.strictEqual(configOptions.exclude.length, 1);
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
normalizeSlashes(configOptions.projectRoot),
|
configOptions.projectRoot.getFilePath(),
|
||||||
realCasePath(combinePaths(cwd, commandLineOptions.configFilePath), service.fs)
|
service.fs.realCasePath(Uri.file(combinePaths(cwd, commandLineOptions.configFilePath))).getFilePath()
|
||||||
);
|
);
|
||||||
|
|
||||||
const fileList = service.test_getFileNamesFromFileSpecs();
|
const fileList = service.test_getFileNamesFromFileSpecs();
|
||||||
@ -157,13 +158,13 @@ test('ConfigBadJson', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('FindExecEnv1', () => {
|
test('FindExecEnv1', () => {
|
||||||
const cwd = normalizePath(process.cwd());
|
const cwd = Uri.file(normalizePath(process.cwd()));
|
||||||
const configOptions = new ConfigOptions(cwd);
|
const configOptions = new ConfigOptions(cwd);
|
||||||
|
|
||||||
// Build a config option with three execution environments.
|
// Build a config option with three execution environments.
|
||||||
const execEnv1 = new ExecutionEnvironment(
|
const execEnv1 = new ExecutionEnvironment(
|
||||||
'python',
|
'python',
|
||||||
'src/foo',
|
cwd.combinePaths('src/foo'),
|
||||||
/* defaultPythonVersion */ undefined,
|
/* defaultPythonVersion */ undefined,
|
||||||
/* defaultPythonPlatform */ undefined,
|
/* defaultPythonPlatform */ undefined,
|
||||||
/* defaultExtraPaths */ undefined
|
/* defaultExtraPaths */ undefined
|
||||||
@ -171,28 +172,29 @@ test('FindExecEnv1', () => {
|
|||||||
configOptions.executionEnvironments.push(execEnv1);
|
configOptions.executionEnvironments.push(execEnv1);
|
||||||
const execEnv2 = new ExecutionEnvironment(
|
const execEnv2 = new ExecutionEnvironment(
|
||||||
'python',
|
'python',
|
||||||
'src',
|
cwd.combinePaths('src'),
|
||||||
/* defaultPythonVersion */ undefined,
|
/* defaultPythonVersion */ undefined,
|
||||||
/* defaultPythonPlatform */ undefined,
|
/* defaultPythonPlatform */ undefined,
|
||||||
/* defaultExtraPaths */ undefined
|
/* defaultExtraPaths */ undefined
|
||||||
);
|
);
|
||||||
configOptions.executionEnvironments.push(execEnv2);
|
configOptions.executionEnvironments.push(execEnv2);
|
||||||
|
|
||||||
const file1 = normalizeSlashes(combinePaths(cwd, 'src/foo/bar.py'));
|
const file1 = cwd.combinePaths('src/foo/bar.py');
|
||||||
assert.strictEqual(configOptions.findExecEnvironment(file1), execEnv1);
|
assert.strictEqual(configOptions.findExecEnvironment(file1), execEnv1);
|
||||||
const file2 = normalizeSlashes(combinePaths(cwd, 'src/foo2/bar.py'));
|
const file2 = cwd.combinePaths('src/foo2/bar.py');
|
||||||
assert.strictEqual(configOptions.findExecEnvironment(file2), execEnv2);
|
assert.strictEqual(configOptions.findExecEnvironment(file2), execEnv2);
|
||||||
|
|
||||||
// If none of the execution environments matched, we should get
|
// If none of the execution environments matched, we should get
|
||||||
// a default environment with the root equal to that of the config.
|
// a default environment with the root equal to that of the config.
|
||||||
const file4 = '/nothing/bar.py';
|
const file4 = Uri.file('/nothing/bar.py');
|
||||||
const defaultExecEnv = configOptions.findExecEnvironment(file4);
|
const defaultExecEnv = configOptions.findExecEnvironment(file4);
|
||||||
assert(defaultExecEnv.root);
|
assert(defaultExecEnv.root);
|
||||||
assert.strictEqual(normalizeSlashes(defaultExecEnv.root), normalizeSlashes(configOptions.projectRoot));
|
const rootFilePath = Uri.isUri(defaultExecEnv.root) ? defaultExecEnv.root.getFilePath() : defaultExecEnv.root;
|
||||||
|
assert.strictEqual(normalizeSlashes(rootFilePath), normalizeSlashes(configOptions.projectRoot.getFilePath()));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('PythonPlatform', () => {
|
test('PythonPlatform', () => {
|
||||||
const cwd = normalizePath(process.cwd());
|
const cwd = Uri.file(normalizePath(process.cwd()));
|
||||||
|
|
||||||
const configOptions = new ConfigOptions(cwd);
|
const configOptions = new ConfigOptions(cwd);
|
||||||
|
|
||||||
@ -216,16 +218,16 @@ test('PythonPlatform', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('AutoSearchPathsOn', () => {
|
test('AutoSearchPathsOn', () => {
|
||||||
const cwd = normalizePath(combinePaths(process.cwd(), 'src/tests/samples/project_src'));
|
const cwd = Uri.file(normalizePath(combinePaths(process.cwd(), 'src/tests/samples/project_src')));
|
||||||
const nullConsole = new NullConsole();
|
const nullConsole = new NullConsole();
|
||||||
const service = createAnalyzer(nullConsole);
|
const service = createAnalyzer(nullConsole);
|
||||||
const commandLineOptions = new CommandLineOptions(cwd, /* fromVsCodeExtension */ false);
|
const commandLineOptions = new CommandLineOptions(cwd.getFilePath(), /* fromVsCodeExtension */ false);
|
||||||
commandLineOptions.autoSearchPaths = true;
|
commandLineOptions.autoSearchPaths = true;
|
||||||
service.setOptions(commandLineOptions);
|
service.setOptions(commandLineOptions);
|
||||||
|
|
||||||
const configOptions = service.test_getConfigOptions(commandLineOptions);
|
const configOptions = service.test_getConfigOptions(commandLineOptions);
|
||||||
|
|
||||||
const expectedExtraPaths = [realCasePath(combinePaths(cwd, 'src'), service.fs)];
|
const expectedExtraPaths = [service.fs.realCasePath(cwd.combinePaths('src'))];
|
||||||
assert.deepStrictEqual(configOptions.defaultExtraPaths, expectedExtraPaths);
|
assert.deepStrictEqual(configOptions.defaultExtraPaths, expectedExtraPaths);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -274,19 +276,22 @@ test('AutoSearchPathsOnWithConfigExecEnv', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('AutoSearchPathsOnAndExtraPaths', () => {
|
test('AutoSearchPathsOnAndExtraPaths', () => {
|
||||||
const cwd = normalizePath(combinePaths(process.cwd(), 'src/tests/samples/project_src_with_config_no_extra_paths'));
|
|
||||||
const nullConsole = new NullConsole();
|
const nullConsole = new NullConsole();
|
||||||
const service = createAnalyzer(nullConsole);
|
const service = createAnalyzer(nullConsole);
|
||||||
const commandLineOptions = new CommandLineOptions(cwd, /* fromVsCodeExtension */ false);
|
const cwd = Uri.file(
|
||||||
|
normalizePath(combinePaths(process.cwd(), 'src/tests/samples/project_src_with_config_no_extra_paths')),
|
||||||
|
service.fs.isCaseSensitive
|
||||||
|
);
|
||||||
|
const commandLineOptions = new CommandLineOptions(cwd.getFilePath(), /* fromVsCodeExtension */ false);
|
||||||
commandLineOptions.autoSearchPaths = true;
|
commandLineOptions.autoSearchPaths = true;
|
||||||
commandLineOptions.extraPaths = ['src/_vendored'];
|
commandLineOptions.extraPaths = ['src/_vendored'];
|
||||||
service.setOptions(commandLineOptions);
|
service.setOptions(commandLineOptions);
|
||||||
|
|
||||||
const configOptions = service.test_getConfigOptions(commandLineOptions);
|
const configOptions = service.test_getConfigOptions(commandLineOptions);
|
||||||
|
|
||||||
const expectedExtraPaths: string[] = [
|
const expectedExtraPaths: Uri[] = [
|
||||||
realCasePath(combinePaths(cwd, 'src'), service.fs),
|
service.fs.realCasePath(cwd.combinePaths('src')),
|
||||||
realCasePath(combinePaths(cwd, 'src', '_vendored'), service.fs),
|
service.fs.realCasePath(cwd.combinePaths('src', '_vendored')),
|
||||||
];
|
];
|
||||||
|
|
||||||
assert.deepStrictEqual(configOptions.defaultExtraPaths, expectedExtraPaths);
|
assert.deepStrictEqual(configOptions.defaultExtraPaths, expectedExtraPaths);
|
||||||
@ -314,11 +319,11 @@ test('FindFilesInMemoryOnly', () => {
|
|||||||
service.setOptions(commandLineOptions);
|
service.setOptions(commandLineOptions);
|
||||||
|
|
||||||
// Open a file that is not backed by the file system.
|
// Open a file that is not backed by the file system.
|
||||||
const untitled = 'untitled:Untitled-1.py';
|
const untitled = Uri.parse('untitled:Untitled-1.py', true);
|
||||||
service.setFileOpened(untitled, 1, '# empty');
|
service.setFileOpened(untitled, 1, '# empty');
|
||||||
|
|
||||||
const fileList = service.test_getFileNamesFromFileSpecs();
|
const fileList = service.test_getFileNamesFromFileSpecs();
|
||||||
assert(fileList.filter((f) => f === untitled));
|
assert(fileList.filter((f) => f.equals(untitled)));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('verify config fileSpecs after cloning', () => {
|
test('verify config fileSpecs after cloning', () => {
|
||||||
@ -327,7 +332,7 @@ test('verify config fileSpecs after cloning', () => {
|
|||||||
ignore: ['**/node_modules/**'],
|
ignore: ['**/node_modules/**'],
|
||||||
};
|
};
|
||||||
|
|
||||||
const config = new ConfigOptions(process.cwd());
|
const config = new ConfigOptions(Uri.file(process.cwd()));
|
||||||
const sp = createServiceProvider(fs, new NullConsole());
|
const sp = createServiceProvider(fs, new NullConsole());
|
||||||
config.initializeFromJson(configFile, undefined, sp, new TestAccessHost());
|
config.initializeFromJson(configFile, undefined, sp, new TestAccessHost());
|
||||||
const cloned = createConfigOptionsFrom(config);
|
const cloned = createConfigOptionsFrom(config);
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
* Tests documentSymbolCollector
|
* Tests documentSymbolCollector
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { Uri } from '../common/uri/uri';
|
||||||
import { parseAndGetTestState } from './harness/fourslash/testState';
|
import { parseAndGetTestState } from './harness/fourslash/testState';
|
||||||
import { verifyReferencesAtPosition } from './testStateUtils';
|
import { verifyReferencesAtPosition } from './testStateUtils';
|
||||||
|
|
||||||
@ -225,7 +226,7 @@ test('use localName import alias', () => {
|
|||||||
const references = state
|
const references = state
|
||||||
.getRangesByText()
|
.getRangesByText()
|
||||||
.get('tools')!
|
.get('tools')!
|
||||||
.map((r) => ({ path: r.fileName, range: state.convertPositionRange(r) }));
|
.map((r) => ({ uri: Uri.file(r.fileName), range: state.convertPositionRange(r) }));
|
||||||
|
|
||||||
state.verifyFindAllReferences({
|
state.verifyFindAllReferences({
|
||||||
marker1: { references },
|
marker1: { references },
|
||||||
@ -288,7 +289,7 @@ test('use localName import module', () => {
|
|||||||
const references = state
|
const references = state
|
||||||
.getRangesByText()
|
.getRangesByText()
|
||||||
.get('tools')!
|
.get('tools')!
|
||||||
.map((r) => ({ path: r.fileName, range: state.convertPositionRange(r) }));
|
.map((r) => ({ uri: Uri.file(r.fileName), range: state.convertPositionRange(r) }));
|
||||||
|
|
||||||
state.verifyFindAllReferences({
|
state.verifyFindAllReferences({
|
||||||
marker1: { references },
|
marker1: { references },
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
import assert from 'assert';
|
import assert from 'assert';
|
||||||
|
|
||||||
import { combinePaths, normalizeSlashes } from '../common/pathUtils';
|
import { combinePaths, normalizeSlashes } from '../common/pathUtils';
|
||||||
|
import { Uri } from '../common/uri/uri';
|
||||||
import * as host from './harness/testHost';
|
import * as host from './harness/testHost';
|
||||||
import * as factory from './harness/vfs/factory';
|
import * as factory from './harness/vfs/factory';
|
||||||
import * as vfs from './harness/vfs/filesystem';
|
import * as vfs from './harness/vfs/filesystem';
|
||||||
@ -20,54 +21,55 @@ test('CreateVFS', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('Folders', () => {
|
test('Folders', () => {
|
||||||
const cwd = normalizeSlashes('/');
|
const cwd = Uri.file(normalizeSlashes('/'));
|
||||||
const fs = new vfs.TestFileSystem(/*ignoreCase*/ true, { cwd });
|
const fs = new vfs.TestFileSystem(/*ignoreCase*/ true, { cwd: cwd.getFilePath() });
|
||||||
|
|
||||||
// no such dir exist
|
// no such dir exist
|
||||||
assert.throws(() => {
|
assert.throws(() => {
|
||||||
fs.chdir('a');
|
fs.chdir(cwd.combinePaths('a'));
|
||||||
});
|
});
|
||||||
|
|
||||||
fs.mkdirSync('a');
|
fs.mkdirSync(cwd.combinePaths('a'));
|
||||||
fs.chdir('a');
|
fs.chdir(cwd.combinePaths('a'));
|
||||||
assert.equal(fs.cwd(), normalizeSlashes('/a'));
|
assert.equal(fs.cwd(), normalizeSlashes('/a'));
|
||||||
|
|
||||||
fs.chdir('..');
|
fs.chdir(cwd.combinePaths('..'));
|
||||||
fs.rmdirSync('a');
|
fs.rmdirSync(cwd.combinePaths('a'));
|
||||||
|
|
||||||
// no such dir exist
|
// no such dir exist
|
||||||
assert.throws(() => {
|
assert.throws(() => {
|
||||||
fs.chdir('a');
|
fs.chdir(cwd.combinePaths('a'));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Folders Recursive', () => {
|
test('Folders Recursive', () => {
|
||||||
const cwd = normalizeSlashes('/');
|
const cwd = Uri.file(normalizeSlashes('/'));
|
||||||
const fs = new vfs.TestFileSystem(/*ignoreCase*/ true, { cwd });
|
const fs = new vfs.TestFileSystem(/*ignoreCase*/ true, { cwd: cwd.getFilePath() });
|
||||||
|
|
||||||
// no such dir exist
|
// no such dir exist
|
||||||
assert.throws(() => {
|
assert.throws(() => {
|
||||||
fs.chdir('a');
|
fs.chdir(cwd.combinePaths('a'));
|
||||||
});
|
});
|
||||||
|
|
||||||
const path = combinePaths('/', 'a', 'b', 'c');
|
const path = cwd.combinePaths('a', 'b', 'c');
|
||||||
fs.mkdirSync(path, { recursive: true });
|
fs.mkdirSync(path, { recursive: true });
|
||||||
|
|
||||||
assert(fs.existsSync(path));
|
assert(fs.existsSync(path));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Files', () => {
|
test('Files', () => {
|
||||||
const cwd = normalizeSlashes('/');
|
const cwd = Uri.file(normalizeSlashes('/'));
|
||||||
const fs = new vfs.TestFileSystem(/*ignoreCase*/ true, { cwd });
|
const fs = new vfs.TestFileSystem(/*ignoreCase*/ true, { cwd: cwd.getFilePath() });
|
||||||
|
|
||||||
fs.writeFileSync('1.txt', 'hello', 'utf8');
|
const uri = cwd.combinePaths('1.txt');
|
||||||
const buffer1 = fs.readFileSync('1.txt');
|
fs.writeFileSync(uri, 'hello', 'utf8');
|
||||||
|
const buffer1 = fs.readFileSync(uri);
|
||||||
assert.equal(buffer1.toString(), 'hello');
|
assert.equal(buffer1.toString(), 'hello');
|
||||||
|
|
||||||
const p = normalizeSlashes('a/b/c');
|
const p = cwd.combinePaths('a/b/c');
|
||||||
fs.mkdirpSync(p);
|
fs.mkdirpSync(p.getFilePath());
|
||||||
|
|
||||||
const f = combinePaths(p, '2.txt');
|
const f = p.combinePaths('2.txt');
|
||||||
fs.writeFileSync(f, 'hi');
|
fs.writeFileSync(f, 'hi');
|
||||||
|
|
||||||
const str = fs.readFileSync(f, 'utf8');
|
const str = fs.readFileSync(f, 'utf8');
|
||||||
@ -90,11 +92,11 @@ test('CreateRich', () => {
|
|||||||
// files + directory + root
|
// files + directory + root
|
||||||
assert.equal(entries.length, 10);
|
assert.equal(entries.length, 10);
|
||||||
|
|
||||||
assert.equal(fs.readFileSync(normalizeSlashes('/a/b/c/1.txt'), 'ascii'), 'hello1');
|
assert.equal(fs.readFileSync(Uri.file(normalizeSlashes('/a/b/c/1.txt')), 'ascii'), 'hello1');
|
||||||
assert.equal(fs.readFileSync(normalizeSlashes('/a/b/2.txt'), 'utf8'), 'hello2');
|
assert.equal(fs.readFileSync(Uri.file(normalizeSlashes('/a/b/2.txt')), 'utf8'), 'hello2');
|
||||||
assert.equal(fs.readFileSync(normalizeSlashes('/a/3.txt'), 'utf-8'), 'hello3');
|
assert.equal(fs.readFileSync(Uri.file(normalizeSlashes('/a/3.txt')), 'utf-8'), 'hello3');
|
||||||
assert.equal(fs.readFileSync(normalizeSlashes('/4.txt'), 'utf16le'), 'hello4');
|
assert.equal(fs.readFileSync(Uri.file(normalizeSlashes('/4.txt')), 'utf16le'), 'hello4');
|
||||||
assert.equal(fs.readFileSync(normalizeSlashes('/a/c/5.txt'), 'ucs2'), 'hello5');
|
assert.equal(fs.readFileSync(Uri.file(normalizeSlashes('/a/c/5.txt')), 'ucs2'), 'hello5');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Shadow', () => {
|
test('Shadow', () => {
|
||||||
@ -124,19 +126,19 @@ test('Shadow', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('Diffing', () => {
|
test('Diffing', () => {
|
||||||
const cwd = normalizeSlashes('/');
|
const cwd = Uri.file(normalizeSlashes('/'));
|
||||||
const fs = new vfs.TestFileSystem(/*ignoreCase*/ true, { cwd });
|
const fs = new vfs.TestFileSystem(/*ignoreCase*/ true, { cwd: cwd.getFilePath() });
|
||||||
|
|
||||||
// first snapshot
|
// first snapshot
|
||||||
fs.snapshot();
|
fs.snapshot();
|
||||||
fs.writeFileSync('test1.txt', 'hello1');
|
fs.writeFileSync(cwd.combinePaths('test1.txt'), 'hello1');
|
||||||
|
|
||||||
// compared with original
|
// compared with original
|
||||||
assert.equal(countFile(fs.diff()!), 1);
|
assert.equal(countFile(fs.diff()!), 1);
|
||||||
|
|
||||||
// second snapshot
|
// second snapshot
|
||||||
fs.snapshot();
|
fs.snapshot();
|
||||||
fs.writeFileSync('test2.txt', 'hello2');
|
fs.writeFileSync(cwd.combinePaths('test2.txt'), 'hello2');
|
||||||
|
|
||||||
// compared with first snapshot
|
// compared with first snapshot
|
||||||
assert.equal(countFile(fs.diff()!), 1);
|
assert.equal(countFile(fs.diff()!), 1);
|
||||||
@ -148,11 +150,11 @@ test('Diffing', () => {
|
|||||||
const s = fs.shadowRoot!.shadow();
|
const s = fs.shadowRoot!.shadow();
|
||||||
|
|
||||||
// "test2.txt" only exist in first snapshot
|
// "test2.txt" only exist in first snapshot
|
||||||
assert(!s.existsSync('test2.txt'));
|
assert(!s.existsSync(cwd.combinePaths('test2.txt')));
|
||||||
|
|
||||||
// create parallel universe where it has another version of test2.txt with different content
|
// create parallel universe where it has another version of test2.txt with different content
|
||||||
// compared to second snapshot which forked from same first snapshot
|
// compared to second snapshot which forked from same first snapshot
|
||||||
s.writeFileSync('test2.txt', 'hello3');
|
s.writeFileSync(cwd.combinePaths('test2.txt'), 'hello3');
|
||||||
|
|
||||||
// diff between non direct snapshots
|
// diff between non direct snapshots
|
||||||
// diff gives test2.txt even though it exist in both snapshot
|
// diff gives test2.txt even though it exist in both snapshot
|
||||||
@ -170,16 +172,16 @@ test('createFromFileSystem1', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// check existing typeshed folder on virtual path inherited from base snapshot from physical file system
|
// check existing typeshed folder on virtual path inherited from base snapshot from physical file system
|
||||||
const entries = fs.readdirSync(factory.typeshedFolder);
|
const entries = fs.readdirSync(Uri.file(factory.typeshedFolder));
|
||||||
assert(entries.length > 0);
|
assert(entries.length > 0);
|
||||||
|
|
||||||
// confirm file
|
// confirm file
|
||||||
assert.equal(fs.readFileSync(filepath, 'utf8'), content);
|
assert.equal(fs.readFileSync(Uri.file(filepath), 'utf8'), content);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('createFromFileSystem2', () => {
|
test('createFromFileSystem2', () => {
|
||||||
const fs = factory.createFromFileSystem(host.HOST, /* ignoreCase */ true, { cwd: factory.srcFolder });
|
const fs = factory.createFromFileSystem(host.HOST, /* ignoreCase */ true, { cwd: factory.srcFolder });
|
||||||
const entries = fs.readdirSync(factory.typeshedFolder.toUpperCase());
|
const entries = fs.readdirSync(Uri.file(factory.typeshedFolder.toUpperCase()));
|
||||||
assert(entries.length > 0);
|
assert(entries.length > 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -190,7 +192,7 @@ test('createFromFileSystemWithCustomTypeshedPath', () => {
|
|||||||
meta: { [factory.typeshedFolder]: invalidpath },
|
meta: { [factory.typeshedFolder]: invalidpath },
|
||||||
});
|
});
|
||||||
|
|
||||||
const entries = fs.readdirSync(factory.typeshedFolder);
|
const entries = fs.readdirSync(Uri.file(factory.typeshedFolder));
|
||||||
assert(entries.filter((e) => e.endsWith('.md')).length > 0);
|
assert(entries.filter((e) => e.endsWith('.md')).length > 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -200,7 +202,7 @@ test('createFromFileSystemWithMetadata', () => {
|
|||||||
meta: { unused: 'unused' },
|
meta: { unused: 'unused' },
|
||||||
});
|
});
|
||||||
|
|
||||||
assert(fs.existsSync(factory.srcFolder));
|
assert(fs.existsSync(Uri.file(factory.srcFolder)));
|
||||||
});
|
});
|
||||||
|
|
||||||
function countFile(files: vfs.FileSet): number {
|
function countFile(files: vfs.FileSet): number {
|
||||||
|
@ -9,8 +9,9 @@
|
|||||||
|
|
||||||
import assert from 'assert';
|
import assert from 'assert';
|
||||||
|
|
||||||
import { combinePaths, getBaseFileName, normalizeSlashes } from '../common/pathUtils';
|
import { getBaseFileName, normalizeSlashes } from '../common/pathUtils';
|
||||||
import { compareStringsCaseSensitive } from '../common/stringUtils';
|
import { compareStringsCaseSensitive } from '../common/stringUtils';
|
||||||
|
import { Uri } from '../common/uri/uri';
|
||||||
import { parseTestData } from './harness/fourslash/fourSlashParser';
|
import { parseTestData } from './harness/fourslash/fourSlashParser';
|
||||||
import { CompilerSettings } from './harness/fourslash/fourSlashTypes';
|
import { CompilerSettings } from './harness/fourslash/fourSlashTypes';
|
||||||
import * as host from './harness/testHost';
|
import * as host from './harness/testHost';
|
||||||
@ -89,7 +90,7 @@ test('Library options', () => {
|
|||||||
|
|
||||||
const data = parseTestData('.', code, 'test.py');
|
const data = parseTestData('.', code, 'test.py');
|
||||||
|
|
||||||
assert.equal(data.files[0].fileName, normalizeSlashes(combinePaths(factory.libFolder, 'file1.py')));
|
assert.equal(data.files[0].fileName, factory.libFolder.combinePaths('file1.py').getFilePath());
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Range', () => {
|
test('Range', () => {
|
||||||
@ -222,8 +223,7 @@ test('Multiple Files', () => {
|
|||||||
|
|
||||||
assert.equal(data.files.filter((f) => f.fileName === normalizeSlashes('./src/A.py'))[0].content, getContent('A'));
|
assert.equal(data.files.filter((f) => f.fileName === normalizeSlashes('./src/A.py'))[0].content, getContent('A'));
|
||||||
assert.equal(
|
assert.equal(
|
||||||
data.files.filter((f) => f.fileName === normalizeSlashes(combinePaths(factory.libFolder, 'src/B.py')))[0]
|
data.files.filter((f) => f.fileName === factory.libFolder.combinePaths('src/B.py').getFilePath())[0].content,
|
||||||
.content,
|
|
||||||
getContent('B')
|
getContent('B')
|
||||||
);
|
);
|
||||||
assert.equal(data.files.filter((f) => f.fileName === normalizeSlashes('./src/C.py'))[0].content, getContent('C'));
|
assert.equal(data.files.filter((f) => f.fileName === normalizeSlashes('./src/C.py'))[0].content, getContent('C'));
|
||||||
@ -312,7 +312,10 @@ test('fourSlashWithFileSystem', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
for (const file of data.files) {
|
for (const file of data.files) {
|
||||||
assert.equal(fs.readFileSync(file.fileName, 'utf8'), getContent(getBaseFileName(file.fileName, '.py', false)));
|
assert.equal(
|
||||||
|
fs.readFileSync(Uri.file(file.fileName), 'utf8'),
|
||||||
|
getContent(getBaseFileName(file.fileName, '.py', false))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -17,13 +17,13 @@ import {
|
|||||||
} from '../../../common/pathUtils';
|
} from '../../../common/pathUtils';
|
||||||
import { distlibFolder, libFolder } from '../vfs/factory';
|
import { distlibFolder, libFolder } from '../vfs/factory';
|
||||||
import {
|
import {
|
||||||
fileMetadataNames,
|
|
||||||
FourSlashData,
|
FourSlashData,
|
||||||
FourSlashFile,
|
FourSlashFile,
|
||||||
GlobalMetadataOptionNames,
|
GlobalMetadataOptionNames,
|
||||||
Marker,
|
Marker,
|
||||||
MetadataOptionNames,
|
MetadataOptionNames,
|
||||||
Range,
|
Range,
|
||||||
|
fileMetadataNames,
|
||||||
} from './fourSlashTypes';
|
} from './fourSlashTypes';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -70,13 +70,13 @@ export function parseTestData(basePath: string, contents: string, fileName: stri
|
|||||||
|
|
||||||
if (toBoolean(currentFileOptions[MetadataOptionNames.library])) {
|
if (toBoolean(currentFileOptions[MetadataOptionNames.library])) {
|
||||||
currentFileName = normalizePath(
|
currentFileName = normalizePath(
|
||||||
combinePaths(libFolder, getRelativePath(currentFileName, normalizedBasePath))
|
combinePaths(libFolder.getFilePath(), getRelativePath(currentFileName, normalizedBasePath))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (toBoolean(currentFileOptions[MetadataOptionNames.distLibrary])) {
|
if (toBoolean(currentFileOptions[MetadataOptionNames.distLibrary])) {
|
||||||
currentFileName = normalizePath(
|
currentFileName = normalizePath(
|
||||||
combinePaths(distlibFolder, getRelativePath(currentFileName, normalizedBasePath))
|
combinePaths(distlibFolder.getFilePath(), getRelativePath(currentFileName, normalizedBasePath))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user