Skip to main content

osp_cli/repl/
mod.rs

1//! The REPL module exists to own interactive shell behavior that the ordinary
2//! CLI host should not know about.
3//!
4//! The layering here is intentional:
5//!
6//! - `engine` owns the line editor boundary, prompt rendering, history
7//!   picker/completion adapters, and debug surfaces.
8//! - `dispatch` owns command execution and shell-scope behavior once a line
9//!   has been accepted.
10//! - `completion` shapes the live command catalog into REPL-aware completion
11//!   trees.
12//! - `presentation` owns prompt appearance and intro/help material that is
13//!   specific to interactive use.
14//!
15//! A submitted line travels through the layers like this:
16//!
17//! ```text
18//! user keystrokes
19//!       │
20//!       ▼ [ engine ]    reedline editor, prompt, completion/history menus
21//!       │ line accepted
22//!       ▼ [ dispatch ]  execute command, apply shell scope and aliases
23//!       │ ReplLineResult
24//!       ├── Continue     → render output, show next prompt
25//!       ├── ReplaceInput → update input buffer without printing
26//!       ├── Restart      → [ lifecycle ] rebuild REPL state, loop again
27//!       └── Exit         → return ReplRunResult to the caller
28//! ```
29//!
30//! Embedders drive the loop with [`crate::repl::run_repl`], configured through
31//! [`crate::repl::ReplRunConfig::builder`]. The engine, dispatch, and
32//! presentation layers are internal; only the config/result types cross the
33//! boundary.
34//!
35//! Minimal embedder path:
36//!
37//! ```no_run
38//! use anyhow::Result;
39//! use osp_cli::repl::{
40//!     HistoryConfig, ReplLineResult, ReplPrompt, ReplRunConfig, run_repl,
41//! };
42//!
43//! let config = ReplRunConfig::builder(
44//!     ReplPrompt::simple("osp> "),
45//!     HistoryConfig::builder().build(),
46//! )
47//! .build();
48//!
49//! let _result = run_repl(config, |line, _history| -> Result<ReplLineResult> {
50//!     match line.trim() {
51//!         "exit" | "quit" => Ok(ReplLineResult::Exit(0)),
52//!         _ => Ok(ReplLineResult::Continue(String::new())),
53//!     }
54//! })?;
55//! # Ok::<(), anyhow::Error>(())
56//! ```
57//!
58//! Choose [`crate::app`] instead when you want the full `osp` host with config
59//! loading, command dispatch, and rendering already wired together. Choose
60//! this module directly when you already own the execution callback and only
61//! want the interactive editor loop.
62//!
63//! When debugging the REPL, first decide whether the issue is editor/runtime
64//! state, dispatch semantics, or rendering. That is usually enough to choose
65//! the right submodule.
66//!
67//! Contract:
68//!
69//! - this module may depend on editor/runtime adapters, completion, UI, and
70//!   dispatch code
71//! - it should not become the owner of generic command execution rules, config
72//!   resolution, or non-interactive CLI parsing
73//!
74//! Public API shape:
75//!
76//! - primary host-facing entrypoints are [`crate::repl::run_repl`],
77//!   [`crate::repl::ReplRunConfig`], [`crate::repl::HistoryConfig`], and
78//!   [`crate::repl::ReplPrompt`]
79//! - debug snapshots and inspection helpers such as
80//!   [`crate::repl::CompletionDebug`] and
81//!   [`crate::repl::debug_completion`] stay available without becoming the
82//!   default path for ordinary embedders
83//! - host-style REPL configuration flows through concrete builders and
84//!   factories such as [`crate::repl::ReplRunConfig::builder`],
85//!   [`crate::repl::ReplAppearance::builder`], and
86//!   [`crate::repl::HistoryConfig::builder`]
87//! - guided REPL configuration follows the crate-wide naming rule:
88//!   `new(...)` for exact constructors, `builder(...)` for staged
89//!   configuration, `with_*` setters, and `build()` as the terminal step
90
91pub(crate) mod completion;
92pub(crate) mod dispatch;
93mod engine;
94pub(crate) mod help;
95mod highlight;
96pub(crate) mod history;
97mod history_store;
98mod host;
99pub(crate) mod input;
100pub(crate) mod lifecycle;
101mod menu;
102mod menu_core;
103pub(crate) mod presentation;
104pub(crate) mod surface;
105
106#[cfg(test)]
107pub(crate) use dispatch::apply_repl_shell_prefix;
108#[cfg(test)]
109pub(crate) use engine::ReplCompleter;
110// Primary host-facing entry points.
111pub use engine::{
112    HistoryConfig, HistoryConfigBuilder, HistoryEntry, HistoryShellContext, LineProjection,
113    LineProjector, PromptRightRenderer, ReplAppearance, ReplAppearanceBuilder, ReplInputMode,
114    ReplLineResult, ReplPrompt, ReplReloadKind, ReplRunConfig, ReplRunConfigBuilder, ReplRunResult,
115    SharedHistory, color_from_style_spec, default_pipe_verbs, run_repl,
116};
117// Debug surfaces for REPL completion, highlight, and history inspection.
118pub use engine::{
119    CompletionDebug, CompletionDebugFrame, CompletionDebugMatch, CompletionDebugOptions, DebugStep,
120    HighlightDebugSpan, debug_completion, debug_completion_steps, debug_highlight,
121    debug_history_menu, debug_history_menu_steps,
122};
123pub(crate) use engine::{
124    CompletionTraceEvent, CompletionTraceMenuState, expand_history, trace_completion,
125    trace_completion_enabled,
126};
127pub(crate) use host::{
128    ReplViewContext, repl_command_spec, run_plugin_repl, run_repl_debug_command_for,
129};
130#[cfg(test)]
131pub(crate) use input::{ReplParsedLine, is_repl_shellable_command};
132#[cfg(test)]
133pub(crate) use presentation::render_repl_prompt_right_for_test;