pub struct SessionRuntime<'a> { /* private fields */ }Expand description
Runtime that implements all session API traits.
Bundles Session + KernelContext + CommandExecutor.
Changes accumulate internally; runner takes at end via take_changes.
§Compositor Integration
The compositor is accessed via session.compositor. When present,
CompositorApi methods delegate to it. When absent, they return errors.
§Per-Client State (#471, #477)
SessionRuntime ALWAYS operates on per-client state. The per-client fields
are required (no Option wrappers, no fallback to shared state):
mode_stack(#471): Per-client mode (INSERT, NORMAL, etc.)windows(#471): Per-client cursor positionsextensions(#477): Per-client module state (VimSessionState, etc.)
This enforces multi-client isolation at compile time - you cannot create
a SessionRuntime without providing per-client state.
§Client Binding (#471)
The owner field tracks which client this runtime is bound to. When present,
it makes explicit which client’s state we’re operating on. This is useful for:
- Debugging and logging
- Assertions in tests
- Future multi-client coordination
Implementations§
Source§impl<'a> SessionRuntime<'a>
impl<'a> SessionRuntime<'a>
Sourcepub fn new(
session: &'a mut Session,
client: ClientContext<'a>,
kernel: &'a KernelContext,
executor: &'a dyn CommandExecutor,
) -> Self
pub fn new( session: &'a mut Session, client: ClientContext<'a>, kernel: &'a KernelContext, executor: &'a dyn CommandExecutor, ) -> Self
Create a new runtime with per-client state (#471, #477).
All per-client state is required. There are no fallbacks to shared session state. This enforces multi-client isolation at compile time.
§Arguments
session- Shared session state (compositor, home mode)mode_stack- Per-client mode stack (source of truth for mode)windows- Per-client window layout with cursorsextensions- Per-client module extensionskernel- Kernel context (buffers, registers, marks)executor- Command executor
§Multi-Client Isolation
Each client has independent:
- Mode state (Client A in INSERT while Client B in NORMAL)
- Cursor positions (Client A at line 5, Client B at line 10)
- Module state (Client A’s
pending_countdoesn’t affect Client B)
§Example
// From server level, get per-client EditingState
let editing_state = session.client_state_mut(client_id)?;
// Create runtime with per-client state bundle
let client = editing_state.client_context();
let mut runtime = SessionRuntime::new(
&mut driver_session,
client,
&kernel,
&executor,
);
// All operations use per-client state
runtime.push_mode(insert_mode, ctx); // Only affects this client
runtime.windows().active(); // This client's active window
runtime.ext_mut::<VimSessionState>(); // This client's vim stateSourcepub fn with_owner(
owner: ClientId,
session: &'a mut Session,
client: ClientContext<'a>,
kernel: &'a KernelContext,
executor: &'a dyn CommandExecutor,
) -> Self
pub fn with_owner( owner: ClientId, session: &'a mut Session, client: ClientContext<'a>, kernel: &'a KernelContext, executor: &'a dyn CommandExecutor, ) -> Self
Create a runtime with explicit client binding (#471).
Like new, but also records which client this runtime is bound to.
The owner field can be retrieved via owner().
§Arguments
owner- TheClientIdthis runtime is bound tosession- Shared session infrastructureclient- Per-client state bundle (mode, windows, extensions, registers, etc.)kernel- Kernel context (buffers, global marks)executor- Command executor
§Example
let client = editing_state.client_context();
let mut runtime = SessionRuntime::with_owner(
client_id,
&mut driver_session,
client,
&kernel,
&executor,
);
// Query which client owns this runtime
assert_eq!(runtime.owner(), Some(client_id));Sourcepub const fn owner(&self) -> Option<ClientId>
pub const fn owner(&self) -> Option<ClientId>
Get the client ID this runtime is bound to (#471).
Returns Some(ClientId) if created with with_owner, None otherwise.
Use this for debugging, logging, or assertions about which client owns
this runtime.
§Example
let runtime = SessionRuntime::with_owner(client_id, ...);
assert_eq!(runtime.owner(), Some(client_id));
let shared_runtime = SessionRuntime::new(...);
assert_eq!(shared_runtime.owner(), None);Set shared extensions for session-wide state access (#543).
Called by server code that has access to AppState.extensions.
Enables shared_ext() / shared_ext_mut() in commands.
Sourcepub fn signal(&mut self, signal: RuntimeSignal)
pub fn signal(&mut self, signal: RuntimeSignal)
Push a lifecycle signal onto the queue (#547).
Commands call this during execution. The server drains
the queue after the command returns and acts on the signals.
Same pattern as StateChanges — accumulate during execution,
drain after.
Sourcepub fn take_signals(&mut self) -> Vec<RuntimeSignal>
pub fn take_signals(&mut self) -> Vec<RuntimeSignal>
Drain the signal queue, returning all accumulated signals (#547).
Called by the server after command execution completes. Returns the signals and empties the queue.
Sourcepub fn has_compositor(&self) -> bool
pub fn has_compositor(&self) -> bool
Check if compositor is available.
Sourcepub const fn session_mut(&mut self) -> &mut Session
pub const fn session_mut(&mut self) -> &mut Session
Get mutable access to the session.
Sourcepub const fn kernel(&self) -> &KernelContext
pub const fn kernel(&self) -> &KernelContext
Get direct access to the kernel context.
Sourcepub fn with_buffer_read<F, R>(&self, buffer: BufferId, f: F) -> Option<R>
pub fn with_buffer_read<F, R>(&self, buffer: BufferId, f: F) -> Option<R>
Execute a read-only operation on a buffer.
This method provides temporary read access to a buffer for complex calculations that need the full buffer interface (e.g., motion calculations, text object matching).
§Arguments
buffer- The buffer ID to accessf- A function that receives a read guard to the buffer
§Returns
Some(R) with the function’s return value if the buffer exists,
None if the buffer doesn’t exist.
§Example
let target = runtime.with_buffer_read(buffer_id, |buffer| {
MotionEngine::calculate(buffer, &cursor, motion, count)
});Sourcepub fn record_global_option_change(
&mut self,
name: impl Into<String>,
value: OptionValue,
)
pub fn record_global_option_change( &mut self, name: impl Into<String>, value: OptionValue, )
Record a global option change for notification emission.
Call this after setting a global option via kernel.options.set().
The change will be emitted as an OPTION_CHANGED notification.
Sourcepub fn record_window_option_change(
&mut self,
name: impl Into<String>,
value: OptionValue,
window_id: WindowId,
)
pub fn record_window_option_change( &mut self, name: impl Into<String>, value: OptionValue, window_id: WindowId, )
Record a window-scoped option change for notification emission.
Call this after setting a window option via kernel.options.set().
The change will be emitted as an OPTION_CHANGED notification.
Sourcepub fn record_buffer_modified(&mut self, buffer: BufferId)
pub fn record_buffer_modified(&mut self, buffer: BufferId)
Record that a buffer was modified for notification emission.
Call this after modifying buffer content directly (e.g., via OperatorContext
which bypasses SessionRuntime’s BufferApi methods like delete_range()).
Sourcepub const fn windows(&self) -> &WindowLayout
pub const fn windows(&self) -> &WindowLayout
Get the per-client windows.
Phase #471: Direct access to per-client windows. No fallback, no Option. Commands use this to access the active window’s cursor position.
§Example
let Some(window) = runtime.windows().active() else {
return CommandResult::error("No active window");
};
let pos = Position::new(window.cursor.line, window.cursor.column);Sourcepub fn windows_mut(&mut self) -> &mut WindowLayout
pub fn windows_mut(&mut self) -> &mut WindowLayout
Sourcepub const fn registers(&self) -> &RegisterBank
pub const fn registers(&self) -> &RegisterBank
Get per-client registers (#515).
Sourcepub fn registers_mut(&mut self) -> &mut RegisterBank
pub fn registers_mut(&mut self) -> &mut RegisterBank
Get per-client registers mutably (#515).
Sourcepub const fn clipboard_history(&self) -> &HistoryRing
pub const fn clipboard_history(&self) -> &HistoryRing
Get per-client clipboard history (#515).
Sourcepub fn clipboard_history_mut(&mut self) -> &mut HistoryRing
pub fn clipboard_history_mut(&mut self) -> &mut HistoryRing
Get per-client clipboard history mutably (#515).
Sourcepub const fn kernel_and_registers(
&mut self,
) -> (&KernelContext, &mut RegisterBank, &mut HistoryRing)
pub const fn kernel_and_registers( &mut self, ) -> (&KernelContext, &mut RegisterBank, &mut HistoryRing)
Get kernel, registers, and clipboard history together (#515).
Splits the borrow so the caller can hold &KernelContext and
&mut RegisterBank / &mut HistoryRing simultaneously, which
isn’t possible through separate accessor calls.
Sourcepub const fn local_marks(&self) -> &MarkBank
pub const fn local_marks(&self) -> &MarkBank
Get per-client local marks (#515).
Sourcepub fn local_marks_mut(&mut self) -> &mut MarkBank
pub fn local_marks_mut(&mut self) -> &mut MarkBank
Get per-client local marks mutably (#515).
Sourcepub fn jumplist_mut(&mut self) -> &mut Jumplist
pub fn jumplist_mut(&mut self) -> &mut Jumplist
Get per-client jump list mutably (#654).
Source§impl SessionRuntime<'_>
impl SessionRuntime<'_>
Sourcepub fn push_to_clipboard_history(&mut self, content: RegisterContent)
pub fn push_to_clipboard_history(&mut self, content: RegisterContent)
Push content to the per-client clipboard history (#515).
Should be called after every yank/delete operation to populate numbered registers (0-9). This is an inherent method because history is per-client state, not a shared service.
Sourcepub fn store_register_with_sync(
&mut self,
register: Option<char>,
content: RegisterContent,
)
pub fn store_register_with_sync( &mut self, register: Option<char>, content: RegisterContent, )
Store content to register with clipboard sync (#515 Phase 4).
Coordinates RegisterApi and ClipboardApi:
- Stores content in per-client register via
RegisterApi - Syncs to OS clipboard for
+/*registers viaClipboardApi - Pushes to clipboard history for numbered register rotation
Use this from commands that operate on SessionRuntime directly
(visual mode operators, editor commands). Vim operators that hold
separate borrows via OperatorContext use registers::store_and_sync
instead.
Sourcepub fn get_register_with_clipboard(
&self,
register: Option<char>,
) -> Option<RegisterContent>
pub fn get_register_with_clipboard( &self, register: Option<char>, ) -> Option<RegisterContent>
Get register content with clipboard fallback for +/* (#515 Phase 4).
For system clipboard registers:
+: reads from OS clipboard, falls back to per-client register*: reads from OS selection, falls back to per-client register- All others: reads directly from per-client
RegisterBank
Trait Implementations§
Source§impl BufferApi for SessionRuntime<'_>
impl BufferApi for SessionRuntime<'_>
Source§fn active_buffer(&self) -> Option<BufferId>
fn active_buffer(&self) -> Option<BufferId>
Source§fn buffer_line(&self, buffer: BufferId, line: usize) -> Option<String>
fn buffer_line(&self, buffer: BufferId, line: usize) -> Option<String>
Source§fn buffer_line_count(&self, buffer: BufferId) -> Option<usize>
fn buffer_line_count(&self, buffer: BufferId) -> Option<usize>
Source§fn buffer_line_len(&self, buffer: BufferId, line: usize) -> Option<usize>
fn buffer_line_len(&self, buffer: BufferId, line: usize) -> Option<usize>
Source§fn buffer_text_range(
&self,
buffer: BufferId,
start: Position,
end: Position,
) -> Option<String>
fn buffer_text_range( &self, buffer: BufferId, start: Position, end: Position, ) -> Option<String>
Source§fn buffer_content(&self, buffer: BufferId) -> Option<String>
fn buffer_content(&self, buffer: BufferId) -> Option<String>
Source§fn buffer_file_path(&self, buffer: BufferId) -> Option<String>
fn buffer_file_path(&self, buffer: BufferId) -> Option<String>
Source§fn is_buffer_modified(&self, buffer: BufferId) -> Option<bool>
fn is_buffer_modified(&self, buffer: BufferId) -> Option<bool>
Source§fn set_buffer_modified(&mut self, buffer: BufferId, modified: bool)
fn set_buffer_modified(&mut self, buffer: BufferId, modified: bool)
Source§fn insert_text(&mut self, buffer: BufferId, pos: Position, text: &str)
fn insert_text(&mut self, buffer: BufferId, pos: Position, text: &str)
Source§fn replace_content(&mut self, buffer: BufferId, content: &str)
fn replace_content(&mut self, buffer: BufferId, content: &str)
Source§fn create_buffer(&mut self, name: Option<&str>, content: &str) -> BufferId
fn create_buffer(&mut self, name: Option<&str>, content: &str) -> BufferId
Source§fn delete_buffer(&mut self, buffer: BufferId) -> Result<(), BufferError>
fn delete_buffer(&mut self, buffer: BufferId) -> Result<(), BufferError>
Source§fn rename_buffer(&mut self, buffer: BufferId, new_name: &str)
fn rename_buffer(&mut self, buffer: BufferId, new_name: &str)
Source§impl ChangeTracker for SessionRuntime<'_>
impl ChangeTracker for SessionRuntime<'_>
Source§fn take_changes(&mut self) -> StateChanges
fn take_changes(&mut self) -> StateChanges
Source§fn record_cursor_move(&mut self, buffer: BufferId)
fn record_cursor_move(&mut self, buffer: BufferId)
Source§fn record_selection_change(&mut self, buffer: BufferId)
fn record_selection_change(&mut self, buffer: BufferId)
Source§impl ClipboardApi for SessionRuntime<'_>
impl ClipboardApi for SessionRuntime<'_>
Source§fn copy_to_clipboard(&self, text: &str) -> bool
fn copy_to_clipboard(&self, text: &str) -> bool
+ register). Read moreSource§fn paste_from_clipboard(&self) -> Option<String>
fn paste_from_clipboard(&self) -> Option<String>
+ register). Read moreSource§impl CommandApi for SessionRuntime<'_>
impl CommandApi for SessionRuntime<'_>
Source§fn execute_command(
&mut self,
cmd: CommandId,
ctx: CommandContext,
) -> CommandResult
fn execute_command( &mut self, cmd: CommandId, ctx: CommandContext, ) -> CommandResult
Source§impl CompositorApi for SessionRuntime<'_>
impl CompositorApi for SessionRuntime<'_>
Source§fn split(
&mut self,
direction: SplitDirection,
) -> Result<WindowId, CompositorError>
fn split( &mut self, direction: SplitDirection, ) -> Result<WindowId, CompositorError>
Source§fn close_current_window(&mut self) -> Result<WindowId, CompositorError>
fn close_current_window(&mut self) -> Result<WindowId, CompositorError>
Source§fn close_others(&mut self) -> Result<(), CompositorError>
fn close_others(&mut self) -> Result<(), CompositorError>
Source§fn resize(
&mut self,
direction: NavigateDirection,
delta: i16,
) -> Result<(), CompositorError>
fn resize( &mut self, direction: NavigateDirection, delta: i16, ) -> Result<(), CompositorError>
Source§fn equalize(&mut self) -> Result<(), CompositorError>
fn equalize(&mut self) -> Result<(), CompositorError>
Source§fn cycle(&self, forward: bool) -> Result<WindowId, CompositorError>
fn cycle(&self, forward: bool) -> Result<WindowId, CompositorError>
Source§fn focus(&mut self, window: WindowId) -> Result<(), CompositorError>
fn focus(&mut self, window: WindowId) -> Result<(), CompositorError>
Source§fn compositor_window_count(&self) -> usize
fn compositor_window_count(&self) -> usize
Source§fn arrange(&self, screen: Rect) -> Vec<WindowPlacement>
fn arrange(&self, screen: Rect) -> Vec<WindowPlacement>
Source§fn active_layer(&self) -> Option<LayerId>
fn active_layer(&self) -> Option<LayerId>
Source§fn set_screen(&mut self, screen: Rect)
fn set_screen(&mut self, screen: Rect)
Source§fn toggle_float(&mut self) -> Result<(), CompositorError>
fn toggle_float(&mut self) -> Result<(), CompositorError>
Source§fn raise_float(&mut self) -> Result<(), CompositorError>
fn raise_float(&mut self) -> Result<(), CompositorError>
Source§fn lower_float(&mut self) -> Result<(), CompositorError>
fn lower_float(&mut self) -> Result<(), CompositorError>
Source§fn show_overlay(
&mut self,
constraints: OverlayConstraints,
) -> Result<WindowId, CompositorError>
fn show_overlay( &mut self, constraints: OverlayConstraints, ) -> Result<WindowId, CompositorError>
Source§fn hide_overlay(&mut self, window: WindowId) -> Result<(), CompositorError>
fn hide_overlay(&mut self, window: WindowId) -> Result<(), CompositorError>
Source§fn resize_overlay(
&mut self,
window: WindowId,
width: u16,
height: u16,
) -> Result<(), CompositorError>
fn resize_overlay( &mut self, window: WindowId, width: u16, height: u16, ) -> Result<(), CompositorError>
Source§fn hide_all_overlays(&mut self) -> Result<(), CompositorError>
fn hide_all_overlays(&mut self) -> Result<(), CompositorError>
Source§fn set_active_layer_opacity(
&mut self,
opacity: f32,
) -> Result<(), CompositorError>
fn set_active_layer_opacity( &mut self, opacity: f32, ) -> Result<(), CompositorError>
Source§fn active_layer_opacity(&self) -> Result<f32, CompositorError>
fn active_layer_opacity(&self) -> Result<f32, CompositorError>
Source§fn adjust_active_layer_opacity(
&mut self,
delta: f32,
) -> Result<f32, CompositorError>
fn adjust_active_layer_opacity( &mut self, delta: f32, ) -> Result<f32, CompositorError>
Source§fn tab_new(&mut self) -> Result<TabId, CompositorError>
fn tab_new(&mut self) -> Result<TabId, CompositorError>
Source§fn tab_next(&mut self) -> Result<TabId, CompositorError>
fn tab_next(&mut self) -> Result<TabId, CompositorError>
Source§fn tab_prev(&mut self) -> Result<TabId, CompositorError>
fn tab_prev(&mut self) -> Result<TabId, CompositorError>
Source§fn tab_goto(&mut self, index: usize) -> Result<TabId, CompositorError>
fn tab_goto(&mut self, index: usize) -> Result<TabId, CompositorError>
Source§fn active_tab_id(&self) -> Option<TabId>
fn active_tab_id(&self) -> Option<TabId>
Source§impl ExtensionApi for SessionRuntime<'_>
Per-client extensions (#477, #471 Phase 0).
impl ExtensionApi for SessionRuntime<'_>
Per-client extensions (#477, #471 Phase 0).
Extensions are now ALWAYS per-client (no fallback to shared session).
This enforces complete module state isolation between clients. For example,
VimSessionState.pending_count is per-client, so Client A pressing 5
doesn’t affect Client B’s motions.
Source§fn ext<T: SessionExtension>(&self) -> Option<&T>
fn ext<T: SessionExtension>(&self) -> Option<&T>
Source§fn ext_mut<T: SessionExtension>(&mut self) -> &mut T
fn ext_mut<T: SessionExtension>(&mut self) -> &mut T
Source§impl ModeApi for SessionRuntime<'_>
Per-client state (#471): Mode operations use the per-client mode stack directly.
impl ModeApi for SessionRuntime<'_>
Per-client state (#471): Mode operations use the per-client mode stack directly.
Since per-client state is now required (no Option), all mode operations
directly access self.mode_stack without fallback to session.