import { ReadStream, WriteStream } from "fs"; import { Disposable, FileSavedEvent, HandleableErrorEvent, HistoryTransactionOptions, HistoryTraversalOptions, TextEditOptions, } from "../../../index"; import { FindMarkerOptions, Marker, MarkerLayer, Point, PointCompatible, Range, RangeCompatible, TextChange, } from "./text-buffer"; export * from "./display-marker"; export * from "./display-marker-layer"; export * from "./helpers"; export * from "./marker"; export * from "./marker-layer"; export * from "./point"; export * from "./range"; /** * A mutable text container with undo/redo support and the ability to * annotate logical regions in the text. */ export class TextBuffer { /** The unique identifier for this buffer. */ readonly id: string; /** The number of retainers for the buffer. */ readonly refcount: number; /** Whether or not the bufffer has been destroyed. */ readonly destroyed: boolean; /** Create a new buffer backed by the given file path. */ static load(filePath: string | TextBufferFileBackend, params?: BufferLoadOptions): Promise; /** * Create a new buffer backed by the given file path. For better performance, * use TextBuffer.load instead. */ static loadSync(filePath: string, params?: BufferLoadOptions): TextBuffer; /** * Restore a TextBuffer based on an earlier state created using the * TextBuffer::serialize method. */ static deserialize(params: object): Promise; /** Create a new buffer with the given starting text. */ constructor(text: string); /** Create a new buffer with the given params. */ constructor(params?: { /** The initial string text of the buffer. */ text?: string | undefined; /** * A function that returns a Boolean indicating whether the buffer should * be destroyed if its file is deleted. */ shouldDestroyOnFileDelete?(): boolean; }); /** Returns a plain javascript object representation of the TextBuffer. */ serialize(options?: { markerLayers?: boolean | undefined; history?: boolean | undefined }): object; /** Returns the unique identifier for this buffer. */ getId(): string; // Event Subscription /** * Invoke the given callback synchronously before the content of the buffer * changes. */ onWillChange(callback: (event: BufferChangingEvent) => void): Disposable; /** * Invoke the given callback synchronously when the content of the buffer * changes. You should probably not be using this in packages. */ onDidChange(callback: (event: BufferChangedEvent) => void): Disposable; /** * Invoke the given callback synchronously when a transaction finishes with * a list of all the changes in the transaction. */ onDidChangeText(callback: (event: BufferStoppedChangingEvent) => void): Disposable; /** * Invoke the given callback asynchronously following one or more changes after * ::getStoppedChangingDelay milliseconds elapse without an additional change. */ onDidStopChanging(callback: (event: BufferStoppedChangingEvent) => void): Disposable; /** * Invoke the given callback when the in-memory contents of the buffer become * in conflict with the contents of the file on disk. */ onDidConflict(callback: () => void): Disposable; /** Invoke the given callback if the value of ::isModified changes. */ onDidChangeModified(callback: (modified: boolean) => void): Disposable; /** * Invoke the given callback when all marker ::onDidChange observers have been * notified following a change to the buffer. */ onDidUpdateMarkers(callback: () => void): Disposable; onDidCreateMarker(callback: (marker: Marker) => void): Disposable; /** Invoke the given callback when the value of ::getPath changes. */ onDidChangePath(callback: (path: string) => void): Disposable; /** Invoke the given callback when the value of ::getEncoding changes. */ onDidChangeEncoding(callback: (encoding: string) => void): Disposable; /** * Invoke the given callback before the buffer is saved to disk. If the * given callback returns a promise, then the buffer will not be saved until * the promise resolves. */ onWillSave(callback: () => Promise | void): Disposable; /** Invoke the given callback after the buffer is saved to disk. */ onDidSave(callback: (event: FileSavedEvent) => void): Disposable; /** Invoke the given callback after the file backing the buffer is deleted. */ onDidDelete(callback: () => void): Disposable; /** * Invoke the given callback before the buffer is reloaded from the contents * of its file on disk. */ onWillReload(callback: () => void): Disposable; /** * Invoke the given callback after the buffer is reloaded from the contents * of its file on disk. */ onDidReload(callback: () => void): Disposable; /** Invoke the given callback when the buffer is destroyed. */ onDidDestroy(callback: () => void): Disposable; /** Invoke the given callback when there is an error in watching the file. */ onWillThrowWatchError(callback: (errorObject: HandleableErrorEvent) => void): Disposable; /** * Get the number of milliseconds that will elapse without a change before * ::onDidStopChanging observers are invoked following a change. */ getStoppedChangingDelay(): number; // File Details /** * Determine if the in-memory contents of the buffer differ from its contents * on disk. * If the buffer is unsaved, always returns true unless the buffer is empty. */ isModified(): boolean; /** * Determine if the in-memory contents of the buffer conflict with the on-disk * contents of its associated file. */ isInConflict(): boolean; /** Get the path of the associated file. */ getPath(): string | undefined; /** Set the path for the buffer's associated file. */ setPath(filePath: string): void; /** Experimental: Set a custom {TextBufferFileBackend} object as the buffer's backing store. */ setFile(fileBackend: TextBufferFileBackend): void; /** Sets the character set encoding for this buffer. */ setEncoding(encoding: string): void; /** Returns the string encoding of this buffer. */ getEncoding(): string; /** Get the path of the associated file. */ getUri(): string; // Reading Text /** Determine whether the buffer is empty. */ isEmpty(): boolean; /** * Get the entire text of the buffer. Avoid using this unless you know that * the buffer's text is reasonably short. */ getText(): string; /** Get the text in a range. */ getTextInRange(range: RangeCompatible): string; /** Get the text of all lines in the buffer, without their line endings. */ getLines(): string[]; /** Get the text of the last line of the buffer, without its line ending. */ getLastLine(): string; /** * Get the text of the line at the given 0-indexed row, without its line ending. * @param row A number representing the row. */ lineForRow(row: number): string | undefined; /** Get the line ending for the given 0-indexed row. */ lineEndingForRow(row: number): string | undefined; /** * Get the length of the line for the given 0-indexed row, without its line * ending. */ lineLengthForRow(row: number): number; /** Determine if the given row contains only whitespace. */ isRowBlank(row: number): boolean; /** * Given a row, find the first preceding row that's not blank. * Returns a number or null if there's no preceding non-blank row. */ previousNonBlankRow(startRow: number): number | null; /** * Given a row, find the next row that's not blank. * Returns a number or null if there's no next non-blank row. */ nextNonBlankRow(startRow: number): number | null; /** * Return true if the buffer contains any astral-plane Unicode characters that * are encoded as surrogate pairs. */ hasAstral(): boolean; // Mutating Text /** Replace the entire contents of the buffer with the given text. */ setText(text: string): Range; /** * Replace the current buffer contents by applying a diff based on the * given text. */ setTextViaDiff(text: string): void; /** Set the text in the given range. */ setTextInRange(range: RangeCompatible, text: string, options?: TextEditOptions): Range; /** Insert text at the given position. */ insert(position: PointCompatible, text: string, options?: TextEditOptions): Range; /** Append text to the end of the buffer. */ append(text: string, options?: TextEditOptions): Range; /** Delete the text in the given range. */ delete(range: RangeCompatible): Range; /** * Delete the line associated with a specified 0-indexed row. * @param row A number representing the row to delete. */ deleteRow(row: number): Range; /** * Delete the lines associated with the specified 0-indexed row range. * * If the row range is out of bounds, it will be clipped. If the `startRow` * is greater than the `endRow`, they will be reordered. */ deleteRows(startRow: number, endRow: number): Range; // Markers /** Create a layer to contain a set of related markers. */ addMarkerLayer( options?: { maintainHistory?: boolean | undefined; persistent?: boolean | undefined; role?: string | undefined; }, ): MarkerLayer; /** * Get a MarkerLayer by id. * Returns a MarkerLayer or undefined if no layer exists with the given id. */ getMarkerLayer(id: string): MarkerLayer | undefined; /** Get the default MarkerLayer. */ getDefaultMarkerLayer(): MarkerLayer; /** Create a marker with the given range in the default marker layer. */ markRange( range: RangeCompatible, properties?: { reversed?: boolean | undefined; invalidate?: "never" | "surround" | "overlap" | "inside" | "touch" | undefined; exclusive?: boolean | undefined; }, ): Marker; /** Create a marker at the given position with no tail in the default marker layer. */ markPosition( position: PointCompatible, options?: { invalidate?: "never" | "surround" | "overlap" | "inside" | "touch" | undefined; exclusive?: boolean | undefined; }, ): Marker; /** Get all existing markers on the default marker layer. */ getMarkers(): Marker[]; /** Get an existing marker by its id from the default marker layer. */ getMarker(id: number): Marker; /** Find markers conforming to the given parameters in the default marker layer. */ findMarkers(params: FindMarkerOptions): Marker[]; /** Get the number of markers in the default marker layer. */ getMarkerCount(): number; // History /** * Undo the last operation. If a transaction is in progress, aborts it. * @return A boolean of whether or not a change was made. */ undo(options?: HistoryTraversalOptions): boolean; /** * Redo the last operation. * @return A boolean of whether or not a change was made. */ redo(options?: HistoryTraversalOptions): boolean; /** Batch multiple operations as a single undo/redo step. */ transact( optionsOrInterval: number | ({ groupingInterval?: number | undefined } & HistoryTransactionOptions), fn: () => T, ): T; /** Batch multiple operations as a single undo/redo step. */ transact(fn: () => T): T; /** * Abort the currently running transaction. * * Only intended to be called within the `fn` option to `::transact`. */ abortTransaction(): void; /** Clear the undo stack. */ clearUndoStack(): void; /** * Create a pointer to the current state of the buffer for use with * `::revertToCheckpoint` and `::groupChangesSinceCheckpoint`. * @return A checkpoint ID value. */ createCheckpoint(options?: HistoryTransactionOptions): number; /** * Revert the buffer to the state it was in when the given checkpoint was created. * @return A boolean indicating whether the operation succeeded. */ revertToCheckpoint(checkpoint: number, options?: HistoryTraversalOptions): boolean; /** * Group all changes since the given checkpoint into a single transaction for * purposes of undo/redo. * @return A boolean indicating whether the operation succeeded. */ groupChangesSinceCheckpoint(checkpoint: number, options?: HistoryTransactionOptions): boolean; /** * Group the last two text changes for purposes of undo/redo. * * This operation will only succeed if there are two changes on the undo stack. * It will not group past the beginning of an open transaction. * @return A boolean indicating whether the operation succeeded. */ groupLastChanges(): boolean; /** * Returns a list of changes since the given checkpoint. * If the given checkpoint is no longer present in the undo history, this method * will return an empty Array. */ getChangesSinceCheckpoint( checkpoint: number, ): Array<{ /** A Point representing where the change started. */ start: Point; /** A Point representing the replaced extent. */ oldExtent: Point; /** A Point representing the replacement extent. */ newExtent: Point; /** A String representing the replacement text. */ newText: string; }>; // Search and Replace /** * Scan regular expression matches in the entire buffer, calling the given * iterator function on each match. */ scan(regex: RegExp, iterator: (params: BufferScanResult) => void): void; /** * Scan regular expression matches in the entire buffer, calling the given * iterator function on each match. */ scan(regex: RegExp, options: ScanContextOptions, iterator: (params: ContextualBufferScanResult) => void): void; /** * Scan regular expression matches in the entire buffer in reverse order, * calling the given iterator function on each match. */ backwardsScan(regex: RegExp, iterator: (params: BufferScanResult) => void): void; /** * Scan regular expression matches in the entire buffer in reverse order, * calling the given iterator function on each match. */ backwardsScan( regex: RegExp, options: ScanContextOptions, iterator: (params: ContextualBufferScanResult) => void, ): void; /** * Scan regular expression matches in a given range , calling the given * iterator function on each match. */ scanInRange(regex: RegExp, range: RangeCompatible, iterator: (params: BufferScanResult) => void): void; /** * Scan regular expression matches in a given range , calling the given * iterator function on each match. */ scanInRange( regex: RegExp, range: RangeCompatible, options: ScanContextOptions, iterator: (params: ContextualBufferScanResult) => void, ): void; /** * Scan regular expression matches in a given range in reverse order, * calling the given iterator function on each match. */ backwardsScanInRange(regex: RegExp, range: RangeCompatible, iterator: (params: BufferScanResult) => void): void; /** * Scan regular expression matches in a given range in reverse order, * calling the given iterator function on each match. */ backwardsScanInRange( regex: RegExp, range: RangeCompatible, options: ScanContextOptions, iterator: (params: ContextualBufferScanResult) => void, ): void; /** Replace all regular expression matches in the entire buffer. */ replace(regex: RegExp, replacementText: string): number; // Buffer Range Details /** Get the range spanning from [0, 0] to ::getEndPosition. */ getRange(): Range; /** Get the number of lines in the buffer. */ getLineCount(): number; /** Get the last 0-indexed row in the buffer. */ getLastRow(): number; /** Get the first position in the buffer, which is always [0, 0]. */ getFirstPosition(): Point; /** Get the maximal position in the buffer, where new text would be appended. */ getEndPosition(): Point; /** Get the length of the buffer's text. */ getLength(): number; /** Get the length of the buffer in characters. */ getMaxCharacterIndex(): number; /** * Get the range for the given row. * @param row A number representing a 0-indexed row. * @param includeNewline A boolean indicating whether or not to include the * newline, which results in a range that extends to the start of the next line. * (default: false) */ rangeForRow(row: number, includeNewline?: boolean): Range; /** * Convert a position in the buffer in row/column coordinates to an absolute * character offset, inclusive of line ending characters. */ characterIndexForPosition(position: PointCompatible): number; /** * Convert an absolute character offset, inclusive of newlines, to a position * in the buffer in row/column coordinates. */ positionForCharacterIndex(offset: number): Point; /** Clip the given range so it starts and ends at valid positions. */ clipRange(range: RangeCompatible): Range; /** Clip the given point so it is at a valid position in the buffer. */ clipPosition(position: PointCompatible): Point; // Buffer Operations /** Save the buffer. */ save(): Promise; /** Save the buffer at a specific path. */ saveAs(filePath: string): Promise; /** Reload the buffer's contents from disk. */ reload(): void; /** Destroy the buffer, even if there are retainers for it. */ destroy(): void; /** Returns whether or not this buffer is alive. */ isAlive(): boolean; /** Returns whether or not this buffer has been destroyed. */ isDestroyed(): boolean; /** Returns whether or not this buffer has a retainer. */ isRetained(): boolean; /** * Places a retainer on the buffer, preventing its destruction until the * final retainer has called ::release(). */ retain(): TextBuffer; /** * Releases a retainer on the buffer, destroying the buffer if there are * no additional retainers. */ release(): TextBuffer; /** Identifies if the buffer belongs to multiple editors. */ hasMultipleEditors(): boolean; } export interface TextBufferFileBackend { /** A {Function} that returns the {String} path to the file. */ getPath(): string; /** * A {Function} that returns a `Readable` stream * that can be used to load the file's content. */ createReadStream(): ReadStream; /** * A {Function} that returns a `Writable` stream * that can be used to save content to the file. */ createWriteStream(): WriteStream; /** A {Function} that returns a {Boolean}, true if the file exists, false otherwise. */ existsSync(): boolean; /** * A {Function} that invokes its callback argument * when the file changes. The method should return a {Disposable} that * can be used to prevent further calls to the callback. */ onDidChange?(callback: () => void): Disposable; /** * A {Function} that invokes its callback argument * when the file is deleted. The method should return a {Disposable} that * can be used to prevent further calls to the callback. */ onDidDelete?(callback: () => void): Disposable; /** * A {Function} that invokes its callback argument * when the file is renamed. The method should return a {Disposable} that * can be used to prevent further calls to the callback. */ onDidRename?(callback: () => void): Disposable; } export interface BufferChangingEvent { /** Range of the old text. */ oldRange: Range; } export interface BufferChangedEvent { /** * An array of objects summarizing the aggregated changes that occurred * during the transaction. */ changes: Array<{ /** * The Range of the deleted text in the contents of the buffer as it existed * before the batch of changes reported by this event. */ oldRange: Range; /** The Range of the inserted text in the current contents of the buffer. */ newRange: Range; }>; /** Range of the old text. */ oldRange: Range; /** Range of the new text. */ newRange: Range; /** String containing the text that was replaced. */ oldText: string; /** String containing the text that was inserted. */ newText: string; } export interface BufferStoppedChangingEvent { changes: TextChange[]; } export interface BufferLoadOptions { /** The file's encoding. */ encoding?: string | undefined; /** * A function that returns a boolean indicating whether the buffer should * be destroyed if its file is deleted. */ shouldDestroyOnFileDelete?(): boolean; } export interface BufferScanResult { buffer: TextBuffer; lineText: string; match: RegExpExecArray; matchText: string; range: Range; replace(replacementText: string): void; stop(): void; stopped: boolean; } export interface ContextualBufferScanResult extends BufferScanResult { leadingContextLines: string[]; trailingContextLines: string[]; } export interface ScanContextOptions { /** The number of lines before the matched line to include in the results object. */ leadingContextLineCount?: number | undefined; /** The number of lines after the matched line to include in the results object. */ trailingContextLineCount?: number | undefined; }