Path: blob/main/src/vs/editor/common/model/tokens/abstractSyntaxTokenBackend.ts
3296 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 } 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 } 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 { equalsIfDefined, itemEquals, itemsEquals } from '../../../../base/common/equals.js';1920/**21* @internal22*/23export class AttachedViews {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: itemsEquals(itemEquals())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}59}6061/**62* @internal63*/64export class AttachedViewState {65constructor(66readonly visibleLineRanges: readonly LineRange[],67readonly stabilized: boolean,68) { }6970public equals(other: AttachedViewState): boolean {71if (this === other) {72return true;73}74if (!equals(this.visibleLineRanges, other.visibleLineRanges, (a, b) => a.equals(b))) {75return false;76}77if (this.stabilized !== other.stabilized) {78return false;79}80return true;81}82}8384class AttachedViewImpl implements IAttachedView {85private readonly _state: ISettableObservable<AttachedViewState | undefined>;86public get state(): IObservable<AttachedViewState | undefined> { return this._state; }8788constructor(89private readonly handleStateChange: (state: AttachedViewState) => void90) {91this._state = observableValueOpts<AttachedViewState | undefined>({ owner: this, equalsFn: equalsIfDefined((a, b) => a.equals(b)) }, undefined);92}9394setVisibleLines(visibleLines: { startLineNumber: number; endLineNumber: number }[], stabilized: boolean): void {95const visibleLineRanges = visibleLines.map((line) => new LineRange(line.startLineNumber, line.endLineNumber + 1));96const state = new AttachedViewState(visibleLineRanges, stabilized);97this._state.set(state, undefined, undefined);98this.handleStateChange(state);99}100}101102103export class AttachedViewHandler extends Disposable {104private readonly runner = this._register(new RunOnceScheduler(() => this.update(), 50));105106private _computedLineRanges: readonly LineRange[] = [];107private _lineRanges: readonly LineRange[] = [];108public get lineRanges(): readonly LineRange[] { return this._lineRanges; }109110constructor(private readonly _refreshTokens: () => void) {111super();112}113114private update(): void {115if (equals(this._computedLineRanges, this._lineRanges, (a, b) => a.equals(b))) {116return;117}118this._computedLineRanges = this._lineRanges;119this._refreshTokens();120}121122public handleStateChange(state: AttachedViewState): void {123this._lineRanges = state.visibleLineRanges;124if (state.stabilized) {125this.runner.cancel();126this.update();127} else {128this.runner.schedule();129}130}131}132133export abstract class AbstractSyntaxTokenBackend extends Disposable {134protected abstract _backgroundTokenizationState: BackgroundTokenizationState;135public get backgroundTokenizationState(): BackgroundTokenizationState {136return this._backgroundTokenizationState;137}138139protected abstract readonly _onDidChangeBackgroundTokenizationState: Emitter<void>;140/** @internal, should not be exposed by the text model! */141public abstract readonly onDidChangeBackgroundTokenizationState: Event<void>;142143protected readonly _onDidChangeTokens = this._register(new Emitter<IModelTokensChangedEvent>());144/** @internal, should not be exposed by the text model! */145public readonly onDidChangeTokens: Event<IModelTokensChangedEvent> = this._onDidChangeTokens.event;146147constructor(148protected readonly _languageIdCodec: ILanguageIdCodec,149protected readonly _textModel: TextModel,150) {151super();152}153154public abstract todo_resetTokenization(fireTokenChangeEvent?: boolean): void;155156public abstract handleDidChangeAttached(): void;157158public abstract handleDidChangeContent(e: IModelContentChangedEvent): void;159160public abstract forceTokenization(lineNumber: number): void;161162public abstract hasAccurateTokensForLine(lineNumber: number): boolean;163164public abstract isCheapToTokenize(lineNumber: number): boolean;165166public tokenizeIfCheap(lineNumber: number): void {167if (this.isCheapToTokenize(lineNumber)) {168this.forceTokenization(lineNumber);169}170}171172public abstract getLineTokens(lineNumber: number): LineTokens;173174public abstract getTokenTypeIfInsertingCharacter(lineNumber: number, column: number, character: string): StandardTokenType;175176public abstract tokenizeLinesAt(lineNumber: number, lines: string[]): LineTokens[] | null;177178public abstract get hasTokens(): boolean;179}180181182