enya-plugin
A host-agnostic plugin system for extending application functionality, inspired by neovim's plugin architecture.
Overview
This crate provides the core infrastructure for a plugin system that can be embedded in any Rust application. It's designed to be completely decoupled from any specific host implementation through the PluginHost trait.
Key Features
- Host-agnostic: Works with any application that implements
PluginHost - Lua scripting: Write plugins in Lua with conditional logic, validation, and HTTP requests
- Native plugins: Implement the
Plugintrait in Rust for maximum performance - Capability-based: Plugins declare what features they provide
- Hook system: Intercept and extend host behavior
- Custom themes: Plugins can define complete color schemes
- Lifecycle management: Full control over plugin initialization, activation, and cleanup
Architecture
┌──────────────────────────────────────────────────────┐
│ Host Application │
│ (implements PluginHost trait) │
└──────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────┐
│ PluginContext │
│ (provides host services to plugins) │
└──────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────┐
│ PluginRegistry │
│ (manages plugin lifecycle) │
└──────────────────────────────────────────────────────┘
│
┌─────────┴─────────┐
▼ ▼
┌──────────┐ ┌──────────┐
│ Lua │ │ Native │
│ (.lua) │ │ (Rust) │
└──────────┘ └──────────┘
Quick Start
1. Implement PluginHost for your application
use ;
use FxHashMap;
use Arc;
2. Set up the plugin registry
use ;
3. Execute plugin commands
// Execute a command provided by any active plugin
if registry.execute_command
// Get all commands from active plugins
for in registry.all_commands
Lua Plugins
Lua plugins provide a dynamic scripting environment with full access to conditional logic, input validation, and HTTP requests.
Basic Example
plugin =
enya.
enya.
-- Lifecycle hooks
Lua API
Registration (during load):
enya.register_command(name, config, callback)- Register a commandenya.keymap(keys, command, description, [modes])- Register a keybinding
Runtime (in callbacks):
enya.notify(level, message)- Show notification ("info", "warn", "error")enya.log(level, message)- Log a messageenya.request_repaint()- Request UI refreshenya.editor_version()- Get host versionenya.is_wasm()- Check if running in WASMenya.theme_name()- Get current theme nameenya.clipboard_write(text)- Write to clipboardenya.clipboard_read()- Read from clipboardenya.execute(command, [args])- Execute another commandenya.http_get(url, [headers])- HTTP GET requestenya.http_post(url, body, [headers])- HTTP POST request
Pane Management:
enya.add_query_pane(query, [title])- Add a query pane with PromQL queryenya.add_logs_pane()- Add a logs pane with current time rangeenya.add_tracing_pane([trace_id])- Add a tracing paneenya.add_terminal_pane()- Add a terminal pane (native only)enya.add_sql_pane()- Add a SQL paneenya.close_pane()- Close the focused paneenya.focus_pane(direction)- Focus pane in direction ("left", "right", "up", "down")
Time Range:
enya.set_time_range(preset)- Set time range preset ("5m", "1h", "24h", etc.)enya.set_time_range_absolute(start, end)- Set absolute time range (seconds)enya.get_time_range()- Get current time range as{start, end}(seconds)
Custom Themes in Lua
plugin =
theme =
Native Plugins (Rust)
For maximum performance or deep integration, implement the Plugin trait:
use ;
use Any;
Custom Themes in Rust
use ;
Plugin Capabilities
Plugins declare their capabilities using bitflags:
| Capability | Description |
|---|---|
COMMANDS |
Provides command palette commands |
PANES |
Provides custom pane types |
KEYBOARD |
Provides custom keybindings |
THEMING |
Reacts to theme changes |
CUSTOM_THEMES |
Defines custom color themes |
VISUALIZATIONS |
Provides custom chart types |
DATA_SOURCES |
Provides backend integrations |
AGENT_COMMANDS |
Provides AI integration commands |
STATUS_LINE |
Provides status line segments |
FINDER_SOURCES |
Provides unified finder sources |
Hook System
Plugins can intercept and extend host behavior through hooks:
Lifecycle Hooks
Command Hooks
Keyboard Hooks
Theme Hooks
Pane Hooks
Plugin Lifecycle
Registration ──► Initialization ──► Activation ──► Runtime ──► Deactivation
│ │ │ │ │
│ │ │ │ │
▼ ▼ ▼ ▼ ▼
Registered Inactive Active Hooks Inactive
state state state dispatch state
- Registration: Plugin added to registry (
PluginState::Registered) - Initialization:
init()called, metadata collected (PluginState::Inactive) - Activation:
activate()called (PluginState::Active) - Runtime: Commands executed, hooks dispatched
- Deactivation:
deactivate()called (PluginState::Inactive)
Platform Support
| Feature | Native | WASM |
|---|---|---|
| Lua plugins | ✓ | ✗ |
| Native plugins | ✓ | ✓ |
| Custom themes | ✓ | ✓ |
| HTTP requests | ✓ | Host-dependent |
| Clipboard | ✓ | Host-dependent |
| Shell commands | ✓ | ✗ |
Module Structure
src/
├── lib.rs # Crate entry, re-exports
├── types.rs # Core types (PluginHost, PluginContext, etc.)
├── traits.rs # Plugin trait, capabilities, configs
├── registry.rs # PluginRegistry, lifecycle management
├── hooks.rs # Hook traits (Lifecycle, Command, Keyboard, etc.)
├── theme.rs # ThemeDefinition, ThemeColors
├── loader.rs # PluginLoader, filesystem discovery (native only)
└── lua.rs # LuaPlugin, Lua API (native only)
License
MIT