Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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 TypeC TypeNotes
Stringsint64_tNaN-boxed StringHeader pointer, allocated by host
Arraysint64_tNaN-boxed array pointer
Booleansint32_t0 or 1
IDsint32_tOpaque handle (decoration types, status bar items, panels)
Complex structsint64_tPointer to host-allocated struct
VoidvoidNo return value

Plugin Lifecycle Functions

The host expects these exported symbols from the plugin binary:

SymbolWhen CalledPurpose
hone_plugin_initLoad timeReceives host API pointer
hone_plugin_activateAfter initPlugin initializes state
hone_plugin_deactivateBefore unloadPlugin cleans up resources

Hook Functions

The host calls optional hook functions when relevant events occur. Plugins export only the hooks they handle:

SymbolEvent
hone_plugin_on_document_openDocument opened in editor
hone_plugin_on_document_closeDocument closed
hone_plugin_on_document_changeDocument content changed
hone_plugin_on_document_saveDocument saved
hone_plugin_on_document_formatFormat request
hone_plugin_on_commandRegistered command invoked
hone_plugin_on_selection_changeCursor/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 StringHeader pointers. Rust receives *const u8 and uses str_from_header().
  • Perry generates __wrapper_<function_name> symbols (double underscore prefix).
  • All FFI functions must be listed in package.json under perry.nativeLibrary.functions.
  • Use f64 for numeric FFI parameters and i64 for string/pointer parameters. Using i32 causes verifier errors.