mirror of
https://github.com/microsoft/vscode.git
synced 2026-04-22 01:29:04 +01:00
Display more information in the TS version status bar item
For #91510 Switching TS versions is fairly uncommon, so repurpose the status bar entry to have additional project commands in it (including the ability to switch TS versions)
This commit is contained in:
@@ -4,15 +4,10 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import * as nls from 'vscode-nls';
|
||||
import TypeScriptServiceClientHost from '../typeScriptServiceClientHost';
|
||||
import { nulToken } from '../utils/cancellation';
|
||||
import { Command } from '../utils/commandManager';
|
||||
import { Lazy } from '../utils/lazy';
|
||||
import { isImplicitProjectConfigFile, openOrCreateConfigFile } from '../utils/tsconfig';
|
||||
import { ServerResponse } from '../typescriptService';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
import { openProjectConfigForFile, ProjectType } from '../utils/tsconfig';
|
||||
|
||||
export class TypeScriptGoToProjectConfigCommand implements Command {
|
||||
public readonly id = 'typescript.goToProjectConfig';
|
||||
@@ -24,7 +19,7 @@ export class TypeScriptGoToProjectConfigCommand implements Command {
|
||||
public execute() {
|
||||
const editor = vscode.window.activeTextEditor;
|
||||
if (editor) {
|
||||
goToProjectConfig(this.lazyClientHost.value, true, editor.document.uri);
|
||||
openProjectConfigForFile(ProjectType.TypeScript, this.lazyClientHost.value.serviceClient, editor.document.uri);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -39,79 +34,8 @@ export class JavaScriptGoToProjectConfigCommand implements Command {
|
||||
public execute() {
|
||||
const editor = vscode.window.activeTextEditor;
|
||||
if (editor) {
|
||||
goToProjectConfig(this.lazyClientHost.value, false, editor.document.uri);
|
||||
openProjectConfigForFile(ProjectType.JavaScript, this.lazyClientHost.value.serviceClient, editor.document.uri);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function goToProjectConfig(
|
||||
clientHost: TypeScriptServiceClientHost,
|
||||
isTypeScriptProject: boolean,
|
||||
resource: vscode.Uri
|
||||
): Promise<void> {
|
||||
const client = clientHost.serviceClient;
|
||||
const rootPath = client.getWorkspaceRootForResource(resource);
|
||||
if (!rootPath) {
|
||||
vscode.window.showInformationMessage(
|
||||
localize(
|
||||
'typescript.projectConfigNoWorkspace',
|
||||
'Please open a folder in VS Code to use a TypeScript or JavaScript project'));
|
||||
return;
|
||||
}
|
||||
|
||||
const file = client.toPath(resource);
|
||||
// TSServer errors when 'projectInfo' is invoked on a non js/ts file
|
||||
if (!file || !await clientHost.handles(resource)) {
|
||||
vscode.window.showWarningMessage(
|
||||
localize(
|
||||
'typescript.projectConfigUnsupportedFile',
|
||||
'Could not determine TypeScript or JavaScript project. Unsupported file type'));
|
||||
return;
|
||||
}
|
||||
|
||||
let res: ServerResponse.Response<protocol.ProjectInfoResponse> | undefined;
|
||||
try {
|
||||
res = await client.execute('projectInfo', { file, needFileNameList: false }, nulToken);
|
||||
} catch {
|
||||
// noop
|
||||
}
|
||||
|
||||
if (res?.type !== 'response' || !res.body) {
|
||||
vscode.window.showWarningMessage(localize('typescript.projectConfigCouldNotGetInfo', 'Could not determine TypeScript or JavaScript project'));
|
||||
return;
|
||||
}
|
||||
|
||||
const { configFileName } = res.body;
|
||||
if (!isImplicitProjectConfigFile(configFileName)) {
|
||||
const doc = await vscode.workspace.openTextDocument(configFileName);
|
||||
vscode.window.showTextDocument(doc, vscode.window.activeTextEditor ? vscode.window.activeTextEditor.viewColumn : undefined);
|
||||
return;
|
||||
}
|
||||
|
||||
enum ProjectConfigAction {
|
||||
None,
|
||||
CreateConfig,
|
||||
LearnMore,
|
||||
}
|
||||
|
||||
interface ProjectConfigMessageItem extends vscode.MessageItem {
|
||||
id: ProjectConfigAction;
|
||||
}
|
||||
|
||||
const selected = await vscode.window.showInformationMessage<ProjectConfigMessageItem>(
|
||||
(isTypeScriptProject
|
||||
? localize('typescript.noTypeScriptProjectConfig', 'File is not part of a TypeScript project. Click [here]({0}) to learn more.', 'https://go.microsoft.com/fwlink/?linkid=841896')
|
||||
: localize('typescript.noJavaScriptProjectConfig', 'File is not part of a JavaScript project Click [here]({0}) to learn more.', 'https://go.microsoft.com/fwlink/?linkid=759670')
|
||||
), {
|
||||
title: isTypeScriptProject
|
||||
? localize('typescript.configureTsconfigQuickPick', 'Configure tsconfig.json')
|
||||
: localize('typescript.configureJsconfigQuickPick', 'Configure jsconfig.json'),
|
||||
id: ProjectConfigAction.CreateConfig,
|
||||
});
|
||||
|
||||
switch (selected && selected.id) {
|
||||
case ProjectConfigAction.CreateConfig:
|
||||
openOrCreateConfigFile(isTypeScriptProject, rootPath, client.configuration);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,6 @@ export class SelectTypeScriptVersionCommand implements Command {
|
||||
) { }
|
||||
|
||||
public execute() {
|
||||
this.lazyClientHost.value.serviceClient.onVersionStatusClicked();
|
||||
this.lazyClientHost.value.serviceClient.showVersionPicker();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ import { lazy, Lazy } from './utils/lazy';
|
||||
import LogDirectoryProvider from './utils/logDirectoryProvider';
|
||||
import ManagedFileContextManager from './utils/managedFileContext';
|
||||
import { PluginManager } from './utils/plugins';
|
||||
import * as ProjectStatus from './utils/projectStatus';
|
||||
import * as ProjectStatus from './utils/largeProjectStatus';
|
||||
import TscTaskProvider from './features/task';
|
||||
|
||||
export function activate(
|
||||
|
||||
@@ -15,6 +15,7 @@ import LanguageProvider from './languageProvider';
|
||||
import * as Proto from './protocol';
|
||||
import * as PConst from './protocol.const';
|
||||
import TypeScriptServiceClient from './typescriptServiceClient';
|
||||
import { coalesce, flatten } from './utils/arrays';
|
||||
import { CommandManager } from './utils/commandManager';
|
||||
import { Disposable } from './utils/dispose';
|
||||
import { DiagnosticLanguage, LanguageDescription } from './utils/languageDescription';
|
||||
@@ -23,7 +24,6 @@ import { PluginManager } from './utils/plugins';
|
||||
import * as typeConverters from './utils/typeConverters';
|
||||
import TypingsStatus, { AtaProgressReporter } from './utils/typingsStatus';
|
||||
import VersionStatus from './utils/versionStatus';
|
||||
import { flatten, coalesce } from './utils/arrays';
|
||||
|
||||
// Style check diagnostics that can be reported as warnings
|
||||
const styleCheckDiagnostics = [
|
||||
@@ -37,11 +37,14 @@ const styleCheckDiagnostics = [
|
||||
];
|
||||
|
||||
export default class TypeScriptServiceClientHost extends Disposable {
|
||||
private readonly typingsStatus: TypingsStatus;
|
||||
|
||||
private readonly client: TypeScriptServiceClient;
|
||||
private readonly languages: LanguageProvider[] = [];
|
||||
private readonly languagePerId = new Map<string, LanguageProvider>();
|
||||
|
||||
private readonly typingsStatus: TypingsStatus;
|
||||
private readonly versionStatus: VersionStatus;
|
||||
|
||||
private readonly fileConfigurationManager: FileConfigurationManager;
|
||||
|
||||
private reportStyleCheckAsWarnings: boolean = true;
|
||||
@@ -71,7 +74,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.versionStatus = this._register(new VersionStatus(resource => this.client.toPath(resource)));
|
||||
this.versionStatus = this._register(new VersionStatus(this.client, commandManager));
|
||||
|
||||
this._register(new AtaProgressReporter(this.client));
|
||||
this.typingsStatus = this._register(new TypingsStatus(this.client));
|
||||
|
||||
@@ -120,6 +120,10 @@ export interface ITypeScriptServiceClient {
|
||||
readonly onDidEndInstallTypings: vscode.Event<Proto.EndInstallTypesEventBody>;
|
||||
readonly onTypesInstallerInitializationFailed: vscode.Event<Proto.TypesInstallerInitializationFailedEventBody>;
|
||||
|
||||
onReady(f: () => void): Promise<void>;
|
||||
|
||||
showVersionPicker(): void;
|
||||
|
||||
readonly apiVersion: API;
|
||||
readonly pluginManager: PluginManager;
|
||||
readonly configuration: TypeScriptServiceConfiguration;
|
||||
|
||||
@@ -24,7 +24,7 @@ import { TypeScriptPluginPathsProvider } from './utils/pluginPathsProvider';
|
||||
import { PluginManager } from './utils/plugins';
|
||||
import { TelemetryReporter, VSCodeTelemetryReporter, TelemetryProperties } from './utils/telemetry';
|
||||
import Tracer from './utils/tracer';
|
||||
import { inferredProjectCompilerOptions } from './utils/tsconfig';
|
||||
import { inferredProjectCompilerOptions, ProjectType } from './utils/tsconfig';
|
||||
import { TypeScriptVersionPicker } from './utils/versionPicker';
|
||||
import { TypeScriptVersion, TypeScriptVersionProvider } from './utils/versionProvider';
|
||||
|
||||
@@ -414,16 +414,11 @@ export default class TypeScriptServiceClient extends Disposable implements IType
|
||||
return this.serverState;
|
||||
}
|
||||
|
||||
public onVersionStatusClicked(): Thenable<void> {
|
||||
return this.showVersionPicker();
|
||||
}
|
||||
|
||||
private showVersionPicker(): Thenable<void> {
|
||||
return this.versionPicker.show().then(change => {
|
||||
if (change.newVersion && change.oldVersion && change.oldVersion.eq(change.newVersion)) {
|
||||
this.restartTsServer();
|
||||
}
|
||||
});
|
||||
public async showVersionPicker(): Promise<void> {
|
||||
const change = await this.versionPicker.show();
|
||||
if (change.newVersion && change.oldVersion && change.oldVersion.eq(change.newVersion)) {
|
||||
this.restartTsServer();
|
||||
}
|
||||
}
|
||||
|
||||
public async openTsServerLogFile(): Promise<boolean> {
|
||||
@@ -512,7 +507,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType
|
||||
|
||||
private getCompilerOptionsForInferredProjects(configuration: TypeScriptServiceConfiguration): Proto.ExternalProjectCompilerOptions {
|
||||
return {
|
||||
...inferredProjectCompilerOptions(true, configuration),
|
||||
...inferredProjectCompilerOptions(ProjectType.TypeScript, configuration),
|
||||
allowJs: true,
|
||||
allowSyntheticDefaultImports: true,
|
||||
allowNonTsExtensions: true,
|
||||
|
||||
@@ -7,7 +7,7 @@ import * as vscode from 'vscode';
|
||||
import { loadMessageBundle } from 'vscode-nls';
|
||||
import { ITypeScriptServiceClient } from '../typescriptService';
|
||||
import { TelemetryReporter } from './telemetry';
|
||||
import { isImplicitProjectConfigFile, openOrCreateConfigFile } from './tsconfig';
|
||||
import { isImplicitProjectConfigFile, openOrCreateConfig, ProjectType } from './tsconfig';
|
||||
|
||||
const localize = loadMessageBundle();
|
||||
|
||||
@@ -101,8 +101,8 @@ function onConfigureExcludesSelected(
|
||||
} else {
|
||||
const root = client.getWorkspaceRootForResource(vscode.Uri.file(configFileName));
|
||||
if (root) {
|
||||
openOrCreateConfigFile(
|
||||
configFileName.match(/tsconfig\.?.*\.json/) !== null,
|
||||
openOrCreateConfig(
|
||||
/tsconfig\.?.*\.json/.test(configFileName) ? ProjectType.TypeScript : ProjectType.JavaScript,
|
||||
root,
|
||||
client.configuration);
|
||||
}
|
||||
@@ -5,15 +5,25 @@
|
||||
|
||||
import * as path from 'path';
|
||||
import * as vscode from 'vscode';
|
||||
import * as nls from 'vscode-nls';
|
||||
import type * as Proto from '../protocol';
|
||||
import { ITypeScriptServiceClient, ServerResponse } from '../typescriptService';
|
||||
import { nulToken } from '../utils/cancellation';
|
||||
import { TypeScriptServiceConfiguration } from './configuration';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
export const enum ProjectType {
|
||||
TypeScript,
|
||||
JavaScript,
|
||||
}
|
||||
|
||||
export function isImplicitProjectConfigFile(configFileName: string) {
|
||||
return configFileName.startsWith('/dev/null/');
|
||||
}
|
||||
|
||||
export function inferredProjectCompilerOptions(
|
||||
isTypeScriptProject: boolean,
|
||||
projectType: ProjectType,
|
||||
serviceConfig: TypeScriptServiceConfiguration,
|
||||
): Proto.ExternalProjectCompilerOptions {
|
||||
const projectConfig: Proto.ExternalProjectCompilerOptions = {
|
||||
@@ -24,7 +34,7 @@ export function inferredProjectCompilerOptions(
|
||||
|
||||
if (serviceConfig.checkJs) {
|
||||
projectConfig.checkJs = true;
|
||||
if (isTypeScriptProject) {
|
||||
if (projectType === ProjectType.TypeScript) {
|
||||
projectConfig.allowJs = true;
|
||||
}
|
||||
}
|
||||
@@ -33,7 +43,7 @@ export function inferredProjectCompilerOptions(
|
||||
projectConfig.experimentalDecorators = true;
|
||||
}
|
||||
|
||||
if (isTypeScriptProject) {
|
||||
if (projectType === ProjectType.TypeScript) {
|
||||
projectConfig.sourceMap = true;
|
||||
}
|
||||
|
||||
@@ -41,10 +51,10 @@ export function inferredProjectCompilerOptions(
|
||||
}
|
||||
|
||||
function inferredProjectConfigSnippet(
|
||||
isTypeScriptProject: boolean,
|
||||
projectType: ProjectType,
|
||||
config: TypeScriptServiceConfiguration
|
||||
) {
|
||||
const baseConfig = inferredProjectCompilerOptions(isTypeScriptProject, config);
|
||||
const baseConfig = inferredProjectCompilerOptions(projectType, config);
|
||||
const compilerOptions = Object.keys(baseConfig).map(key => `"${key}": ${JSON.stringify(baseConfig[key])}`);
|
||||
return new vscode.SnippetString(`{
|
||||
"compilerOptions": {
|
||||
@@ -57,13 +67,13 @@ function inferredProjectConfigSnippet(
|
||||
}`);
|
||||
}
|
||||
|
||||
export async function openOrCreateConfigFile(
|
||||
isTypeScriptProject: boolean,
|
||||
export async function openOrCreateConfig(
|
||||
projectType: ProjectType,
|
||||
rootPath: string,
|
||||
config: TypeScriptServiceConfiguration
|
||||
configuration: TypeScriptServiceConfiguration,
|
||||
): Promise<vscode.TextEditor | null> {
|
||||
const configFile = vscode.Uri.file(path.join(rootPath, isTypeScriptProject ? 'tsconfig.json' : 'jsconfig.json'));
|
||||
const col = vscode.window.activeTextEditor ? vscode.window.activeTextEditor.viewColumn : undefined;
|
||||
const configFile = vscode.Uri.file(path.join(rootPath, projectType === ProjectType.TypeScript ? 'tsconfig.json' : 'jsconfig.json'));
|
||||
const col = vscode.window.activeTextEditor?.viewColumn;
|
||||
try {
|
||||
const doc = await vscode.workspace.openTextDocument(configFile);
|
||||
return vscode.window.showTextDocument(doc, col);
|
||||
@@ -71,8 +81,79 @@ export async function openOrCreateConfigFile(
|
||||
const doc = await vscode.workspace.openTextDocument(configFile.with({ scheme: 'untitled' }));
|
||||
const editor = await vscode.window.showTextDocument(doc, col);
|
||||
if (editor.document.getText().length === 0) {
|
||||
await editor.insertSnippet(inferredProjectConfigSnippet(isTypeScriptProject, config));
|
||||
await editor.insertSnippet(inferredProjectConfigSnippet(projectType, configuration));
|
||||
}
|
||||
return editor;
|
||||
}
|
||||
}
|
||||
|
||||
export async function openProjectConfigOrPromptToCreate(
|
||||
projectType: ProjectType,
|
||||
client: ITypeScriptServiceClient,
|
||||
rootPath: string,
|
||||
configFileName: string,
|
||||
): Promise<void> {
|
||||
if (!isImplicitProjectConfigFile(configFileName)) {
|
||||
const doc = await vscode.workspace.openTextDocument(configFileName);
|
||||
vscode.window.showTextDocument(doc, vscode.window.activeTextEditor?.viewColumn);
|
||||
return;
|
||||
}
|
||||
|
||||
const CreateConfigItem: vscode.MessageItem = {
|
||||
title: projectType === ProjectType.TypeScript
|
||||
? localize('typescript.configureTsconfigQuickPick', 'Configure tsconfig.json')
|
||||
: localize('typescript.configureJsconfigQuickPick', 'Configure jsconfig.json'),
|
||||
};
|
||||
|
||||
const selected = await vscode.window.showInformationMessage(
|
||||
(projectType === ProjectType.TypeScript
|
||||
? localize('typescript.noTypeScriptProjectConfig', 'File is not part of a TypeScript project. Click [here]({0}) to learn more.', 'https://go.microsoft.com/fwlink/?linkid=841896')
|
||||
: localize('typescript.noJavaScriptProjectConfig', 'File is not part of a JavaScript project Click [here]({0}) to learn more.', 'https://go.microsoft.com/fwlink/?linkid=759670')
|
||||
),
|
||||
CreateConfigItem);
|
||||
|
||||
switch (selected) {
|
||||
case CreateConfigItem:
|
||||
openOrCreateConfig(projectType, rootPath, client.configuration);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
export async function openProjectConfigForFile(
|
||||
projectType: ProjectType,
|
||||
client: ITypeScriptServiceClient,
|
||||
resource: vscode.Uri,
|
||||
): Promise<void> {
|
||||
const rootPath = client.getWorkspaceRootForResource(resource);
|
||||
if (!rootPath) {
|
||||
vscode.window.showInformationMessage(
|
||||
localize(
|
||||
'typescript.projectConfigNoWorkspace',
|
||||
'Please open a folder in VS Code to use a TypeScript or JavaScript project'));
|
||||
return;
|
||||
}
|
||||
|
||||
const file = client.toPath(resource);
|
||||
// TSServer errors when 'projectInfo' is invoked on a non js/ts file
|
||||
if (!file || !await client.toPath(resource)) {
|
||||
vscode.window.showWarningMessage(
|
||||
localize(
|
||||
'typescript.projectConfigUnsupportedFile',
|
||||
'Could not determine TypeScript or JavaScript project. Unsupported file type'));
|
||||
return;
|
||||
}
|
||||
|
||||
let res: ServerResponse.Response<protocol.ProjectInfoResponse> | undefined;
|
||||
try {
|
||||
res = await client.execute('projectInfo', { file, needFileNameList: false }, nulToken);
|
||||
} catch {
|
||||
// noop
|
||||
}
|
||||
|
||||
if (res?.type !== 'response' || !res.body) {
|
||||
vscode.window.showWarningMessage(localize('typescript.projectConfigCouldNotGetInfo', 'Could not determine TypeScript or JavaScript project'));
|
||||
return;
|
||||
}
|
||||
return openProjectConfigOrPromptToCreate(projectType, client, rootPath, res.body.configFileName);
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ export class TypeScriptVersionPicker {
|
||||
}
|
||||
}
|
||||
|
||||
public get useWorkspaceTsdkSetting(): boolean {
|
||||
private get useWorkspaceTsdkSetting(): boolean {
|
||||
return this.workspaceState.get<boolean>(useWorkspaceTsdkStorageKey, false);
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ export class TypeScriptVersionPicker {
|
||||
this._currentVersion = this.versionProvider.bundledVersion;
|
||||
}
|
||||
|
||||
public async show(firstRun?: boolean): Promise<{ oldVersion?: TypeScriptVersion, newVersion?: TypeScriptVersion }> {
|
||||
public async show(): Promise<{ oldVersion?: TypeScriptVersion, newVersion?: TypeScriptVersion }> {
|
||||
const pickOptions: MyQuickPickItem[] = [];
|
||||
|
||||
const shippedVersion = this.versionProvider.defaultVersion;
|
||||
@@ -77,7 +77,7 @@ export class TypeScriptVersionPicker {
|
||||
}
|
||||
|
||||
pickOptions.push({
|
||||
label: localize('learnMore', 'Learn More'),
|
||||
label: localize('learnMore', 'Learn more about managing TypeScript versions'),
|
||||
description: '',
|
||||
id: MessageAction.learnMore
|
||||
});
|
||||
@@ -86,7 +86,6 @@ export class TypeScriptVersionPicker {
|
||||
placeHolder: localize(
|
||||
'selectTsVersion',
|
||||
"Select the TypeScript version used for JavaScript and TypeScript language features"),
|
||||
ignoreFocusOut: firstRun,
|
||||
});
|
||||
|
||||
if (!selected) {
|
||||
|
||||
@@ -4,50 +4,189 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import * as languageModeIds from './languageModeIds';
|
||||
import { TypeScriptVersion } from './versionProvider';
|
||||
import { Disposable } from './dispose';
|
||||
import * as nls from 'vscode-nls';
|
||||
import { ITypeScriptServiceClient } from '../typescriptService';
|
||||
import { coalesce } from '../utils/arrays';
|
||||
import { Command, CommandManager } from '../utils/commandManager';
|
||||
import { isTypeScriptDocument } from '../utils/languageModeIds';
|
||||
import { isImplicitProjectConfigFile, openOrCreateConfig, openProjectConfigOrPromptToCreate, openProjectConfigForFile, ProjectType } from '../utils/tsconfig';
|
||||
import { Disposable } from './dispose';
|
||||
import { TypeScriptVersion } from './versionProvider';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
|
||||
namespace ProjectInfoState {
|
||||
export const enum Type { None, Pending, Resolved }
|
||||
|
||||
export const None = Object.freeze({ type: Type.None } as const);
|
||||
|
||||
export class Pending {
|
||||
public readonly type = Type.Pending;
|
||||
|
||||
public readonly cancellation = new vscode.CancellationTokenSource();
|
||||
|
||||
constructor(
|
||||
public readonly resource: vscode.Uri,
|
||||
) { }
|
||||
}
|
||||
|
||||
export class Resolved {
|
||||
public readonly type = Type.Resolved;
|
||||
|
||||
constructor(
|
||||
public readonly resource: vscode.Uri,
|
||||
public readonly configFile: string,
|
||||
) { }
|
||||
}
|
||||
|
||||
export type State = typeof None | Pending | Resolved;
|
||||
}
|
||||
|
||||
interface QuickPickItem extends vscode.QuickPickItem {
|
||||
run(): void;
|
||||
}
|
||||
|
||||
class ProjectStatusCommand implements Command {
|
||||
public readonly id = '_typescript.projectStatus';
|
||||
|
||||
public constructor(
|
||||
private readonly _client: ITypeScriptServiceClient,
|
||||
private readonly _delegate: () => ProjectInfoState.State,
|
||||
) { }
|
||||
|
||||
public async execute(): Promise<void> {
|
||||
const info = this._delegate();
|
||||
|
||||
|
||||
const result = await vscode.window.showQuickPick<QuickPickItem>(coalesce([
|
||||
this.getProjectItem(info),
|
||||
this.getVersionItem(),
|
||||
this.getHelpItem(),
|
||||
]), {
|
||||
placeHolder: localize('projectQuickPick.placeholder', "TypeScript Project Info"),
|
||||
});
|
||||
|
||||
return result?.run();
|
||||
}
|
||||
|
||||
private getVersionItem(): QuickPickItem {
|
||||
return {
|
||||
label: localize('projectQuickPick.version.label', "Select TypeScript Version..."),
|
||||
description: this._client.apiVersion.displayName,
|
||||
run: () => {
|
||||
this._client.showVersionPicker();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private getProjectItem(info: ProjectInfoState.State): QuickPickItem | undefined {
|
||||
const rootPath = info.type === ProjectInfoState.Type.Resolved ? this._client.getWorkspaceRootForResource(info.resource) : undefined;
|
||||
if (!rootPath) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (info.type === ProjectInfoState.Type.Resolved) {
|
||||
if (isImplicitProjectConfigFile(info.configFile)) {
|
||||
return {
|
||||
label: localize('projectQuickPick.project.create', "Create tsconfig"),
|
||||
detail: localize('projectQuickPick.project.create.description', "This file is currently not part of a tsconfig/jsconfig project"),
|
||||
run: () => {
|
||||
openOrCreateConfig(ProjectType.TypeScript, rootPath, this._client.configuration);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
label: localize('projectQuickPick.version.goProjectConfig', "Open tsconfig"),
|
||||
description: info.type === ProjectInfoState.Type.Resolved ? vscode.workspace.asRelativePath(info.configFile) : undefined,
|
||||
run: () => {
|
||||
if (info.type === ProjectInfoState.Type.Resolved) {
|
||||
openProjectConfigOrPromptToCreate(ProjectType.TypeScript, this._client, rootPath, info.configFile);
|
||||
} else if (info.type === ProjectInfoState.Type.Pending) {
|
||||
openProjectConfigForFile(ProjectType.TypeScript, this._client, info.resource);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private getHelpItem(): QuickPickItem {
|
||||
return {
|
||||
label: localize('projectQuickPick.help', "TypeScript help"),
|
||||
run: () => {
|
||||
vscode.env.openExternal(vscode.Uri.parse('https://go.microsoft.com/fwlink/?linkid=839919')); // TODO:
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default class VersionStatus extends Disposable {
|
||||
private readonly _versionBarEntry: vscode.StatusBarItem;
|
||||
|
||||
private readonly _statusBarEntry: vscode.StatusBarItem;
|
||||
|
||||
private _ready = false;
|
||||
private _state: ProjectInfoState.State = ProjectInfoState.None;
|
||||
|
||||
constructor(
|
||||
private readonly _normalizePath: (resource: vscode.Uri) => string | undefined
|
||||
private readonly _client: ITypeScriptServiceClient,
|
||||
commandManager: CommandManager,
|
||||
) {
|
||||
super();
|
||||
this._versionBarEntry = this._register(vscode.window.createStatusBarItem({
|
||||
id: 'status.typescript.version',
|
||||
name: localize('typescriptVersion', "TypeScript: Version"),
|
||||
|
||||
this._statusBarEntry = this._register(vscode.window.createStatusBarItem({
|
||||
id: 'status.typescript',
|
||||
name: localize('projectInfo.name', "TypeScript: Project Info"),
|
||||
alignment: vscode.StatusBarAlignment.Right,
|
||||
priority: 99 /* to the right of editor status (100) */
|
||||
}));
|
||||
vscode.window.onDidChangeActiveTextEditor(this.showHideStatus, this, this._disposables);
|
||||
|
||||
const command = new ProjectStatusCommand(this._client, () => this._state);
|
||||
commandManager.register(command);
|
||||
this._statusBarEntry.command = command.id;
|
||||
|
||||
vscode.window.onDidChangeActiveTextEditor(this.updateStatus, this, this._disposables);
|
||||
|
||||
this._client.onReady(() => {
|
||||
this._ready = true;
|
||||
this.updateStatus();
|
||||
});
|
||||
}
|
||||
|
||||
public onDidChangeTypeScriptVersion(version: TypeScriptVersion) {
|
||||
this.showHideStatus();
|
||||
this._versionBarEntry.text = version.displayName;
|
||||
this._versionBarEntry.tooltip = version.path;
|
||||
this._versionBarEntry.command = 'typescript.selectTypeScriptVersion';
|
||||
this._statusBarEntry.text = version.displayName;
|
||||
this._statusBarEntry.tooltip = version.path;
|
||||
this.updateStatus();
|
||||
}
|
||||
|
||||
private showHideStatus() {
|
||||
private async updateStatus() {
|
||||
if (!vscode.window.activeTextEditor) {
|
||||
this._versionBarEntry.hide();
|
||||
this.hide();
|
||||
return;
|
||||
}
|
||||
|
||||
const doc = vscode.window.activeTextEditor.document;
|
||||
if (vscode.languages.match([languageModeIds.typescript, languageModeIds.typescriptreact], doc)) {
|
||||
if (this._normalizePath(doc.uri)) {
|
||||
this._versionBarEntry.show();
|
||||
} else {
|
||||
this._versionBarEntry.hide();
|
||||
if (isTypeScriptDocument(doc)) {
|
||||
const file = this._client.normalizedPath(doc.uri);
|
||||
if (file) {
|
||||
this._statusBarEntry.show();
|
||||
if (!this._ready) {
|
||||
return;
|
||||
}
|
||||
|
||||
const pendingState = new ProjectInfoState.Pending(doc.uri);
|
||||
this.updateState(pendingState);
|
||||
|
||||
const response = await this._client.execute('projectInfo', { file, needFileNameList: false }, pendingState.cancellation.token);
|
||||
if (response.type === 'response' && response.body) {
|
||||
if (this._state === pendingState) {
|
||||
this.updateState(new ProjectInfoState.Resolved(doc.uri, response.body.configFileName));
|
||||
this._statusBarEntry.show();
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!vscode.window.activeTextEditor.viewColumn) {
|
||||
@@ -56,6 +195,24 @@ export default class VersionStatus extends Disposable {
|
||||
return;
|
||||
}
|
||||
|
||||
this._versionBarEntry.hide();
|
||||
this.hide();
|
||||
}
|
||||
|
||||
private hide(): void {
|
||||
this._statusBarEntry.hide();
|
||||
this.updateState(ProjectInfoState.None);
|
||||
}
|
||||
|
||||
private updateState(newState: ProjectInfoState.State): void {
|
||||
if (this._state === newState) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._state.type === ProjectInfoState.Type.Pending) {
|
||||
this._state.cancellation.cancel();
|
||||
this._state.cancellation.dispose();
|
||||
}
|
||||
|
||||
this._state = newState;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user