Sitelet https://getfresh.dev/docs/plugins/api/buffer
Skip to content

Buffers & Editing ​

Read and change buffer text, move cursors, open, save and close files, and search.

Buffer Queries ​

getActiveBufferId ​

Get the active buffer ID (0 if none) This is the buffer in the focused editor pane. Use the ID with other buffer operations such as insertText.

typescript
getActiveBufferId(): number;

listBuffers ​

List all open buffers - returns array of BufferInfo objects

typescript
listBuffers(): BufferInfo[];

getBufferPath ​

Get file path for a buffer Returns an empty string for unsaved buffers, virtual buffers, or an unknown buffer ID. Use the path to determine file type, construct related paths, or display to the user.

typescript
getBufferPath(bufferId: number): string;

getBufferLength ​

Get buffer length in bytes Returns 0 if the buffer doesn't exist.

typescript
getBufferLength(bufferId: number): number;

isBufferModified ​

Check if buffer has unsaved changes Returns false if the buffer doesn't exist. Setting a virtual buffer's content does not mark it modified.

typescript
isBufferModified(bufferId: number): boolean;

getBufferInfo ​

Get buffer info by ID Returns null if the buffer doesn't exist.

typescript
getBufferInfo(bufferId: number): BufferInfo | null;

getLineStartPosition ​

Get the byte offset of the start of a line (0-indexed line number) Returns null if the line number is out of range

typescript
getLineStartPosition(line: number): Promise<number | null>;

getLineEndPosition ​

Get the byte offset of the end of a line (0-indexed line number) Returns the position after the last character of the line (before newline) Returns null if the line number is out of range

typescript
getLineEndPosition(line: number): Promise<number | null>;

getBufferLineCount ​

Get the total number of lines in the active buffer Returns null if buffer not found

typescript
getBufferLineCount(): Promise<number | null>;

findBufferByPath ​

Find buffer by file path, returns buffer ID or 0 if not found

typescript
findBufferByPath(path: string): number;

getBufferSavedDiff ​

Get diff between buffer content and last saved version

typescript
getBufferSavedDiff(bufferId: number): BufferSavedDiff | null;

getBufferText ​

Read buffer text.

getBufferText(id) returns the whole buffer — the common case, and the one that used to require reading length from getBufferInfo and passing it back. getBufferText(id, start, end) still reads a byte range, so existing callers are unaffected.

Byte offsets, not character or line offsets.

typescript
getBufferText(bufferId: number, start?: number, end?: number): Promise<string>;

Cursors & Viewport ​

getCursorPosition ​

Get cursor position in active buffer The position is a byte offset, not a character index. Returns 0 if there is no cursor. For multiple cursors, use getAllCursors.

typescript
getCursorPosition(): number;

getPrimaryCursor ​

Get primary cursor info for active buffer The result includes the cursor's selection, if any. Returns null if there is no active cursor.

typescript
getPrimaryCursor(): CursorInfo | null;

getAllCursors ​

Get all cursors for active buffer

typescript
getAllCursors(): CursorInfo[];

getAllCursorPositions ​

Get all cursor positions as byte offsets

Returns an empty array if there are no cursors. For selection info use getAllCursors instead.

typescript
getAllCursorPositions(): number[];

getViewport ​

Get viewport info for active buffer

typescript
getViewport(): ViewportInfo | null;

getCursorLine ​

Get the line number (0-indexed) of the primary cursor.

@deprecated Use getPrimaryCursor()?.line instead. This accessor cannot represent "line index unavailable" (huge files before their line scan) — it returns 0 in that case, indistinguishable from a real first line. getPrimaryCursor().line is number | null and also covers every cursor via getAllCursors().

typescript
getCursorLine(): number;

scrollToLineCenter ​

Scroll a split to center a specific line in the viewport Line is 0-indexed (0 = first line)

typescript
scrollToLineCenter(splitId: number, bufferId: number, line: number): boolean;

scrollBufferToLine ​

Scroll any split/panel showing buffer_id so line is visible. Unlike scrollToLineCenter, this does not require a split id — it updates every split's viewport whose active buffer is the given buffer, including inner leaves of a buffer group. Use this from a panel plugin to keep the user's "selected" row in view after arrow-key navigation (the plugin's own selection state isn't automatically reflected in the buffer cursor, so the core-driven viewport would otherwise stay put).

typescript
scrollBufferToLine(bufferId: number, line: number): boolean;

setBufferCursor ​

Set cursor position in a buffer

The cursor moves in every split showing the buffer, and each viewport scrolls to keep it visible.

typescript
setBufferCursor(bufferId: number, position: number): boolean;
ParameterDescription
positionByte offset position for the cursor

setBufferShowCursors ​

Toggle whether the editor draws a native caret in this buffer.

Buffer-group panel buffers default to show_cursors = false, which also blocks all native movement actions in action_to_events. Plugins that want native cursor motion in a panel (e.g. magit-style row navigation) call this with true after createBufferGroup returns.

typescript
setBufferShowCursors(bufferId: number, show: boolean): boolean;

Text Editing ​

insertText ​

Insert text at a byte position in a buffer.

The text is inserted before the byte at position, and all text after it shifts. The operation is asynchronous: the return value is true if the command was sent, not that the edit was applied.

typescript
insertText(bufferId: number, position: number, text: string): boolean;
ParameterDescription
positionByte offset where text will be inserted (0 to buffer length, at a UTF-8 char boundary)
textUTF-8 text to insert

deleteRange ​

Delete a byte range from a buffer.

Both positions must be at valid UTF-8 char boundaries. The operation is asynchronous: the return value is true if the command was sent, not that the edit was applied.

typescript
deleteRange(bufferId: number, start: number, end: number): boolean;
ParameterDescription
startStart byte offset (inclusive)
endEnd byte offset (exclusive)

insertAtCursor ​

Insert text at cursor position in active buffer

typescript
insertAtCursor(text: string): boolean;

Opening, Saving & Closing ​

saveBufferToPath ​

Save a buffer to a specific file path Used by :w filename to save unnamed buffers or save-as

typescript
saveBufferToPath(bufferId: number, path: string): boolean;

openFile ​

Open a file, optionally at a specific line/column.

editor.openFile(path) is the whole request most of the time.

typescript
openFile(path: string, line?: number | null, column?: number | null): boolean;
ParameterDescription
pathFile path to open
line1-based line number to jump to; omit or pass null for no jump
column1-based column (a byte offset within the line) to jump to; omit or pass null for no jump

openFileInBackground ​

Open a file in the background — no focus change, no active-split mutation. windowId defaults to the active session. Setting it to an inactive session id loads the file's buffer and adds it as a tab in that session's stashed split tree, ready to be revealed on next dive. Orchestrator uses this to populate worktree sessions with preselected files.

Pairs with createTerminal's windowId for setting up an inactive session's contents without diving.

typescript
openFileInBackground(path: string, windowId?: number): boolean;

openFileInSplit ​

Open a file in a specific split

typescript
openFileInSplit(splitId: number, path: string, line?: number, column?: number): boolean;
ParameterDescription
splitIdThe split ID to open the file in
pathFile path to open
line1-based line number to jump to; defaults to the first line
column1-based column (a byte offset within the line) to jump to; defaults to the first column

previewFileInSplit ​

Preview a file in a specific split, as the editor's single preview (ephemeral) tab — what the File Explorer does on a single click, pointed at a split you name.

Use this instead of openFileInSplit while the user is browsing a list of locations — search results, references, diagnostics — and call it again as the selection moves. The previous preview is replaced rather than piling up as tabs, a file the user already had open is switched to and never demoted to a preview, and the buffer becomes a permanent tab as soon as they commit to it (open it, edit it, or move focus to another split). Focus does not move, so the panel or prompt driving the browse keeps the keys.

line / column are 1-indexed and optional. Returns false only when the editor can no longer take commands (for example while it shuts down); a file that cannot be previewed (unreadable, or large enough that loading it would have to ask the user about its encoding) is skipped quietly on the editor side — a browse never raises a dialog. Pair with dismissPreview when the browse ends without a choice.

typescript
previewFileInSplit(splitId: number, path: string, line?: number, column?: number): boolean;

dismissPreview ​

Drop the preview tab opened by previewFileInSplit, if it is still the preview — the browse ended without a choice (the user cancelled the prompt), so the split goes back to what it was showing.

A preview the user edited is kept and promoted to a permanent tab: their typing was the commitment. Safe to call when there is no preview.

typescript
dismissPreview(): boolean;

openFileStreaming ​

Open path as a regular buffer in forced large-file (file-backed) mode. The file is created (empty) if missing — designed for buffers that will be filled by a concurrent spawnProcess with stdoutTo. Resolves with the new buffer's id, or null on failure.

Pair with refreshBufferFromDisk to grow the buffer as the streaming write advances.

typescript
openFileStreaming(path: string): Promise<number | null>;

refreshBufferFromDisk ​

Re-stat the file backing bufferId and extend the buffer if the file has grown. Resolves with the new total byte length, or null if the buffer has no file path or doesn't exist.

Used to drive a streaming display: while a spawnProcess writes to a temp file, the plugin polls this on a timer so the buffer length tracks the file length.

typescript
refreshBufferFromDisk(bufferId: number): Promise<number | null>;

showBuffer ​

Show a buffer in the current split

typescript
showBuffer(bufferId: number): boolean;

closeBuffer ​

Close a buffer. Pass force: true to discard unsaved changes.

A closed buffer is removed from all splits that show it.

A modified buffer is not closed unless force is set — the user's unsaved edits are not a plugin's to throw away. A scratch buffer the plugin created and filled itself counts as modified, so disposing of one needs closeBuffer(id, true).

The returned boolean is "the request was delivered", not "the buffer closed": this call is fire-and-forget, and the editor decides afterwards. A refusal is logged editor-side but is invisible here, so confirm with listBuffers() (after await editor.flush()) when it matters. Without force the sequence that used to be required was delete-the-contents, saveBufferToPath, then close — three round-trips, the first two of which returned true while achieving nothing.

typescript
closeBuffer(bufferId: number, force?: boolean | null): boolean;

closeOtherBuffersInSplit ​

Close other buffers in split

typescript
closeOtherBuffersInSplit(bufferId: number, splitId: number): boolean;

closeAllBuffersInSplit ​

Close all buffers in split

typescript
closeAllBuffersInSplit(splitId: number): boolean;

closeBuffersToRightInSplit ​

Close buffers to right in split

typescript
closeBuffersToRightInSplit(bufferId: number, splitId: number): boolean;

closeBuffersToLeftInSplit ​

Close buffers to left in split

typescript
closeBuffersToLeftInSplit(bufferId: number, splitId: number): boolean;

moveTabToLeft ​

Move the active tab to the left in the active split

typescript
moveTabToLeft(): boolean;

moveTabToRight ​

Move the active tab to the right in the active split

typescript
moveTabToRight(): boolean;

markFileReadOnly ​

Mark the buffer backing path read-only. Race-free right after openFile because both are FIFO commands.

typescript
markFileReadOnly(path: string): boolean;

Clipboard ​

copyToClipboard ​

typescript
copyToClipboard(text: string): void;

setClipboard ​

Copy text to the clipboard. Copies the text to both the internal and the system clipboard. The system copy uses OSC 52 and arboard, as enabled in the clipboard settings.

typescript
setClipboard(text: string): void;

Search & Replace ​

hasActiveSearch ​

Returns true when search highlights are currently active in the buffer. Becomes true after a search is confirmed; false once cleared.

typescript
hasActiveSearch(): boolean;

grepProject ​

Project-wide grep search (async) Searches all files in the project, respecting .gitignore. Open buffers with dirty edits are searched in-memory.

typescript
grepProject(pattern: string, fixedString: boolean | null, caseSensitive: boolean | null, maxResults: number | null, wholeWords: boolean | null): Promise<GrepMatch[]>;

beginSearch ​

Begin a streaming project-wide search and return a SearchHandle. The producer (host) writes matches at full speed into shared state; the consumer drains via handle.take() at its own cadence. Call handle.cancel() to abort.

typescript
beginSearch(pattern: string, opts?: {
  fixedString?: boolean;
  caseSensitive?: boolean;
  maxResults?: number;
  wholeWords?: boolean;
  sourceBufferId?: number;
  fileGlob?: string;
}): SearchHandle;

replaceInFile ​

Replace matches in a file's buffer (async) Opens the file if not already in a buffer, applies edits via the buffer model, and saves. All edits are grouped as a single undo action.

Pass regex — the search the matches came from — to treat replacement as a template: $1, ${name} and \n are expanded per match. Without it, replacement is written as is.

typescript
replaceInFile(filePath: string, matches: number[][], replacement: string, bufferId?: number, regex?: {
  pattern: string;
  caseSensitive?: boolean;
  wholeWords?: boolean;
}): Promise<ReplaceResult>;

Diff Baselines ​

registerDiffBaseline ​

Register a diff baseline for a buffer (async). kind is one of "saved" | "disk" | "gitRef" | "gitIndex"; gitRef carries the ref for kind "gitRef". Resolves with the baseline id once the reference content is loaded host-side — no file content ever crosses the plugin bridge. Baselines are dropped automatically when their buffer closes, or explicitly via releaseDiffBaseline.

typescript
registerDiffBaseline(bufferId: number, kind: string, gitRef: string | null): Promise<number>;

diffAgainstBaseline ​

Diff a buffer's live content against a registered baseline (async). Resolves with a DiffBaselineResult; check its revision against the buffer's current version before anchoring decorations on the hunks.

typescript
diffAgainstBaseline(bufferId: number, baselineId: number): Promise<DiffBaselineResult>;

diffBaselinePair ​

Diff two registered baselines against each other (async) — e.g. disk vs HEAD, the git-gutter comparison. Resolves with a DiffBaselineResult whose revision is 0.

typescript
diffBaselinePair(oldBaselineId: number, newBaselineId: number): Promise<DiffBaselineResult>;

getBaselineLines ​

Fetch baseline lines for (startLine, count) ranges in one batched call (async). Lines come back without trailing newlines, grouped per requested range — fetch only the old-side lines a diff view actually renders.

typescript
getBaselineLines(baselineId: number, ranges: number[][]): Promise<string[][]>;

refreshDiffBaseline ​

Reload a baseline's reference content (async; call after a HEAD move or an external write). Resolves once the fresh content is serving.

typescript
refreshDiffBaseline(baselineId: number): Promise<void>;

releaseDiffBaseline ​

Drop a registered diff baseline.

typescript
releaseDiffBaseline(baselineId: number): void;

Released under the Apache 2.0 License