oxur_cli/repl/
oxur_prompt.rs

1//! Custom prompt with multi-line support
2//!
3//! Provides the Oxur REPL prompt with support for continuation lines
4//! when entering multi-line S-expressions.
5
6use reedline::{Prompt, PromptEditMode, PromptHistorySearch, PromptHistorySearchStatus};
7use std::borrow::Cow;
8
9/// Custom prompt for Oxur REPL
10///
11/// Implements reedline's `Prompt` trait to provide:
12/// - Custom primary prompt (e.g., "oxur> ")
13/// - Continuation prompt for multi-line input (e.g., "....> ")
14/// - History search indicator
15pub struct OxurPrompt {
16    primary: String,
17    continuation: String,
18}
19
20impl OxurPrompt {
21    /// Create a new Oxur prompt
22    ///
23    /// # Arguments
24    ///
25    /// * `primary` - The main prompt string (e.g., "oxur> ")
26    /// * `continuation` - The continuation prompt for multi-line input (e.g., "....> ")
27    pub fn new(primary: String, continuation: String) -> Self {
28        Self { primary, continuation }
29    }
30}
31
32impl Prompt for OxurPrompt {
33    fn render_prompt_left(&self) -> Cow<'_, str> {
34        Cow::Borrowed(&self.primary)
35    }
36
37    fn render_prompt_right(&self) -> Cow<'_, str> {
38        Cow::Borrowed("")
39    }
40
41    fn render_prompt_indicator(&self, _edit_mode: PromptEditMode) -> Cow<'_, str> {
42        Cow::Borrowed("")
43    }
44
45    fn render_prompt_multiline_indicator(&self) -> Cow<'_, str> {
46        Cow::Borrowed(&self.continuation)
47    }
48
49    fn render_prompt_history_search_indicator(
50        &self,
51        history_search: PromptHistorySearch,
52    ) -> Cow<'_, str> {
53        let status = match history_search.status {
54            PromptHistorySearchStatus::Passing => "",
55            PromptHistorySearchStatus::Failing => "failing ",
56        };
57        Cow::Owned(format!("({}reverse-search: {}) ", status, history_search.term))
58    }
59}
60
61#[cfg(test)]
62mod tests {
63    use super::*;
64    use reedline::PromptEditMode;
65
66    #[test]
67    fn test_new_prompt() {
68        let prompt = OxurPrompt::new("oxur> ".to_string(), "....> ".to_string());
69        assert_eq!(prompt.render_prompt_left(), "oxur> ");
70        assert_eq!(prompt.render_prompt_multiline_indicator(), "....> ");
71    }
72
73    #[test]
74    fn test_right_prompt_empty() {
75        let prompt = OxurPrompt::new("oxur> ".to_string(), "....> ".to_string());
76        assert_eq!(prompt.render_prompt_right(), "");
77    }
78
79    #[test]
80    fn test_prompt_indicator_empty() {
81        let prompt = OxurPrompt::new("oxur> ".to_string(), "....> ".to_string());
82        assert_eq!(prompt.render_prompt_indicator(PromptEditMode::Default), "");
83    }
84
85    #[test]
86    fn test_history_search_passing() {
87        let prompt = OxurPrompt::new("oxur> ".to_string(), "....> ".to_string());
88        let search = PromptHistorySearch {
89            status: PromptHistorySearchStatus::Passing,
90            term: "test".to_string(),
91        };
92        let result = prompt.render_prompt_history_search_indicator(search);
93        assert!(result.contains("reverse-search"));
94        assert!(result.contains("test"));
95        assert!(!result.contains("failing"));
96    }
97
98    #[test]
99    fn test_history_search_failing() {
100        let prompt = OxurPrompt::new("oxur> ".to_string(), "....> ".to_string());
101        let search = PromptHistorySearch {
102            status: PromptHistorySearchStatus::Failing,
103            term: "test".to_string(),
104        };
105        let result = prompt.render_prompt_history_search_indicator(search);
106        assert!(result.contains("failing"));
107        assert!(result.contains("reverse-search"));
108        assert!(result.contains("test"));
109    }
110
111    #[test]
112    fn test_custom_prompts() {
113        let prompt = OxurPrompt::new(">>> ".to_string(), "... ".to_string());
114        assert_eq!(prompt.render_prompt_left(), ">>> ");
115        assert_eq!(prompt.render_prompt_multiline_indicator(), "... ");
116    }
117}