docs/quickjs.md
Fresh uses QuickJS for its JavaScript plugin runtime (with oxc_transformer for TypeScript transpilation).
Benefits:
| Component | Status |
|---|---|
| QuickJS runtime (rquickjs 0.11) | ✅ Complete |
| TypeScript transpilation (oxc 0.108) | ✅ Complete |
| ES module bundling | ✅ Complete |
| Proc macro TypeScript generation | ✅ Complete |
| ts-rs type export integration | ✅ Complete |
| Plugin API methods | ⚠️ 75/122 methods (61%) |
| Async operations (tokio) | ✅ Complete |
Test coverage: 52 unit tests + 23 e2e tests passing
The plugin API is exposed via JsEditorApi using rquickjs class bindings with automatic camelCase conversion.
Key patterns:
#[rquickjs::class] - Expose struct to JS#[rquickjs::methods(rename_all = "camelCase")] - Auto-convert method namesrquickjs::function::Opt<T> - Optional parametersrquickjs::function::Rest<T> - Variadic argumentsrquickjs_serde::to_value() - Rust → JS conversionAsync methods use a callback-based pattern:
_xxxStart() → returns callbackIdPluginCommand to appresolve_callback(id, result)src/services/plugins/
├── backend/quickjs_backend.rs # JsEditorApi implementation
├── api.rs # PluginCommand, EditorStateSnapshot
├── transpile.rs # TypeScript → JS
└── thread.rs # Plugin thread runner
crates/fresh-plugin-api-macros/ # TypeScript definition generation
plugins/lib/fresh.d.ts # Generated TypeScript definitions
rquickjs 0.11 - QuickJS bindingsrquickjs-serde 0.4 - Serde integrationoxc_* 0.108 - TypeScript transpilationfresh-plugin-api-macros - Proc macrosThe following methods need to be added to JsEditorApi in quickjs_backend.rs:
Priority 1 - Core Functionality:
getBufferInfo(buffer_id) → BufferInfo | nullgetPrimaryCursor() → CursorInfo | nullgetAllCursors() → CursorInfo[]getViewport() → ViewportInfo | nullgetCursorLine() → numbergetAllCursorPositions() → number[]findBufferByPath(path) → numbergetBufferSavedDiff(buffer_id) → TsBufferSavedDiff | nullPriority 2 - Virtual Text/Overlays:
addVirtualLine(buffer_id, position, text, fg_rgb, bg_rgb, above, namespace, priority)addVirtualText(buffer_id, id, position, text, rgb, before, use_bg)removeVirtualText(buffer_id, id)removeVirtualTextsByPrefix(buffer_id, prefix)clearVirtualTexts(buffer_id)clearVirtualTextNamespace(buffer_id, namespace)removeOverlay(buffer_id, handle)clearOverlaysInRange(buffer_id, start, end)Priority 3 - View Transforms:
submitViewTransform(buffer_id, split_id, start, end, tokens, layout_hints)clearViewTransform(buffer_id, split_id)Priority 4 - Composite Buffers:
createCompositeBuffer(options) → Promise<number>updateCompositeAlignment(buffer_id, hunks)closeCompositeBuffer(buffer_id)Priority 5 - Scroll Sync:
createScrollSyncGroup(group_id, left_split, right_split)setScrollSyncAnchors(group_id, anchors)removeScrollSyncGroup(group_id)Priority 6 - Split Operations:
createVirtualBufferInExistingSplit(options) → Promise<number>setSplitScroll(split_id, top_byte)setSplitRatio(split_id, ratio)distributeSplitsEvenly()setLineNumbers(buffer_id, enabled)Priority 7 - File Explorer:
setFileExplorerDecorations(namespace, decorations)clearFileExplorerDecorations(namespace)Priority 8 - Diagnostics/LSP:
getAllDiagnostics() → TsDiagnostic[]getHighlights(buffer_id, start, end) → Promise<TsHighlightSpan[]>disableLspForLanguage(language)Priority 9 - Process Management:
isProcessRunning(process_id) → booleanspawnProcessWait(process_id) → Promise<SpawnResult>killProcess(process_id) → Promise<boolean>Priority 10 - UI:
showActionPopup(options) → Promise<ActionPopupResult>deleteTheme(name) → Promise<void>Priority 11 - Misc:
executeActions(actions) → boolean (batch execution)getHandlers(event_name) → string[]pluginTranslate(plugin_name, key, args) → stringfileStat(path) → FileStatAdd to api.rs with #[derive(TS)] and register in ts_export.rs:
Core: BufferInfo, CursorInfo, ViewportInfo, SelectionRange, LayoutHints Virtual Buffers: CreateVirtualBufferOptions, CreateVirtualBufferInCurrentSplitOptions, CreateVirtualBufferInExistingSplitOptions, CreateVirtualBufferResult, TextPropertyEntry Composite: TsCompositeLayoutConfig, TsCompositeSourceConfig, TsCompositePaneStyle, TsCompositeHunk, CreateCompositeBufferOptions View Transform: ViewTokenWire, ViewTokenWireKind Diagnostics: TsDiagnostic, TsDiagnosticPosition, TsDiagnosticRange, TsBufferSavedDiff UI: TsActionPopupAction, TsActionPopupOptions, PromptSuggestion, ActionSpecJs File System: DirEntry, FileStat, FileExplorerDecoration
These methods exist but have incompatible signatures:
read_only parameterargs?: Record<string, string>PromptSuggestion[]TextPropertyEntry[]unknown | nullrquickjs supports native async via AsyncRuntime/AsyncContext and the Promised wrapper. This could replace the _xxxStart + JS wrapper pattern but would require architectural changes. The current callback-based pattern works well.