Some checks failed
Run mypy_primer on push / Run mypy_primer on push (push) Has been cancelled
Validation / Typecheck (push) Has been cancelled
Validation / Style (push) Has been cancelled
Validation / Test macos-latest (push) Has been cancelled
Validation / Test ubuntu-latest (push) Has been cancelled
Validation / Test windows-latest (push) Has been cancelled
Validation / Build (push) Has been cancelled
Validation / Required (push) Has been cancelled
313 lines
11 KiB
TypeScript
313 lines
11 KiB
TypeScript
/*
|
|
* BackgroundAnalysisProgram.ts
|
|
* Copyright (c) Microsoft Corporation.
|
|
* Licensed under the MIT license.
|
|
*
|
|
* Applies operations to both the foreground program and a background
|
|
* analysis running in a worker process.
|
|
*/
|
|
|
|
import { CancellationToken } from 'vscode-languageserver';
|
|
|
|
import { IBackgroundAnalysis, RefreshOptions } from '../backgroundAnalysisBase';
|
|
import { ConfigOptions, ExecutionEnvironment } from '../common/configOptions';
|
|
import { Diagnostic } from '../common/diagnostic';
|
|
import { FileDiagnostics } from '../common/diagnosticSink';
|
|
import { ServiceProvider } from '../common/serviceProvider';
|
|
import '../common/serviceProviderExtensions';
|
|
import { Range } from '../common/textRange';
|
|
import { Uri } from '../common/uri/uri';
|
|
import { AnalysisCompleteCallback, analyzeProgram } from './analysis';
|
|
import { ImportResolver } from './importResolver';
|
|
import { MaxAnalysisTime, OpenFileOptions, Program } from './program';
|
|
import { TypeStubWriter } from './typeStubWriter';
|
|
|
|
export enum InvalidatedReason {
|
|
Reanalyzed,
|
|
SourceWatcherChanged,
|
|
LibraryWatcherChanged,
|
|
LibraryWatcherContentOnlyChanged,
|
|
}
|
|
|
|
export class BackgroundAnalysisProgram {
|
|
private _program: Program;
|
|
private _disposed = false;
|
|
private _onAnalysisCompletion: AnalysisCompleteCallback | undefined;
|
|
private _preEditAnalysis: IBackgroundAnalysis | undefined;
|
|
|
|
constructor(
|
|
protected readonly serviceId: string,
|
|
private readonly _serviceProvider: ServiceProvider,
|
|
private _configOptions: ConfigOptions,
|
|
private _importResolver: ImportResolver,
|
|
private _backgroundAnalysis?: IBackgroundAnalysis,
|
|
private readonly _maxAnalysisTime?: MaxAnalysisTime,
|
|
private readonly _disableChecker?: boolean,
|
|
program?: Program
|
|
) {
|
|
this._program =
|
|
program ??
|
|
new Program(
|
|
this.importResolver,
|
|
this.configOptions,
|
|
this._serviceProvider,
|
|
undefined,
|
|
this._disableChecker,
|
|
serviceId
|
|
);
|
|
this._backgroundAnalysis?.setProgramView(this._program);
|
|
}
|
|
|
|
get serviceProvider() {
|
|
return this._serviceProvider;
|
|
}
|
|
|
|
get configOptions() {
|
|
return this._configOptions;
|
|
}
|
|
|
|
get importResolver() {
|
|
return this._importResolver;
|
|
}
|
|
|
|
get program() {
|
|
return this._program;
|
|
}
|
|
|
|
get host() {
|
|
return this._importResolver.host;
|
|
}
|
|
|
|
get backgroundAnalysis() {
|
|
return this._backgroundAnalysis;
|
|
}
|
|
|
|
hasSourceFile(fileUri: Uri): boolean {
|
|
return !!this._program.getSourceFile(fileUri);
|
|
}
|
|
|
|
setConfigOptions(configOptions: ConfigOptions) {
|
|
this._configOptions = configOptions;
|
|
this._backgroundAnalysis?.setConfigOptions(configOptions);
|
|
this._program.setConfigOptions(configOptions);
|
|
}
|
|
|
|
setImportResolver(importResolver: ImportResolver) {
|
|
this._importResolver = importResolver;
|
|
this._backgroundAnalysis?.setImportResolver(importResolver);
|
|
|
|
this._program.setImportResolver(importResolver);
|
|
this.configOptions.getExecutionEnvironments().forEach((e) => this._ensurePartialStubPackages(e));
|
|
}
|
|
|
|
setTrackedFiles(fileUris: Uri[]) {
|
|
this._backgroundAnalysis?.setTrackedFiles(fileUris);
|
|
const diagnostics = this._program.setTrackedFiles(fileUris);
|
|
this._reportDiagnosticsForRemovedFiles(diagnostics);
|
|
}
|
|
|
|
setAllowedThirdPartyImports(importNames: string[]) {
|
|
this._backgroundAnalysis?.setAllowedThirdPartyImports(importNames);
|
|
this._program.setAllowedThirdPartyImports(importNames);
|
|
}
|
|
|
|
setFileOpened(fileUri: Uri, version: number | null, contents: string, options: OpenFileOptions) {
|
|
this._backgroundAnalysis?.setFileOpened(fileUri, version, contents, options);
|
|
this._program.setFileOpened(fileUri, version, contents, options);
|
|
}
|
|
|
|
getChainedUri(fileUri: Uri): Uri | undefined {
|
|
return this._program.getChainedUri(fileUri);
|
|
}
|
|
|
|
updateChainedUri(fileUri: Uri, chainedUri: Uri | undefined) {
|
|
this._backgroundAnalysis?.updateChainedUri(fileUri, chainedUri);
|
|
this._program.updateChainedUri(fileUri, chainedUri);
|
|
}
|
|
|
|
updateOpenFileContents(uri: Uri, version: number | null, contents: string, options: OpenFileOptions) {
|
|
this._backgroundAnalysis?.setFileOpened(uri, version, contents, options);
|
|
this._program.setFileOpened(uri, version, contents, options);
|
|
this.markFilesDirty([uri], /* evenIfContentsAreSame */ true);
|
|
}
|
|
|
|
setFileClosed(fileUri: Uri) {
|
|
this._backgroundAnalysis?.setFileClosed(fileUri);
|
|
const diagnostics = this._program.setFileClosed(fileUri);
|
|
this._reportDiagnosticsForRemovedFiles(diagnostics);
|
|
}
|
|
|
|
addInterimFile(fileUri: Uri) {
|
|
this._backgroundAnalysis?.addInterimFile(fileUri);
|
|
this._program.addInterimFile(fileUri);
|
|
}
|
|
|
|
markAllFilesDirty(evenIfContentsAreSame: boolean) {
|
|
this._backgroundAnalysis?.markAllFilesDirty(evenIfContentsAreSame);
|
|
this._program.markAllFilesDirty(evenIfContentsAreSame);
|
|
}
|
|
|
|
markFilesDirty(fileUris: Uri[], evenIfContentsAreSame: boolean) {
|
|
this._backgroundAnalysis?.markFilesDirty(fileUris, evenIfContentsAreSame);
|
|
this._program.markFilesDirty(fileUris, evenIfContentsAreSame);
|
|
}
|
|
|
|
setCompletionCallback(callback?: AnalysisCompleteCallback) {
|
|
this._onAnalysisCompletion = callback;
|
|
this._backgroundAnalysis?.setCompletionCallback(callback);
|
|
}
|
|
|
|
startAnalysis(token: CancellationToken): boolean {
|
|
if (this._backgroundAnalysis) {
|
|
this._backgroundAnalysis.startAnalysis(token);
|
|
return false;
|
|
}
|
|
|
|
return analyzeProgram(
|
|
this._program,
|
|
this._maxAnalysisTime,
|
|
this._configOptions,
|
|
this._onAnalysisCompletion,
|
|
this._serviceProvider.console(),
|
|
token
|
|
);
|
|
}
|
|
|
|
async analyzeFile(fileUri: Uri, token: CancellationToken): Promise<boolean> {
|
|
if (this._backgroundAnalysis) {
|
|
return this._backgroundAnalysis.analyzeFile(fileUri, token);
|
|
}
|
|
|
|
return this._program.analyzeFile(fileUri, token);
|
|
}
|
|
|
|
async analyzeFileAndGetDiagnostics(fileUri: Uri, token: CancellationToken): Promise<Diagnostic[]> {
|
|
if (this._backgroundAnalysis) {
|
|
return this._backgroundAnalysis.analyzeFileAndGetDiagnostics(fileUri, token);
|
|
}
|
|
|
|
return this._program.analyzeFileAndGetDiagnostics(fileUri, token);
|
|
}
|
|
|
|
libraryUpdated(): boolean {
|
|
return false;
|
|
}
|
|
|
|
async getDiagnosticsForRange(fileUri: Uri, range: Range, token: CancellationToken): Promise<Diagnostic[]> {
|
|
if (this._backgroundAnalysis) {
|
|
return this._backgroundAnalysis.getDiagnosticsForRange(fileUri, range, token);
|
|
}
|
|
|
|
return this._program.getDiagnosticsForRange(fileUri, range);
|
|
}
|
|
|
|
async writeTypeStub(
|
|
targetImportUri: Uri,
|
|
targetIsSingleFile: boolean,
|
|
stubUri: Uri,
|
|
token: CancellationToken
|
|
): Promise<any> {
|
|
if (this._backgroundAnalysis) {
|
|
return this._backgroundAnalysis.writeTypeStub(targetImportUri, targetIsSingleFile, stubUri, token);
|
|
}
|
|
|
|
return new TypeStubWriter(this._program).writeTypeStub(
|
|
{
|
|
targetImportPath: targetImportUri,
|
|
targetIsSingleFile,
|
|
outputPath: stubUri,
|
|
},
|
|
token
|
|
);
|
|
}
|
|
|
|
invalidateAndForceReanalysis(reason: InvalidatedReason, refreshOptions?: RefreshOptions) {
|
|
this._backgroundAnalysis?.invalidateAndForceReanalysis(reason);
|
|
|
|
// Make sure the import resolver doesn't have invalid
|
|
// cached entries.
|
|
this._importResolver.invalidateCache();
|
|
|
|
// If we have specific changed file URIs and it's a content-only change, mark only those files dirty.
|
|
// Otherwise, mark all files with one or more errors dirty.
|
|
if (
|
|
refreshOptions?.changedFileUris &&
|
|
refreshOptions.changedFileUris.size > 0 &&
|
|
reason === InvalidatedReason.LibraryWatcherContentOnlyChanged
|
|
) {
|
|
// Convert UriMap keys to array for markFilesDirty
|
|
const changedFileUris = Array.from(refreshOptions.changedFileUris.keys());
|
|
this.markFilesDirty(changedFileUris, /* evenIfContentsAreSame */ true);
|
|
} else {
|
|
// Mark all files with one or more errors dirty.
|
|
this._program.markAllFilesDirty(/* evenIfContentsAreSame */ true);
|
|
}
|
|
}
|
|
|
|
restart() {
|
|
this._backgroundAnalysis?.restart();
|
|
}
|
|
|
|
dispose() {
|
|
if (this._disposed) {
|
|
return;
|
|
}
|
|
|
|
this._disposed = true;
|
|
this._program.dispose();
|
|
this._backgroundAnalysis?.shutdown();
|
|
this._backgroundAnalysis?.dispose();
|
|
}
|
|
|
|
enterEditMode() {
|
|
// Turn off analysis while in edit mode.
|
|
this._preEditAnalysis = this._backgroundAnalysis;
|
|
this._backgroundAnalysis = undefined;
|
|
|
|
// Forward this request to the program.
|
|
this._program.enterEditMode();
|
|
}
|
|
|
|
exitEditMode() {
|
|
this._backgroundAnalysis = this._preEditAnalysis;
|
|
this._preEditAnalysis = undefined;
|
|
return this._program.exitEditMode();
|
|
}
|
|
|
|
private _ensurePartialStubPackages(execEnv: ExecutionEnvironment) {
|
|
this._backgroundAnalysis?.ensurePartialStubPackages(execEnv.root?.toString());
|
|
return this._importResolver.ensurePartialStubPackages(execEnv);
|
|
}
|
|
|
|
private _reportDiagnosticsForRemovedFiles(fileDiags: FileDiagnostics[]) {
|
|
if (fileDiags.length === 0) {
|
|
return;
|
|
}
|
|
|
|
// If analysis is running in the foreground process, report any
|
|
// diagnostics that resulted from the close operation (used to
|
|
// clear diagnostics that are no longer of interest).
|
|
if (!this._backgroundAnalysis && this._onAnalysisCompletion) {
|
|
this._onAnalysisCompletion({
|
|
diagnostics: fileDiags,
|
|
filesInProgram: this._program.getFileCount(),
|
|
requiringAnalysisCount: this._program.getFilesToAnalyzeCount(),
|
|
checkingOnlyOpenFiles: this._program.isCheckingOnlyOpenFiles(),
|
|
fatalErrorOccurred: false,
|
|
configParseErrorOccurred: false,
|
|
elapsedTime: 0,
|
|
reason: 'tracking',
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
export type BackgroundAnalysisProgramFactory = (
|
|
serviceId: string,
|
|
serviceProvider: ServiceProvider,
|
|
configOptions: ConfigOptions,
|
|
importResolver: ImportResolver,
|
|
backgroundAnalysis?: IBackgroundAnalysis,
|
|
maxAnalysisTime?: MaxAnalysisTime
|
|
) => BackgroundAnalysisProgram;
|