Path: blob/main/src/vs/editor/common/textModelEvents.ts
5222 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 { IPosition } from './core/position.js';6import { IRange, Range } from './core/range.js';7import { Selection } from './core/selection.js';8import { IModelDecoration, InjectedTextOptions } from './model.js';9import { IModelContentChange } from './model/mirrorTextModel.js';10import { AnnotationsUpdate } from './model/tokens/annotations.js';11import { TextModelEditSource } from './textModelEditSource.js';1213/**14* An event describing that the current language associated with a model has changed.15*/16export interface IModelLanguageChangedEvent {17/**18* Previous language19*/20readonly oldLanguage: string;21/**22* New language23*/24readonly newLanguage: string;2526/**27* Source of the call that caused the event.28*/29readonly source: string;30}3132/**33* An event describing that the language configuration associated with a model has changed.34*/35export interface IModelLanguageConfigurationChangedEvent {36}3738/**39* An event describing a change in the text of a model.40*/41export interface IModelContentChangedEvent {42/**43* The changes are ordered from the end of the document to the beginning, so they should be safe to apply in sequence.44*/45readonly changes: IModelContentChange[];46/**47* The (new) end-of-line character.48*/49readonly eol: string;50/**51* The new version id the model has transitioned to.52*/53readonly versionId: number;54/**55* Flag that indicates that this event was generated while undoing.56*/57readonly isUndoing: boolean;58/**59* Flag that indicates that this event was generated while redoing.60*/61readonly isRedoing: boolean;62/**63* Flag that indicates that all decorations were lost with this edit.64* The model has been reset to a new value.65*/66readonly isFlush: boolean;6768/**69* Flag that indicates that this event describes an eol change.70*/71readonly isEolChange: boolean;7273/**74* Detailed reason information for the change75* @internal76*/77readonly detailedReasons: TextModelEditSource[];7879/**80* The sum of these lengths equals changes.length.81* The length of this array must equal the length of detailedReasons.82*/83readonly detailedReasonsChangeLengths: number[];84}8586export interface ISerializedModelContentChangedEvent {87/**88* The changes are ordered from the end of the document to the beginning, so they should be safe to apply in sequence.89*/90readonly changes: IModelContentChange[];91/**92* The (new) end-of-line character.93*/94readonly eol: string;95/**96* The new version id the model has transitioned to.97*/98readonly versionId: number;99/**100* Flag that indicates that this event was generated while undoing.101*/102readonly isUndoing: boolean;103/**104* Flag that indicates that this event was generated while redoing.105*/106readonly isRedoing: boolean;107/**108* Flag that indicates that all decorations were lost with this edit.109* The model has been reset to a new value.110*/111readonly isFlush: boolean;112113/**114* Flag that indicates that this event describes an eol change.115*/116readonly isEolChange: boolean;117118/**119* Detailed reason information for the change120* @internal121*/122readonly detailedReason: Record<string, unknown> | undefined;123}124125/**126* An event describing that model decorations have changed.127*/128export interface IModelDecorationsChangedEvent {129readonly affectsMinimap: boolean;130readonly affectsOverviewRuler: boolean;131readonly affectsGlyphMargin: boolean;132readonly affectsLineNumber: boolean;133}134135/**136* An event describing that some ranges of lines have been tokenized (their tokens have changed).137* @internal138*/139export interface IModelTokensChangedEvent {140readonly semanticTokensApplied: boolean;141readonly ranges: {142/**143* The start of the range (inclusive)144*/145readonly fromLineNumber: number;146/**147* The end of the range (inclusive)148*/149readonly toLineNumber: number;150}[];151}152153/**154* @internal155*/156export interface IFontTokenOption {157/**158* Font family of the token.159*/160readonly fontFamily?: string;161/**162* Font size of the token.163*/164readonly fontSizeMultiplier?: number;165/**166* Line height of the token.167*/168readonly lineHeightMultiplier?: number;169}170171/**172* An event describing a token font change event173* @internal174*/175export interface IModelFontTokensChangedEvent {176changes: FontTokensUpdate;177}178179/**180* @internal181*/182export type FontTokensUpdate = AnnotationsUpdate<IFontTokenOption | undefined>;183184/**185* @internal186*/187export function serializeFontTokenOptions(): (options: IFontTokenOption) => IFontTokenOption {188return (annotation: IFontTokenOption) => {189return {190fontFamily: annotation.fontFamily ?? '',191fontSizeMultiplier: annotation.fontSizeMultiplier ?? 0,192lineHeightMultiplier: annotation.lineHeightMultiplier ?? 0193};194};195}196197/**198* @internal199*/200export function deserializeFontTokenOptions(): (options: IFontTokenOption) => IFontTokenOption {201return (annotation: IFontTokenOption) => {202return {203fontFamily: annotation.fontFamily ? String(annotation.fontFamily) : undefined,204fontSizeMultiplier: annotation.fontSizeMultiplier ? Number(annotation.fontSizeMultiplier) : undefined,205lineHeightMultiplier: annotation.lineHeightMultiplier ? Number(annotation.lineHeightMultiplier) : undefined206};207};208}209210export interface IModelOptionsChangedEvent {211readonly tabSize: boolean;212readonly indentSize: boolean;213readonly insertSpaces: boolean;214readonly trimAutoWhitespace: boolean;215}216217/**218* @internal219*/220export const enum RawContentChangedType {221Flush = 1,222LineChanged = 2,223LinesDeleted = 3,224LinesInserted = 4,225EOLChanged = 5226}227228/**229* An event describing that a model has been reset to a new value.230* @internal231*/232export class ModelRawFlush {233public readonly changeType = RawContentChangedType.Flush;234}235236/**237* Represents text injected on a line238* @internal239*/240export class LineInjectedText {241public static applyInjectedText(lineText: string, injectedTexts: LineInjectedText[] | null): string {242if (!injectedTexts || injectedTexts.length === 0) {243return lineText;244}245let result = '';246let lastOriginalOffset = 0;247for (const injectedText of injectedTexts) {248result += lineText.substring(lastOriginalOffset, injectedText.column - 1);249lastOriginalOffset = injectedText.column - 1;250result += injectedText.options.content;251}252result += lineText.substring(lastOriginalOffset);253return result;254}255256public static fromDecorations(decorations: IModelDecoration[]): LineInjectedText[] {257const result: LineInjectedText[] = [];258for (const decoration of decorations) {259if (decoration.options.before && decoration.options.before.content.length > 0) {260result.push(new LineInjectedText(261decoration.ownerId,262decoration.range.startLineNumber,263decoration.range.startColumn,264decoration.options.before,2650,266));267}268if (decoration.options.after && decoration.options.after.content.length > 0) {269result.push(new LineInjectedText(270decoration.ownerId,271decoration.range.endLineNumber,272decoration.range.endColumn,273decoration.options.after,2741,275));276}277}278result.sort((a, b) => {279if (a.lineNumber === b.lineNumber) {280if (a.column === b.column) {281return a.order - b.order;282}283return a.column - b.column;284}285return a.lineNumber - b.lineNumber;286});287return result;288}289290constructor(291public readonly ownerId: number,292public readonly lineNumber: number,293public readonly column: number,294public readonly options: InjectedTextOptions,295public readonly order: number296) { }297298public withText(text: string): LineInjectedText {299return new LineInjectedText(this.ownerId, this.lineNumber, this.column, { ...this.options, content: text }, this.order);300}301}302303/**304* An event describing that a line has changed in a model.305* @internal306*/307export class ModelRawLineChanged {308public readonly changeType = RawContentChangedType.LineChanged;309/**310* The line that has changed.311*/312public readonly lineNumber: number;313/**314* The new value of the line.315*/316public readonly detail: string;317/**318* The injected text on the line.319*/320public readonly injectedText: LineInjectedText[] | null;321322constructor(lineNumber: number, detail: string, injectedText: LineInjectedText[] | null) {323this.lineNumber = lineNumber;324this.detail = detail;325this.injectedText = injectedText;326}327}328329330/**331* An event describing that a line height has changed in the model.332* @internal333*/334export class ModelLineHeightChanged {335/**336* Editor owner ID337*/338public readonly ownerId: number;339/**340* The decoration ID that has changed.341*/342public readonly decorationId: string;343/**344* The line that has changed.345*/346public readonly lineNumber: number;347/**348* The line height on the line.349*/350public readonly lineHeightMultiplier: number | null;351352constructor(ownerId: number, decorationId: string, lineNumber: number, lineHeightMultiplier: number | null) {353this.ownerId = ownerId;354this.decorationId = decorationId;355this.lineNumber = lineNumber;356this.lineHeightMultiplier = lineHeightMultiplier;357}358}359360/**361* An event describing that a line height has changed in the model.362* @internal363*/364export class ModelFontChanged {365/**366* Editor owner ID367*/368public readonly ownerId: number;369/**370* The line that has changed.371*/372public readonly lineNumber: number;373374constructor(ownerId: number, lineNumber: number) {375this.ownerId = ownerId;376this.lineNumber = lineNumber;377}378}379380/**381* An event describing that line(s) have been deleted in a model.382* @internal383*/384export class ModelRawLinesDeleted {385public readonly changeType = RawContentChangedType.LinesDeleted;386/**387* At what line the deletion began (inclusive).388*/389public readonly fromLineNumber: number;390/**391* At what line the deletion stopped (inclusive).392*/393public readonly toLineNumber: number;394395constructor(fromLineNumber: number, toLineNumber: number) {396this.fromLineNumber = fromLineNumber;397this.toLineNumber = toLineNumber;398}399}400401/**402* An event describing that line(s) have been inserted in a model.403* @internal404*/405export class ModelRawLinesInserted {406public readonly changeType = RawContentChangedType.LinesInserted;407/**408* Before what line did the insertion begin409*/410public readonly fromLineNumber: number;411/**412* `toLineNumber` - `fromLineNumber` + 1 denotes the number of lines that were inserted413*/414public readonly toLineNumber: number;415/**416* The text that was inserted417*/418public readonly detail: string[];419/**420* The injected texts for every inserted line.421*/422public readonly injectedTexts: (LineInjectedText[] | null)[];423424constructor(fromLineNumber: number, toLineNumber: number, detail: string[], injectedTexts: (LineInjectedText[] | null)[]) {425this.injectedTexts = injectedTexts;426this.fromLineNumber = fromLineNumber;427this.toLineNumber = toLineNumber;428this.detail = detail;429}430}431432/**433* An event describing that a model has had its EOL changed.434* @internal435*/436export class ModelRawEOLChanged {437public readonly changeType = RawContentChangedType.EOLChanged;438}439440/**441* @internal442*/443export type ModelRawChange = ModelRawFlush | ModelRawLineChanged | ModelRawLinesDeleted | ModelRawLinesInserted | ModelRawEOLChanged;444445/**446* An event describing a change in the text of a model.447* @internal448*/449export class ModelRawContentChangedEvent {450451public readonly changes: ModelRawChange[];452/**453* The new version id the model has transitioned to.454*/455public readonly versionId: number;456/**457* Flag that indicates that this event was generated while undoing.458*/459public readonly isUndoing: boolean;460/**461* Flag that indicates that this event was generated while redoing.462*/463public readonly isRedoing: boolean;464465public resultingSelection: Selection[] | null;466467constructor(changes: ModelRawChange[], versionId: number, isUndoing: boolean, isRedoing: boolean) {468this.changes = changes;469this.versionId = versionId;470this.isUndoing = isUndoing;471this.isRedoing = isRedoing;472this.resultingSelection = null;473}474475public containsEvent(type: RawContentChangedType): boolean {476for (let i = 0, len = this.changes.length; i < len; i++) {477const change = this.changes[i];478if (change.changeType === type) {479return true;480}481}482return false;483}484485public static merge(a: ModelRawContentChangedEvent, b: ModelRawContentChangedEvent): ModelRawContentChangedEvent {486const changes = ([] as ModelRawChange[]).concat(a.changes).concat(b.changes);487const versionId = b.versionId;488const isUndoing = (a.isUndoing || b.isUndoing);489const isRedoing = (a.isRedoing || b.isRedoing);490return new ModelRawContentChangedEvent(changes, versionId, isUndoing, isRedoing);491}492}493494/**495* An event describing a change in injected text.496* @internal497*/498export class ModelInjectedTextChangedEvent {499500public readonly changes: ModelRawLineChanged[];501502constructor(changes: ModelRawLineChanged[]) {503this.changes = changes;504}505}506507/**508* An event describing a change of a line height.509* @internal510*/511export class ModelLineHeightChangedEvent {512513public readonly changes: ModelLineHeightChanged[];514515constructor(changes: ModelLineHeightChanged[]) {516this.changes = changes;517}518519public affects(rangeOrPosition: IRange | IPosition) {520if (Range.isIRange(rangeOrPosition)) {521for (const change of this.changes) {522if (change.lineNumber >= rangeOrPosition.startLineNumber && change.lineNumber <= rangeOrPosition.endLineNumber) {523return true;524}525}526return false;527} else {528for (const change of this.changes) {529if (change.lineNumber === rangeOrPosition.lineNumber) {530return true;531}532}533return false;534}535}536}537538/**539* An event describing a change in fonts.540* @internal541*/542export class ModelFontChangedEvent {543544public readonly changes: ModelFontChanged[];545546constructor(changes: ModelFontChanged[]) {547this.changes = changes;548}549}550551/**552* @internal553*/554export class InternalModelContentChangeEvent {555constructor(556public readonly rawContentChangedEvent: ModelRawContentChangedEvent,557public readonly contentChangedEvent: IModelContentChangedEvent,558) { }559560public merge(other: InternalModelContentChangeEvent): InternalModelContentChangeEvent {561const rawContentChangedEvent = ModelRawContentChangedEvent.merge(this.rawContentChangedEvent, other.rawContentChangedEvent);562const contentChangedEvent = InternalModelContentChangeEvent._mergeChangeEvents(this.contentChangedEvent, other.contentChangedEvent);563return new InternalModelContentChangeEvent(rawContentChangedEvent, contentChangedEvent);564}565566private static _mergeChangeEvents(a: IModelContentChangedEvent, b: IModelContentChangedEvent): IModelContentChangedEvent {567const changes = ([] as IModelContentChange[]).concat(a.changes).concat(b.changes);568const eol = b.eol;569const versionId = b.versionId;570const isUndoing = (a.isUndoing || b.isUndoing);571const isRedoing = (a.isRedoing || b.isRedoing);572const isFlush = (a.isFlush || b.isFlush);573const isEolChange = a.isEolChange && b.isEolChange; // both must be true to not confuse listeners who skip such edits574return {575changes: changes,576eol: eol,577isEolChange: isEolChange,578versionId: versionId,579isUndoing: isUndoing,580isRedoing: isRedoing,581isFlush: isFlush,582detailedReasons: a.detailedReasons.concat(b.detailedReasons),583detailedReasonsChangeLengths: a.detailedReasonsChangeLengths.concat(b.detailedReasonsChangeLengths),584};585}586}587588589