mirror of
https://github.com/microsoft/pyright.git
synced 2024-10-26 10:55:06 +03:00
Streaming references/symbols, analysis error, indexing changes (#1093)
This commit is contained in:
parent
089c9d362a
commit
1e360c99b2
4
.github/workflows/validation.yml
vendored
4
.github/workflows/validation.yml
vendored
@ -26,11 +26,15 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
node-version: ${{ env.NODE_VERSION }}
|
node-version: ${{ env.NODE_VERSION }}
|
||||||
|
|
||||||
|
# Don't cache on Windows; the cache ends up being very large and
|
||||||
|
# the Windows implementation of the cache task uses a much slower archiver.
|
||||||
- name: Get npm cache directory
|
- name: Get npm cache directory
|
||||||
|
if: runner.os != 'Windows'
|
||||||
id: npm-cache
|
id: npm-cache
|
||||||
run: |
|
run: |
|
||||||
echo "::set-output name=dir::$(npm config get cache)"
|
echo "::set-output name=dir::$(npm config get cache)"
|
||||||
- uses: actions/cache@v2
|
- uses: actions/cache@v2
|
||||||
|
if: runner.os != 'Windows'
|
||||||
with:
|
with:
|
||||||
path: ${{ steps.npm-cache.outputs.dir }}
|
path: ${{ steps.npm-cache.outputs.dir }}
|
||||||
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
|
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
|
||||||
|
@ -28,6 +28,7 @@ export interface AnalysisResults {
|
|||||||
fatalErrorOccurred: boolean;
|
fatalErrorOccurred: boolean;
|
||||||
configParseErrorOccurred: boolean;
|
configParseErrorOccurred: boolean;
|
||||||
elapsedTime: number;
|
elapsedTime: number;
|
||||||
|
error?: Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type AnalysisCompleteCallback = (results: AnalysisResults) => void;
|
export type AnalysisCompleteCallback = (results: AnalysisResults) => void;
|
||||||
@ -85,6 +86,7 @@ export function analyzeProgram(
|
|||||||
fatalErrorOccurred: true,
|
fatalErrorOccurred: true,
|
||||||
configParseErrorOccurred: false,
|
configParseErrorOccurred: false,
|
||||||
elapsedTime: 0,
|
elapsedTime: 0,
|
||||||
|
error: debug.getSerializableError(e),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ import {
|
|||||||
stripFileExtension,
|
stripFileExtension,
|
||||||
stripTrailingDirectorySeparator,
|
stripTrailingDirectorySeparator,
|
||||||
} from '../common/pathUtils';
|
} from '../common/pathUtils';
|
||||||
import { versionToString } from '../common/pythonVersion';
|
import { getPythonVersionStrings } from '../common/pythonVersion';
|
||||||
import { equateStringsCaseInsensitive } from '../common/stringUtils';
|
import { equateStringsCaseInsensitive } from '../common/stringUtils';
|
||||||
import * as StringUtils from '../common/stringUtils';
|
import * as StringUtils from '../common/stringUtils';
|
||||||
import { isIdentifierChar, isIdentifierStartChar } from '../parser/characters';
|
import { isIdentifierChar, isIdentifierStartChar } from '../parser/characters';
|
||||||
@ -396,17 +396,16 @@ export class ImportResolver {
|
|||||||
const importFailureInfo: string[] = [];
|
const importFailureInfo: string[] = [];
|
||||||
const roots = [];
|
const roots = [];
|
||||||
|
|
||||||
const pythonVersion = execEnv.pythonVersion;
|
const versionFolders = getPythonVersionStrings(execEnv.pythonVersion);
|
||||||
const minorVersion = pythonVersion & 0xff;
|
|
||||||
const versionFolders = ['2and3', '3'];
|
|
||||||
if (minorVersion > 0) {
|
|
||||||
versionFolders.push(versionToString(0x300 + minorVersion));
|
|
||||||
}
|
|
||||||
|
|
||||||
const stdTypeshed = this._getTypeshedPath(true, execEnv, importFailureInfo);
|
const stdTypeshed = this._getTypeshedPath(true, execEnv, importFailureInfo);
|
||||||
if (stdTypeshed) {
|
if (stdTypeshed) {
|
||||||
if (useTypeshedVersionedFolders) {
|
if (useTypeshedVersionedFolders) {
|
||||||
roots.push(...versionFolders.map((vf) => combinePaths(stdTypeshed, vf)));
|
for (const version of versionFolders) {
|
||||||
|
const path = combinePaths(stdTypeshed, version);
|
||||||
|
if (this.fileSystem.existsSync(path)) {
|
||||||
|
roots.push(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
roots.push(stdTypeshed);
|
roots.push(stdTypeshed);
|
||||||
}
|
}
|
||||||
@ -422,7 +421,12 @@ export class ImportResolver {
|
|||||||
const typeshedPath = this._getTypeshedPath(false, execEnv, importFailureInfo);
|
const typeshedPath = this._getTypeshedPath(false, execEnv, importFailureInfo);
|
||||||
if (typeshedPath) {
|
if (typeshedPath) {
|
||||||
if (useTypeshedVersionedFolders) {
|
if (useTypeshedVersionedFolders) {
|
||||||
roots.push(...versionFolders.map((vf) => combinePaths(typeshedPath, vf)));
|
for (const version of versionFolders) {
|
||||||
|
const path = combinePaths(typeshedPath, version);
|
||||||
|
if (this.fileSystem.existsSync(path)) {
|
||||||
|
roots.push(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
roots.push(typeshedPath);
|
roots.push(typeshedPath);
|
||||||
}
|
}
|
||||||
@ -976,13 +980,7 @@ export class ImportResolver {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const pythonVersion = execEnv.pythonVersion;
|
for (const pythonVersionString of getPythonVersionStrings(execEnv.pythonVersion)) {
|
||||||
let minorVersion = pythonVersion & 0xff;
|
|
||||||
|
|
||||||
// Search for module starting at "3.x" down to "3.1", then "3", then "2and3".
|
|
||||||
while (true) {
|
|
||||||
const pythonVersionString =
|
|
||||||
minorVersion > 0 ? versionToString(0x300 + minorVersion) : minorVersion === 0 ? '3' : '2and3';
|
|
||||||
const testPath = combinePaths(typeshedPath, pythonVersionString);
|
const testPath = combinePaths(typeshedPath, pythonVersionString);
|
||||||
if (this.fileSystem.existsSync(testPath)) {
|
if (this.fileSystem.existsSync(testPath)) {
|
||||||
const importInfo = this.resolveAbsoluteImport(
|
const importInfo = this.resolveAbsoluteImport(
|
||||||
@ -996,12 +994,6 @@ export class ImportResolver {
|
|||||||
return importInfo;
|
return importInfo;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// We use -1 to indicate "2and3", which is searched after "3.0".
|
|
||||||
if (minorVersion === -1) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
minorVersion--;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
importFailureInfo.push(`Typeshed path not found`);
|
importFailureInfo.push(`Typeshed path not found`);
|
||||||
@ -1021,23 +1013,11 @@ export class ImportResolver {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const pythonVersion = execEnv.pythonVersion;
|
for (const pythonVersionString of getPythonVersionStrings(execEnv.pythonVersion)) {
|
||||||
let minorVersion = pythonVersion & 0xff;
|
|
||||||
|
|
||||||
// Search for module starting at "3.x" down to "3.1", then "3", then "2and3".
|
|
||||||
while (true) {
|
|
||||||
const pythonVersionString =
|
|
||||||
minorVersion > 0 ? versionToString(0x300 + minorVersion) : minorVersion === 0 ? '3' : '2and3';
|
|
||||||
const testPath = combinePaths(typeshedPath, pythonVersionString);
|
const testPath = combinePaths(typeshedPath, pythonVersionString);
|
||||||
if (this.fileSystem.existsSync(testPath)) {
|
if (this.fileSystem.existsSync(testPath)) {
|
||||||
this._getCompletionSuggestionsAbsolute(testPath, moduleDescriptor, suggestions, similarityLimit);
|
this._getCompletionSuggestionsAbsolute(testPath, moduleDescriptor, suggestions, similarityLimit);
|
||||||
}
|
}
|
||||||
|
|
||||||
// We use -1 to indicate "2and3", which is searched after "3.0".
|
|
||||||
if (minorVersion === -1) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
minorVersion--;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,9 +47,9 @@ import {
|
|||||||
} from '../languageService/autoImporter';
|
} from '../languageService/autoImporter';
|
||||||
import { CallHierarchyProvider } from '../languageService/callHierarchyProvider';
|
import { CallHierarchyProvider } from '../languageService/callHierarchyProvider';
|
||||||
import { CompletionResults } from '../languageService/completionProvider';
|
import { CompletionResults } from '../languageService/completionProvider';
|
||||||
import { IndexResults } from '../languageService/documentSymbolProvider';
|
import { IndexOptions, IndexResults, WorkspaceSymbolCallback } from '../languageService/documentSymbolProvider';
|
||||||
import { HoverResults } from '../languageService/hoverProvider';
|
import { HoverResults } from '../languageService/hoverProvider';
|
||||||
import { ReferencesResult } from '../languageService/referencesProvider';
|
import { ReferenceCallback, ReferencesResult } from '../languageService/referencesProvider';
|
||||||
import { SignatureHelpResults } from '../languageService/signatureHelpProvider';
|
import { SignatureHelpResults } from '../languageService/signatureHelpProvider';
|
||||||
import { ImportLookupResult } from './analyzerFileInfo';
|
import { ImportLookupResult } from './analyzerFileInfo';
|
||||||
import * as AnalyzerNodeInfo from './analyzerNodeInfo';
|
import * as AnalyzerNodeInfo from './analyzerNodeInfo';
|
||||||
@ -142,10 +142,10 @@ export class Program {
|
|||||||
initialConfigOptions: ConfigOptions,
|
initialConfigOptions: ConfigOptions,
|
||||||
console?: ConsoleInterface,
|
console?: ConsoleInterface,
|
||||||
private _extension?: LanguageServiceExtension,
|
private _extension?: LanguageServiceExtension,
|
||||||
logPrefix = 'FG'
|
logTracker?: LogTracker
|
||||||
) {
|
) {
|
||||||
this._console = console || new StandardConsole();
|
this._console = console || new StandardConsole();
|
||||||
this._logTracker = new LogTracker(console, logPrefix);
|
this._logTracker = logTracker ?? new LogTracker(console, 'FG');
|
||||||
this._importResolver = initialImportResolver;
|
this._importResolver = initialImportResolver;
|
||||||
this._configOptions = initialConfigOptions;
|
this._configOptions = initialConfigOptions;
|
||||||
this._createNewEvaluator();
|
this._createNewEvaluator();
|
||||||
@ -436,19 +436,24 @@ export class Program {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let count = 0;
|
||||||
return this._runEvaluatorWithCancellationToken(token, () => {
|
return this._runEvaluatorWithCancellationToken(token, () => {
|
||||||
// Go through all workspace files to create indexing data.
|
// Go through all workspace files to create indexing data.
|
||||||
// This will cause all files in the workspace to be parsed and bound. We might
|
// This will cause all files in the workspace to be parsed and bound. But
|
||||||
// need to drop some of those parse tree and binding info once indexing is done
|
// _handleMemoryHighUsage will make sure we don't OOM
|
||||||
// if it didn't exist before.
|
|
||||||
for (const sourceFileInfo of this._sourceFileList) {
|
for (const sourceFileInfo of this._sourceFileList) {
|
||||||
if (!this._isUserCode(sourceFileInfo)) {
|
if (!this._isUserCode(sourceFileInfo)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._bindFile(sourceFileInfo);
|
this._bindFile(sourceFileInfo);
|
||||||
const results = sourceFileInfo.sourceFile.index(false, token);
|
const results = sourceFileInfo.sourceFile.index({ indexingForAutoImportMode: false }, token);
|
||||||
if (results) {
|
if (results) {
|
||||||
|
if (++count > 2000) {
|
||||||
|
this._console.warn(`Workspace indexing has hit its upper limit: 2000 files`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
callback(sourceFileInfo.sourceFile.getFilePath(), results);
|
callback(sourceFileInfo.sourceFile.getFilePath(), results);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -658,12 +663,12 @@ export class Program {
|
|||||||
return this._evaluator;
|
return this._evaluator;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _parseFile(fileToParse: SourceFileInfo) {
|
private _parseFile(fileToParse: SourceFileInfo, content?: string) {
|
||||||
if (!this._isFileNeeded(fileToParse) || !fileToParse.sourceFile.isParseRequired()) {
|
if (!this._isFileNeeded(fileToParse) || !fileToParse.sourceFile.isParseRequired()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fileToParse.sourceFile.parse(this._configOptions, this._importResolver)) {
|
if (fileToParse.sourceFile.parse(this._configOptions, this._importResolver, content)) {
|
||||||
this._parsedFileCount++;
|
this._parsedFileCount++;
|
||||||
this._updateSourceFileImports(fileToParse, this._configOptions);
|
this._updateSourceFileImports(fileToParse, this._configOptions);
|
||||||
}
|
}
|
||||||
@ -683,12 +688,12 @@ export class Program {
|
|||||||
|
|
||||||
// Binds the specified file and all of its dependencies, recursively. If
|
// Binds the specified file and all of its dependencies, recursively. If
|
||||||
// it runs out of time, it returns true. If it completes, it returns false.
|
// it runs out of time, it returns true. If it completes, it returns false.
|
||||||
private _bindFile(fileToAnalyze: SourceFileInfo): void {
|
private _bindFile(fileToAnalyze: SourceFileInfo, content?: string): void {
|
||||||
if (!this._isFileNeeded(fileToAnalyze) || !fileToAnalyze.sourceFile.isBindingRequired()) {
|
if (!this._isFileNeeded(fileToAnalyze) || !fileToAnalyze.sourceFile.isBindingRequired()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._parseFile(fileToAnalyze);
|
this._parseFile(fileToAnalyze, content);
|
||||||
|
|
||||||
// We need to parse and bind the builtins import first.
|
// We need to parse and bind the builtins import first.
|
||||||
let builtinsScope: Scope | undefined;
|
let builtinsScope: Scope | undefined;
|
||||||
@ -737,9 +742,16 @@ export class Program {
|
|||||||
|
|
||||||
// Build a map of all modules within this program and the module-
|
// Build a map of all modules within this program and the module-
|
||||||
// level scope that contains the symbol table for the module.
|
// level scope that contains the symbol table for the module.
|
||||||
private _buildModuleSymbolsMap(sourceFileToExclude: SourceFileInfo, token: CancellationToken): ModuleSymbolMap {
|
private _buildModuleSymbolsMap(
|
||||||
|
sourceFileToExclude: SourceFileInfo,
|
||||||
|
userFileOnly: boolean,
|
||||||
|
token: CancellationToken
|
||||||
|
): ModuleSymbolMap {
|
||||||
|
// If we have library map, always use the map for library symbols.
|
||||||
return buildModuleSymbolsMap(
|
return buildModuleSymbolsMap(
|
||||||
this._sourceFileList.filter((s) => s !== sourceFileToExclude),
|
this._sourceFileList.filter(
|
||||||
|
(s) => s !== sourceFileToExclude && (userFileOnly ? this._isUserCode(s) : true)
|
||||||
|
),
|
||||||
token
|
token
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -972,9 +984,9 @@ export class Program {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const writtenWord = fileContents.substr(textRange.start, textRange.length);
|
const writtenWord = fileContents.substr(textRange.start, textRange.length);
|
||||||
const map = this._buildModuleSymbolsMap(sourceFileInfo, token);
|
const map = this._buildModuleSymbolsMap(sourceFileInfo, !!libraryMap, token);
|
||||||
const autoImporter = new AutoImporter(
|
const autoImporter = new AutoImporter(
|
||||||
this._configOptions,
|
this._configOptions.findExecEnvironment(filePath),
|
||||||
this._importResolver,
|
this._importResolver,
|
||||||
parseTree,
|
parseTree,
|
||||||
range.start,
|
range.start,
|
||||||
@ -1084,16 +1096,17 @@ export class Program {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getReferencesForPosition(
|
reportReferencesForPosition(
|
||||||
filePath: string,
|
filePath: string,
|
||||||
position: Position,
|
position: Position,
|
||||||
includeDeclaration: boolean,
|
includeDeclaration: boolean,
|
||||||
|
reporter: ReferenceCallback,
|
||||||
token: CancellationToken
|
token: CancellationToken
|
||||||
): DocumentRange[] | undefined {
|
) {
|
||||||
return this._runEvaluatorWithCancellationToken(token, () => {
|
this._runEvaluatorWithCancellationToken(token, () => {
|
||||||
const sourceFileInfo = this._getSourceFileInfoFromPath(filePath);
|
const sourceFileInfo = this._getSourceFileInfoFromPath(filePath);
|
||||||
if (!sourceFileInfo) {
|
if (!sourceFileInfo) {
|
||||||
return undefined;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const invokedFromUserFile = this._isUserCode(sourceFileInfo);
|
const invokedFromUserFile = this._isUserCode(sourceFileInfo);
|
||||||
@ -1104,11 +1117,12 @@ export class Program {
|
|||||||
this._createSourceMapper(execEnv),
|
this._createSourceMapper(execEnv),
|
||||||
position,
|
position,
|
||||||
this._evaluator!,
|
this._evaluator!,
|
||||||
|
reporter,
|
||||||
token
|
token
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!referencesResult) {
|
if (!referencesResult) {
|
||||||
return undefined;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do we need to do a global search as well?
|
// Do we need to do a global search as well?
|
||||||
@ -1155,19 +1169,18 @@ export class Program {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const tempResult: ReferencesResult = {
|
const tempResult = new ReferencesResult(
|
||||||
requiresGlobalSearch: referencesResult.requiresGlobalSearch,
|
referencesResult.requiresGlobalSearch,
|
||||||
nodeAtOffset: referencesResult.nodeAtOffset,
|
referencesResult.nodeAtOffset,
|
||||||
symbolName: referencesResult.symbolName,
|
referencesResult.symbolName,
|
||||||
declarations: referencesResult.declarations,
|
referencesResult.declarations
|
||||||
locations: [],
|
);
|
||||||
};
|
|
||||||
|
|
||||||
declFileInfo.sourceFile.addReferences(tempResult, includeDeclaration, this._evaluator!, token);
|
declFileInfo.sourceFile.addReferences(tempResult, includeDeclaration, this._evaluator!, token);
|
||||||
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.path === decl.path && doesRangeContain(decl.range, loc.range)) {
|
||||||
referencesResult.locations.push(loc);
|
referencesResult.addLocations(loc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1175,13 +1188,11 @@ export class Program {
|
|||||||
} else {
|
} else {
|
||||||
sourceFileInfo.sourceFile.addReferences(referencesResult, includeDeclaration, this._evaluator!, token);
|
sourceFileInfo.sourceFile.addReferences(referencesResult, includeDeclaration, this._evaluator!, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
return referencesResult.locations;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getFileIndex(filePath: string, importSymbolsOnly: boolean, token: CancellationToken): IndexResults | undefined {
|
getFileIndex(filePath: string, options: IndexOptions, token: CancellationToken): IndexResults | undefined {
|
||||||
if (importSymbolsOnly) {
|
if (options.indexingForAutoImportMode) {
|
||||||
// Memory optimization. We only want to hold onto symbols
|
// Memory optimization. We only want to hold onto symbols
|
||||||
// usable outside when importSymbolsOnly is on.
|
// usable outside when importSymbolsOnly is on.
|
||||||
const name = stripFileExtension(getFileName(filePath));
|
const name = stripFileExtension(getFileName(filePath));
|
||||||
@ -1198,8 +1209,26 @@ export class Program {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._bindFile(sourceFileInfo);
|
let content: string | undefined = undefined;
|
||||||
return sourceFileInfo.sourceFile.index(importSymbolsOnly, token);
|
if (
|
||||||
|
options.indexingForAutoImportMode &&
|
||||||
|
!sourceFileInfo.sourceFile.isStubFile() &&
|
||||||
|
sourceFileInfo.sourceFile.getClientVersion() === null
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
// Perf optimization. if py file doesn't contain __all__
|
||||||
|
// No need to parse and bind.
|
||||||
|
content = this._fs.readFileSync(filePath, 'utf8');
|
||||||
|
if (content.indexOf('__all__') < 0) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
content = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this._bindFile(sourceFileInfo, content);
|
||||||
|
return sourceFileInfo.sourceFile.index(options, token);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1217,8 +1246,8 @@ export class Program {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
addSymbolsForWorkspace(symbolList: SymbolInformation[], query: string, token: CancellationToken) {
|
reportSymbolsForWorkspace(query: string, reporter: WorkspaceSymbolCallback, token: CancellationToken) {
|
||||||
return this._runEvaluatorWithCancellationToken(token, () => {
|
this._runEvaluatorWithCancellationToken(token, () => {
|
||||||
// Don't do a search if the query is empty. We'll return
|
// Don't do a search if the query is empty. We'll return
|
||||||
// too many results in this case.
|
// too many results in this case.
|
||||||
if (!query) {
|
if (!query) {
|
||||||
@ -1236,7 +1265,10 @@ export class Program {
|
|||||||
this._bindFile(sourceFileInfo);
|
this._bindFile(sourceFileInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceFileInfo.sourceFile.addSymbolsForDocument(symbolList, query, token);
|
const symbolList = sourceFileInfo.sourceFile.getSymbolsForDocument(query, token);
|
||||||
|
if (symbolList.length > 0) {
|
||||||
|
reporter(symbolList);
|
||||||
|
}
|
||||||
|
|
||||||
// This operation can consume significant memory, so check
|
// This operation can consume significant memory, so check
|
||||||
// for situations where we need to discard the type cache.
|
// for situations where we need to discard the type cache.
|
||||||
@ -1334,7 +1366,7 @@ export class Program {
|
|||||||
this._evaluator!,
|
this._evaluator!,
|
||||||
this._createSourceMapper(execEnv, /* mapCompiled */ true),
|
this._createSourceMapper(execEnv, /* mapCompiled */ true),
|
||||||
libraryMap,
|
libraryMap,
|
||||||
() => this._buildModuleSymbolsMap(sourceFileInfo, token),
|
() => this._buildModuleSymbolsMap(sourceFileInfo, !!libraryMap, token),
|
||||||
token
|
token
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -1384,7 +1416,7 @@ export class Program {
|
|||||||
this._evaluator!,
|
this._evaluator!,
|
||||||
this._createSourceMapper(execEnv, /* mapCompiled */ true),
|
this._createSourceMapper(execEnv, /* mapCompiled */ true),
|
||||||
libraryMap,
|
libraryMap,
|
||||||
() => this._buildModuleSymbolsMap(sourceFileInfo, token),
|
() => this._buildModuleSymbolsMap(sourceFileInfo, !!libraryMap, token),
|
||||||
completionItem,
|
completionItem,
|
||||||
token
|
token
|
||||||
);
|
);
|
||||||
@ -1410,6 +1442,7 @@ export class Program {
|
|||||||
this._createSourceMapper(execEnv),
|
this._createSourceMapper(execEnv),
|
||||||
position,
|
position,
|
||||||
this._evaluator!,
|
this._evaluator!,
|
||||||
|
undefined,
|
||||||
token
|
token
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1472,6 +1505,7 @@ export class Program {
|
|||||||
this._createSourceMapper(execEnv),
|
this._createSourceMapper(execEnv),
|
||||||
position,
|
position,
|
||||||
this._evaluator!,
|
this._evaluator!,
|
||||||
|
undefined,
|
||||||
token
|
token
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1508,6 +1542,7 @@ export class Program {
|
|||||||
this._createSourceMapper(execEnv),
|
this._createSourceMapper(execEnv),
|
||||||
position,
|
position,
|
||||||
this._evaluator!,
|
this._evaluator!,
|
||||||
|
undefined,
|
||||||
token
|
token
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1563,6 +1598,7 @@ export class Program {
|
|||||||
this._createSourceMapper(execEnv),
|
this._createSourceMapper(execEnv),
|
||||||
position,
|
position,
|
||||||
this._evaluator!,
|
this._evaluator!,
|
||||||
|
undefined,
|
||||||
token
|
token
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -46,8 +46,9 @@ import {
|
|||||||
import { DocumentRange, Position, Range } from '../common/textRange';
|
import { DocumentRange, Position, Range } from '../common/textRange';
|
||||||
import { timingStats } from '../common/timing';
|
import { timingStats } from '../common/timing';
|
||||||
import { CompletionResults } from '../languageService/completionProvider';
|
import { CompletionResults } from '../languageService/completionProvider';
|
||||||
import { IndexResults } from '../languageService/documentSymbolProvider';
|
import { IndexResults, WorkspaceSymbolCallback } from '../languageService/documentSymbolProvider';
|
||||||
import { HoverResults } from '../languageService/hoverProvider';
|
import { HoverResults } from '../languageService/hoverProvider';
|
||||||
|
import { ReferenceCallback } from '../languageService/referencesProvider';
|
||||||
import { SignatureHelpResults } from '../languageService/signatureHelpProvider';
|
import { SignatureHelpResults } from '../languageService/signatureHelpProvider';
|
||||||
import { AnalysisCompleteCallback } from './analysis';
|
import { AnalysisCompleteCallback } from './analysis';
|
||||||
import { BackgroundAnalysisProgram, BackgroundAnalysisProgramFactory } from './backgroundAnalysisProgram';
|
import { BackgroundAnalysisProgram, BackgroundAnalysisProgramFactory } from './backgroundAnalysisProgram';
|
||||||
@ -244,21 +245,22 @@ export class AnalyzerService {
|
|||||||
return this._program.getDefinitionsForPosition(filePath, position, token);
|
return this._program.getDefinitionsForPosition(filePath, position, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
getReferencesForPosition(
|
reportReferencesForPosition(
|
||||||
filePath: string,
|
filePath: string,
|
||||||
position: Position,
|
position: Position,
|
||||||
includeDeclaration: boolean,
|
includeDeclaration: boolean,
|
||||||
|
reporter: ReferenceCallback,
|
||||||
token: CancellationToken
|
token: CancellationToken
|
||||||
): DocumentRange[] | undefined {
|
) {
|
||||||
return this._program.getReferencesForPosition(filePath, position, includeDeclaration, token);
|
this._program.reportReferencesForPosition(filePath, position, includeDeclaration, reporter, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
addSymbolsForDocument(filePath: string, symbolList: DocumentSymbol[], token: CancellationToken) {
|
addSymbolsForDocument(filePath: string, symbolList: DocumentSymbol[], token: CancellationToken) {
|
||||||
this._program.addSymbolsForDocument(filePath, symbolList, token);
|
this._program.addSymbolsForDocument(filePath, symbolList, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
addSymbolsForWorkspace(symbolList: SymbolInformation[], query: string, token: CancellationToken) {
|
reportSymbolsForWorkspace(query: string, reporter: WorkspaceSymbolCallback, token: CancellationToken) {
|
||||||
this._program.addSymbolsForWorkspace(symbolList, query, token);
|
this._program.reportSymbolsForWorkspace(query, reporter, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
getHoverForPosition(filePath: string, position: Position, token: CancellationToken): HoverResults | undefined {
|
getHoverForPosition(filePath: string, position: Position, token: CancellationToken): HoverResults | undefined {
|
||||||
|
@ -36,10 +36,10 @@ import { CompletionResults } from '../languageService/completionProvider';
|
|||||||
import { CompletionItemData, CompletionProvider } from '../languageService/completionProvider';
|
import { CompletionItemData, CompletionProvider } from '../languageService/completionProvider';
|
||||||
import { DefinitionProvider } from '../languageService/definitionProvider';
|
import { DefinitionProvider } from '../languageService/definitionProvider';
|
||||||
import { DocumentHighlightProvider } from '../languageService/documentHighlightProvider';
|
import { DocumentHighlightProvider } from '../languageService/documentHighlightProvider';
|
||||||
import { DocumentSymbolProvider, IndexResults } from '../languageService/documentSymbolProvider';
|
import { DocumentSymbolProvider, IndexOptions, IndexResults } from '../languageService/documentSymbolProvider';
|
||||||
import { HoverProvider, HoverResults } from '../languageService/hoverProvider';
|
import { HoverProvider, HoverResults } from '../languageService/hoverProvider';
|
||||||
import { performQuickAction } from '../languageService/quickActions';
|
import { performQuickAction } from '../languageService/quickActions';
|
||||||
import { ReferencesProvider, ReferencesResult } from '../languageService/referencesProvider';
|
import { ReferenceCallback, ReferencesProvider, ReferencesResult } from '../languageService/referencesProvider';
|
||||||
import { SignatureHelpProvider, SignatureHelpResults } from '../languageService/signatureHelpProvider';
|
import { SignatureHelpProvider, SignatureHelpResults } from '../languageService/signatureHelpProvider';
|
||||||
import { Localizer } from '../localization/localize';
|
import { Localizer } from '../localization/localize';
|
||||||
import { ModuleNode } from '../parser/parseNodes';
|
import { ModuleNode } from '../parser/parseNodes';
|
||||||
@ -508,7 +508,7 @@ export class SourceFile {
|
|||||||
// Parse the file and update the state. Callers should wait for completion
|
// Parse the file and update the state. Callers should wait for completion
|
||||||
// (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): boolean {
|
parse(configOptions: ConfigOptions, importResolver: ImportResolver, content?: string): boolean {
|
||||||
return this._logTracker.log(`parsing: ${this._filePath}`, (logState) => {
|
return this._logTracker.log(`parsing: ${this._filePath}`, (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()) {
|
||||||
@ -520,14 +520,15 @@ export class SourceFile {
|
|||||||
let fileContents = this._fileContents;
|
let fileContents = this._fileContents;
|
||||||
if (this._clientVersion === null) {
|
if (this._clientVersion === null) {
|
||||||
try {
|
try {
|
||||||
timingStats.readFileTime.timeOperation(() => {
|
const elapsedTime = timingStats.readFileTime.timeOperation(() => {
|
||||||
// Read the file's contents.
|
// Read the file's contents.
|
||||||
fileContents = this.fileSystem.readFileSync(this._filePath, 'utf8');
|
fileContents = content ?? this.fileSystem.readFileSync(this._filePath, 'utf8');
|
||||||
|
|
||||||
// Remember the length and hash for comparison purposes.
|
// Remember the length and hash for comparison purposes.
|
||||||
this._lastFileContentLength = fileContents.length;
|
this._lastFileContentLength = fileContents.length;
|
||||||
this._lastFileContentHash = StringUtils.hashString(fileContents);
|
this._lastFileContentHash = StringUtils.hashString(fileContents);
|
||||||
});
|
});
|
||||||
|
logState.add(`fs read ${elapsedTime}ms`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
diagSink.addError(`Source file could not be read`, getEmptyRange());
|
diagSink.addError(`Source file could not be read`, getEmptyRange());
|
||||||
fileContents = '';
|
fileContents = '';
|
||||||
@ -634,18 +635,22 @@ export class SourceFile {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
index(importSymbolsOnly: boolean, token: CancellationToken): IndexResults | undefined {
|
index(options: IndexOptions, token: CancellationToken): IndexResults | undefined {
|
||||||
// If we have no completed analysis job, there's nothing to do.
|
return this._logTracker.log(`indexing: ${this._filePath}`, (ls) => {
|
||||||
if (!this._parseResults || !this.isIndexingRequired()) {
|
// If we have no completed analysis job, there's nothing to do.
|
||||||
return undefined;
|
if (!this._parseResults || !this.isIndexingRequired()) {
|
||||||
}
|
ls.suppress();
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
this._indexingNeeded = false;
|
this._indexingNeeded = false;
|
||||||
const symbols = DocumentSymbolProvider.indexSymbols(this._parseResults, importSymbolsOnly, token);
|
const symbols = DocumentSymbolProvider.indexSymbols(this._parseResults, options, token);
|
||||||
|
ls.add(`found ${symbols.length}`);
|
||||||
|
|
||||||
const name = stripFileExtension(getFileName(this._filePath));
|
const name = stripFileExtension(getFileName(this._filePath));
|
||||||
const privateOrProtected = SymbolNameUtils.isPrivateOrProtectedName(name);
|
const privateOrProtected = SymbolNameUtils.isPrivateOrProtectedName(name);
|
||||||
return { privateOrProtected, symbols };
|
return { privateOrProtected, symbols };
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getDefinitionsForPosition(
|
getDefinitionsForPosition(
|
||||||
@ -672,6 +677,7 @@ export class SourceFile {
|
|||||||
sourceMapper: SourceMapper,
|
sourceMapper: SourceMapper,
|
||||||
position: Position,
|
position: Position,
|
||||||
evaluator: TypeEvaluator,
|
evaluator: TypeEvaluator,
|
||||||
|
reporter: ReferenceCallback | undefined,
|
||||||
token: CancellationToken
|
token: CancellationToken
|
||||||
): ReferencesResult | undefined {
|
): ReferencesResult | undefined {
|
||||||
// If we have no completed analysis job, there's nothing to do.
|
// If we have no completed analysis job, there's nothing to do.
|
||||||
@ -685,6 +691,7 @@ export class SourceFile {
|
|||||||
this._filePath,
|
this._filePath,
|
||||||
position,
|
position,
|
||||||
evaluator,
|
evaluator,
|
||||||
|
reporter,
|
||||||
token
|
token
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -724,18 +731,17 @@ export class SourceFile {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
addSymbolsForDocument(symbolList: SymbolInformation[], query: string, token: CancellationToken) {
|
getSymbolsForDocument(query: string, token: CancellationToken) {
|
||||||
// If we have no completed analysis job, there's nothing to do.
|
// If we have no completed analysis job, there's nothing to do.
|
||||||
if (!this._parseResults && !this._cachedIndexResults) {
|
if (!this._parseResults && !this._cachedIndexResults) {
|
||||||
return;
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
DocumentSymbolProvider.addSymbolsForDocument(
|
return DocumentSymbolProvider.getSymbolsForDocument(
|
||||||
this.getCachedIndexResults(),
|
this.getCachedIndexResults(),
|
||||||
this._parseResults,
|
this._parseResults,
|
||||||
this._filePath,
|
this._filePath,
|
||||||
query,
|
query,
|
||||||
symbolList,
|
|
||||||
token
|
token
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
* (parse node trees).
|
* (parse node trees).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ExecutionEnvironment } from '../common/configOptions';
|
import { ExecutionEnvironment, PythonPlatform } from '../common/configOptions';
|
||||||
import { ExpressionNode, NumberNode, ParseNodeType, TupleNode } from '../parser/parseNodes';
|
import { ExpressionNode, NumberNode, ParseNodeType, TupleNode } from '../parser/parseNodes';
|
||||||
import { KeywordType, OperatorType } from '../parser/tokenizerTypes';
|
import { KeywordType, OperatorType } from '../parser/tokenizerTypes';
|
||||||
|
|
||||||
@ -220,11 +220,11 @@ function _isOsNameInfoExpression(node: ExpressionNode): boolean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function _getExpectedPlatformNameFromPlatform(execEnv: ExecutionEnvironment): string | undefined {
|
function _getExpectedPlatformNameFromPlatform(execEnv: ExecutionEnvironment): string | undefined {
|
||||||
if (execEnv.pythonPlatform === 'Darwin') {
|
if (execEnv.pythonPlatform === PythonPlatform.Darwin) {
|
||||||
return 'darwin';
|
return 'darwin';
|
||||||
} else if (execEnv.pythonPlatform === 'Windows') {
|
} else if (execEnv.pythonPlatform === PythonPlatform.Windows) {
|
||||||
return 'win32';
|
return 'win32';
|
||||||
} else if (execEnv.pythonPlatform === 'Linux') {
|
} else if (execEnv.pythonPlatform === PythonPlatform.Linux) {
|
||||||
return 'linux';
|
return 'linux';
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -232,11 +232,11 @@ function _getExpectedPlatformNameFromPlatform(execEnv: ExecutionEnvironment): st
|
|||||||
}
|
}
|
||||||
|
|
||||||
function _getExpectedOsNameFromPlatform(execEnv: ExecutionEnvironment): string | undefined {
|
function _getExpectedOsNameFromPlatform(execEnv: ExecutionEnvironment): string | undefined {
|
||||||
if (execEnv.pythonPlatform === 'Darwin') {
|
if (execEnv.pythonPlatform === PythonPlatform.Darwin) {
|
||||||
return 'posix';
|
return 'posix';
|
||||||
} else if (execEnv.pythonPlatform === 'Windows') {
|
} else if (execEnv.pythonPlatform === PythonPlatform.Windows) {
|
||||||
return 'nt';
|
return 'nt';
|
||||||
} else if (execEnv.pythonPlatform === 'Linux') {
|
} else if (execEnv.pythonPlatform === PythonPlatform.Linux) {
|
||||||
return 'posix';
|
return 'posix';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,6 +32,7 @@ import { Diagnostic } from './common/diagnostic';
|
|||||||
import { FileDiagnostics } from './common/diagnosticSink';
|
import { FileDiagnostics } from './common/diagnosticSink';
|
||||||
import { LanguageServiceExtension } from './common/extensibility';
|
import { LanguageServiceExtension } from './common/extensibility';
|
||||||
import { FileSystem } from './common/fileSystem';
|
import { FileSystem } from './common/fileSystem';
|
||||||
|
import { LogTracker } from './common/logTracker';
|
||||||
import { Range } from './common/textRange';
|
import { Range } from './common/textRange';
|
||||||
import { IndexResults } from './languageService/documentSymbolProvider';
|
import { IndexResults } from './languageService/documentSymbolProvider';
|
||||||
|
|
||||||
@ -252,12 +253,13 @@ export class BackgroundAnalysisRunnerBase extends BackgroundThreadBase {
|
|||||||
|
|
||||||
this._configOptions = new ConfigOptions(data.rootDirectory);
|
this._configOptions = new ConfigOptions(data.rootDirectory);
|
||||||
this._importResolver = this.createImportResolver(this.fs, this._configOptions);
|
this._importResolver = this.createImportResolver(this.fs, this._configOptions);
|
||||||
|
const console = this.getConsole();
|
||||||
this._program = new Program(
|
this._program = new Program(
|
||||||
this._importResolver,
|
this._importResolver,
|
||||||
this._configOptions,
|
this._configOptions,
|
||||||
this.getConsole(),
|
console,
|
||||||
this._extension,
|
this._extension,
|
||||||
`BG(${threadId})`
|
new LogTracker(console, `BG(${threadId})`)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,6 +31,12 @@ import {
|
|||||||
versionToString,
|
versionToString,
|
||||||
} from './pythonVersion';
|
} from './pythonVersion';
|
||||||
|
|
||||||
|
export enum PythonPlatform {
|
||||||
|
Darwin = 'Darwin',
|
||||||
|
Windows = 'Windows',
|
||||||
|
Linux = 'Linux',
|
||||||
|
}
|
||||||
|
|
||||||
export class ExecutionEnvironment {
|
export class ExecutionEnvironment {
|
||||||
// Default to "." which indicates every file in the project.
|
// Default to "." which indicates every file in the project.
|
||||||
constructor(root: string, defaultPythonVersion?: PythonVersion, defaultPythonPlatform?: string) {
|
constructor(root: string, defaultPythonVersion?: PythonVersion, defaultPythonPlatform?: string) {
|
||||||
@ -1224,11 +1230,11 @@ export class ConfigOptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (process.platform === 'darwin') {
|
if (process.platform === 'darwin') {
|
||||||
this.defaultPythonPlatform = 'Darwin';
|
this.defaultPythonPlatform = PythonPlatform.Darwin;
|
||||||
} else if (process.platform === 'linux') {
|
} else if (process.platform === 'linux') {
|
||||||
this.defaultPythonPlatform = 'Linux';
|
this.defaultPythonPlatform = PythonPlatform.Linux;
|
||||||
} else if (process.platform === 'win32') {
|
} else if (process.platform === 'win32') {
|
||||||
this.defaultPythonPlatform = 'Windows';
|
this.defaultPythonPlatform = PythonPlatform.Windows;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.defaultPythonPlatform !== undefined) {
|
if (this.defaultPythonPlatform !== undefined) {
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { stableSort } from './collectionUtils';
|
import { stableSort } from './collectionUtils';
|
||||||
import { AnyFunction, compareValues, hasProperty } from './core';
|
import { AnyFunction, compareValues, hasProperty, isString } from './core';
|
||||||
|
|
||||||
export function assert(
|
export function assert(
|
||||||
expression: boolean,
|
expression: boolean,
|
||||||
@ -106,6 +106,25 @@ export function getErrorString(error: any): string {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getSerializableError(error: any): Error | undefined {
|
||||||
|
if (!error) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const exception = JSON.stringify(error);
|
||||||
|
if (exception.length > 2) {
|
||||||
|
// Given error object is JSON.stringify serializable. Use it as it is
|
||||||
|
// to preserve properties.
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert error to JSON.stringify serializable Error shape.
|
||||||
|
const name = error.name ? (isString(error.name) ? error.name : 'noname') : 'noname';
|
||||||
|
const message = error.message ? (isString(error.message) ? error.message : 'nomessage') : 'nomessage';
|
||||||
|
const stack = error.stack ? (isString(error.stack) ? error.stack : undefined) : undefined;
|
||||||
|
return { name, message, stack };
|
||||||
|
}
|
||||||
|
|
||||||
function getEnumMembers(enumObject: any) {
|
function getEnumMembers(enumObject: any) {
|
||||||
const result: [number, string][] = [];
|
const result: [number, string][] = [];
|
||||||
for (const name of Object.keys(enumObject)) {
|
for (const name of Object.keys(enumObject)) {
|
||||||
|
@ -914,3 +914,14 @@ export function isFileSystemCaseSensitiveInternal(fs: FileSystem) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
@ -71,3 +71,23 @@ export function versionFromMajorMinor(major: number, minor: number): PythonVersi
|
|||||||
export function is3x(version: PythonVersion): boolean {
|
export function is3x(version: PythonVersion): boolean {
|
||||||
return version >> 8 === 3;
|
return version >> 8 === 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getPythonVersionStrings(pythonVersion: PythonVersion) {
|
||||||
|
const versionStrings: string[] = [];
|
||||||
|
|
||||||
|
let minorVersion = pythonVersion & 0xff;
|
||||||
|
while (true) {
|
||||||
|
const pythonVersionString =
|
||||||
|
minorVersion > 0 ? versionToString(0x300 + minorVersion) : minorVersion === 0 ? '3' : '2and3';
|
||||||
|
versionStrings.push(pythonVersionString);
|
||||||
|
|
||||||
|
// We use -1 to indicate "2and3", which is searched after "3.0".
|
||||||
|
if (minorVersion === -1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
minorVersion--;
|
||||||
|
}
|
||||||
|
|
||||||
|
return versionStrings;
|
||||||
|
}
|
||||||
|
@ -39,8 +39,11 @@ export class TimingStat {
|
|||||||
this.isTiming = true;
|
this.isTiming = true;
|
||||||
const duration = new Duration();
|
const duration = new Duration();
|
||||||
callback();
|
callback();
|
||||||
this.totalTime += duration.getDurationInMilliseconds();
|
const elapsedTime = duration.getDurationInMilliseconds();
|
||||||
|
this.totalTime += elapsedTime;
|
||||||
this.isTiming = false;
|
this.isTiming = false;
|
||||||
|
|
||||||
|
return elapsedTime;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,10 +76,12 @@ import {
|
|||||||
import { containsPath, convertPathToUri, convertUriToPath } from './common/pathUtils';
|
import { containsPath, convertPathToUri, convertUriToPath } from './common/pathUtils';
|
||||||
import { ProgressReporter, ProgressReportTracker } from './common/progressReporter';
|
import { ProgressReporter, ProgressReportTracker } from './common/progressReporter';
|
||||||
import { convertWorkspaceEdits } from './common/textEditUtils';
|
import { convertWorkspaceEdits } from './common/textEditUtils';
|
||||||
import { Position } from './common/textRange';
|
import { DocumentRange, Position } from './common/textRange';
|
||||||
import { AnalyzerServiceExecutor } from './languageService/analyzerServiceExecutor';
|
import { AnalyzerServiceExecutor } from './languageService/analyzerServiceExecutor';
|
||||||
import { CompletionItemData, CompletionResults } from './languageService/completionProvider';
|
import { CompletionItemData, CompletionResults } from './languageService/completionProvider';
|
||||||
|
import { WorkspaceSymbolCallback } from './languageService/documentSymbolProvider';
|
||||||
import { convertHoverResults } from './languageService/hoverProvider';
|
import { convertHoverResults } from './languageService/hoverProvider';
|
||||||
|
import { ReferenceCallback } from './languageService/referencesProvider';
|
||||||
import { Localizer } from './localization/localize';
|
import { Localizer } from './localization/localize';
|
||||||
import { WorkspaceMap } from './workspaceMap';
|
import { WorkspaceMap } from './workspaceMap';
|
||||||
|
|
||||||
@ -438,7 +440,7 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
|
|||||||
return locations.map((loc) => Location.create(convertPathToUri(loc.path), loc.range));
|
return locations.map((loc) => Location.create(convertPathToUri(loc.path), loc.range));
|
||||||
});
|
});
|
||||||
|
|
||||||
this._connection.onReferences(async (params, token, reporter) => {
|
this._connection.onReferences(async (params, token, workDoneReporter, resultReporter) => {
|
||||||
if (this._pendingFindAllRefsCancellationSource) {
|
if (this._pendingFindAllRefsCancellationSource) {
|
||||||
this._pendingFindAllRefsCancellationSource.cancel();
|
this._pendingFindAllRefsCancellationSource.cancel();
|
||||||
this._pendingFindAllRefsCancellationSource = undefined;
|
this._pendingFindAllRefsCancellationSource = undefined;
|
||||||
@ -449,9 +451,10 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
|
|||||||
// any long-running actions.
|
// any long-running actions.
|
||||||
const progress = await this._getProgressReporter(
|
const progress = await this._getProgressReporter(
|
||||||
params.workDoneToken,
|
params.workDoneToken,
|
||||||
reporter,
|
workDoneReporter,
|
||||||
Localizer.CodeAction.findingReferences()
|
Localizer.CodeAction.findingReferences()
|
||||||
);
|
);
|
||||||
|
|
||||||
const source = CancelAfter(token, progress.token);
|
const source = CancelAfter(token, progress.token);
|
||||||
this._pendingFindAllRefsCancellationSource = source;
|
this._pendingFindAllRefsCancellationSource = source;
|
||||||
|
|
||||||
@ -467,18 +470,24 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const locations = workspace.serviceInstance.getReferencesForPosition(
|
const convert = (locs: DocumentRange[]): Location[] => {
|
||||||
|
return locs.map((loc) => Location.create(convertPathToUri(loc.path), loc.range));
|
||||||
|
};
|
||||||
|
|
||||||
|
const locations: Location[] = [];
|
||||||
|
const reporter: ReferenceCallback = resultReporter
|
||||||
|
? (locs) => resultReporter.report(convert(locs))
|
||||||
|
: (locs) => locations.push(...convert(locs));
|
||||||
|
|
||||||
|
workspace.serviceInstance.reportReferencesForPosition(
|
||||||
filePath,
|
filePath,
|
||||||
position,
|
position,
|
||||||
params.context.includeDeclaration,
|
params.context.includeDeclaration,
|
||||||
|
reporter,
|
||||||
source.token
|
source.token
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!locations) {
|
return locations;
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
return locations.map((loc) => Location.create(convertPathToUri(loc.path), loc.range));
|
|
||||||
} finally {
|
} finally {
|
||||||
progress.reporter.done();
|
progress.reporter.done();
|
||||||
source.dispose();
|
source.dispose();
|
||||||
@ -500,13 +509,17 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
|
|||||||
return symbolList;
|
return symbolList;
|
||||||
});
|
});
|
||||||
|
|
||||||
this._connection.onWorkspaceSymbol(async (params, token) => {
|
this._connection.onWorkspaceSymbol(async (params, token, _, resultReporter) => {
|
||||||
const symbolList: SymbolInformation[] = [];
|
const symbolList: SymbolInformation[] = [];
|
||||||
|
|
||||||
|
const reporter: WorkspaceSymbolCallback = resultReporter
|
||||||
|
? (symbols) => resultReporter.report(symbols)
|
||||||
|
: (symbols) => symbolList.push(...symbols);
|
||||||
|
|
||||||
for (const workspace of this._workspaceMap.values()) {
|
for (const workspace of this._workspaceMap.values()) {
|
||||||
await workspace.isInitialized.promise;
|
await workspace.isInitialized.promise;
|
||||||
if (!workspace.disableLanguageServices) {
|
if (!workspace.disableLanguageServices) {
|
||||||
workspace.serviceInstance.addSymbolsForWorkspace(symbolList, params.query, token);
|
workspace.serviceInstance.reportSymbolsForWorkspace(params.query, reporter, token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,6 @@
|
|||||||
|
|
||||||
import { CancellationToken, CompletionItemKind, SymbolKind } from 'vscode-languageserver';
|
import { CancellationToken, CompletionItemKind, SymbolKind } from 'vscode-languageserver';
|
||||||
|
|
||||||
import * as AnalyzerNodeInfo from '../analyzer/analyzerNodeInfo';
|
|
||||||
import { DeclarationType } from '../analyzer/declaration';
|
import { DeclarationType } from '../analyzer/declaration';
|
||||||
import { ImportResolver, ModuleNameAndType } from '../analyzer/importResolver';
|
import { ImportResolver, ModuleNameAndType } from '../analyzer/importResolver';
|
||||||
import { ImportType } from '../analyzer/importResult';
|
import { ImportType } from '../analyzer/importResult';
|
||||||
@ -23,19 +22,14 @@ import { SourceFileInfo } from '../analyzer/program';
|
|||||||
import { Symbol } from '../analyzer/symbol';
|
import { Symbol } from '../analyzer/symbol';
|
||||||
import * as SymbolNameUtils from '../analyzer/symbolNameUtils';
|
import * as SymbolNameUtils from '../analyzer/symbolNameUtils';
|
||||||
import { throwIfCancellationRequested } from '../common/cancellationUtils';
|
import { throwIfCancellationRequested } from '../common/cancellationUtils';
|
||||||
import { ConfigOptions } 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 { combinePaths, getDirectoryPath, getFileName, 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 { ParseNodeType } from '../parser/parseNodes';
|
import { ParseNodeType } from '../parser/parseNodes';
|
||||||
import { ParseResults } from '../parser/parser';
|
import { ParseResults } from '../parser/parser';
|
||||||
import {
|
import { IndexAliasData, IndexResults } from './documentSymbolProvider';
|
||||||
getIndexAliasData,
|
|
||||||
includeAliasDeclarationInIndex,
|
|
||||||
IndexAliasData,
|
|
||||||
IndexResults,
|
|
||||||
} from './documentSymbolProvider';
|
|
||||||
|
|
||||||
export interface AutoImportSymbol {
|
export interface AutoImportSymbol {
|
||||||
readonly importAlias?: IndexAliasData;
|
readonly importAlias?: IndexAliasData;
|
||||||
@ -74,7 +68,6 @@ export function buildModuleSymbolsMap(files: SourceFileInfo[], token: Cancellati
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const fileInfo = AnalyzerNodeInfo.getFileInfo(file.sourceFile.getParseResults()!.parseTree);
|
|
||||||
moduleSymbolMap.set(filePath, {
|
moduleSymbolMap.set(filePath, {
|
||||||
forEach(callbackfn: (value: AutoImportSymbol, key: string) => void): void {
|
forEach(callbackfn: (value: AutoImportSymbol, key: string) => void): void {
|
||||||
symbolTable.forEach((symbol, name) => {
|
symbolTable.forEach((symbol, name) => {
|
||||||
@ -92,13 +85,10 @@ export function buildModuleSymbolsMap(files: SourceFileInfo[], token: Cancellati
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let importAlias: IndexAliasData | undefined;
|
|
||||||
if (declaration.type === DeclarationType.Alias) {
|
if (declaration.type === DeclarationType.Alias) {
|
||||||
if (!includeAliasDeclarationInIndex(declaration)) {
|
// We don't include import alias in auto import
|
||||||
return;
|
// for workspace files.
|
||||||
}
|
return;
|
||||||
|
|
||||||
importAlias = getIndexAliasData(fileInfo?.importLookup, declaration);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const variableKind =
|
const variableKind =
|
||||||
@ -107,7 +97,7 @@ export function buildModuleSymbolsMap(files: SourceFileInfo[], token: Cancellati
|
|||||||
!declaration.isFinal
|
!declaration.isFinal
|
||||||
? CompletionItemKind.Variable
|
? CompletionItemKind.Variable
|
||||||
: undefined;
|
: undefined;
|
||||||
callbackfn({ importAlias, symbol, kind: variableKind }, name);
|
callbackfn({ symbol, kind: variableKind }, name);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -146,14 +136,14 @@ interface ImportAliasData {
|
|||||||
importParts: ImportParts;
|
importParts: ImportParts;
|
||||||
importGroup: ImportGroup;
|
importGroup: ImportGroup;
|
||||||
symbol?: Symbol;
|
symbol?: Symbol;
|
||||||
|
kind?: CompletionItemKind;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AutoImporter {
|
export class AutoImporter {
|
||||||
private _importStatements: ImportStatements;
|
private _importStatements: ImportStatements;
|
||||||
private _filePath: string;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private _configOptions: ConfigOptions,
|
private _execEnvironment: ExecutionEnvironment,
|
||||||
private _importResolver: ImportResolver,
|
private _importResolver: ImportResolver,
|
||||||
private _parseResults: ParseResults,
|
private _parseResults: ParseResults,
|
||||||
private _invocationPosition: Position,
|
private _invocationPosition: Position,
|
||||||
@ -161,7 +151,6 @@ export class AutoImporter {
|
|||||||
private _moduleSymbolMap: ModuleSymbolMap,
|
private _moduleSymbolMap: ModuleSymbolMap,
|
||||||
private _libraryMap?: Map<string, IndexResults>
|
private _libraryMap?: Map<string, IndexResults>
|
||||||
) {
|
) {
|
||||||
this._filePath = AnalyzerNodeInfo.getFileInfo(this._parseResults.parseTree)!.filePath;
|
|
||||||
this._importStatements = getTopLevelImports(this._parseResults.parseTree);
|
this._importStatements = getTopLevelImports(this._parseResults.parseTree);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -309,6 +298,7 @@ export class AutoImporter {
|
|||||||
},
|
},
|
||||||
importGroup,
|
importGroup,
|
||||||
symbol: autoImportSymbol.symbol,
|
symbol: autoImportSymbol.symbol,
|
||||||
|
kind: convertSymbolKindToCompletionItemKind(autoImportSymbol.importAlias.kind),
|
||||||
},
|
},
|
||||||
importAliasMap
|
importAliasMap
|
||||||
);
|
);
|
||||||
@ -356,7 +346,7 @@ export class AutoImporter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this._addToImportAliasMap(
|
this._addToImportAliasMap(
|
||||||
{ modulePath: filePath, originalName: importParts.importName },
|
{ modulePath: filePath, originalName: importParts.importName, kind: SymbolKind.Module },
|
||||||
{ importParts, importGroup },
|
{ importParts, importGroup },
|
||||||
importAliasMap
|
importAliasMap
|
||||||
);
|
);
|
||||||
@ -386,6 +376,7 @@ export class AutoImporter {
|
|||||||
name: importAliasData.importParts.importName,
|
name: importAliasData.importParts.importName,
|
||||||
alias: aliasName,
|
alias: aliasName,
|
||||||
symbol: importAliasData.symbol,
|
symbol: importAliasData.symbol,
|
||||||
|
kind: importAliasData.kind,
|
||||||
source: importAliasData.importParts.importFrom,
|
source: importAliasData.importParts.importFrom,
|
||||||
edits: autoImportTextEdits,
|
edits: autoImportTextEdits,
|
||||||
});
|
});
|
||||||
@ -525,8 +516,7 @@ export class AutoImporter {
|
|||||||
// 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(filePath: string): ModuleNameAndType {
|
||||||
const execEnvironment = this._configOptions.findExecEnvironment(this._filePath);
|
return this._importResolver.getModuleNameForImport(filePath, this._execEnvironment);
|
||||||
return this._importResolver.getModuleNameForImport(filePath, execEnvironment);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getImportGroupFromModuleNameAndType(moduleNameAndType: ModuleNameAndType): ImportGroup {
|
private _getImportGroupFromModuleNameAndType(moduleNameAndType: ModuleNameAndType): ImportGroup {
|
||||||
|
@ -965,7 +965,7 @@ export class CompletionProvider {
|
|||||||
private _getAutoImportCompletions(priorWord: string, completionList: CompletionList) {
|
private _getAutoImportCompletions(priorWord: string, completionList: CompletionList) {
|
||||||
const moduleSymbolMap = this._moduleSymbolsCallback();
|
const moduleSymbolMap = this._moduleSymbolsCallback();
|
||||||
const autoImporter = new AutoImporter(
|
const autoImporter = new AutoImporter(
|
||||||
this._configOptions,
|
this._configOptions.findExecEnvironment(this._filePath),
|
||||||
this._importResolver,
|
this._importResolver,
|
||||||
this._parseResults,
|
this._parseResults,
|
||||||
this._position,
|
this._position,
|
||||||
|
@ -20,24 +20,27 @@ import { getLastTypedDeclaredForSymbol } from '../analyzer/symbolUtils';
|
|||||||
import { TypeEvaluator } from '../analyzer/typeEvaluator';
|
import { TypeEvaluator } from '../analyzer/typeEvaluator';
|
||||||
import { isProperty } from '../analyzer/typeUtils';
|
import { isProperty } from '../analyzer/typeUtils';
|
||||||
import { throwIfCancellationRequested } from '../common/cancellationUtils';
|
import { throwIfCancellationRequested } from '../common/cancellationUtils';
|
||||||
|
import { getLibraryPathWithoutExtension } from '../common/pathUtils';
|
||||||
import { convertOffsetsToRange } from '../common/positionUtils';
|
import { convertOffsetsToRange } from '../common/positionUtils';
|
||||||
import * as StringUtils from '../common/stringUtils';
|
import * as StringUtils from '../common/stringUtils';
|
||||||
import { Range } from '../common/textRange';
|
import { Range } from '../common/textRange';
|
||||||
|
import { ModuleNode, ParseNodeType } from '../parser/parseNodes';
|
||||||
import { ParseResults } from '../parser/parser';
|
import { ParseResults } from '../parser/parser';
|
||||||
|
|
||||||
export interface IndexAliasData {
|
export interface IndexAliasData {
|
||||||
readonly originalName: string;
|
readonly originalName: string;
|
||||||
readonly modulePath: string;
|
readonly modulePath: string;
|
||||||
|
readonly kind: SymbolKind;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IndexSymbolData {
|
export interface IndexSymbolData {
|
||||||
readonly name: string;
|
readonly name: string;
|
||||||
readonly alias: IndexAliasData | undefined;
|
|
||||||
readonly externallyVisible: boolean;
|
readonly externallyVisible: boolean;
|
||||||
readonly kind: SymbolKind;
|
readonly kind: SymbolKind;
|
||||||
readonly range: Range;
|
readonly alias?: IndexAliasData;
|
||||||
readonly selectionRange: Range;
|
readonly range?: Range;
|
||||||
readonly children: IndexSymbolData[];
|
readonly selectionRange?: Range;
|
||||||
|
readonly children?: IndexSymbolData[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IndexResults {
|
export interface IndexResults {
|
||||||
@ -45,30 +48,62 @@ export interface IndexResults {
|
|||||||
readonly symbols: IndexSymbolData[];
|
readonly symbols: IndexSymbolData[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function includeAliasDeclarationInIndex(declaration: AliasDeclaration): boolean {
|
export interface IndexOptions {
|
||||||
return declaration.usesLocalName && !!declaration.symbolName && declaration.path.length > 0;
|
indexingForAutoImportMode: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type WorkspaceSymbolCallback = (symbols: SymbolInformation[]) => void;
|
||||||
|
|
||||||
|
export function includeAliasDeclarationInIndex(
|
||||||
|
importLookup: ImportLookup,
|
||||||
|
modulePath: string,
|
||||||
|
declaration: AliasDeclaration
|
||||||
|
): boolean {
|
||||||
|
const aliasUsed = declaration.usesLocalName && !!declaration.symbolName && declaration.path.length > 0;
|
||||||
|
const wildcardUsed = declaration.node.nodeType === ParseNodeType.ImportFrom && declaration.node.isWildcardImport;
|
||||||
|
if (!aliasUsed && !wildcardUsed) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure imported symbol is a submodule of same package.
|
||||||
|
if (!getLibraryPathWithoutExtension(declaration.path).startsWith(modulePath)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wildcardUsed) {
|
||||||
|
// if "import *" is used, resolve the alias to see whether we should include it.
|
||||||
|
const resolved = resolveAliasDeclaration(importLookup, declaration, true);
|
||||||
|
if (!resolved) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!getLibraryPathWithoutExtension(resolved.path).startsWith(modulePath)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getIndexAliasData(
|
export function getIndexAliasData(
|
||||||
importLookup: ImportLookup | undefined,
|
importLookup: ImportLookup,
|
||||||
declaration: AliasDeclaration
|
declaration: AliasDeclaration
|
||||||
): IndexAliasData | undefined {
|
): IndexAliasData | undefined {
|
||||||
if (!declaration.symbolName) {
|
if (!declaration.symbolName) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const aliasData = { originalName: declaration.symbolName!, modulePath: declaration.path };
|
|
||||||
if (!importLookup) {
|
|
||||||
return aliasData;
|
|
||||||
}
|
|
||||||
|
|
||||||
const resolved = resolveAliasDeclaration(importLookup, declaration, true);
|
const resolved = resolveAliasDeclaration(importLookup, declaration, true);
|
||||||
const nameValue = resolved ? getNameFromDeclaration(resolved) : undefined;
|
const nameValue = resolved ? getNameFromDeclaration(resolved) : undefined;
|
||||||
if (!nameValue || resolved!.path.length <= 0) {
|
if (!nameValue || resolved!.path.length <= 0) {
|
||||||
return aliasData;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
return { originalName: nameValue, modulePath: resolved!.path };
|
return {
|
||||||
|
originalName: nameValue,
|
||||||
|
modulePath: resolved!.path,
|
||||||
|
kind: getSymbolKind(nameValue, resolved!) ?? SymbolKind.Module,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// We'll use a somewhat-arbitrary cutoff value here to determine
|
// We'll use a somewhat-arbitrary cutoff value here to determine
|
||||||
@ -76,21 +111,25 @@ export function getIndexAliasData(
|
|||||||
const similarityLimit = 0.5;
|
const similarityLimit = 0.5;
|
||||||
|
|
||||||
export class DocumentSymbolProvider {
|
export class DocumentSymbolProvider {
|
||||||
static addSymbolsForDocument(
|
static getSymbolsForDocument(
|
||||||
indexResults: IndexResults | undefined,
|
indexResults: IndexResults | undefined,
|
||||||
parseResults: ParseResults | undefined,
|
parseResults: ParseResults | undefined,
|
||||||
filePath: string,
|
filePath: string,
|
||||||
query: string,
|
query: string,
|
||||||
symbolList: SymbolInformation[],
|
|
||||||
token: CancellationToken
|
token: CancellationToken
|
||||||
) {
|
): SymbolInformation[] {
|
||||||
|
const symbolList: SymbolInformation[] = [];
|
||||||
|
|
||||||
if (!indexResults && !parseResults) {
|
if (!indexResults && !parseResults) {
|
||||||
return;
|
return symbolList;
|
||||||
}
|
}
|
||||||
|
|
||||||
const indexSymbolData =
|
const indexSymbolData =
|
||||||
indexResults?.symbols ?? DocumentSymbolProvider.indexSymbols(parseResults!, false, token);
|
(indexResults?.symbols as IndexSymbolData[]) ??
|
||||||
|
DocumentSymbolProvider.indexSymbols(parseResults!, { indexingForAutoImportMode: false }, token);
|
||||||
|
|
||||||
appendWorkspaceSymbolsRecursive(indexSymbolData, filePath, query, '', symbolList, token);
|
appendWorkspaceSymbolsRecursive(indexSymbolData, filePath, query, '', symbolList, token);
|
||||||
|
return symbolList;
|
||||||
}
|
}
|
||||||
|
|
||||||
static addHierarchicalSymbolsForDocument(
|
static addHierarchicalSymbolsForDocument(
|
||||||
@ -104,17 +143,18 @@ export class DocumentSymbolProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const indexSymbolData =
|
const indexSymbolData =
|
||||||
indexResults?.symbols ?? DocumentSymbolProvider.indexSymbols(parseResults!, false, token);
|
(indexResults?.symbols as IndexSymbolData[]) ??
|
||||||
|
DocumentSymbolProvider.indexSymbols(parseResults!, { indexingForAutoImportMode: false }, token);
|
||||||
appendDocumentSymbolsRecursive(indexSymbolData, symbolList, token);
|
appendDocumentSymbolsRecursive(indexSymbolData, symbolList, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
static indexSymbols(
|
static indexSymbols(
|
||||||
parseResults: ParseResults,
|
parseResults: ParseResults,
|
||||||
importSymbolsOnly: boolean,
|
options: IndexOptions,
|
||||||
token: CancellationToken
|
token: CancellationToken
|
||||||
): IndexSymbolData[] {
|
): IndexSymbolData[] {
|
||||||
const indexSymbolData: IndexSymbolData[] = [];
|
const indexSymbolData: IndexSymbolData[] = [];
|
||||||
collectSymbolIndexData(parseResults, parseResults.parseTree, importSymbolsOnly, indexSymbolData, token);
|
collectSymbolIndexData(parseResults, parseResults.parseTree, options, indexSymbolData, token);
|
||||||
|
|
||||||
return indexSymbolData;
|
return indexSymbolData;
|
||||||
}
|
}
|
||||||
@ -168,7 +208,7 @@ function getSymbolKind(name: string, declaration: Declaration, evaluator?: TypeE
|
|||||||
}
|
}
|
||||||
|
|
||||||
function appendWorkspaceSymbolsRecursive(
|
function appendWorkspaceSymbolsRecursive(
|
||||||
indexSymbolData: IndexSymbolData[],
|
indexSymbolData: IndexSymbolData[] | undefined,
|
||||||
filePath: string,
|
filePath: string,
|
||||||
query: string,
|
query: string,
|
||||||
container: string,
|
container: string,
|
||||||
@ -177,6 +217,10 @@ function appendWorkspaceSymbolsRecursive(
|
|||||||
) {
|
) {
|
||||||
throwIfCancellationRequested(token);
|
throwIfCancellationRequested(token);
|
||||||
|
|
||||||
|
if (!indexSymbolData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (const symbolData of indexSymbolData) {
|
for (const symbolData of indexSymbolData) {
|
||||||
if (symbolData.alias) {
|
if (symbolData.alias) {
|
||||||
continue;
|
continue;
|
||||||
@ -186,7 +230,7 @@ function appendWorkspaceSymbolsRecursive(
|
|||||||
if (similarity >= similarityLimit) {
|
if (similarity >= similarityLimit) {
|
||||||
const location: Location = {
|
const location: Location = {
|
||||||
uri: URI.file(filePath).toString(),
|
uri: URI.file(filePath).toString(),
|
||||||
range: symbolData.selectionRange,
|
range: symbolData.selectionRange!,
|
||||||
};
|
};
|
||||||
|
|
||||||
const symbolInfo: SymbolInformation = {
|
const symbolInfo: SymbolInformation = {
|
||||||
@ -219,12 +263,16 @@ function appendWorkspaceSymbolsRecursive(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function appendDocumentSymbolsRecursive(
|
function appendDocumentSymbolsRecursive(
|
||||||
indexSymbolData: IndexSymbolData[],
|
indexSymbolData: IndexSymbolData[] | undefined,
|
||||||
symbolList: DocumentSymbol[],
|
symbolList: DocumentSymbol[],
|
||||||
token: CancellationToken
|
token: CancellationToken
|
||||||
) {
|
) {
|
||||||
throwIfCancellationRequested(token);
|
throwIfCancellationRequested(token);
|
||||||
|
|
||||||
|
if (!indexSymbolData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (const symbolData of indexSymbolData) {
|
for (const symbolData of indexSymbolData) {
|
||||||
if (symbolData.alias) {
|
if (symbolData.alias) {
|
||||||
continue;
|
continue;
|
||||||
@ -236,19 +284,41 @@ function appendDocumentSymbolsRecursive(
|
|||||||
const symbolInfo: DocumentSymbol = {
|
const symbolInfo: DocumentSymbol = {
|
||||||
name: symbolData.name,
|
name: symbolData.name,
|
||||||
kind: symbolData.kind,
|
kind: symbolData.kind,
|
||||||
range: symbolData.range,
|
range: symbolData.range!,
|
||||||
selectionRange: symbolData.selectionRange,
|
selectionRange: symbolData.selectionRange!,
|
||||||
children: children,
|
children: children!,
|
||||||
};
|
};
|
||||||
|
|
||||||
symbolList.push(symbolInfo);
|
symbolList.push(symbolInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getAllNameTable(autoImportMode: boolean, root: ModuleNode) {
|
||||||
|
if (!autoImportMode) {
|
||||||
|
// We only care about __all__ in auto import mode.
|
||||||
|
// other cases such as workspace symbols, document symbols, we will collect all symbols
|
||||||
|
// regardless whether it shows up in __all__ or not.
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If __all__ is defined, we only care ones in the __all__
|
||||||
|
const allNames = AnalyzerNodeInfo.getDunderAllNames(root);
|
||||||
|
if (allNames) {
|
||||||
|
return new Set<string>(allNames);
|
||||||
|
}
|
||||||
|
|
||||||
|
const file = AnalyzerNodeInfo.getFileInfo(root);
|
||||||
|
if (file && file.isStubFile) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Set<string>();
|
||||||
|
}
|
||||||
|
|
||||||
function collectSymbolIndexData(
|
function collectSymbolIndexData(
|
||||||
parseResults: ParseResults,
|
parseResults: ParseResults,
|
||||||
node: AnalyzerNodeInfo.ScopedNode,
|
node: AnalyzerNodeInfo.ScopedNode,
|
||||||
autoImportMode: boolean,
|
options: IndexOptions,
|
||||||
indexSymbolData: IndexSymbolData[],
|
indexSymbolData: IndexSymbolData[],
|
||||||
token: CancellationToken
|
token: CancellationToken
|
||||||
) {
|
) {
|
||||||
@ -259,13 +329,9 @@ function collectSymbolIndexData(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build __all__ map for regular python file to reduce candidate in autoImportMode.
|
const allNameTable = getAllNameTable(options.indexingForAutoImportMode, parseResults.parseTree);
|
||||||
const file = AnalyzerNodeInfo.getFileInfo(parseResults.parseTree);
|
|
||||||
let allNameTable: Set<string> | undefined;
|
|
||||||
if (autoImportMode && !file?.isStubFile) {
|
|
||||||
allNameTable = new Set<string>(AnalyzerNodeInfo.getDunderAllNames(parseResults.parseTree) ?? []);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
let modulePath: string | undefined = undefined;
|
||||||
const symbolTable = scope.symbolTable;
|
const symbolTable = scope.symbolTable;
|
||||||
symbolTable.forEach((symbol, name) => {
|
symbolTable.forEach((symbol, name) => {
|
||||||
if (symbol.isIgnoredForProtocolMatch()) {
|
if (symbol.isIgnoredForProtocolMatch()) {
|
||||||
@ -290,11 +356,21 @@ function collectSymbolIndexData(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (DeclarationType.Alias === declaration.type) {
|
if (DeclarationType.Alias === declaration.type) {
|
||||||
|
if (!options.indexingForAutoImportMode) {
|
||||||
|
// We don't include import alias for workspace files.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (declaration.path.length <= 0) {
|
if (declaration.path.length <= 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!allNameTable && !includeAliasDeclarationInIndex(declaration)) {
|
const lookup = AnalyzerNodeInfo.getFileInfo(parseResults.parseTree)!.importLookup;
|
||||||
|
modulePath =
|
||||||
|
modulePath ??
|
||||||
|
getLibraryPathWithoutExtension(AnalyzerNodeInfo.getFileInfo(parseResults.parseTree)!.filePath);
|
||||||
|
|
||||||
|
if (!allNameTable && !includeAliasDeclarationInIndex(lookup, modulePath, declaration)) {
|
||||||
// For import alias, we only put the alias in the index if it is the form of
|
// For import alias, we only put the alias in the index if it is the form of
|
||||||
// "from x import y as z" or the alias is explicitly listed in __all__
|
// "from x import y as z" or the alias is explicitly listed in __all__
|
||||||
return;
|
return;
|
||||||
@ -304,7 +380,7 @@ function collectSymbolIndexData(
|
|||||||
collectSymbolIndexDataForName(
|
collectSymbolIndexDataForName(
|
||||||
parseResults,
|
parseResults,
|
||||||
declaration,
|
declaration,
|
||||||
autoImportMode,
|
options,
|
||||||
!symbol.isExternallyHidden(),
|
!symbol.isExternallyHidden(),
|
||||||
name,
|
name,
|
||||||
indexSymbolData,
|
indexSymbolData,
|
||||||
@ -316,13 +392,13 @@ function collectSymbolIndexData(
|
|||||||
function collectSymbolIndexDataForName(
|
function collectSymbolIndexDataForName(
|
||||||
parseResults: ParseResults,
|
parseResults: ParseResults,
|
||||||
declaration: Declaration,
|
declaration: Declaration,
|
||||||
autoImportMode: boolean,
|
options: IndexOptions,
|
||||||
externallyVisible: boolean,
|
externallyVisible: boolean,
|
||||||
name: string,
|
name: string,
|
||||||
indexSymbolData: IndexSymbolData[],
|
indexSymbolData: IndexSymbolData[],
|
||||||
token: CancellationToken
|
token: CancellationToken
|
||||||
) {
|
) {
|
||||||
if (autoImportMode && !externallyVisible) {
|
if (options.indexingForAutoImportMode && !externallyVisible) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -336,29 +412,28 @@ function collectSymbolIndexDataForName(
|
|||||||
const children: IndexSymbolData[] = [];
|
const children: IndexSymbolData[] = [];
|
||||||
|
|
||||||
if (declaration.type === DeclarationType.Class || declaration.type === DeclarationType.Function) {
|
if (declaration.type === DeclarationType.Class || declaration.type === DeclarationType.Function) {
|
||||||
if (!autoImportMode) {
|
if (!options.indexingForAutoImportMode) {
|
||||||
collectSymbolIndexData(parseResults, declaration.node, false, children, token);
|
collectSymbolIndexData(parseResults, declaration.node, options, children, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
const nameRange = convertOffsetsToRange(
|
range = convertOffsetsToRange(
|
||||||
declaration.node.start,
|
declaration.node.start,
|
||||||
declaration.node.name.start + declaration.node.length,
|
declaration.node.name.start + declaration.node.length,
|
||||||
parseResults.tokenizerOutput.lines
|
parseResults.tokenizerOutput.lines
|
||||||
);
|
);
|
||||||
range = nameRange;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const data: IndexSymbolData = {
|
const data: IndexSymbolData = {
|
||||||
name,
|
name,
|
||||||
alias:
|
|
||||||
DeclarationType.Alias === declaration.type
|
|
||||||
? getIndexAliasData(AnalyzerNodeInfo.getFileInfo(parseResults.parseTree)?.importLookup, declaration)
|
|
||||||
: undefined,
|
|
||||||
externallyVisible,
|
externallyVisible,
|
||||||
kind: symbolKind,
|
kind: symbolKind,
|
||||||
range,
|
alias:
|
||||||
selectionRange,
|
DeclarationType.Alias === declaration.type
|
||||||
children,
|
? getIndexAliasData(AnalyzerNodeInfo.getFileInfo(parseResults.parseTree)!.importLookup, declaration)
|
||||||
|
: undefined,
|
||||||
|
range: options.indexingForAutoImportMode ? undefined : range,
|
||||||
|
selectionRange: options.indexingForAutoImportMode ? undefined : selectionRange,
|
||||||
|
children: options.indexingForAutoImportMode ? undefined : children,
|
||||||
};
|
};
|
||||||
|
|
||||||
indexSymbolData.push(data);
|
indexSymbolData.push(data);
|
||||||
|
@ -24,15 +24,39 @@ import { TextRange } from '../common/textRange';
|
|||||||
import { ModuleNameNode, NameNode, ParseNode, ParseNodeType } from '../parser/parseNodes';
|
import { ModuleNameNode, NameNode, ParseNode, ParseNodeType } from '../parser/parseNodes';
|
||||||
import { ParseResults } from '../parser/parser';
|
import { ParseResults } from '../parser/parser';
|
||||||
|
|
||||||
export interface ReferencesResult {
|
export type ReferenceCallback = (locations: DocumentRange[]) => void;
|
||||||
requiresGlobalSearch: boolean;
|
|
||||||
nodeAtOffset: ParseNode;
|
export class ReferencesResult {
|
||||||
symbolName: string;
|
private readonly _locations: DocumentRange[] = [];
|
||||||
declarations: Declaration[];
|
|
||||||
locations: DocumentRange[];
|
constructor(
|
||||||
|
readonly requiresGlobalSearch: boolean,
|
||||||
|
readonly nodeAtOffset: ParseNode,
|
||||||
|
readonly symbolName: string,
|
||||||
|
readonly declarations: Declaration[],
|
||||||
|
private readonly _reporter?: ReferenceCallback
|
||||||
|
) {}
|
||||||
|
|
||||||
|
get locations(): readonly DocumentRange[] {
|
||||||
|
return this._locations;
|
||||||
|
}
|
||||||
|
|
||||||
|
addLocations(...locs: DocumentRange[]) {
|
||||||
|
if (locs.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._reporter) {
|
||||||
|
this._reporter(locs);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._locations.push(...locs);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class FindReferencesTreeWalker extends ParseTreeWalker {
|
class FindReferencesTreeWalker extends ParseTreeWalker {
|
||||||
|
private readonly _locationsFound: DocumentRange[] = [];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private _parseResults: ParseResults,
|
private _parseResults: ParseResults,
|
||||||
private _filePath: string,
|
private _filePath: string,
|
||||||
@ -46,6 +70,8 @@ class FindReferencesTreeWalker extends ParseTreeWalker {
|
|||||||
|
|
||||||
findReferences() {
|
findReferences() {
|
||||||
this.walk(this._parseResults.parseTree);
|
this.walk(this._parseResults.parseTree);
|
||||||
|
|
||||||
|
return this._locationsFound;
|
||||||
}
|
}
|
||||||
|
|
||||||
walk(node: ParseNode) {
|
walk(node: ParseNode) {
|
||||||
@ -74,7 +100,7 @@ class FindReferencesTreeWalker extends ParseTreeWalker {
|
|||||||
if (declarations.some((decl) => this._resultsContainsDeclaration(decl))) {
|
if (declarations.some((decl) => this._resultsContainsDeclaration(decl))) {
|
||||||
// Is it the same symbol?
|
// Is it the same symbol?
|
||||||
if (this._includeDeclaration || node !== this._referencesResult.nodeAtOffset) {
|
if (this._includeDeclaration || node !== this._referencesResult.nodeAtOffset) {
|
||||||
this._referencesResult.locations.push({
|
this._locationsFound.push({
|
||||||
path: this._filePath,
|
path: this._filePath,
|
||||||
range: {
|
range: {
|
||||||
start: convertOffsetToPosition(node.start, this._parseResults.tokenizerOutput.lines),
|
start: convertOffsetToPosition(node.start, this._parseResults.tokenizerOutput.lines),
|
||||||
@ -129,6 +155,7 @@ export class ReferencesProvider {
|
|||||||
filePath: string,
|
filePath: string,
|
||||||
position: Position,
|
position: Position,
|
||||||
evaluator: TypeEvaluator,
|
evaluator: TypeEvaluator,
|
||||||
|
reporter: ReferenceCallback | undefined,
|
||||||
token: CancellationToken
|
token: CancellationToken
|
||||||
): ReferencesResult | undefined {
|
): ReferencesResult | undefined {
|
||||||
throwIfCancellationRequested(token);
|
throwIfCancellationRequested(token);
|
||||||
@ -207,13 +234,7 @@ export class ReferencesProvider {
|
|||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return new ReferencesResult(requiresGlobalSearch, node, node.value, resolvedDeclarations, reporter);
|
||||||
requiresGlobalSearch,
|
|
||||||
nodeAtOffset: node,
|
|
||||||
symbolName: node.value,
|
|
||||||
declarations: resolvedDeclarations,
|
|
||||||
locations: [],
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static _addIfUnique(declarations: Declaration[], itemToAdd: Declaration) {
|
private static _addIfUnique(declarations: Declaration[], itemToAdd: Declaration) {
|
||||||
@ -242,6 +263,7 @@ export class ReferencesProvider {
|
|||||||
evaluator,
|
evaluator,
|
||||||
token
|
token
|
||||||
);
|
);
|
||||||
refTreeWalker.findReferences();
|
|
||||||
|
referencesResult.addLocations(...refTreeWalker.findReferences());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1006,7 +1006,15 @@ export class TestState {
|
|||||||
const expected = map[name].references;
|
const expected = map[name].references;
|
||||||
|
|
||||||
const position = this.convertOffsetToPosition(fileName, marker.position);
|
const position = this.convertOffsetToPosition(fileName, marker.position);
|
||||||
const actual = this.program.getReferencesForPosition(fileName, position, true, CancellationToken.None);
|
|
||||||
|
const actual: DocumentRange[] = [];
|
||||||
|
this.program.reportReferencesForPosition(
|
||||||
|
fileName,
|
||||||
|
position,
|
||||||
|
true,
|
||||||
|
(locs) => actual.push(...locs),
|
||||||
|
CancellationToken.None
|
||||||
|
);
|
||||||
|
|
||||||
assert.equal(actual?.length ?? 0, expected.length);
|
assert.equal(actual?.length ?? 0, expected.length);
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "webpack --mode production --progress",
|
"build": "webpack --mode production --progress",
|
||||||
"clean": "shx rm -rf ./dist ./out README.md",
|
"clean": "shx rm -rf ./dist ./out README.md",
|
||||||
"prepublishOnly": "npm run clean && shx cp ../../README.md . && npm run build",
|
"prepack": "npm run clean && shx cp ../../README.md . && npm run build",
|
||||||
"webpack": "webpack --mode development --progress"
|
"webpack": "webpack --mode development --progress"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
Loading…
Reference in New Issue
Block a user