Host API (C ABI)
The plugin host communicates with plugins through a C ABI struct of function pointers. This page documents the raw interface between the host and plugin binaries.
Entry Point
Every plugin compiled by Perry exposes a single initialization function:
void hone_plugin_init(HoneHostAPI* host);
The host passes a HoneHostAPI struct populated with function pointers. Function pointers for undeclared capabilities are null.
HoneHostAPI Struct
typedef struct {
// ── Always available ──────────────────────────────────────
void (*log)(int32_t level, const uint8_t* msg, uint32_t msg_len);
int64_t (*get_config)(const uint8_t* key, uint32_t key_len);
int64_t (*get_workspace_path)(void);
// ── editor.read ───────────────────────────────────────────
int64_t (*buffer_get_text)(int64_t buffer_id);
int64_t (*buffer_get_lines)(int64_t buffer_id, int32_t start, int32_t end);
int64_t (*buffer_get_selection)(int64_t buffer_id);
int64_t (*buffer_get_selections)(int64_t buffer_id);
int64_t (*buffer_get_language_id)(int64_t buffer_id);
int64_t (*buffer_get_file_path)(int64_t buffer_id);
int32_t (*buffer_get_line_count)(int64_t buffer_id);
int64_t (*get_active_buffer_id)(void);
int64_t (*get_open_buffer_ids)(void);
// ── editor.write ──────────────────────────────────────────
int64_t (*buffer_submit_edits)(int64_t buffer_id, int64_t edits_ptr);
void (*buffer_set_selection)(int64_t buffer_id, int64_t sel_ptr);
void (*buffer_set_selections)(int64_t buffer_id, int64_t sels_ptr);
// ── editor.decorations ────────────────────────────────────
int32_t (*create_decoration_type)(int64_t opts_ptr);
void (*set_decorations)(int64_t buffer_id, int32_t type_id, int64_t ranges_ptr);
void (*clear_decorations)(int32_t type_id);
// ── filesystem.read ───────────────────────────────────────
int64_t (*file_read_text)(const uint8_t* path, uint32_t path_len);
int32_t (*file_exists)(const uint8_t* path, uint32_t path_len);
int64_t (*file_stat)(const uint8_t* path, uint32_t path_len);
int64_t (*directory_list)(const uint8_t* path, uint32_t path_len);
// ── filesystem.write ──────────────────────────────────────
void (*file_write_text)(const uint8_t* path, uint32_t path_len,
const uint8_t* content, uint32_t content_len);
void (*file_delete)(const uint8_t* path, uint32_t path_len);
void (*directory_create)(const uint8_t* path, uint32_t path_len,
int32_t recursive);
// ── process.spawn ─────────────────────────────────────────
int64_t (*spawn)(const uint8_t* cmd, uint32_t cmd_len,
int64_t args_ptr, int64_t opts_ptr);
// ── network ───────────────────────────────────────────────
int64_t (*http_request)(int64_t req_ptr);
// ── ui.statusbar ──────────────────────────────────────────
int32_t (*statusbar_create_item)(int64_t opts_ptr);
void (*statusbar_update_item)(int32_t id, int64_t opts_ptr);
void (*statusbar_remove_item)(int32_t id);
// ── ui.panel ──────────────────────────────────────────────
int32_t (*panel_create)(int64_t opts_ptr);
void (*panel_update)(int32_t id, int64_t content_ptr);
void (*panel_dispose)(int32_t id);
// ── ui.commandPalette ─────────────────────────────────────
void (*command_register)(const uint8_t* id, uint32_t id_len,
const uint8_t* title, uint32_t title_len);
void (*command_unregister)(const uint8_t* id, uint32_t id_len);
// ── ui.notifications ──────────────────────────────────────
void (*notify)(int64_t opts_ptr);
} HoneHostAPI;
String Encoding
All strings in the C ABI are UTF-8 encoded with explicit length. There are no null terminators.
Perry uses NaN-boxed StringHeader pointers internally. On the Rust side, the host extracts strings using str_from_header(), which reads the pointer and length from the NaN-boxed value.
Passing strings to the host: Provide a const uint8_t* pointer and a uint32_t length.
Receiving strings from the host: The host returns a NaN-boxed StringHeader pointer (int64_t). Use Perry’s string utilities to read the content.
Return Value Encoding
| Return Type | C Type | Notes |
|---|---|---|
| Strings | int64_t | NaN-boxed StringHeader pointer, allocated by host |
| Arrays | int64_t | NaN-boxed array pointer |
| Booleans | int32_t | 0 or 1 |
| IDs | int32_t | Opaque handle (decoration types, status bar items, panels) |
| Complex structs | int64_t | Pointer to host-allocated struct |
| Void | void | No return value |
Plugin Lifecycle Functions
The host expects these exported symbols from the plugin binary:
| Symbol | When Called | Purpose |
|---|---|---|
hone_plugin_init | Load time | Receives host API pointer |
hone_plugin_activate | After init | Plugin initializes state |
hone_plugin_deactivate | Before unload | Plugin cleans up resources |
Hook Functions
The host calls optional hook functions when relevant events occur. Plugins export only the hooks they handle:
| Symbol | Event |
|---|---|
hone_plugin_on_document_open | Document opened in editor |
hone_plugin_on_document_close | Document closed |
hone_plugin_on_document_change | Document content changed |
hone_plugin_on_document_save | Document saved |
hone_plugin_on_document_format | Format request |
hone_plugin_on_command | Registered command invoked |
hone_plugin_on_selection_change | Cursor/selection changed |
Each hook receives an int64_t pointer to an event struct allocated by the host. The plugin reads event data from this struct and must not free it.
FFI Conventions
These Perry FFI rules apply to all plugin code:
- String parameters are NaN-boxed
StringHeaderpointers. Rust receives*const u8and usesstr_from_header(). - Perry generates
__wrapper_<function_name>symbols (double underscore prefix). - All FFI functions must be listed in
package.jsonunderperry.nativeLibrary.functions. - Use
f64for numeric FFI parameters andi64for string/pointer parameters. Usingi32causes verifier errors.