Path: blob/main/src/vs/editor/common/textModelEvents.ts
3292 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 { TextModelEditSource } from './textModelEditSource.js';1112/**13* An event describing that the current language associated with a model has changed.14*/15export interface IModelLanguageChangedEvent {16/**17* Previous language18*/19readonly oldLanguage: string;20/**21* New language22*/23readonly newLanguage: string;2425/**26* Source of the call that caused the event.27*/28readonly source: string;29}3031/**32* An event describing that the language configuration associated with a model has changed.33*/34export interface IModelLanguageConfigurationChangedEvent {35}3637/**38* An event describing a change in the text of a model.39*/40export interface IModelContentChangedEvent {41/**42* The changes are ordered from the end of the document to the beginning, so they should be safe to apply in sequence.43*/44readonly changes: IModelContentChange[];45/**46* The (new) end-of-line character.47*/48readonly eol: string;49/**50* The new version id the model has transitioned to.51*/52readonly versionId: number;53/**54* Flag that indicates that this event was generated while undoing.55*/56readonly isUndoing: boolean;57/**58* Flag that indicates that this event was generated while redoing.59*/60readonly isRedoing: boolean;61/**62* Flag that indicates that all decorations were lost with this edit.63* The model has been reset to a new value.64*/65readonly isFlush: boolean;6667/**68* Flag that indicates that this event describes an eol change.69*/70readonly isEolChange: boolean;7172/**73* Detailed reason information for the change74* @internal75*/76readonly detailedReasons: TextModelEditSource[];7778/**79* The sum of these lengths equals changes.length.80* The length of this array must equal the length of detailedReasons.81*/82readonly detailedReasonsChangeLengths: number[];83}8485export interface ISerializedModelContentChangedEvent {86/**87* The changes are ordered from the end of the document to the beginning, so they should be safe to apply in sequence.88*/89readonly changes: IModelContentChange[];90/**91* The (new) end-of-line character.92*/93readonly eol: string;94/**95* The new version id the model has transitioned to.96*/97readonly versionId: number;98/**99* Flag that indicates that this event was generated while undoing.100*/101readonly isUndoing: boolean;102/**103* Flag that indicates that this event was generated while redoing.104*/105readonly isRedoing: boolean;106/**107* Flag that indicates that all decorations were lost with this edit.108* The model has been reset to a new value.109*/110readonly isFlush: boolean;111112/**113* Flag that indicates that this event describes an eol change.114*/115readonly isEolChange: boolean;116117/**118* Detailed reason information for the change119* @internal120*/121readonly detailedReason: Record<string, unknown> | undefined;122}123124/**125* An event describing that model decorations have changed.126*/127export interface IModelDecorationsChangedEvent {128readonly affectsMinimap: boolean;129readonly affectsOverviewRuler: boolean;130readonly affectsGlyphMargin: boolean;131readonly affectsLineNumber: boolean;132}133134/**135* An event describing that some ranges of lines have been tokenized (their tokens have changed).136* @internal137*/138export interface IModelTokensChangedEvent {139readonly semanticTokensApplied: boolean;140readonly ranges: {141/**142* The start of the range (inclusive)143*/144readonly fromLineNumber: number;145/**146* The end of the range (inclusive)147*/148readonly toLineNumber: number;149}[];150}151152export interface IModelOptionsChangedEvent {153readonly tabSize: boolean;154readonly indentSize: boolean;155readonly insertSpaces: boolean;156readonly trimAutoWhitespace: boolean;157}158159/**160* @internal161*/162export const enum RawContentChangedType {163Flush = 1,164LineChanged = 2,165LinesDeleted = 3,166LinesInserted = 4,167EOLChanged = 5168}169170/**171* An event describing that a model has been reset to a new value.172* @internal173*/174export class ModelRawFlush {175public readonly changeType = RawContentChangedType.Flush;176}177178/**179* Represents text injected on a line180* @internal181*/182export class LineInjectedText {183public static applyInjectedText(lineText: string, injectedTexts: LineInjectedText[] | null): string {184if (!injectedTexts || injectedTexts.length === 0) {185return lineText;186}187let result = '';188let lastOriginalOffset = 0;189for (const injectedText of injectedTexts) {190result += lineText.substring(lastOriginalOffset, injectedText.column - 1);191lastOriginalOffset = injectedText.column - 1;192result += injectedText.options.content;193}194result += lineText.substring(lastOriginalOffset);195return result;196}197198public static fromDecorations(decorations: IModelDecoration[]): LineInjectedText[] {199const result: LineInjectedText[] = [];200for (const decoration of decorations) {201if (decoration.options.before && decoration.options.before.content.length > 0) {202result.push(new LineInjectedText(203decoration.ownerId,204decoration.range.startLineNumber,205decoration.range.startColumn,206decoration.options.before,2070,208));209}210if (decoration.options.after && decoration.options.after.content.length > 0) {211result.push(new LineInjectedText(212decoration.ownerId,213decoration.range.endLineNumber,214decoration.range.endColumn,215decoration.options.after,2161,217));218}219}220result.sort((a, b) => {221if (a.lineNumber === b.lineNumber) {222if (a.column === b.column) {223return a.order - b.order;224}225return a.column - b.column;226}227return a.lineNumber - b.lineNumber;228});229return result;230}231232constructor(233public readonly ownerId: number,234public readonly lineNumber: number,235public readonly column: number,236public readonly options: InjectedTextOptions,237public readonly order: number238) { }239240public withText(text: string): LineInjectedText {241return new LineInjectedText(this.ownerId, this.lineNumber, this.column, { ...this.options, content: text }, this.order);242}243}244245/**246* An event describing that a line has changed in a model.247* @internal248*/249export class ModelRawLineChanged {250public readonly changeType = RawContentChangedType.LineChanged;251/**252* The line that has changed.253*/254public readonly lineNumber: number;255/**256* The new value of the line.257*/258public readonly detail: string;259/**260* The injected text on the line.261*/262public readonly injectedText: LineInjectedText[] | null;263264constructor(lineNumber: number, detail: string, injectedText: LineInjectedText[] | null) {265this.lineNumber = lineNumber;266this.detail = detail;267this.injectedText = injectedText;268}269}270271272/**273* An event describing that a line height has changed in the model.274* @internal275*/276export class ModelLineHeightChanged {277/**278* Editor owner ID279*/280public readonly ownerId: number;281/**282* The decoration ID that has changed.283*/284public readonly decorationId: string;285/**286* The line that has changed.287*/288public readonly lineNumber: number;289/**290* The line height on the line.291*/292public readonly lineHeight: number | null;293294constructor(ownerId: number, decorationId: string, lineNumber: number, lineHeight: number | null) {295this.ownerId = ownerId;296this.decorationId = decorationId;297this.lineNumber = lineNumber;298this.lineHeight = lineHeight;299}300}301302/**303* An event describing that a line height has changed in the model.304* @internal305*/306export class ModelFontChanged {307/**308* Editor owner ID309*/310public readonly ownerId: number;311/**312* The line that has changed.313*/314public readonly lineNumber: number;315316constructor(ownerId: number, lineNumber: number) {317this.ownerId = ownerId;318this.lineNumber = lineNumber;319}320}321322/**323* An event describing that line(s) have been deleted in a model.324* @internal325*/326export class ModelRawLinesDeleted {327public readonly changeType = RawContentChangedType.LinesDeleted;328/**329* At what line the deletion began (inclusive).330*/331public readonly fromLineNumber: number;332/**333* At what line the deletion stopped (inclusive).334*/335public readonly toLineNumber: number;336337constructor(fromLineNumber: number, toLineNumber: number) {338this.fromLineNumber = fromLineNumber;339this.toLineNumber = toLineNumber;340}341}342343/**344* An event describing that line(s) have been inserted in a model.345* @internal346*/347export class ModelRawLinesInserted {348public readonly changeType = RawContentChangedType.LinesInserted;349/**350* Before what line did the insertion begin351*/352public readonly fromLineNumber: number;353/**354* `toLineNumber` - `fromLineNumber` + 1 denotes the number of lines that were inserted355*/356public readonly toLineNumber: number;357/**358* The text that was inserted359*/360public readonly detail: string[];361/**362* The injected texts for every inserted line.363*/364public readonly injectedTexts: (LineInjectedText[] | null)[];365366constructor(fromLineNumber: number, toLineNumber: number, detail: string[], injectedTexts: (LineInjectedText[] | null)[]) {367this.injectedTexts = injectedTexts;368this.fromLineNumber = fromLineNumber;369this.toLineNumber = toLineNumber;370this.detail = detail;371}372}373374/**375* An event describing that a model has had its EOL changed.376* @internal377*/378export class ModelRawEOLChanged {379public readonly changeType = RawContentChangedType.EOLChanged;380}381382/**383* @internal384*/385export type ModelRawChange = ModelRawFlush | ModelRawLineChanged | ModelRawLinesDeleted | ModelRawLinesInserted | ModelRawEOLChanged;386387/**388* An event describing a change in the text of a model.389* @internal390*/391export class ModelRawContentChangedEvent {392393public readonly changes: ModelRawChange[];394/**395* The new version id the model has transitioned to.396*/397public readonly versionId: number;398/**399* Flag that indicates that this event was generated while undoing.400*/401public readonly isUndoing: boolean;402/**403* Flag that indicates that this event was generated while redoing.404*/405public readonly isRedoing: boolean;406407public resultingSelection: Selection[] | null;408409constructor(changes: ModelRawChange[], versionId: number, isUndoing: boolean, isRedoing: boolean) {410this.changes = changes;411this.versionId = versionId;412this.isUndoing = isUndoing;413this.isRedoing = isRedoing;414this.resultingSelection = null;415}416417public containsEvent(type: RawContentChangedType): boolean {418for (let i = 0, len = this.changes.length; i < len; i++) {419const change = this.changes[i];420if (change.changeType === type) {421return true;422}423}424return false;425}426427public static merge(a: ModelRawContentChangedEvent, b: ModelRawContentChangedEvent): ModelRawContentChangedEvent {428const changes = ([] as ModelRawChange[]).concat(a.changes).concat(b.changes);429const versionId = b.versionId;430const isUndoing = (a.isUndoing || b.isUndoing);431const isRedoing = (a.isRedoing || b.isRedoing);432return new ModelRawContentChangedEvent(changes, versionId, isUndoing, isRedoing);433}434}435436/**437* An event describing a change in injected text.438* @internal439*/440export class ModelInjectedTextChangedEvent {441442public readonly changes: ModelRawLineChanged[];443444constructor(changes: ModelRawLineChanged[]) {445this.changes = changes;446}447}448449/**450* An event describing a change of a line height.451* @internal452*/453export class ModelLineHeightChangedEvent {454455public readonly changes: ModelLineHeightChanged[];456457constructor(changes: ModelLineHeightChanged[]) {458this.changes = changes;459}460461public affects(rangeOrPosition: IRange | IPosition) {462if (Range.isIRange(rangeOrPosition)) {463for (const change of this.changes) {464if (change.lineNumber >= rangeOrPosition.startLineNumber && change.lineNumber <= rangeOrPosition.endLineNumber) {465return true;466}467}468return false;469} else {470for (const change of this.changes) {471if (change.lineNumber === rangeOrPosition.lineNumber) {472return true;473}474}475return false;476}477}478}479480/**481* An event describing a change in fonts.482* @internal483*/484export class ModelFontChangedEvent {485486public readonly changes: ModelFontChanged[];487488constructor(changes: ModelFontChanged[]) {489this.changes = changes;490}491}492493/**494* @internal495*/496export class InternalModelContentChangeEvent {497constructor(498public readonly rawContentChangedEvent: ModelRawContentChangedEvent,499public readonly contentChangedEvent: IModelContentChangedEvent,500) { }501502public merge(other: InternalModelContentChangeEvent): InternalModelContentChangeEvent {503const rawContentChangedEvent = ModelRawContentChangedEvent.merge(this.rawContentChangedEvent, other.rawContentChangedEvent);504const contentChangedEvent = InternalModelContentChangeEvent._mergeChangeEvents(this.contentChangedEvent, other.contentChangedEvent);505return new InternalModelContentChangeEvent(rawContentChangedEvent, contentChangedEvent);506}507508private static _mergeChangeEvents(a: IModelContentChangedEvent, b: IModelContentChangedEvent): IModelContentChangedEvent {509const changes = ([] as IModelContentChange[]).concat(a.changes).concat(b.changes);510const eol = b.eol;511const versionId = b.versionId;512const isUndoing = (a.isUndoing || b.isUndoing);513const isRedoing = (a.isRedoing || b.isRedoing);514const isFlush = (a.isFlush || b.isFlush);515const isEolChange = a.isEolChange && b.isEolChange; // both must be true to not confuse listeners who skip such edits516return {517changes: changes,518eol: eol,519isEolChange: isEolChange,520versionId: versionId,521isUndoing: isUndoing,522isRedoing: isRedoing,523isFlush: isFlush,524detailedReasons: a.detailedReasons.concat(b.detailedReasons),525detailedReasonsChangeLengths: a.detailedReasonsChangeLengths.concat(b.detailedReasonsChangeLengths),526};527}528}529530531