editStack.ts ×46

Frontier kind: Code frontier

unlabeled · c_1edf562208a9

862 tests · 10025 LOC · 52 files · introduces 0 tests · 268 LOC · 2 files

Introduces — evidence that enters the hierarchy at this concept

Code
67 ranges268 lines · 2 files
Tests
0 tests

Contains — complete concept membership

All code (extent)
1368 ranges10025 lines · 52 files · Browse complete extent
All tests (intent)
862 testsBrowse complete intent

Neighbourhood graph

The orange circle is the focus. Violet and green circles are every ancestor and descendant, broader and narrower, at any distance; blue squares and pink diamonds are the introduced files and exact introduced tests of every visible concept, not only the focus's. Arrows point from broader to narrower concepts and bridge only concepts omitted from this view. Undirected links show source or test introduction. Concept and file size follows LOC; exact test nodes use test-count units.

Introduced files, introduced tests, and structurally relevant concept specialization

In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the native relationship evidence on this page.

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the native relationship evidence on this page while the interactive map is unavailable.

Native relationship evidence

Every exact file and test below is linked only from the concept that introduces it.

Introduced tests

Every collected test enters the hierarchy at exactly one concept.

No tests are introduced at this concept. Its intent tests are introduced by other concepts.

Introduced code

Every collected source range enters the hierarchy at exactly one concept.

2 files ranked by introduced lines: 268 introduced LOC across 67 ranges. Expand a file to inspect source; the > gutter marks introduced lines.

src/vs/editor/common/model/editStack.ts 160 introduced LOC · 46 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- editStack.ts
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 * as nls from '../../../nls.js';
7 > import { onUnexpectedError } from '../../../base/common/errors.js';
8 > import { Selection } from '../core/selection.js';
9 > import { EndOfLineSequence, ICursorStateComputer, IValidEditOperation, ITextModel } from '../model.js';
10 > import { TextModel } from './textModel.js';
11 > import { IUndoRedoService, IResourceUndoRedoElement, UndoRedoElementType, IWorkspaceUndoRedoElement, UndoRedoGroup } from '../../../platform/undoRedo/common/undoRedo.js';
12 > import { URI } from '../../../base/common/uri.js';
13 > import { TextChange, compressConsecutiveTextChanges } from '../core/textChange.js';
14 > import * as buffer from '../../../base/common/buffer.js';
15 > import { IDisposable } from '../../../base/common/lifecycle.js';
16 > import { basename } from '../../../base/common/resources.js';
17 > import { ISingleEditOperation } from '../core/editOperation.js';
18 > import { EditSources, TextModelEditSource } from '../textModelEditSource.js';
19 >
20 function uriGetComparisonKey(resource: URI): string {
21 return resource.toString();
22 }
24 > export class SingleModelEditStackData {
25 >
26 > public static create(model: ITextModel, beforeCursorState: Selection[] | null): SingleModelEditStackData {
27 > const alternativeVersionId = model.getAlternativeVersionId();
28 > const eol = getModelEOL(model);
29 > return new SingleModelEditStackData(
30 > alternativeVersionId,
31 > alternativeVersionId,
32 > eol,
33 > eol,
34 > beforeCursorState,
35 > beforeCursorState,
36 > []
37 > );
38 > }
39 >
40 > constructor(
41 public readonly beforeVersionId: number,
42 public afterVersionId: number,
47 public changes: TextChange[]
48 ) { }
50 > public append(model: ITextModel, textChanges: TextChange[], afterEOL: EndOfLineSequence, afterVersionId: number, afterCursorState: Selection[] | null): void {
51 if (textChanges.length > 0) {
52 this.changes = compressConsecutiveTextChanges(this.changes, textChanges);
56 this.afterCursorState = afterCursorState;
57 }
59 > private static _writeSelectionsSize(selections: Selection[] | null): number {
60 return 4 + 4 * 4 * (selections ? selections.length : 0);
61 }
63 > private static _writeSelections(b: Uint8Array, selections: Selection[] | null, offset: number): number {
64 buffer.writeUInt32BE(b, (selections ? selections.length : 0), offset); offset += 4;
65 if (selections) {
73 return offset;
74 }
76 > private static _readSelections(b: Uint8Array, offset: number, dest: Selection[]): number {
77 const count = buffer.readUInt32BE(b, offset); offset += 4;
78 for (let i = 0; i < count; i++) {
85 return offset;
86 }
88 > public serialize(): ArrayBuffer {
89 let necessarySize = (
90 + 4 // beforeVersionId
114 return b.buffer;
115 }
116 > editStack.ts
117 > public static deserialize(source: ArrayBuffer): SingleModelEditStackData {
118 const b = new Uint8Array(source);
119 let offset = 0;
141 );
142 }
143 > } editStack.ts
144 >
145 > export interface IUndoRedoDelegate {
146 > prepareUndoRedo(element: MultiModelEditStackElement): Promise<IDisposable> | IDisposable | void;
147 > }
148 >
149 > export class SingleModelEditStackElement implements IResourceUndoRedoElement {
150 >
151 > public model: ITextModel | URI;
152 > private _data: SingleModelEditStackData | ArrayBuffer;
153 >
154 > public get type(): UndoRedoElementType.Resource {
155 > return UndoRedoElementType.Resource;
156 > }
157 >
158 > public get resource(): URI {
159 if (URI.isUri(this.model)) {
160 return this.model;
162 return this.model.uri;
163 }
164 > editStack.ts
165 > constructor(
166 public readonly label: string,
167 public readonly code: string,
172 this._data = SingleModelEditStackData.create(model, beforeCursorState);
173 }
174 > editStack.ts
175 > public toString(): string {
176 const data = (this._data instanceof SingleModelEditStackData ? this._data : SingleModelEditStackData.deserialize(this._data));
177 return data.changes.map(change => change.toString()).join(', ');
178 }
179 > editStack.ts
180 > public matchesResource(resource: URI): boolean {
181 const uri = (URI.isUri(this.model) ? this.model : this.model.uri);
182 return (uri.toString() === resource.toString());
183 }
184 > editStack.ts
185 > public setModel(model: ITextModel | URI): void {
186 this.model = model;
187 }
188 > editStack.ts
189 > public canAppend(model: ITextModel): boolean {
190 return (this.model === model && this._data instanceof SingleModelEditStackData);
191 }
192 > editStack.ts
193 > public append(model: ITextModel, textChanges: TextChange[], afterEOL: EndOfLineSequence, afterVersionId: number, afterCursorState: Selection[] | null): void {
194 if (this._data instanceof SingleModelEditStackData) {
195 this._data.append(model, textChanges, afterEOL, afterVersionId, afterCursorState);
196 }
197 }
198 > editStack.ts
199 > public close(): void {
200 if (this._data instanceof SingleModelEditStackData) {
201 this._data = this._data.serialize();
202 }
203 }
204 > editStack.ts
205 > public open(): void {
206 if (!(this._data instanceof SingleModelEditStackData)) {
207 this._data = SingleModelEditStackData.deserialize(this._data);
208 }
209 }
210 > editStack.ts
211 > public undo(): void {
212 if (URI.isUri(this.model)) {
213 // don't have a model
220 this.model._applyUndo(data.changes, data.beforeEOL, data.beforeVersionId, data.beforeCursorState);
221 }
222 > editStack.ts
223 > public redo(): void {
224 if (URI.isUri(this.model)) {
225 // don't have a model
232 this.model._applyRedo(data.changes, data.afterEOL, data.afterVersionId, data.afterCursorState);
233 }
234 > editStack.ts
235 > public heapSize(): number {
236 if (this._data instanceof SingleModelEditStackData) {
237 this._data = this._data.serialize();
239 return this._data.byteLength + 168/*heap overhead*/;
240 }
241 > } editStack.ts
242 >
243 > export class MultiModelEditStackElement implements IWorkspaceUndoRedoElement {
244 >
245 > public readonly type = UndoRedoElementType.Workspace;
246 > private _isOpen: boolean;
247 >
248 > private readonly _editStackElementsArr: SingleModelEditStackElement[];
249 > private readonly _editStackElementsMap: Map<string, SingleModelEditStackElement>;
250 >
251 > private _delegate: IUndoRedoDelegate | null;
252 >
253 > public get resources(): readonly URI[] {
254 > return this._editStackElementsArr.map(editStackElement => editStackElement.resource);
255 > }
256 >
257 > constructor(
258 public readonly label: string,
259 public readonly code: string,
269 this._delegate = null;
270 }
271 > editStack.ts
272 > public setDelegate(delegate: IUndoRedoDelegate): void {
273 this._delegate = delegate;
274 }
275 > editStack.ts
276 > public prepareUndoRedo(): Promise<IDisposable> | IDisposable | void {
277 if (this._delegate) {
278 return this._delegate.prepareUndoRedo(this);
279 }
280 }
281 > editStack.ts
282 > public getMissingModels(): URI[] {
283 const result: URI[] = [];
284 for (const editStackElement of this._editStackElementsArr) {
289 return result;
290 }
291 > editStack.ts
292 > public matchesResource(resource: URI): boolean {
293 const key = uriGetComparisonKey(resource);
294 return (this._editStackElementsMap.has(key));
295 }
296 > editStack.ts
297 > public setModel(model: ITextModel | URI): void {
298 const key = uriGetComparisonKey(URI.isUri(model) ? model : model.uri);
299 if (this._editStackElementsMap.has(key)) {
301 }
302 }
303 > editStack.ts
304 > public canAppend(model: ITextModel): boolean {
305 if (!this._isOpen) {
306 return false;
313 return false;
314 }
315 > editStack.ts
316 > public append(model: ITextModel, textChanges: TextChange[], afterEOL: EndOfLineSequence, afterVersionId: number, afterCursorState: Selection[] | null): void {
317 const key = uriGetComparisonKey(model.uri);
318 const editStackElement = this._editStackElementsMap.get(key)!;
319 editStackElement.append(model, textChanges, afterEOL, afterVersionId, afterCursorState);
320 }
321 > editStack.ts
322 > public close(): void {
323 this._isOpen = false;
324 }
325 > editStack.ts
326 > public open(): void {
327 // cannot reopen
328 }
329 > editStack.ts
330 > public undo(): void {
331 this._isOpen = false;
332
335 }
336 }
337 > editStack.ts
338 > public redo(): void {
339 for (const editStackElement of this._editStackElementsArr) {
340 editStackElement.redo();
341 }
342 }
343 > editStack.ts
344 > public heapSize(resource: URI): number {
345 const key = uriGetComparisonKey(resource);
346 if (this._editStackElementsMap.has(key)) {
350 return 0;
351 }
352 > editStack.ts
353 > public split(): IResourceUndoRedoElement[] {
354 return this._editStackElementsArr;
355 }
356 > editStack.ts
357 > public toString(): string {
358 const result: string[] = [];
359 for (const editStackElement of this._editStackElementsArr) {
362 return `{${result.join(', ')}}`;
363 }
364 > } editStack.ts
365 >
366 > export type EditStackElement = SingleModelEditStackElement | MultiModelEditStackElement;
367 >
368 function getModelEOL(model: ITextModel): EndOfLineSequence {
369 const eol = model.getEOL();
374 }
375 }
376 > editStack.ts
377 > export function isEditStackElement(element: IResourceUndoRedoElement | IWorkspaceUndoRedoElement | null): element is EditStackElement {
378 if (!element) {
379 return false;
381 return ((element instanceof SingleModelEditStackElement) || (element instanceof MultiModelEditStackElement));
382 }
383 > editStack.ts
384 > export class EditStack {
385 >
386 > private readonly _model: TextModel;
387 > private readonly _undoRedoService: IUndoRedoService;
388 >
389 > constructor(model: TextModel, undoRedoService: IUndoRedoService) {
390 this._model = model;
391 this._undoRedoService = undoRedoService;
392 }
393 > editStack.ts
394 > public pushStackElement(): void {
395 const lastElement = this._undoRedoService.getLastElement(this._model.uri);
396 if (isEditStackElement(lastElement)) {
398 }
399 }
400 > editStack.ts
401 > public popStackElement(): void {
402 const lastElement = this._undoRedoService.getLastElement(this._model.uri);
403 if (isEditStackElement(lastElement)) {
405 }
406 }
407 > editStack.ts
408 > public clear(): void {
409 this._undoRedoService.removeElements(this._model.uri);
410 }
411 > editStack.ts
412 > private _getOrCreateEditStackElement(beforeCursorState: Selection[] | null, group: UndoRedoGroup | undefined): EditStackElement {
413 const lastElement = this._undoRedoService.getLastElement(this._model.uri);
414 if (isEditStackElement(lastElement) && lastElement.canAppend(this._model)) {
419 return newElement;
420 }
421 > editStack.ts
422 > public pushEOL(eol: EndOfLineSequence): void {
423 const editStackElement = this._getOrCreateEditStackElement(null, undefined);
424 this._model.setEOL(eol);
425 editStackElement.append(this._model, [], getModelEOL(this._model), this._model.getAlternativeVersionId(), null);
426 }
427 > editStack.ts
428 > public pushEditOperation(beforeCursorState: Selection[] | null, editOperations: ISingleEditOperation[], cursorStateComputer: ICursorStateComputer | null, group?: UndoRedoGroup, reason: TextModelEditSource = EditSources.unknown({ name: 'pushEditOperation' })): Selection[] | null {
429 const editStackElement = this._getOrCreateEditStackElement(beforeCursorState, group);
430 const inverseEditOperations = this._model.applyEdits(editOperations, true, reason);
440 return afterCursorState;
441 }
442 > editStack.ts
443 > private static _computeCursorState(cursorStateComputer: ICursorStateComputer | null, inverseEditOperations: IValidEditOperation[]): Selection[] | null {
444 try {
445 return cursorStateComputer ? cursorStateComputer(inverseEditOperations) : null;
src/vs/editor/common/textModelEditSource.ts 108 introduced LOC · 21 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- textModelEditSource.ts
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 { sumBy } from '../../base/common/arrays.js';
7 > import { prefixedUuid } from '../../base/common/uuid.js';
8 > import { LineEdit } from './core/edits/lineEdit.js';
9 > import { BaseStringEdit } from './core/edits/stringEdit.js';
10 > import { StringText } from './core/text/abstractText.js';
11 > import { TextLength } from './core/text/textLength.js';
12 > import { ProviderId, VersionedExtensionId } from './languages.js';
13 >
14 > const privateSymbol = Symbol('TextModelEditSource');
15 >
16 > export class TextModelEditSource {
17 > constructor(
18 public readonly metadata: ITextModelEditSourceMetadata,
19 _privateCtorGuard: typeof privateSymbol,
20 ) { }
22 > public toString(): string {
23 return `${this.metadata.source}`;
24 }
26 > public getType(): string {
27 const metadata = this.metadata;
28 switch (metadata.source) {
37 }
38 }
40 > /**
41 > * Converts the metadata to a key string.
42 > * Only includes properties/values that have `level` many `$` prefixes or less.
43 > */
44 > public toKey(level: number, filter: { [TKey in ITextModelEditSourceMetadataKeys]?: boolean } = {}): string {
45 const metadata = this.metadata;
46 const keys = Object.entries(metadata).filter(([key, value]) => {
55 return keys.join('-');
56 }
58 > public get props(): Record<ITextModelEditSourceMetadataKeys, string | undefined> {
59 // eslint-disable-next-line local/code-no-any-casts, @typescript-eslint/no-explicit-any
60 return this.metadata as any;
61 }
63 >
64 > type TextModelEditSourceT<T> = TextModelEditSource & {
65 > metadataT: T;
66 > };
67 >
68 > // eslint-disable-next-line @typescript-eslint/no-explicit-any
69 function createEditSource<T extends Record<string, any>>(metadata: T): TextModelEditSourceT<T> {
70 // eslint-disable-next-line local/code-no-any-casts, @typescript-eslint/no-explicit-any
71 return new TextModelEditSource(metadata as any, privateSymbol) as any;
72 }
74 > export function isAiEdit(source: TextModelEditSource): boolean {
75 switch (source.metadata.source) {
76 case 'inlineCompletionAccept':
82 return false;
83 }
85 > export function isUserEdit(source: TextModelEditSource): boolean {
86 switch (source.metadata.source) {
87 case 'cursor':
90 return false;
91 }
93 > export const EditSources = {
94 > unknown(data: { name?: string | null }) {
95 return createEditSource({
96 source: 'unknown',
98 } as const);
99 },
101 > rename: (oldName: string | undefined, newName: string) => createEditSource({ source: 'rename', $$$oldName: oldName, $$$newName: newName } as const),
102 >
103 > chatApplyEdits(data: {
104 modelId: string | undefined;
105 sessionId: string | undefined;
122 } as const);
123 },
125 > chatUndoEdits: () => createEditSource({ source: 'Chat.undoEdits' } as const),
126 > chatReset: () => createEditSource({ source: 'Chat.reset' } as const),
127 >
128 > inlineCompletionAccept(data: { nes: boolean; requestUuid: string; languageId: string; providerId?: ProviderId; correlationId: string | undefined }) {
129 return createEditSource({
130 source: 'inlineCompletionAccept',
136 } as const);
137 },
139 > inlineCompletionPartialAccept(data: { nes: boolean; requestUuid: string; languageId: string; providerId?: ProviderId; correlationId: string | undefined; type: 'word' | 'line' }) {
140 return createEditSource({
141 source: 'inlineCompletionPartialAccept',
148 } as const);
149 },
151 > inlineChatApplyEdit(data: { modelId: string | undefined; requestId: string | undefined; sessionId: string | undefined; languageId: string; extensionId: VersionedExtensionId | undefined }) {
152 return createEditSource({
153 source: 'inlineChat.applyEdits',
160 } as const);
161 },
163 > reloadFromDisk: () => createEditSource({ source: 'reloadFromDisk' } as const),
164 >
165 > cursor(data: { kind: 'compositionType' | 'compositionEnd' | 'type' | 'paste' | 'cut' | 'executeCommands' | 'executeCommand'; detailedSource?: string | null }) {
166 return createEditSource({
167 source: 'cursor',
170 } as const);
171 },
173 > setValue: () => createEditSource({ source: 'setValue' } as const),
174 > eolChange: () => createEditSource({ source: 'eolChange' } as const),
175 > applyEdits: () => createEditSource({ source: 'applyEdits' } as const),
176 > snippet: () => createEditSource({ source: 'snippet' } as const),
177 > suggest: (data: { providerId: ProviderId | undefined }) => createEditSource({ source: 'suggest', ...toProperties(data.providerId) } as const),
178 >
179 > codeAction: (data: { kind: string | undefined; providerId: ProviderId | undefined }) => createEditSource({ source: 'codeAction', $kind: data.kind, ...toProperties(data.providerId) } as const)
180 > };
181 >
182 function toProperties(version: ProviderId | undefined) {
183 if (!version) {
190 };
191 }
193 > type Values<T> = T[keyof T];
194 > export type ITextModelEditSourceMetadata = Values<{ [TKey in keyof typeof EditSources]: ReturnType<typeof EditSources[TKey]>['metadataT'] }>;
195 > type ITextModelEditSourceMetadataKeys = Values<{ [TKey in keyof typeof EditSources]: keyof ReturnType<typeof EditSources[TKey]>['metadataT'] }>;
196 >
197 >
198 function avoidPathRedaction(str: string | undefined): string | undefined {
199 if (str === undefined) {
203 return str.replaceAll('/', '|');
204 }
206 >
207 > export class EditDeltaInfo {
208 > public static fromText(text: string): EditDeltaInfo {
209 > const linesAdded = TextLength.ofText(text).lineCount;
210 > const charsAdded = text.length;
211 > return new EditDeltaInfo(linesAdded, 0, charsAdded, 0);
212 > }
213 >
214 > /** @internal */
215 > public static fromEdit(edit: BaseStringEdit, originalString: StringText): EditDeltaInfo {
216 const lineEdit = LineEdit.fromStringEdit(edit, originalString);
217 const linesAdded = sumBy(lineEdit.replacements, r => r.newLines.length);
221 return new EditDeltaInfo(linesAdded, linesRemoved, charsAdded, charsRemoved);
222 }
224 > public static tryCreate(
225 linesAdded: number | undefined,
226 linesRemoved: number | undefined,
233 return new EditDeltaInfo(linesAdded, linesRemoved, charsAdded, charsRemoved);
234 }
236 > constructor(
237 public readonly linesAdded: number,
238 public readonly linesRemoved: number,
240 public readonly charsRemoved: number
241 ) { }
243 >
244 >
245 > /**
246 > * This is an opaque serializable type that represents a unique identity for an edit.
247 > */
248 > export interface EditSuggestionId {
249 > readonly _brand: 'EditIdentity';
250 > }
251 >
252 > export namespace EditSuggestionId {
253 > /**
254 > * Use AiEditTelemetryServiceImpl to create a new id!
255 > */
256 > export function newId(genPrefixedUuid?: (ns: string) => string): EditSuggestionId {
257 const id = genPrefixedUuid ? genPrefixedUuid('sgt') : prefixedUuid('sgt');
258 return toEditIdentity(id);
259 }
261 >
262 function toEditIdentity(id: string): EditSuggestionId {
263 return id as unknown as EditSuggestionId;