Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/editor/common/model/tokens/abstractSyntaxTokenBackend.ts
5251 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { equals } from '../../../../base/common/arrays.js';
7
import { RunOnceScheduler } from '../../../../base/common/async.js';
8
import { Emitter, Event } from '../../../../base/common/event.js';
9
import { Disposable, IDisposable } from '../../../../base/common/lifecycle.js';
10
import { LineRange } from '../../core/ranges/lineRange.js';
11
import { StandardTokenType } from '../../encodedTokenAttributes.js';
12
import { ILanguageIdCodec } from '../../languages.js';
13
import { IAttachedView } from '../../model.js';
14
import { TextModel } from '../textModel.js';
15
import { IModelContentChangedEvent, IModelTokensChangedEvent, IModelFontTokensChangedEvent } from '../../textModelEvents.js';
16
import { BackgroundTokenizationState } from '../../tokenizationTextModelPart.js';
17
import { LineTokens } from '../../tokens/lineTokens.js';
18
import { derivedOpts, IObservable, ISettableObservable, observableSignal, observableValueOpts } from '../../../../base/common/observable.js';
19
import { equalsIfDefinedC, thisEqualsC, arrayEqualsC } from '../../../../base/common/equals.js';
20
21
/**
22
* @internal
23
*/
24
export class AttachedViews implements IDisposable {
25
private readonly _onDidChangeVisibleRanges = new Emitter<{ view: IAttachedView; state: AttachedViewState | undefined }>();
26
public readonly onDidChangeVisibleRanges = this._onDidChangeVisibleRanges.event;
27
28
private readonly _views = new Set<AttachedViewImpl>();
29
private readonly _viewsChanged = observableSignal(this);
30
31
public readonly visibleLineRanges: IObservable<readonly LineRange[]>;
32
33
constructor() {
34
this.visibleLineRanges = derivedOpts({
35
owner: this,
36
equalsFn: arrayEqualsC(thisEqualsC())
37
}, reader => {
38
this._viewsChanged.read(reader);
39
const ranges = LineRange.joinMany(
40
[...this._views].map(view => view.state.read(reader)?.visibleLineRanges ?? [])
41
);
42
return ranges;
43
});
44
}
45
46
public attachView(): IAttachedView {
47
const view = new AttachedViewImpl((state) => {
48
this._onDidChangeVisibleRanges.fire({ view, state });
49
});
50
this._views.add(view);
51
this._viewsChanged.trigger(undefined);
52
return view;
53
}
54
55
public detachView(view: IAttachedView): void {
56
this._views.delete(view as AttachedViewImpl);
57
this._onDidChangeVisibleRanges.fire({ view, state: undefined });
58
this._viewsChanged.trigger(undefined);
59
}
60
61
public dispose(): void {
62
this._onDidChangeVisibleRanges.dispose();
63
}
64
}
65
66
/**
67
* @internal
68
*/
69
export class AttachedViewState {
70
constructor(
71
readonly visibleLineRanges: readonly LineRange[],
72
readonly stabilized: boolean,
73
) { }
74
75
public equals(other: AttachedViewState): boolean {
76
if (this === other) {
77
return true;
78
}
79
if (!equals(this.visibleLineRanges, other.visibleLineRanges, (a, b) => a.equals(b))) {
80
return false;
81
}
82
if (this.stabilized !== other.stabilized) {
83
return false;
84
}
85
return true;
86
}
87
}
88
89
class AttachedViewImpl implements IAttachedView {
90
private readonly _state: ISettableObservable<AttachedViewState | undefined>;
91
public get state(): IObservable<AttachedViewState | undefined> { return this._state; }
92
93
constructor(
94
private readonly handleStateChange: (state: AttachedViewState) => void
95
) {
96
this._state = observableValueOpts<AttachedViewState | undefined>({ owner: this, equalsFn: equalsIfDefinedC((a, b) => a.equals(b)) }, undefined);
97
}
98
99
setVisibleLines(visibleLines: { startLineNumber: number; endLineNumber: number }[], stabilized: boolean): void {
100
const visibleLineRanges = visibleLines.map((line) => new LineRange(line.startLineNumber, line.endLineNumber + 1));
101
const state = new AttachedViewState(visibleLineRanges, stabilized);
102
this._state.set(state, undefined, undefined);
103
this.handleStateChange(state);
104
}
105
}
106
107
108
export class AttachedViewHandler extends Disposable {
109
private readonly runner = this._register(new RunOnceScheduler(() => this.update(), 50));
110
111
private _computedLineRanges: readonly LineRange[] = [];
112
private _lineRanges: readonly LineRange[] = [];
113
public get lineRanges(): readonly LineRange[] { return this._lineRanges; }
114
115
constructor(private readonly _refreshTokens: () => void) {
116
super();
117
}
118
119
private update(): void {
120
if (equals(this._computedLineRanges, this._lineRanges, (a, b) => a.equals(b))) {
121
return;
122
}
123
this._computedLineRanges = this._lineRanges;
124
this._refreshTokens();
125
}
126
127
public handleStateChange(state: AttachedViewState): void {
128
this._lineRanges = state.visibleLineRanges;
129
if (state.stabilized) {
130
this.runner.cancel();
131
this.update();
132
} else {
133
this.runner.schedule();
134
}
135
}
136
}
137
138
export abstract class AbstractSyntaxTokenBackend extends Disposable {
139
protected abstract _backgroundTokenizationState: BackgroundTokenizationState;
140
public get backgroundTokenizationState(): BackgroundTokenizationState {
141
return this._backgroundTokenizationState;
142
}
143
144
protected abstract readonly _onDidChangeBackgroundTokenizationState: Emitter<void>;
145
/** @internal, should not be exposed by the text model! */
146
public abstract readonly onDidChangeBackgroundTokenizationState: Event<void>;
147
148
protected readonly _onDidChangeTokens = this._register(new Emitter<IModelTokensChangedEvent>());
149
/** @internal, should not be exposed by the text model! */
150
public readonly onDidChangeTokens: Event<IModelTokensChangedEvent> = this._onDidChangeTokens.event;
151
152
protected readonly _onDidChangeFontTokens: Emitter<IModelFontTokensChangedEvent> = this._register(new Emitter<IModelFontTokensChangedEvent>());
153
/** @internal, should not be exposed by the text model! */
154
public readonly onDidChangeFontTokens: Event<IModelFontTokensChangedEvent> = this._onDidChangeFontTokens.event;
155
156
constructor(
157
protected readonly _languageIdCodec: ILanguageIdCodec,
158
protected readonly _textModel: TextModel,
159
) {
160
super();
161
}
162
163
public abstract todo_resetTokenization(fireTokenChangeEvent?: boolean): void;
164
165
public abstract handleDidChangeAttached(): void;
166
167
public abstract handleDidChangeContent(e: IModelContentChangedEvent): void;
168
169
public abstract forceTokenization(lineNumber: number): void;
170
171
public abstract hasAccurateTokensForLine(lineNumber: number): boolean;
172
173
public abstract isCheapToTokenize(lineNumber: number): boolean;
174
175
public tokenizeIfCheap(lineNumber: number): void {
176
if (this.isCheapToTokenize(lineNumber)) {
177
this.forceTokenization(lineNumber);
178
}
179
}
180
181
public abstract getLineTokens(lineNumber: number): LineTokens;
182
183
public abstract getTokenTypeIfInsertingCharacter(lineNumber: number, column: number, character: string): StandardTokenType;
184
185
public abstract tokenizeLinesAt(lineNumber: number, lines: string[]): LineTokens[] | null;
186
187
public abstract get hasTokens(): boolean;
188
}
189
190