diff --git a/src/vs/editor/common/services/languageStatusService.ts b/src/vs/editor/common/services/languageStatusService.ts index e9152560dc9d4..affbfc6ffbc48 100644 --- a/src/vs/editor/common/services/languageStatusService.ts +++ b/src/vs/editor/common/services/languageStatusService.ts @@ -4,8 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { CancellationToken } from 'vs/base/common/cancellation'; -import { onUnexpectedExternalError } from 'vs/base/common/errors'; -import { Emitter, Event } from 'vs/base/common/event'; +import { Event } from 'vs/base/common/event'; import { IMarkdownString } from 'vs/base/common/htmlContent'; import { IDisposable } from 'vs/base/common/lifecycle'; import Severity from 'vs/base/common/severity'; @@ -17,6 +16,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation' export interface ILanguageStatus { + selector: LanguageSelector, severity: Severity; text: string; message: string | IMarkdownString; @@ -34,41 +34,26 @@ export interface ILanguageStatusService { onDidChange: Event; - registerLanguageStatusProvider(selector: LanguageSelector, provider: ILanguageStatusProvider): IDisposable; + addStatus(status: ILanguageStatus): IDisposable; getLanguageStatus(model: ITextModel): Promise; } class LanguageStatusServiceImpl implements ILanguageStatusService { - declare _serviceBrand: undefined; - private readonly _provider = new LanguageFeatureRegistry(); + declare _serviceBrand: undefined; - private readonly _onDidChange = new Emitter(); - readonly onDidChange: Event = Event.any(this._onDidChange.event, this._provider.onDidChange); + private readonly _provider = new LanguageFeatureRegistry(); - dispose() { - this._onDidChange.dispose(); - } + readonly onDidChange: Event = this._provider.onDidChange; - registerLanguageStatusProvider(selector: LanguageSelector, provider: ILanguageStatusProvider): IDisposable { - return this._provider.register(selector, provider); + addStatus(status: ILanguageStatus): IDisposable { + return this._provider.register(status.selector, status); } async getLanguageStatus(model: ITextModel): Promise { - const all: ILanguageStatus[] = []; - for (const provider of this._provider.ordered(model)) { - try { - const status = await provider.provideLanguageStatus(model.getLanguageIdentifier().language, CancellationToken.None); - if (status) { - all.push(status); - } - } catch (err) { - onUnexpectedExternalError(err); - } - } - return all.sort((a, b) => b.severity - a.severity); + return this._provider.ordered(model).sort((a, b) => b.severity - a.severity); } } diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 20466643ba7d2..611f946710534 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -3166,19 +3166,16 @@ declare module 'vscode' { Error = 2 } - class LanguageStatus { + interface LanguageStatusItem { + selector: DocumentSelector; text: string; - detail: string | MarkdownString; + detail: string | MarkdownString severity: LanguageStatusSeverity; - constructor(text: string); - } - - export interface LanguageStatusProvider { - provideLanguageStatus(token: CancellationToken): ProviderResult; + dispose(): void; } namespace languages { - export function registerLanguageStatusProvider(selector: DocumentSelector, provider: LanguageStatusProvider): Disposable; + export function createLanguageStatusItem(selector: DocumentSelector): LanguageStatusItem; } //#endregion diff --git a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts index c706f83678299..45ee8e795fb7e 100644 --- a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts @@ -23,7 +23,6 @@ import * as callh from 'vs/workbench/contrib/callHierarchy/common/callHierarchy' import * as typeh from 'vs/workbench/contrib/typeHierarchy/common/typeHierarchy'; import { mixin } from 'vs/base/common/objects'; import { decodeSemanticTokensDto } from 'vs/editor/common/services/semanticTokensDto'; -import { ILanguageStatus, ILanguageStatusService } from 'vs/editor/common/services/languageStatusService'; @extHostNamedCustomer(MainContext.MainThreadLanguageFeatures) export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesShape { @@ -35,7 +34,6 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha constructor( extHostContext: IExtHostContext, @IModeService modeService: IModeService, - @ILanguageStatusService private readonly _languageStatusService: ILanguageStatusService ) { this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostLanguageFeatures); this._modeService = modeService; @@ -159,16 +157,6 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha //#endregion - // --- language status - - $registerLanguageStatusProvider(handle: number, selector: IDocumentFilterDto[]): void { - this._registrations.set(handle, this._languageStatusService.registerLanguageStatusProvider(selector, { - provideLanguageStatus: (_langId: string, token: CancellationToken): Promise => { - return this._proxy.$provideLanguageStatus(handle, token); - } - })); - } - // --- outline $registerDocumentSymbolProvider(handle: number, selector: IDocumentFilterDto[], displayName: string): void { diff --git a/src/vs/workbench/api/browser/mainThreadLanguages.ts b/src/vs/workbench/api/browser/mainThreadLanguages.ts index 9c11d1a54f920..f4636bb38969c 100644 --- a/src/vs/workbench/api/browser/mainThreadLanguages.ts +++ b/src/vs/workbench/api/browser/mainThreadLanguages.ts @@ -12,6 +12,8 @@ import { IPosition } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; import { StandardTokenType } from 'vs/editor/common/modes'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; +import { ILanguageStatus, ILanguageStatusService } from 'vs/editor/common/services/languageStatusService'; +import { IDisposable } from 'vs/base/common/lifecycle'; @extHostNamedCustomer(MainContext.MainThreadLanguages) export class MainThreadLanguages implements MainThreadLanguagesShape { @@ -21,8 +23,8 @@ export class MainThreadLanguages implements MainThreadLanguagesShape { @IModeService private readonly _modeService: IModeService, @IModelService private readonly _modelService: IModelService, @ITextModelService private _resolverService: ITextModelService, - ) { - } + @ILanguageStatusService private readonly _languageStatusService: ILanguageStatusService, + ) { } dispose(): void { // nothing @@ -62,4 +64,17 @@ export class MainThreadLanguages implements MainThreadLanguagesShape { range: new Range(position.lineNumber, 1 + tokens.getStartOffset(idx), position.lineNumber, 1 + tokens.getEndOffset(idx)) }; } + + // --- language status + + private readonly _status = new Map(); + + $setLanguageStatus(handle: number, status: ILanguageStatus): void { + this._status.get(handle)?.dispose(); + this._status.set(handle, this._languageStatusService.addStatus(status)); + } + + $removeLanguageStatus(handle: number): void { + this._status.get(handle)?.dispose(); + } } diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 75eafe516dd0b..786d8ade7e2cd 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -507,9 +507,9 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I checkProposedApiEnabled(extension); return extHostLanguageFeatures.registerTypeHierarchyProvider(extension, selector, provider); }, - registerLanguageStatusProvider(selector: vscode.DocumentSelector, provider: vscode.LanguageStatusProvider): vscode.Disposable { + createLanguageStatusItem(selector: vscode.DocumentSelector): vscode.LanguageStatusItem { checkProposedApiEnabled(extension); - return extHostLanguageFeatures.registerLanguageStatusProvider(extension, selector, provider); + return extHostLanguages.createLanguageStatusItem(selector); } }; @@ -1284,7 +1284,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I BranchCoverage: extHostTypes.BranchCoverage, FunctionCoverage: extHostTypes.FunctionCoverage, WorkspaceTrustState: extHostTypes.WorkspaceTrustState, - LanguageStatus: extHostTypes.LanguageStatus, LanguageStatusSeverity: extHostTypes.LanguageStatusSeverity, }; }; diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index feeca2223667a..8814c9286bfad 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -383,7 +383,6 @@ export interface IdentifiableInlineCompletion extends modes.InlineCompletion { export interface MainThreadLanguageFeaturesShape extends IDisposable { $unregister(handle: number): void; - $registerLanguageStatusProvider(handle: number, selector: IDocumentFilterDto[]): void; $registerDocumentSymbolProvider(handle: number, selector: IDocumentFilterDto[], label: string): void; $registerCodeLensSupport(handle: number, selector: IDocumentFilterDto[], eventHandle: number | undefined): void; $emitCodeLensEvent(eventHandle: number, event?: any): void; @@ -426,6 +425,8 @@ export interface MainThreadLanguagesShape extends IDisposable { $getLanguages(): Promise; $changeLanguage(resource: UriComponents, languageId: string): Promise; $tokensAtPosition(resource: UriComponents, position: IPosition): Promise; + $setLanguageStatus(handle: number, status: ILanguageStatus): void; + $removeLanguageStatus(handle: number): void; } export interface MainThreadMessageOptions { @@ -1641,7 +1642,6 @@ export interface IInlineValueContextDto { export type ITypeHierarchyItemDto = Dto; export interface ExtHostLanguageFeaturesShape { - $provideLanguageStatus(handle: number, token: CancellationToken): Promise; $provideDocumentSymbols(handle: number, resource: UriComponents, token: CancellationToken): Promise; $provideCodeLenses(handle: number, resource: UriComponents, token: CancellationToken): Promise; $resolveCodeLens(handle: number, symbol: ICodeLensDto, token: CancellationToken): Promise; diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index a1d9be8d8df0a..b021f9c03f721 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -7,7 +7,7 @@ import { URI, UriComponents } from 'vs/base/common/uri'; import { mixin } from 'vs/base/common/objects'; import type * as vscode from 'vscode'; import * as typeConvert from 'vs/workbench/api/common/extHostTypeConverters'; -import { Range, Disposable, CompletionList, SnippetString, CodeActionKind, SymbolInformation, DocumentSymbol, SemanticTokensEdits, SemanticTokens, SemanticTokensEdit, LanguageStatusSeverity } from 'vs/workbench/api/common/extHostTypes'; +import { Range, Disposable, CompletionList, SnippetString, CodeActionKind, SymbolInformation, DocumentSymbol, SemanticTokensEdits, SemanticTokens, SemanticTokensEdit } from 'vs/workbench/api/common/extHostTypes'; import { ISingleEditOperation } from 'vs/editor/common/model'; import * as modes from 'vs/editor/common/modes'; import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments'; @@ -33,37 +33,9 @@ import { Cache } from './cache'; import { StopWatch } from 'vs/base/common/stopwatch'; import { CancellationError } from 'vs/base/common/errors'; import { Emitter } from 'vs/base/common/event'; -import { ILanguageStatus } from 'vs/editor/common/services/languageStatusService'; -import Severity from 'vs/base/common/severity'; // --- adapter -class LanguageStatusAdapter { - - constructor(private readonly _provider: vscode.LanguageStatusProvider) { } - - async provideLanguageStatus(token: CancellationToken): Promise { - - const value = await this._provider.provideLanguageStatus(token); - if (!value) { - return; - } - - let severity = Severity.Info; - if (value.severity === LanguageStatusSeverity.Error) { - severity = Severity.Error; - } else if (value.severity === LanguageStatusSeverity.Warning) { - severity = Severity.Warning; - } - - return { - text: value.text, - message: typeConvert.MarkdownString.from(value.detail), - severity - }; - } -} - class DocumentSymbolAdapter { private _documents: ExtHostDocuments; @@ -1520,7 +1492,7 @@ class TypeHierarchyAdapter { return map?.get(itemId); } } -type Adapter = LanguageStatusAdapter | DocumentSymbolAdapter | CodeLensAdapter | DefinitionAdapter | HoverAdapter +type Adapter = DocumentSymbolAdapter | CodeLensAdapter | DefinitionAdapter | HoverAdapter | DocumentHighlightAdapter | ReferenceAdapter | CodeActionAdapter | DocumentFormattingAdapter | RangeFormattingAdapter | OnTypeFormattingAdapter | NavigateTypeAdapter | RenameAdapter | SuggestAdapter | SignatureHelpAdapter | LinkProviderAdapter | ImplementationAdapter @@ -1652,18 +1624,6 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF return ext.displayName || ext.name; } - // --- language status - - registerLanguageStatusProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.LanguageStatusProvider): vscode.Disposable { - const handle = this._addNewAdapter(new LanguageStatusAdapter(provider), extension); - this._proxy.$registerLanguageStatusProvider(handle, this._transformDocumentSelector(selector)); - return this._createDisposable(handle); - } - - $provideLanguageStatus(handle: number, token: CancellationToken): Promise { - return this._withAdapter(handle, LanguageStatusAdapter, adapter => adapter.provideLanguageStatus(token), undefined); - } - // --- outline registerDocumentSymbolProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.DocumentSymbolProvider, metadata?: vscode.DocumentSymbolProviderMetadata): vscode.Disposable { diff --git a/src/vs/workbench/api/common/extHostLanguages.ts b/src/vs/workbench/api/common/extHostLanguages.ts index 035e22148ddfd..d246aa012b716 100644 --- a/src/vs/workbench/api/common/extHostLanguages.ts +++ b/src/vs/workbench/api/common/extHostLanguages.ts @@ -7,7 +7,10 @@ import { MainContext, MainThreadLanguagesShape, IMainContext } from './extHost.p import type * as vscode from 'vscode'; import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments'; import * as typeConvert from 'vs/workbench/api/common/extHostTypeConverters'; -import { StandardTokenType, Range, Position } from 'vs/workbench/api/common/extHostTypes'; +import { StandardTokenType, Range, Position, LanguageStatusSeverity } from 'vs/workbench/api/common/extHostTypes'; +import Severity from 'vs/base/common/severity'; +import { disposableTimeout } from 'vs/base/common/async'; +import { IDisposable } from 'vs/base/common/lifecycle'; export class ExtHostLanguages { @@ -61,4 +64,69 @@ export class ExtHostLanguages { } return result; } + + private _handlePool: number = 0; + + createLanguageStatusItem(selector: vscode.DocumentSelector): vscode.LanguageStatusItem { + + const handle = this._handlePool++; + const proxy = this._proxy; + + const data: { selector: any, text: string, detail: string | vscode.MarkdownString, severity: vscode.LanguageStatusSeverity } = { + selector, + text: '', + detail: '', + severity: LanguageStatusSeverity.Information, + }; + + let soonHandle: IDisposable | undefined; + const updateAsync = () => { + soonHandle?.dispose(); + soonHandle = disposableTimeout(() => { + this._proxy.$setLanguageStatus(handle, { + selector: data.selector, + text: data.text, + message: typeof data.detail === 'string' ? data.detail : typeConvert.MarkdownString.from(data.detail), + severity: data.severity === LanguageStatusSeverity.Error ? Severity.Error : data.severity === LanguageStatusSeverity.Warning ? Severity.Warning : Severity.Info + }); + }, 0); + }; + + const result: vscode.LanguageStatusItem = { + get selector() { + return data.selector; + }, + set selector(value) { + data.selector = value; + updateAsync(); + }, + get text() { + return data.text; + }, + set text(value) { + data.text = value; + updateAsync(); + }, + get detail() { + return data.detail; + }, + set detail(value) { + data.detail = value; + updateAsync(); + }, + get severity() { + return data.severity; + }, + set severity(value) { + data.severity = value; + updateAsync(); + }, + dispose() { + soonHandle?.dispose(); + proxy.$removeLanguageStatus(handle); + } + }; + updateAsync(); + return result; + } } diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 97fd0e9a03409..59e5c7e9a8fe3 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -1286,18 +1286,7 @@ export enum LanguageStatusSeverity { Error = 2 } -export class LanguageStatus { - text: string; - detail: string | MarkdownString; - severity: LanguageStatusSeverity; - - constructor(text: string) { - this.text = text; - this.detail = ''; - this.severity = LanguageStatusSeverity.Information; - } -} @es5ClassCompat export class CodeLens {