Skip to content

Commit

Permalink
turn language status API proposal into push model, #129037
Browse files Browse the repository at this point in the history
  • Loading branch information
jrieken committed Jul 21, 2021
1 parent ee47400 commit e30d70f
Show file tree
Hide file tree
Showing 9 changed files with 106 additions and 105 deletions.
33 changes: 9 additions & 24 deletions src/vs/editor/common/services/languageStatusService.ts
Expand Up @@ -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';
Expand All @@ -17,6 +16,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'


export interface ILanguageStatus {
selector: LanguageSelector,
severity: Severity;
text: string;
message: string | IMarkdownString;
Expand All @@ -34,41 +34,26 @@ export interface ILanguageStatusService {

onDidChange: Event<void>;

registerLanguageStatusProvider(selector: LanguageSelector, provider: ILanguageStatusProvider): IDisposable;
addStatus(status: ILanguageStatus): IDisposable;

getLanguageStatus(model: ITextModel): Promise<ILanguageStatus[]>;
}


class LanguageStatusServiceImpl implements ILanguageStatusService {
declare _serviceBrand: undefined;

private readonly _provider = new LanguageFeatureRegistry<ILanguageStatusProvider>();
declare _serviceBrand: undefined;

private readonly _onDidChange = new Emitter<void>();
readonly onDidChange: Event<void> = Event.any(this._onDidChange.event, this._provider.onDidChange);
private readonly _provider = new LanguageFeatureRegistry<ILanguageStatus>();

dispose() {
this._onDidChange.dispose();
}
readonly onDidChange: Event<any> = 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<ILanguageStatus[]> {
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);
}
}

Expand Down
13 changes: 5 additions & 8 deletions src/vs/vscode.proposed.d.ts
Expand Up @@ -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<LanguageStatus>;
dispose(): void;
}

namespace languages {
export function registerLanguageStatusProvider(selector: DocumentSelector, provider: LanguageStatusProvider): Disposable;
export function createLanguageStatusItem(selector: DocumentSelector): LanguageStatusItem;
}

//#endregion
Expand Down
12 changes: 0 additions & 12 deletions src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts
Expand Up @@ -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 {
Expand All @@ -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;
Expand Down Expand Up @@ -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<ILanguageStatus | undefined> => {
return this._proxy.$provideLanguageStatus(handle, token);
}
}));
}

// --- outline

$registerDocumentSymbolProvider(handle: number, selector: IDocumentFilterDto[], displayName: string): void {
Expand Down
19 changes: 17 additions & 2 deletions src/vs/workbench/api/browser/mainThreadLanguages.ts
Expand Up @@ -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 {
Expand All @@ -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
Expand Down Expand Up @@ -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<number, IDisposable>();

$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();
}
}
5 changes: 2 additions & 3 deletions src/vs/workbench/api/common/extHost.api.impl.ts
Expand Up @@ -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);
}
};

Expand Down Expand Up @@ -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,
};
};
Expand Down
4 changes: 2 additions & 2 deletions src/vs/workbench/api/common/extHost.protocol.ts
Expand Up @@ -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;
Expand Down Expand Up @@ -426,6 +425,8 @@ export interface MainThreadLanguagesShape extends IDisposable {
$getLanguages(): Promise<string[]>;
$changeLanguage(resource: UriComponents, languageId: string): Promise<void>;
$tokensAtPosition(resource: UriComponents, position: IPosition): Promise<undefined | { type: modes.StandardTokenType, range: IRange }>;
$setLanguageStatus(handle: number, status: ILanguageStatus): void;
$removeLanguageStatus(handle: number): void;
}

export interface MainThreadMessageOptions {
Expand Down Expand Up @@ -1641,7 +1642,6 @@ export interface IInlineValueContextDto {
export type ITypeHierarchyItemDto = Dto<TypeHierarchyItem>;

export interface ExtHostLanguageFeaturesShape {
$provideLanguageStatus(handle: number, token: CancellationToken): Promise<ILanguageStatus | undefined>;
$provideDocumentSymbols(handle: number, resource: UriComponents, token: CancellationToken): Promise<modes.DocumentSymbol[] | undefined>;
$provideCodeLenses(handle: number, resource: UriComponents, token: CancellationToken): Promise<ICodeLensListDto | undefined>;
$resolveCodeLens(handle: number, symbol: ICodeLensDto, token: CancellationToken): Promise<ICodeLensDto | undefined>;
Expand Down
44 changes: 2 additions & 42 deletions src/vs/workbench/api/common/extHostLanguageFeatures.ts
Expand Up @@ -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';
Expand All @@ -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<ILanguageStatus | undefined> {

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;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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<ILanguageStatus | undefined> {
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 {
Expand Down
70 changes: 69 additions & 1 deletion src/vs/workbench/api/common/extHostLanguages.ts
Expand Up @@ -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 {

Expand Down Expand Up @@ -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;
}
}
11 changes: 0 additions & 11 deletions src/vs/workbench/api/common/extHostTypes.ts
Expand Up @@ -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 {

Expand Down

0 comments on commit e30d70f

Please sign in to comment.