Path: blob/main/src/vs/editor/common/model/tokens/abstractSyntaxTokenBackend.ts
5251 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { equals } from '../../../../base/common/arrays.js';6import { RunOnceScheduler } from '../../../../base/common/async.js';7import { Emitter, Event } from '../../../../base/common/event.js';8import { Disposable, IDisposable } from '../../../../base/common/lifecycle.js';9import { LineRange } from '../../core/ranges/lineRange.js';10import { StandardTokenType } from '../../encodedTokenAttributes.js';11import { ILanguageIdCodec } from '../../languages.js';12import { IAttachedView } from '../../model.js';13import { TextModel } from '../textModel.js';14import { IModelContentChangedEvent, IModelTokensChangedEvent, IModelFontTokensChangedEvent } from '../../textModelEvents.js';15import { BackgroundTokenizationState } from '../../tokenizationTextModelPart.js';16import { LineTokens } from '../../tokens/lineTokens.js';17import { derivedOpts, IObservable, ISettableObservable, observableSignal, observableValueOpts } from '../../../../base/common/observable.js';18import { equalsIfDefinedC, thisEqualsC, arrayEqualsC } from '../../../../base/common/equals.js';1920/**21* @internal22*/23export class AttachedViews implements IDisposable {24private readonly _onDidChangeVisibleRanges = new Emitter<{ view: IAttachedView; state: AttachedViewState | undefined }>();25public readonly onDidChangeVisibleRanges = this._onDidChangeVisibleRanges.event;2627private readonly _views = new Set<AttachedViewImpl>();28private readonly _viewsChanged = observableSignal(this);2930public readonly visibleLineRanges: IObservable<readonly LineRange[]>;3132constructor() {33this.visibleLineRanges = derivedOpts({34owner: this,35equalsFn: arrayEqualsC(thisEqualsC())36}, reader => {37this._viewsChanged.read(reader);38const ranges = LineRange.joinMany(39[...this._views].map(view => view.state.read(reader)?.visibleLineRanges ?? [])40);41return ranges;42});43}4445public attachView(): IAttachedView {46const view = new AttachedViewImpl((state) => {47this._onDidChangeVisibleRanges.fire({ view, state });48});49this._views.add(view);50this._viewsChanged.trigger(undefined);51return view;52}5354public detachView(view: IAttachedView): void {55this._views.delete(view as AttachedViewImpl);56this._onDidChangeVisibleRanges.fire({ view, state: undefined });57this._viewsChanged.trigger(undefined);58}5960public dispose(): void {61this._onDidChangeVisibleRanges.dispose();62}63}6465/**66* @internal67*/68export class AttachedViewState {69constructor(70readonly visibleLineRanges: readonly LineRange[],71readonly stabilized: boolean,72) { }7374public equals(other: AttachedViewState): boolean {75if (this === other) {76return true;77}78if (!equals(this.visibleLineRanges, other.visibleLineRanges, (a, b) => a.equals(b))) {79return false;80}81if (this.stabilized !== other.stabilized) {82return false;83}84return true;85}86}8788class AttachedViewImpl implements IAttachedView {89private readonly _state: ISettableObservable<AttachedViewState | undefined>;90public get state(): IObservable<AttachedViewState | undefined> { return this._state; }9192constructor(93private readonly handleStateChange: (state: AttachedViewState) => void94) {95this._state = observableValueOpts<AttachedViewState | undefined>({ owner: this, equalsFn: equalsIfDefinedC((a, b) => a.equals(b)) }, undefined);96}9798setVisibleLines(visibleLines: { startLineNumber: number; endLineNumber: number }[], stabilized: boolean): void {99const visibleLineRanges = visibleLines.map((line) => new LineRange(line.startLineNumber, line.endLineNumber + 1));100const state = new AttachedViewState(visibleLineRanges, stabilized);101this._state.set(state, undefined, undefined);102this.handleStateChange(state);103}104}105106107export class AttachedViewHandler extends Disposable {108private readonly runner = this._register(new RunOnceScheduler(() => this.update(), 50));109110private _computedLineRanges: readonly LineRange[] = [];111private _lineRanges: readonly LineRange[] = [];112public get lineRanges(): readonly LineRange[] { return this._lineRanges; }113114constructor(private readonly _refreshTokens: () => void) {115super();116}117118private update(): void {119if (equals(this._computedLineRanges, this._lineRanges, (a, b) => a.equals(b))) {120return;121}122this._computedLineRanges = this._lineRanges;123this._refreshTokens();124}125126public handleStateChange(state: AttachedViewState): void {127this._lineRanges = state.visibleLineRanges;128if (state.stabilized) {129this.runner.cancel();130this.update();131} else {132this.runner.schedule();133}134}135}136137export abstract class AbstractSyntaxTokenBackend extends Disposable {138protected abstract _backgroundTokenizationState: BackgroundTokenizationState;139public get backgroundTokenizationState(): BackgroundTokenizationState {140return this._backgroundTokenizationState;141}142143protected abstract readonly _onDidChangeBackgroundTokenizationState: Emitter<void>;144/** @internal, should not be exposed by the text model! */145public abstract readonly onDidChangeBackgroundTokenizationState: Event<void>;146147protected readonly _onDidChangeTokens = this._register(new Emitter<IModelTokensChangedEvent>());148/** @internal, should not be exposed by the text model! */149public readonly onDidChangeTokens: Event<IModelTokensChangedEvent> = this._onDidChangeTokens.event;150151protected readonly _onDidChangeFontTokens: Emitter<IModelFontTokensChangedEvent> = this._register(new Emitter<IModelFontTokensChangedEvent>());152/** @internal, should not be exposed by the text model! */153public readonly onDidChangeFontTokens: Event<IModelFontTokensChangedEvent> = this._onDidChangeFontTokens.event;154155constructor(156protected readonly _languageIdCodec: ILanguageIdCodec,157protected readonly _textModel: TextModel,158) {159super();160}161162public abstract todo_resetTokenization(fireTokenChangeEvent?: boolean): void;163164public abstract handleDidChangeAttached(): void;165166public abstract handleDidChangeContent(e: IModelContentChangedEvent): void;167168public abstract forceTokenization(lineNumber: number): void;169170public abstract hasAccurateTokensForLine(lineNumber: number): boolean;171172public abstract isCheapToTokenize(lineNumber: number): boolean;173174public tokenizeIfCheap(lineNumber: number): void {175if (this.isCheapToTokenize(lineNumber)) {176this.forceTokenization(lineNumber);177}178}179180public abstract getLineTokens(lineNumber: number): LineTokens;181182public abstract getTokenTypeIfInsertingCharacter(lineNumber: number, column: number, character: string): StandardTokenType;183184public abstract tokenizeLinesAt(lineNumber: number, lines: string[]): LineTokens[] | null;185186public abstract get hasTokens(): boolean;187}188189190