Add helper for tracking active js/ts editor

Fixes #117813
This commit is contained in:
Matt Bierner 2021-02-26 18:05:06 -08:00
parent 1f567e10f3
commit a74ebb17cf
9 changed files with 123 additions and 28 deletions

View file

@ -3,8 +3,8 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
import TypeScriptServiceClientHost from '../typeScriptServiceClientHost';
import { ActiveJsTsEditorTracker } from '../utils/activeJsTsEditorTracker';
import { Lazy } from '../utils/lazy';
import { openProjectConfigForFile, ProjectType } from '../utils/tsconfig';
import { Command } from './commandManager';
@ -13,11 +13,12 @@ export class TypeScriptGoToProjectConfigCommand implements Command {
public readonly id = 'typescript.goToProjectConfig';
public constructor(
private readonly activeJsTsEditorTracker: ActiveJsTsEditorTracker,
private readonly lazyClientHost: Lazy<TypeScriptServiceClientHost>,
) { }
public execute() {
const editor = vscode.window.activeTextEditor;
const editor = this.activeJsTsEditorTracker.activeJsTsEditor;
if (editor) {
openProjectConfigForFile(ProjectType.TypeScript, this.lazyClientHost.value.serviceClient, editor.document.uri);
}
@ -28,14 +29,14 @@ export class JavaScriptGoToProjectConfigCommand implements Command {
public readonly id = 'javascript.goToProjectConfig';
public constructor(
private readonly activeJsTsEditorTracker: ActiveJsTsEditorTracker,
private readonly lazyClientHost: Lazy<TypeScriptServiceClientHost>,
) { }
public execute() {
const editor = vscode.window.activeTextEditor;
const editor = this.activeJsTsEditorTracker.activeJsTsEditor;
if (editor) {
openProjectConfigForFile(ProjectType.JavaScript, this.lazyClientHost.value.serviceClient, editor.document.uri);
}
}
}

View file

@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import TypeScriptServiceClientHost from '../typeScriptServiceClientHost';
import { ActiveJsTsEditorTracker } from '../utils/activeJsTsEditorTracker';
import { Lazy } from '../utils/lazy';
import { PluginManager } from '../utils/plugins';
import { CommandManager } from './commandManager';
@ -18,15 +19,16 @@ import { SelectTypeScriptVersionCommand } from './selectTypeScriptVersion';
export function registerBaseCommands(
commandManager: CommandManager,
lazyClientHost: Lazy<TypeScriptServiceClientHost>,
pluginManager: PluginManager
pluginManager: PluginManager,
activeJsTsEditorTracker: ActiveJsTsEditorTracker,
): void {
commandManager.register(new ReloadTypeScriptProjectsCommand(lazyClientHost));
commandManager.register(new ReloadJavaScriptProjectsCommand(lazyClientHost));
commandManager.register(new SelectTypeScriptVersionCommand(lazyClientHost));
commandManager.register(new OpenTsServerLogCommand(lazyClientHost));
commandManager.register(new RestartTsServerCommand(lazyClientHost));
commandManager.register(new TypeScriptGoToProjectConfigCommand(lazyClientHost));
commandManager.register(new JavaScriptGoToProjectConfigCommand(lazyClientHost));
commandManager.register(new TypeScriptGoToProjectConfigCommand(activeJsTsEditorTracker, lazyClientHost));
commandManager.register(new JavaScriptGoToProjectConfigCommand(activeJsTsEditorTracker, lazyClientHost));
commandManager.register(new ConfigurePluginCommand(pluginManager));
commandManager.register(new LearnMoreAboutRefactoringsCommand());
}

View file

@ -16,6 +16,7 @@ import API from './utils/api';
import { CommandManager } from './commands/commandManager';
import { TypeScriptServiceConfiguration } from './utils/configuration';
import { PluginManager } from './utils/plugins';
import { ActiveJsTsEditorTracker } from './utils/activeJsTsEditorTracker';
class StaticVersionProvider implements ITypeScriptVersionProvider {
@ -49,6 +50,9 @@ export function activate(
const onCompletionAccepted = new vscode.EventEmitter<vscode.CompletionItem>();
context.subscriptions.push(onCompletionAccepted);
const activeJsTsEditorTracker = new ActiveJsTsEditorTracker();
context.subscriptions.push(activeJsTsEditorTracker);
const versionProvider = new StaticVersionProvider(
new TypeScriptVersion(
TypeScriptVersionSource.Bundled,
@ -61,12 +65,13 @@ export function activate(
logDirectoryProvider: noopLogDirectoryProvider,
cancellerFactory: noopRequestCancellerFactory,
versionProvider,
processFactory: WorkerServerProcess
processFactory: WorkerServerProcess,
activeJsTsEditorTracker
}, item => {
onCompletionAccepted.fire(item);
});
registerBaseCommands(commandManager, lazyClientHost, pluginManager);
registerBaseCommands(commandManager, lazyClientHost, pluginManager, activeJsTsEditorTracker);
// context.subscriptions.push(task.register(lazyClientHost.map(x => x.serviceClient)));
@ -74,7 +79,7 @@ export function activate(
context.subscriptions.push(module.register());
});
context.subscriptions.push(lazilyActivateClient(lazyClientHost, pluginManager));
context.subscriptions.push(lazilyActivateClient(lazyClientHost, pluginManager, activeJsTsEditorTracker));
return getExtensionApi(onCompletionAccepted.event, pluginManager);
}

View file

@ -14,6 +14,7 @@ import { nodeRequestCancellerFactory } from './tsServer/cancellation.electron';
import { NodeLogDirectoryProvider } from './tsServer/logDirectoryProvider.electron';
import { ChildServerProcess } from './tsServer/serverProcess.electron';
import { DiskTypeScriptVersionProvider } from './tsServer/versionProvider.electron';
import { ActiveJsTsEditorTracker } from './utils/activeJsTsEditorTracker';
import { onCaseInsenitiveFileSystem } from './utils/fileSystem.electron';
import { PluginManager } from './utils/plugins';
import * as temp from './utils/temp.electron';
@ -35,6 +36,9 @@ export function activate(
context.subscriptions.push(new LanguageConfigurationManager());
const activeJsTsEditorTracker = new ActiveJsTsEditorTracker();
context.subscriptions.push(activeJsTsEditorTracker);
const lazyClientHost = createLazyClientHost(context, onCaseInsenitiveFileSystem(), {
pluginManager,
commandManager,
@ -42,11 +46,12 @@ export function activate(
cancellerFactory: nodeRequestCancellerFactory,
versionProvider,
processFactory: ChildServerProcess,
activeJsTsEditorTracker,
}, item => {
onCompletionAccepted.fire(item);
});
registerBaseCommands(commandManager, lazyClientHost, pluginManager);
registerBaseCommands(commandManager, lazyClientHost, pluginManager, activeJsTsEditorTracker);
import('./task/taskProvider').then(module => {
context.subscriptions.push(module.register(lazyClientHost.map(x => x.serviceClient)));
@ -56,7 +61,7 @@ export function activate(
context.subscriptions.push(module.register());
});
context.subscriptions.push(lazilyActivateClient(lazyClientHost, pluginManager));
context.subscriptions.push(lazilyActivateClient(lazyClientHost, pluginManager, activeJsTsEditorTracker));
return getExtensionApi(onCompletionAccepted.event, pluginManager);
}

View file

@ -10,6 +10,7 @@ import { ILogDirectoryProvider } from './tsServer/logDirectoryProvider';
import { TsServerProcessFactory } from './tsServer/server';
import { ITypeScriptVersionProvider } from './tsServer/versionProvider';
import TypeScriptServiceClientHost from './typeScriptServiceClientHost';
import { ActiveJsTsEditorTracker } from './utils/activeJsTsEditorTracker';
import { flatten } from './utils/arrays';
import * as fileSchemes from './utils/fileSchemes';
import { standardLanguageDescriptions } from './utils/languageDescription';
@ -27,6 +28,7 @@ export function createLazyClientHost(
cancellerFactory: OngoingRequestCancellerFactory,
versionProvider: ITypeScriptVersionProvider,
processFactory: TsServerProcessFactory,
activeJsTsEditorTracker: ActiveJsTsEditorTracker,
},
onCompletionAccepted: (item: vscode.CompletionItem) => void,
): Lazy<TypeScriptServiceClientHost> {
@ -47,6 +49,7 @@ export function createLazyClientHost(
export function lazilyActivateClient(
lazyClientHost: Lazy<TypeScriptServiceClientHost>,
pluginManager: PluginManager,
activeJsTsEditorTracker: ActiveJsTsEditorTracker,
): vscode.Disposable {
const disposables: vscode.Disposable[] = [];
@ -62,7 +65,7 @@ export function lazilyActivateClient(
// Force activation
void lazyClientHost.value;
disposables.push(new ManagedFileContextManager(resource => {
disposables.push(new ManagedFileContextManager(activeJsTsEditorTracker, resource => {
return lazyClientHost.value.serviceClient.toPath(resource);
}));
return true;

View file

@ -7,6 +7,7 @@ import * as vscode from 'vscode';
import * as nls from 'vscode-nls';
import { Command, CommandManager } from '../commands/commandManager';
import { ITypeScriptServiceClient } from '../typescriptService';
import { ActiveJsTsEditorTracker } from '../utils/activeJsTsEditorTracker';
import { coalesce } from '../utils/arrays';
import { Disposable } from '../utils/dispose';
import { isTypeScriptDocument } from '../utils/languageModeIds';
@ -130,6 +131,7 @@ export default class VersionStatus extends Disposable {
constructor(
private readonly _client: ITypeScriptServiceClient,
commandManager: CommandManager,
private readonly _activeTextEditorManager: ActiveJsTsEditorTracker,
) {
super();
@ -144,7 +146,7 @@ export default class VersionStatus extends Disposable {
commandManager.register(command);
this._statusBarEntry.command = command.id;
vscode.window.onDidChangeActiveTextEditor(this.updateStatus, this, this._disposables);
_activeTextEditorManager.onDidChangeActiveJsTsEditor(this.updateStatus, this, this._disposables);
this._client.onReady(() => {
this._ready = true;
@ -161,12 +163,13 @@ export default class VersionStatus extends Disposable {
}
private async updateStatus() {
if (!vscode.window.activeTextEditor) {
const editor = this._activeTextEditorManager.activeJsTsEditor;
if (!editor) {
this.hide();
return;
}
const doc = vscode.window.activeTextEditor.document;
const doc = editor.document;
if (isTypeScriptDocument(doc)) {
const file = this._client.toOpenedFilePath(doc, { suppressAlertOnFailure: true });
if (file) {
@ -190,12 +193,6 @@ export default class VersionStatus extends Disposable {
}
}
if (!vscode.window.activeTextEditor.viewColumn) {
// viewColumn is undefined for the debug/output panel, but we still want
// to show the version info in the existing editor
return;
}
this.hide();
}

View file

@ -29,6 +29,7 @@ import { PluginManager } from './utils/plugins';
import * as typeConverters from './utils/typeConverters';
import TypingsStatus, { AtaProgressReporter } from './utils/typingsStatus';
import * as ProjectStatus from './utils/largeProjectStatus';
import { ActiveJsTsEditorTracker } from './utils/activeJsTsEditorTracker';
// Style check diagnostics that can be reported as warnings
const styleCheckDiagnostics = new Set([
@ -66,6 +67,7 @@ export default class TypeScriptServiceClientHost extends Disposable {
cancellerFactory: OngoingRequestCancellerFactory,
versionProvider: ITypeScriptVersionProvider,
processFactory: TsServerProcessFactory,
activeJsTsEditorTracker: ActiveJsTsEditorTracker,
},
onCompletionAccepted: (item: vscode.CompletionItem) => void,
) {
@ -87,7 +89,7 @@ export default class TypeScriptServiceClientHost extends Disposable {
this.client.onConfigDiagnosticsReceived(diag => this.configFileDiagnosticsReceived(diag), null, this._disposables);
this.client.onResendModelsRequested(() => this.populateService(), null, this._disposables);
this._register(new VersionStatus(this.client, services.commandManager));
this._register(new VersionStatus(this.client, services.commandManager, services.activeJsTsEditorTracker));
this._register(new AtaProgressReporter(this.client));
this.typingsStatus = this._register(new TypingsStatus(this.client));
this._register(ProjectStatus.create(this.client));

View file

@ -0,0 +1,75 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
import { Disposable } from './dispose';
import { isJsConfigOrTsConfigFileName } from './languageDescription';
import { isSupportedLanguageMode } from './languageModeIds';
/**
* Tracks the active JS/TS editor.
*
* This tries to handle the case where the user focuses in the output view / debug console.
* When this happens, we want to treat the last real focused editor as the active editor,
* instead of using `vscode.window.activeTextEditor`
*/
export class ActiveJsTsEditorTracker extends Disposable {
private _activeJsTsEditor: vscode.TextEditor | undefined;
private readonly _onDidChangeActiveJsTsEditor = this._register(new vscode.EventEmitter<vscode.TextEditor | undefined>());
public readonly onDidChangeActiveJsTsEditor = this._onDidChangeActiveJsTsEditor.event;
public constructor() {
super();
vscode.window.onDidChangeActiveTextEditor(this.onDidChangeActiveTextEditor, this, this._disposables);
vscode.window.onDidChangeVisibleTextEditors(() => {
// Make sure the active editor is still in the visible set.
// This can happen if the output view is focused and the last active TS file is closed
if (this._activeJsTsEditor) {
if (!vscode.window.visibleTextEditors.some(visibleEditor => visibleEditor === this._activeJsTsEditor)) {
this.onDidChangeActiveTextEditor(undefined);
}
}
}, this, this._disposables);
this.onDidChangeActiveTextEditor(vscode.window.activeTextEditor);
}
public get activeJsTsEditor(): vscode.TextEditor | undefined {
return this._activeJsTsEditor;
}
private onDidChangeActiveTextEditor(editor: vscode.TextEditor | undefined): any {
if (editor === this._activeJsTsEditor) {
return;
}
if (editor && !editor.viewColumn) {
// viewColumn is undefined for the debug/output panel, but we still want
// to show the version info for the previous editor
return;
}
if (editor && this.isManagedFile(editor)) {
this._activeJsTsEditor = editor;
} else {
this._activeJsTsEditor = undefined;
}
this._onDidChangeActiveJsTsEditor.fire(this._activeJsTsEditor);
}
private isManagedFile(editor: vscode.TextEditor): boolean {
return this.isManagedScriptFile(editor) || this.isManagedConfigFile(editor);
}
private isManagedScriptFile(editor: vscode.TextEditor): boolean {
return isSupportedLanguageMode(editor.document);
}
private isManagedConfigFile(editor: vscode.TextEditor): boolean {
return isJsConfigOrTsConfigFileName(editor.document.fileName);
}
}

View file

@ -4,11 +4,12 @@
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
import { ActiveJsTsEditorTracker } from './activeJsTsEditorTracker';
import { Disposable } from './dispose';
import { isJsConfigOrTsConfigFileName } from './languageDescription';
import { isSupportedLanguageMode } from './languageModeIds';
/**
/**E
* When clause context set when the current file is managed by vscode's built-in typescript extension.
*/
export default class ManagedFileContextManager extends Disposable {
@ -17,17 +18,20 @@ export default class ManagedFileContextManager extends Disposable {
private isInManagedFileContext: boolean = false;
public constructor(
private readonly normalizePath: (resource: vscode.Uri) => string | undefined
activeJsTsEditorTracker: ActiveJsTsEditorTracker,
private readonly normalizePath: (resource: vscode.Uri) => string | undefined,
) {
super();
vscode.window.onDidChangeActiveTextEditor(this.onDidChangeActiveTextEditor, this, this._disposables);
activeJsTsEditorTracker.onDidChangeActiveJsTsEditor(this.onDidChangeActiveTextEditor, this, this._disposables);
this.onDidChangeActiveTextEditor(vscode.window.activeTextEditor);
this.onDidChangeActiveTextEditor(activeJsTsEditorTracker.activeJsTsEditor);
}
private onDidChangeActiveTextEditor(editor?: vscode.TextEditor): any {
private onDidChangeActiveTextEditor(editor?: vscode.TextEditor): void {
if (editor) {
this.updateContext(this.isManagedFile(editor));
} else {
this.updateContext(false);
}
}
@ -52,3 +56,4 @@ export default class ManagedFileContextManager extends Disposable {
return isJsConfigOrTsConfigFileName(editor.document.fileName);
}
}