> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/sinelaw/fresh/llms.txt
> Use this file to discover all available pages before exploring further.

# Virtual Buffers

> Create and manage virtual buffers for displaying structured data

Virtual buffers are special buffers created by plugins to display structured data like search results, diagnostics, git logs, or file explorers. Unlike regular buffers, they're typically read-only and contain metadata (text properties) that plugins can query.

## Creating Virtual Buffers

### createVirtualBufferInSplit

Create a virtual buffer in a new split below the current pane.

```typescript theme={null}
createvirtualBufferInSplit(options: CreateVirtualBufferOptions): Promise<CreateVirtualBufferResult>
```

<ParamField path="options" type="CreateVirtualBufferOptions" required>
  Buffer configuration object
</ParamField>

<ResponseField name="returns" type="Promise<CreateVirtualBufferResult>">
  Result containing buffer\_id and optional split\_id
</ResponseField>

**CreateVirtualBufferOptions:**

```typescript theme={null}
interface CreateVirtualBufferOptions {
  name: string;                    // Buffer name (convention: "*Name*")
  mode: string;                    // Mode for keybindings
  read_only: boolean;              // Prevent text modifications
  entries: TextPropertyEntry[];    // Content with embedded metadata
  ratio: number;                   // Split ratio (0.3 = 30% of space)
  direction?: string | null;       // "horizontal" or "vertical"
  panel_id?: string | null;        // For idempotent updates
  show_line_numbers?: boolean | null;    // Show line numbers
  show_cursors?: boolean | null;         // Show cursor
  editing_disabled?: boolean | null;     // Disable editing
  line_wrap?: boolean | null;            // Enable line wrapping
}
```

**TextPropertyEntry:**

```typescript theme={null}
interface TextPropertyEntry {
  text: string;                    // Text to display (include \n for separate lines)
  properties: Record<string, unknown>;  // Arbitrary metadata
}
```

<Note>
  The `panel_id` enables idempotent updates: if a panel with that ID exists, its content is replaced instead of creating a new split. Define the mode with `defineMode` first.
</Note>

**Example:**

```typescript theme={null}
// First define the mode with keybindings
editor.defineMode("search-results", "special", [
  ["Return", "search_goto"],
  ["q", "close_buffer"]
], true);

// Then create the buffer
const result = await editor.createVirtualBufferInSplit({
  name: "*Search*",
  mode: "search-results",
  read_only: true,
  entries: [
    {
      text: "src/main.rs:42: match found\n",
      properties: { file: "src/main.rs", line: 42 }
    },
    {
      text: "src/lib.rs:15: another match\n",
      properties: { file: "src/lib.rs", line: 15 }
    }
  ],
  ratio: 0.3,
  panel_id: "search"
});

editor.debug(`Created buffer ${result.buffer_id}`);
```

### createVirtualBufferInExistingSplit

Create a virtual buffer in an existing split.

```typescript theme={null}
createVirtualBufferInExistingSplit(options: CreateVirtualBufferInExistingSplitOptions): Promise<number>
```

<ParamField path="options" type="CreateVirtualBufferInExistingSplitOptions" required>
  Configuration for the virtual buffer
</ParamField>

<ResponseField name="returns" type="Promise<number>">
  The created buffer ID
</ResponseField>

**CreateVirtualBufferInExistingSplitOptions:**

```typescript theme={null}
interface CreateVirtualBufferInExistingSplitOptions {
  name: string;
  mode: string;
  read_only: boolean;
  entries: TextPropertyEntry[];
  split_id: number;               // Target split ID
  show_line_numbers?: boolean | null;
  show_cursors?: boolean | null;
  editing_disabled?: boolean | null;
  line_wrap?: boolean | null;
}
```

**Example:**

```typescript theme={null}
const splitId = editor.getActiveSplitId();
const bufferId = await editor.createVirtualBufferInExistingSplit({
  name: "*Output*",
  mode: "output-mode",
  read_only: true,
  entries: [
    { text: "Build output:\n", properties: {} },
    { text: "Success!\n", properties: { status: "success" } }
  ],
  split_id: splitId
});
```

### createVirtualBuffer

Create a virtual buffer in the current split as a new tab.

```typescript theme={null}
createVirtualBuffer(options: CreateVirtualBufferInCurrentSplitOptions): Promise<number>
```

<ParamField path="options" type="CreateVirtualBufferInCurrentSplitOptions" required>
  Configuration for the virtual buffer
</ParamField>

<ResponseField name="returns" type="Promise<number>">
  The created buffer ID
</ResponseField>

**CreateVirtualBufferInCurrentSplitOptions:**

```typescript theme={null}
interface CreateVirtualBufferInCurrentSplitOptions {
  name: string;
  mode: string;
  read_only: boolean;
  entries: TextPropertyEntry[];
  show_line_numbers?: boolean | null;
  show_cursors?: boolean | null;
  editing_disabled?: boolean | null;
  hidden_from_tabs?: boolean | null;  // Hide from tabs
}
```

**Example:**

```typescript theme={null}
// Create help buffer in current split
const bufferId = await editor.createVirtualBuffer({
  name: "*Help*",
  mode: "help-mode",
  read_only: true,
  entries: [
    { text: "# Help\n", properties: {} },
    { text: "Press q to close\n", properties: {} }
  ],
  show_line_numbers: false
});
```

## Managing Virtual Buffers

### setVirtualBufferContent

Set the content of a virtual buffer with text properties.

```typescript theme={null}
setVirtualBufferContent(buffer_id: number, entries: TextPropertyEntry[]): boolean
```

<ParamField path="buffer_id" type="number" required>
  ID of the virtual buffer
</ParamField>

<ParamField path="entries" type="TextPropertyEntry[]" required>
  Array of text entries with properties
</ParamField>

<ResponseField name="returns" type="boolean">
  `true` if content was set successfully
</ResponseField>

**Example:**

```typescript theme={null}
// Update search results
editor.setVirtualBufferContent(bufferId, [
  { text: "Updated results:\n", properties: {} },
  { text: "src/new.rs:10: found\n", properties: { file: "src/new.rs", line: 10 } }
]);
```

### getTextPropertiesAtCursor

Get text properties at the cursor position in a buffer.

```typescript theme={null}
getTextPropertiesAtCursor(buffer_id: number): Record<string, unknown>[]
```

<ParamField path="buffer_id" type="number" required>
  ID of the buffer to query
</ParamField>

<ResponseField name="returns" type="Record<string, unknown>[]">
  Array of property objects at cursor position
</ResponseField>

**Example:**

```typescript theme={null}
// Implement "go to definition" from search results
globalThis.searchGoto = () => {
  const bufferId = editor.getActiveBufferId();
  const props = editor.getTextPropertiesAtCursor(bufferId);
  
  if (props.length > 0 && props[0].file && props[0].line) {
    editor.openFile(props[0].file as string, props[0].line as number, 0);
  }
};
```

## Modes and Keybindings

### defineMode

Define a buffer mode with keybindings.

```typescript theme={null}
defineMode(
  name: string,
  parent: string,
  bindings: [string, string][],
  read_only: boolean
): boolean
```

<ParamField path="name" type="string" required>
  Mode name (e.g., "diagnostics-list")
</ParamField>

<ParamField path="parent" type="string" required>
  Parent mode name for inheritance (e.g., "special"), or null
</ParamField>

<ParamField path="bindings" type="[string, string][]" required>
  Array of \[key\_string, command\_name] pairs
</ParamField>

<ParamField path="read_only" type="boolean" required>
  Whether buffers in this mode are read-only
</ParamField>

<ResponseField name="returns" type="boolean">
  `true` if mode was defined successfully
</ResponseField>

**Example:**

```typescript theme={null}
editor.defineMode("diagnostics-list", "special", [
  ["Return", "diagnostics_goto"],
  ["n", "diagnostics_next"],
  ["p", "diagnostics_prev"],
  ["q", "close_buffer"]
], true);
```

## Split Management

### getActiveSplitId

Get the ID of the focused split pane.

```typescript theme={null}
getActiveSplitId(): number
```

<ResponseField name="returns" type="number">
  Active split ID
</ResponseField>

### focusSplit

Focus a specific split.

```typescript theme={null}
focusSplit(split_id: number): boolean
```

<ParamField path="split_id" type="number" required>
  ID of the split to focus
</ParamField>

<ResponseField name="returns" type="boolean">
  `true` if successful
</ResponseField>

### setSplitBuffer

Set the buffer displayed in a specific split.

```typescript theme={null}
setSplitBuffer(split_id: number, buffer_id: number): boolean
```

<ParamField path="split_id" type="number" required>
  ID of the split
</ParamField>

<ParamField path="buffer_id" type="number" required>
  ID of the buffer to display
</ParamField>

<ResponseField name="returns" type="boolean">
  `true` if successful
</ResponseField>

### closeSplit

Close a split (if not the last one).

```typescript theme={null}
closeSplit(split_id: number): boolean
```

<ParamField path="split_id" type="number" required>
  ID of the split to close
</ParamField>

<ResponseField name="returns" type="boolean">
  `true` if successful
</ResponseField>

### setSplitRatio

Set the ratio of a split container.

```typescript theme={null}
setSplitRatio(split_id: number, ratio: number): boolean
```

<ParamField path="split_id" type="number" required>
  ID of the split
</ParamField>

<ParamField path="ratio" type="number" required>
  Ratio between 0.0 and 1.0 (0.5 = equal split)
</ParamField>

<ResponseField name="returns" type="boolean">
  `true` if successful
</ResponseField>

### distributeSplitsEvenly

Distribute all visible splits evenly.

```typescript theme={null}
distributeSplitsEvenly(): boolean
```

<ResponseField name="returns" type="boolean">
  `true` if successful
</ResponseField>

This adjusts the ratios of all container splits so each leaf split gets equal space.

## Complete Example: Search Results Panel

```typescript theme={null}
// Define the mode
editor.defineMode("search-results", "special", [
  ["Return", "search_goto"],
  ["n", "search_next"],
  ["p", "search_prev"],
  ["q", "close_buffer"]
], true);

// Search function
async function performSearch(query: string) {
  // Run search command
  const result = await editor.spawnProcess("rg", [
    "--line-number",
    "--no-heading",
    query
  ]);
  
  if (result.exit_code !== 0) {
    editor.setStatus("No matches found");
    return;
  }
  
  // Parse results
  const entries: TextPropertyEntry[] = [];
  for (const line of result.stdout.split("\n")) {
    if (!line) continue;
    
    const match = line.match(/^([^:]+):(\d+):(.*)$/);
    if (match) {
      const [, file, lineNum, text] = match;
      entries.push({
        text: `${file}:${lineNum}: ${text}\n`,
        properties: {
          file: file,
          line: parseInt(lineNum)
        }
      });
    }
  }
  
  // Create or update panel
  const panel = await editor.createVirtualBufferInSplit({
    name: "*Search*",
    mode: "search-results",
    read_only: true,
    entries: entries,
    ratio: 0.3,
    panel_id: "search"  // Reuse existing panel
  });
  
  editor.setStatus(`Found ${entries.length} matches`);
}

// Go to selected result
globalThis.search_goto = () => {
  const bufferId = editor.getActiveBufferId();
  const props = editor.getTextPropertiesAtCursor(bufferId);
  
  if (props.length > 0 && props[0].file && props[0].line) {
    editor.openFile(props[0].file as string, props[0].line as number, 0);
  }
};

// Register command
editor.registerCommand(
  "Search: Grep",
  "Search for text in files",
  "search_grep",
  "normal",
  "plugin"
);

globalThis.search_grep = () => {
  editor.startPrompt("Search: ", "search-input");
};

// Handle prompt submission
globalThis.handleSearchPrompt = async (data) => {
  if (data.prompt_type === "search-input" && data.confirmed) {
    await performSearch(data.value);
  }
};

editor.on("prompt_submit", "handleSearchPrompt");
```
