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;