Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions default_config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -168,3 +168,4 @@ Esc = { EnterMode = "Normal" }
[plugins]
buffer_picker = "buffer_picker.js"
neotree = "neotree.js"
session_restore = "session_restore.js"
26 changes: 25 additions & 1 deletion docs/PLUGIN_SYSTEM.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,11 @@ View loaded plugins with the `dp` keybinding or `ListPlugins` command.
1. Create a JavaScript or TypeScript file that exports an `activate` function:

**Plugin Lifecycle:**
- `activate(red)` - Called when the plugin is loaded
- `activate(red)` - Called when the plugin is loaded. Async activation is
supported, but startup does not wait for it to finish.
- `deactivate(red)` - Optional, called when the plugin is unloaded
- `beforeExit(red, state)` - Optional, awaited after quit succeeds and before
plugin deactivation. `state` is the current editor session snapshot.

```javascript
export async function activate(red) {
Expand Down Expand Up @@ -128,6 +131,7 @@ Subscribes to editor events. Available events include:
- `cursor:moved` - Cursor position changes (may fire frequently)
- `file:opened` - File opened in a buffer
- `file:saved` - File saved from a buffer
- `editor:ready` - Plugins have loaded and startup work can begin

#### Editor Information
```javascript
Expand All @@ -139,6 +143,26 @@ Returns an object containing:
- `size` - Editor dimensions (rows, cols)
- `theme` - Current theme information

#### Session State
```javascript
const state = await red.getEditorState()
const result = await red.restoreEditorState(state)
```

The snapshot includes file-backed buffers, cursor and viewport positions, the
active buffer, cwd, and window split layout. Restore skips missing files and
returns `{ restored, openedFiles, skippedFiles, warnings }`.

#### Plugin Storage
```javascript
await red.storage.set("latest", state)
const state = await red.storage.get("latest")
await red.storage.delete("latest")
```

Storage is JSON, namespaced by plugin, and written under Red's config state
directory.

#### UI Interaction
```javascript
// Show a picker dialog
Expand Down
53 changes: 53 additions & 0 deletions plugins/session_restore.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
const STORAGE_KEY = "latest";

let redApi = null;

export async function activate(red) {
redApi = red;

red.on("editor:ready", async () => {
try {
const startupFileCount = await red.getConfig("startup_file_count");
if (startupFileCount > 0) {
return;
}

const snapshot = await red.storage.get(STORAGE_KEY);
if (!snapshot || snapshot.version !== 1) {
return;
}

const cwd = await red.getConfig("cwd");
if (snapshot.cwd && cwd && snapshot.cwd !== cwd) {
red.logInfo("Session restore skipped: saved cwd differs from current cwd");
return;
}

const result = await red.restoreEditorState(snapshot);
if (!result.restored) {
red.logWarn("Session restore did not restore files", result.warnings);
}
for (const skipped of result.skippedFiles || []) {
red.logWarn("Session restore skipped file", skipped.path, skipped.reason);
}
} catch (error) {
red.logError("Session restore failed", error?.message || error);
}
});
}

export async function beforeExit(red, state) {
const api = red || redApi;
if (!api || !state) return;

const cleanState = {
...state,
buffers: (state.buffers || []).filter((buffer) => buffer.path && !buffer.dirty),
};

await api.storage.set(STORAGE_KEY, cleanState);
}

export function deactivate() {
redApi = null;
}
2 changes: 2 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ pub struct Config {
pub show_diagnostics: bool,
#[serde(default = "default_false")]
pub window_borders_ascii: bool,
#[serde(default, skip_serializing)]
pub startup_file_count: usize,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
Expand Down
Loading
Loading