From f6810663ee5d395bd172271ed9c55c4af1748a80 Mon Sep 17 00:00:00 2001 From: Inhyuk Cho Date: Sat, 26 Oct 2024 04:51:52 +0000 Subject: [PATCH 1/4] feat: add writer attribute --- promkit/src/lib.rs | 19 +++++------- promkit/src/preset/checkbox.rs | 6 +++- promkit/src/preset/form.rs | 7 +++-- promkit/src/preset/json.rs | 6 +++- promkit/src/preset/listbox.rs | 3 +- promkit/src/preset/query_selector.rs | 6 +++- promkit/src/preset/readline.rs | 6 +++- promkit/src/preset/readline/confirm.rs | 7 +++-- promkit/src/preset/readline/password.rs | 7 +++-- promkit/src/preset/tree.rs | 6 +++- promkit/src/terminal.rs | 39 ++++++++++++++++--------- 11 files changed, 75 insertions(+), 37 deletions(-) diff --git a/promkit/src/lib.rs b/promkit/src/lib.rs index 72922934..6f4751c8 100644 --- a/promkit/src/lib.rs +++ b/promkit/src/lib.rs @@ -221,14 +221,15 @@ pub trait Renderer: Finalizer { /// /// This struct encapsulates the rendering logic, /// event handling, and result production for a prompt. -pub struct Prompt { +pub struct Prompt { pub renderer: T, + pub writer: W, } -impl Drop for Prompt { +impl Drop for Prompt { fn drop(&mut self) { execute!( - io::stdout(), + self.writer, cursor::Show, event::DisableMouseCapture, cursor::MoveToNextLine(1), @@ -238,7 +239,7 @@ impl Drop for Prompt { } } -impl Prompt { +impl Prompt { /// Runs the prompt, handling events and producing a result. /// /// This method initializes the terminal, and enters a loop @@ -250,11 +251,11 @@ impl Prompt { /// Returns a `Result` containing the produced result or an error. pub fn run(&mut self) -> anyhow::Result { enable_raw_mode()?; - execute!(io::stdout(), cursor::Hide)?; + execute!(self.writer, cursor::Hide)?; let size = crossterm::terminal::size()?; let panes = self.renderer.create_panes(size.0, size.1); - let mut terminal = Terminal::start_session(&panes)?; + let mut terminal = Terminal::start_session(&panes, &mut self.writer)?; terminal.draw(&panes)?; loop { @@ -262,11 +263,7 @@ impl Prompt { match &ev { Event::Resize(_, _) => { - terminal.position = (0, 0); - crossterm::execute!( - io::stdout(), - crossterm::terminal::Clear(crossterm::terminal::ClearType::Purge), - )?; + terminal.on_resize()?; } _ => { if self.renderer.evaluate(&ev)? == PromptSignal::Quit { diff --git a/promkit/src/preset/checkbox.rs b/promkit/src/preset/checkbox.rs index de4c6851..53c845fb 100644 --- a/promkit/src/preset/checkbox.rs +++ b/promkit/src/preset/checkbox.rs @@ -122,13 +122,17 @@ impl Checkbox { /// Displays the checkbox prompt and waits for user input. /// Returns a `Result` containing the `Prompt` result, /// which is a list of selected options. - pub fn prompt(self) -> anyhow::Result> { + pub fn prompt( + self, + writer: W, + ) -> anyhow::Result> { Ok(Prompt { renderer: render::Renderer { keymap: RefCell::new(self.keymap), title_snapshot: Snapshot::::new(self.title_state), checkbox_snapshot: Snapshot::::new(self.checkbox_state), }, + writer, }) } } diff --git a/promkit/src/preset/form.rs b/promkit/src/preset/form.rs index 786cd51e..d690d6e9 100644 --- a/promkit/src/preset/form.rs +++ b/promkit/src/preset/form.rs @@ -45,7 +45,10 @@ impl Form { } } - pub fn prompt(self) -> anyhow::Result> { + pub fn prompt( + self, + writer: W, + ) -> anyhow::Result> { let default_styles = self .text_editor_states .iter() @@ -62,6 +65,6 @@ impl Form { overwrite_styles: self.overwrite_styles, }; renderer.overwrite_styles(); - Ok(Prompt { renderer }) + Ok(Prompt { renderer, writer }) } } diff --git a/promkit/src/preset/json.rs b/promkit/src/preset/json.rs index 2149664e..1891bfa3 100644 --- a/promkit/src/preset/json.rs +++ b/promkit/src/preset/json.rs @@ -92,13 +92,17 @@ impl Json { } /// Creates a prompt based on the current configuration of the `Json` instance. - pub fn prompt(self) -> anyhow::Result> { + pub fn prompt( + self, + writer: W, + ) -> anyhow::Result> { Ok(Prompt { renderer: render::Renderer { keymap: RefCell::new(self.keymap), title_snapshot: Snapshot::::new(self.title_state), json_snapshot: Snapshot::::new(self.json_state), }, + writer, }) } } diff --git a/promkit/src/preset/listbox.rs b/promkit/src/preset/listbox.rs index e27f8f79..152bb9f4 100644 --- a/promkit/src/preset/listbox.rs +++ b/promkit/src/preset/listbox.rs @@ -92,13 +92,14 @@ impl Listbox { /// Displays the select prompt and waits for user input. /// Returns a `Result` containing the `Prompt` result, /// which is the selected option. - pub fn prompt(self) -> anyhow::Result> { + pub fn prompt(self, writer: W) -> anyhow::Result> { Ok(Prompt { renderer: render::Renderer { keymap: RefCell::new(self.keymap), title_snapshot: Snapshot::::new(self.title_state), listbox_snapshot: Snapshot::::new(self.listbox_state), }, + writer, }) } } diff --git a/promkit/src/preset/query_selector.rs b/promkit/src/preset/query_selector.rs index a2c679a0..c6e8bca1 100644 --- a/promkit/src/preset/query_selector.rs +++ b/promkit/src/preset/query_selector.rs @@ -157,7 +157,10 @@ impl QuerySelector { /// Displays the query select prompt and waits for user input. /// Returns a `Result` containing the `Prompt` result, /// which is the selected option. - pub fn prompt(self) -> anyhow::Result> { + pub fn prompt( + self, + writer: W, + ) -> anyhow::Result> { Ok(Prompt { renderer: render::Renderer { keymap: RefCell::new(self.keymap), @@ -166,6 +169,7 @@ impl QuerySelector { listbox_snapshot: Snapshot::::new(self.listbox_state), filter: self.filter, }, + writer, }) } } diff --git a/promkit/src/preset/readline.rs b/promkit/src/preset/readline.rs index 85875c5b..5fb01f66 100644 --- a/promkit/src/preset/readline.rs +++ b/promkit/src/preset/readline.rs @@ -174,7 +174,10 @@ impl Readline { /// Initiates the prompt process, /// displaying the configured UI elements and handling user input. - pub fn prompt(self) -> anyhow::Result> { + pub fn prompt( + self, + writer: W, + ) -> anyhow::Result> { Ok(Prompt { renderer: render::Renderer { keymap: RefCell::new(self.keymap), @@ -185,6 +188,7 @@ impl Readline { validator: self.validator, error_message_snapshot: Snapshot::::new(self.error_message_state), }, + writer, }) } } diff --git a/promkit/src/preset/readline/confirm.rs b/promkit/src/preset/readline/confirm.rs index f183fc89..0b12f947 100644 --- a/promkit/src/preset/readline/confirm.rs +++ b/promkit/src/preset/readline/confirm.rs @@ -31,7 +31,10 @@ impl Confirm { /// Displays the confirmation prompt and waits for user input. /// Returns a `Result` containing the `Prompt` result, /// which is the user's input. - pub fn prompt(self) -> anyhow::Result> { - self.0.prompt() + pub fn prompt( + self, + writer: W, + ) -> anyhow::Result> { + self.0.prompt(writer) } } diff --git a/promkit/src/preset/readline/password.rs b/promkit/src/preset/readline/password.rs index 65246c42..ec8338f8 100644 --- a/promkit/src/preset/readline/password.rs +++ b/promkit/src/preset/readline/password.rs @@ -67,7 +67,10 @@ impl Password { /// Displays the password prompt and waits for user input. /// Returns a `Result` containing the `Prompt` result, /// which is the user's input. - pub fn prompt(self) -> anyhow::Result> { - self.0.prompt() + pub fn prompt( + self, + writer: W, + ) -> anyhow::Result> { + self.0.prompt(writer) } } diff --git a/promkit/src/preset/tree.rs b/promkit/src/preset/tree.rs index 0904ba85..7c088faa 100644 --- a/promkit/src/preset/tree.rs +++ b/promkit/src/preset/tree.rs @@ -106,13 +106,17 @@ impl Tree { /// Displays the tree prompt and waits for user input. /// Returns a `Result` containing the `Prompt` result, /// which is a list of selected options. - pub fn prompt(self) -> anyhow::Result> { + pub fn prompt( + self, + writer: W, + ) -> anyhow::Result> { Ok(Prompt { renderer: render::Renderer { keymap: RefCell::new(self.keymap), title_snapshot: Snapshot::::new(self.title_state), tree_snapshot: Snapshot::::new(self.tree_state), }, + writer, }) } } diff --git a/promkit/src/terminal.rs b/promkit/src/terminal.rs index d15e0f2d..0b834e50 100644 --- a/promkit/src/terminal.rs +++ b/promkit/src/terminal.rs @@ -1,17 +1,27 @@ -use std::io::{self, Write}; +use std::io::Write; use crate::{ crossterm::{cursor, style, terminal}, pane::Pane, }; -pub struct Terminal { +pub struct Terminal<'a, W: Write> { /// The current cursor position within the terminal. pub position: (u16, u16), + pub writer: &'a mut W, } -impl Terminal { - pub fn start_session(panes: &[Pane]) -> anyhow::Result { +impl<'a, W: Write> Terminal<'a, W> { + pub fn on_resize(&mut self) -> anyhow::Result<()> { + self.position = (0, 0); + crossterm::execute!( + self.writer, + crossterm::terminal::Clear(crossterm::terminal::ClearType::Purge), + )?; + Ok(()) + } + + pub fn start_session(panes: &[Pane], writer: &'a mut W) -> anyhow::Result { let position = cursor::position()?; let size = terminal::size()?; @@ -23,9 +33,9 @@ impl Terminal { // to ensure the next output starts correctly. if position.0 != 0 { if size.1 == position.1 + 1 { - crossterm::queue!(io::stdout(), terminal::ScrollUp(1))?; + crossterm::queue!(writer, terminal::ScrollUp(1))?; } - crossterm::queue!(io::stdout(), cursor::MoveToNextLine(1))?; + crossterm::queue!(writer, cursor::MoveToNextLine(1))?; } // Calculate the total number of rows required by all panes. @@ -39,16 +49,17 @@ impl Terminal { // to maintain its relative position. if size.1 == position.1 + 1 { crossterm::queue!( - io::stdout(), + writer, terminal::ScrollUp(lines as u16), cursor::MoveToPreviousLine(lines as u16), )?; } - io::stdout().flush()?; + writer.flush()?; Ok(Self { position: cursor::position()?, + writer, }) } @@ -62,7 +73,7 @@ impl Terminal { if height < viewable_panes.len() as u16 { return crossterm::execute!( - io::stdout(), + self.writer, terminal::Clear(terminal::ClearType::FromCursorDown), style::Print("⚠️ Insufficient Space"), ) @@ -70,7 +81,7 @@ impl Terminal { } crossterm::queue!( - io::stdout(), + self.writer, cursor::MoveTo(self.position.0, self.position.1), terminal::Clear(terminal::ClearType::FromCursorDown), )?; @@ -88,7 +99,7 @@ impl Terminal { ); used += rows.len(); for (j, row) in rows.iter().enumerate() { - crossterm::queue!(io::stdout(), style::Print(row.styled_display()))?; + crossterm::queue!(self.writer, style::Print(row.styled_display()))?; current_cursor_y = current_cursor_y.saturating_sub(1); @@ -96,14 +107,14 @@ impl Terminal { || i != viewable_panes.len() - 1) && current_cursor_y == 0 { - crossterm::queue!(io::stdout(), terminal::ScrollUp(1))?; + crossterm::queue!(self.writer, terminal::ScrollUp(1))?; self.position.1 = self.position.1.saturating_sub(1); } - crossterm::queue!(io::stdout(), cursor::MoveToNextLine(1))?; + crossterm::queue!(self.writer, cursor::MoveToNextLine(1))?; } } - io::stdout().flush()?; + self.writer.flush()?; Ok(()) } } From 38c911fad1d9885138adb43accd41fc9ad84c344 Mon Sep 17 00:00:00 2001 From: Inhyuk Cho Date: Sat, 26 Oct 2024 04:55:16 +0000 Subject: [PATCH 2/4] chore: update test examples --- promkit/examples/checkbox.rs | 3 ++- promkit/examples/confirm.rs | 3 ++- promkit/examples/form.rs | 3 ++- promkit/examples/json.rs | 3 ++- promkit/examples/listbox.rs | 3 ++- promkit/examples/password.rs | 3 ++- promkit/examples/query_selector.rs | 3 ++- promkit/examples/readline.rs | 3 ++- promkit/examples/readline_loop.rs | 3 ++- promkit/examples/tree.rs | 3 ++- 10 files changed, 20 insertions(+), 10 deletions(-) diff --git a/promkit/examples/checkbox.rs b/promkit/examples/checkbox.rs index b3a2238f..4c324b08 100644 --- a/promkit/examples/checkbox.rs +++ b/promkit/examples/checkbox.rs @@ -1,4 +1,5 @@ use promkit::preset::checkbox::Checkbox; +use std::io; fn main() -> anyhow::Result<()> { let mut p = Checkbox::new(vec![ @@ -15,7 +16,7 @@ fn main() -> anyhow::Result<()> { ]) .title("What are your favorite fruits?") .checkbox_lines(5) - .prompt()?; + .prompt(io::stdout())?; println!("result: {:?}", p.run()?); Ok(()) } diff --git a/promkit/examples/confirm.rs b/promkit/examples/confirm.rs index 8d08de6a..b612a6b1 100644 --- a/promkit/examples/confirm.rs +++ b/promkit/examples/confirm.rs @@ -1,7 +1,8 @@ use promkit::preset::confirm::Confirm; +use std::io; fn main() -> anyhow::Result<()> { - let mut p = Confirm::new("Do you have a pet?").prompt()?; + let mut p = Confirm::new("Do you have a pet?").prompt(io::stdout())?; println!("result: {:?}", p.run()?); Ok(()) } diff --git a/promkit/examples/form.rs b/promkit/examples/form.rs index 3d206fc8..60ffc71b 100644 --- a/promkit/examples/form.rs +++ b/promkit/examples/form.rs @@ -1,4 +1,5 @@ use promkit::{crossterm::style::Color, preset::form::Form, style::StyleBuilder, text_editor}; +use std::io; fn main() -> anyhow::Result<()> { let mut p = Form::new([ @@ -39,7 +40,7 @@ fn main() -> anyhow::Result<()> { lines: Default::default(), }, ]) - .prompt()?; + .prompt(io::stdout())?; println!("result: {:?}", p.run()?); Ok(()) } diff --git a/promkit/examples/json.rs b/promkit/examples/json.rs index b02b0d89..793c9797 100644 --- a/promkit/examples/json.rs +++ b/promkit/examples/json.rs @@ -1,4 +1,5 @@ use promkit::{json::JsonStream, preset::json::Json, serde_json::Deserializer}; +use std::io; fn main() -> anyhow::Result<()> { let stream = JsonStream::new( @@ -23,7 +24,7 @@ fn main() -> anyhow::Result<()> { let mut p = Json::new(stream) .title("JSON viewer") .json_lines(5) - .prompt()?; + .prompt(io::stdout())?; println!("result: {:?}", p.run()?); Ok(()) } diff --git a/promkit/examples/listbox.rs b/promkit/examples/listbox.rs index 126e53b4..30c87538 100644 --- a/promkit/examples/listbox.rs +++ b/promkit/examples/listbox.rs @@ -1,9 +1,10 @@ use promkit::preset::listbox::Listbox; +use std::io; fn main() -> anyhow::Result<()> { let mut p = Listbox::new(0..100) .title("What number do you like?") - .prompt()?; + .prompt(io::stdout())?; println!("result: {:?}", p.run()?); Ok(()) } diff --git a/promkit/examples/password.rs b/promkit/examples/password.rs index c4492391..e2e4e8aa 100644 --- a/promkit/examples/password.rs +++ b/promkit/examples/password.rs @@ -1,4 +1,5 @@ use promkit::preset::password::Password; +use std::io; fn main() -> anyhow::Result<()> { let mut p = Password::default() @@ -7,7 +8,7 @@ fn main() -> anyhow::Result<()> { |text| 4 < text.len() && text.len() < 10, |text| format!("Length must be over 4 and within 10 but got {}", text.len()), ) - .prompt()?; + .prompt(io::stdout())?; println!("result: {:?}", p.run()?); Ok(()) } diff --git a/promkit/examples/query_selector.rs b/promkit/examples/query_selector.rs index 7549b8e0..7092d0aa 100644 --- a/promkit/examples/query_selector.rs +++ b/promkit/examples/query_selector.rs @@ -1,4 +1,5 @@ use promkit::preset::query_selector::QuerySelector; +use std::io; fn main() -> anyhow::Result<()> { let mut p = QuerySelector::new(0..100, |text, items| -> Vec { @@ -14,7 +15,7 @@ fn main() -> anyhow::Result<()> { }) .title("What number do you like?") .listbox_lines(5) - .prompt()?; + .prompt(io::stdout())?; println!("result: {:?}", p.run()?); Ok(()) } diff --git a/promkit/examples/readline.rs b/promkit/examples/readline.rs index d187a627..98d93202 100644 --- a/promkit/examples/readline.rs +++ b/promkit/examples/readline.rs @@ -1,4 +1,5 @@ use promkit::{preset::readline::Readline, suggest::Suggest}; +use std::io; fn main() -> anyhow::Result<()> { let mut p = Readline::default() @@ -13,7 +14,7 @@ fn main() -> anyhow::Result<()> { |text| text.len() > 10, |text| format!("Length must be over 10 but got {}", text.len()), ) - .prompt()?; + .prompt(io::stdout())?; println!("result: {:?}", p.run()?); Ok(()) } diff --git a/promkit/examples/readline_loop.rs b/promkit/examples/readline_loop.rs index 8fe9f223..ecd13a5b 100644 --- a/promkit/examples/readline_loop.rs +++ b/promkit/examples/readline_loop.rs @@ -1,7 +1,8 @@ use promkit::preset::readline::Readline; +use std::io; fn main() -> anyhow::Result<()> { - let mut p = Readline::default().prompt()?; + let mut p = Readline::default().prompt(io::stdout())?; loop { match p.run() { diff --git a/promkit/examples/tree.rs b/promkit/examples/tree.rs index 81123d6b..45b6f5c0 100644 --- a/promkit/examples/tree.rs +++ b/promkit/examples/tree.rs @@ -1,10 +1,11 @@ use promkit::{preset::tree::Tree, tree::Node}; +use std::io; fn main() -> anyhow::Result<()> { let mut p = Tree::new(Node::try_from(&std::env::current_dir()?.join("src"))?) .title("Select a directory or file") .tree_lines(10) - .prompt()?; + .prompt(io::stdout())?; println!("result: {:?}", p.run()?); Ok(()) } From b9eaa95784e0fbbea719e2ac615647b01131804b Mon Sep 17 00:00:00 2001 From: Inhyuk Cho Date: Thu, 5 Dec 2024 15:39:58 +0900 Subject: [PATCH 3/4] fix: to build pattern --- promkit/examples/checkbox.rs | 2 +- promkit/examples/confirm.rs | 2 +- promkit/examples/form.rs | 2 +- promkit/examples/json.rs | 2 +- promkit/examples/listbox.rs | 2 +- promkit/examples/password.rs | 2 +- promkit/examples/query_selector.rs | 2 +- promkit/examples/readline.rs | 2 +- promkit/examples/readline_loop.rs | 2 +- promkit/examples/tree.rs | 2 +- promkit/src/lib.rs | 8 ++++---- promkit/src/preset/checkbox.rs | 19 +++++++++++++------ promkit/src/preset/form.rs | 21 +++++++++++++++------ promkit/src/preset/json.rs | 18 ++++++++++++------ promkit/src/preset/listbox.rs | 15 ++++++++++++--- promkit/src/preset/query_selector.rs | 18 ++++++++++++------ promkit/src/preset/readline.rs | 18 ++++++++++++------ promkit/src/preset/readline/confirm.rs | 7 ++----- promkit/src/preset/readline/password.rs | 7 ++----- promkit/src/preset/tree.rs | 18 ++++++++++++------ 20 files changed, 106 insertions(+), 63 deletions(-) diff --git a/promkit/examples/checkbox.rs b/promkit/examples/checkbox.rs index 4c324b08..8d27e6e4 100644 --- a/promkit/examples/checkbox.rs +++ b/promkit/examples/checkbox.rs @@ -16,7 +16,7 @@ fn main() -> anyhow::Result<()> { ]) .title("What are your favorite fruits?") .checkbox_lines(5) - .prompt(io::stdout())?; + .prompt()?; println!("result: {:?}", p.run()?); Ok(()) } diff --git a/promkit/examples/confirm.rs b/promkit/examples/confirm.rs index b612a6b1..9c482386 100644 --- a/promkit/examples/confirm.rs +++ b/promkit/examples/confirm.rs @@ -2,7 +2,7 @@ use promkit::preset::confirm::Confirm; use std::io; fn main() -> anyhow::Result<()> { - let mut p = Confirm::new("Do you have a pet?").prompt(io::stdout())?; + let mut p = Confirm::new("Do you have a pet?").prompt()?; println!("result: {:?}", p.run()?); Ok(()) } diff --git a/promkit/examples/form.rs b/promkit/examples/form.rs index 60ffc71b..4fef50f0 100644 --- a/promkit/examples/form.rs +++ b/promkit/examples/form.rs @@ -40,7 +40,7 @@ fn main() -> anyhow::Result<()> { lines: Default::default(), }, ]) - .prompt(io::stdout())?; + .prompt()?; println!("result: {:?}", p.run()?); Ok(()) } diff --git a/promkit/examples/json.rs b/promkit/examples/json.rs index 793c9797..8ec8b809 100644 --- a/promkit/examples/json.rs +++ b/promkit/examples/json.rs @@ -24,7 +24,7 @@ fn main() -> anyhow::Result<()> { let mut p = Json::new(stream) .title("JSON viewer") .json_lines(5) - .prompt(io::stdout())?; + .prompt()?; println!("result: {:?}", p.run()?); Ok(()) } diff --git a/promkit/examples/listbox.rs b/promkit/examples/listbox.rs index 30c87538..0d135e17 100644 --- a/promkit/examples/listbox.rs +++ b/promkit/examples/listbox.rs @@ -4,7 +4,7 @@ use std::io; fn main() -> anyhow::Result<()> { let mut p = Listbox::new(0..100) .title("What number do you like?") - .prompt(io::stdout())?; + .prompt()?; println!("result: {:?}", p.run()?); Ok(()) } diff --git a/promkit/examples/password.rs b/promkit/examples/password.rs index e2e4e8aa..3374929e 100644 --- a/promkit/examples/password.rs +++ b/promkit/examples/password.rs @@ -8,7 +8,7 @@ fn main() -> anyhow::Result<()> { |text| 4 < text.len() && text.len() < 10, |text| format!("Length must be over 4 and within 10 but got {}", text.len()), ) - .prompt(io::stdout())?; + .prompt()?; println!("result: {:?}", p.run()?); Ok(()) } diff --git a/promkit/examples/query_selector.rs b/promkit/examples/query_selector.rs index 7092d0aa..ceb41d6c 100644 --- a/promkit/examples/query_selector.rs +++ b/promkit/examples/query_selector.rs @@ -15,7 +15,7 @@ fn main() -> anyhow::Result<()> { }) .title("What number do you like?") .listbox_lines(5) - .prompt(io::stdout())?; + .prompt()?; println!("result: {:?}", p.run()?); Ok(()) } diff --git a/promkit/examples/readline.rs b/promkit/examples/readline.rs index 98d93202..02513610 100644 --- a/promkit/examples/readline.rs +++ b/promkit/examples/readline.rs @@ -14,7 +14,7 @@ fn main() -> anyhow::Result<()> { |text| text.len() > 10, |text| format!("Length must be over 10 but got {}", text.len()), ) - .prompt(io::stdout())?; + .prompt()?; println!("result: {:?}", p.run()?); Ok(()) } diff --git a/promkit/examples/readline_loop.rs b/promkit/examples/readline_loop.rs index ecd13a5b..abda3f0c 100644 --- a/promkit/examples/readline_loop.rs +++ b/promkit/examples/readline_loop.rs @@ -2,7 +2,7 @@ use promkit::preset::readline::Readline; use std::io; fn main() -> anyhow::Result<()> { - let mut p = Readline::default().prompt(io::stdout())?; + let mut p = Readline::default().prompt()?; loop { match p.run() { diff --git a/promkit/examples/tree.rs b/promkit/examples/tree.rs index 45b6f5c0..3045ca3e 100644 --- a/promkit/examples/tree.rs +++ b/promkit/examples/tree.rs @@ -5,7 +5,7 @@ fn main() -> anyhow::Result<()> { let mut p = Tree::new(Node::try_from(&std::env::current_dir()?.join("src"))?) .title("Select a directory or file") .tree_lines(10) - .prompt(io::stdout())?; + .prompt()?; println!("result: {:?}", p.run()?); Ok(()) } diff --git a/promkit/src/lib.rs b/promkit/src/lib.rs index 6f4751c8..22730030 100644 --- a/promkit/src/lib.rs +++ b/promkit/src/lib.rs @@ -221,12 +221,12 @@ pub trait Renderer: Finalizer { /// /// This struct encapsulates the rendering logic, /// event handling, and result production for a prompt. -pub struct Prompt { +pub struct Prompt { pub renderer: T, - pub writer: W, + pub writer: Box, } -impl Drop for Prompt { +impl Drop for Prompt { fn drop(&mut self) { execute!( self.writer, @@ -239,7 +239,7 @@ impl Drop for Prompt { } } -impl Prompt { +impl Prompt { /// Runs the prompt, handling events and producing a result. /// /// This method initializes the terminal, and enters a loop diff --git a/promkit/src/preset/checkbox.rs b/promkit/src/preset/checkbox.rs index 53c845fb..77d1c1f6 100644 --- a/promkit/src/preset/checkbox.rs +++ b/promkit/src/preset/checkbox.rs @@ -1,4 +1,4 @@ -use std::{cell::RefCell, fmt::Display}; +use std::{cell::RefCell, fmt::Display, io}; use crate::{ checkbox, @@ -20,6 +20,8 @@ pub struct Checkbox { title_state: text::State, /// State for the checkbox list itself. checkbox_state: checkbox::State, + /// Writer to which promptkit write its contents + writer: Box, } impl Checkbox { @@ -48,6 +50,7 @@ impl Checkbox { lines: Default::default(), }, keymap: ActiveKeySwitcher::new("default", self::keymap::default), + writer: Box::new(io::stdout()), } } @@ -69,6 +72,7 @@ impl Checkbox { lines: Default::default(), }, keymap: ActiveKeySwitcher::new("default", self::keymap::default), + writer: Box::new(io::stdout()), } } @@ -119,20 +123,23 @@ impl Checkbox { self } + /// Sets writer. + pub fn writer(mut self, writer: W) -> Self { + self.writer = Box::new(writer); + self + } + /// Displays the checkbox prompt and waits for user input. /// Returns a `Result` containing the `Prompt` result, /// which is a list of selected options. - pub fn prompt( - self, - writer: W, - ) -> anyhow::Result> { + pub fn prompt(self) -> anyhow::Result> { Ok(Prompt { renderer: render::Renderer { keymap: RefCell::new(self.keymap), title_snapshot: Snapshot::::new(self.title_state), checkbox_snapshot: Snapshot::::new(self.checkbox_state), }, - writer, + writer: self.writer, }) } } diff --git a/promkit/src/preset/form.rs b/promkit/src/preset/form.rs index d690d6e9..0045fde7 100644 --- a/promkit/src/preset/form.rs +++ b/promkit/src/preset/form.rs @@ -1,4 +1,4 @@ -use std::cell::RefCell; +use std::{cell::RefCell, io}; use crate::{ core::Cursor, @@ -17,6 +17,8 @@ pub struct Form { text_editor_states: Vec, /// Overwrite the default styles of text editor states when unselected. overwrite_styles: Vec, + /// Writer to which promptkit write its contents + writer: Box, } impl Form { @@ -42,13 +44,17 @@ impl Form { keymap: ActiveKeySwitcher::new("default", self::keymap::default as keymap::Keymap), text_editor_states, overwrite_styles, + writer: Box::new(io::stdout()), } } - pub fn prompt( - self, - writer: W, - ) -> anyhow::Result> { + /// Sets writer. + pub fn writer(mut self, writer: W) -> Self { + self.writer = Box::new(writer); + self + } + + pub fn prompt(self) -> anyhow::Result> { let default_styles = self .text_editor_states .iter() @@ -65,6 +71,9 @@ impl Form { overwrite_styles: self.overwrite_styles, }; renderer.overwrite_styles(); - Ok(Prompt { renderer, writer }) + Ok(Prompt { + renderer, + writer: self.writer, + }) } } diff --git a/promkit/src/preset/json.rs b/promkit/src/preset/json.rs index 1891bfa3..d89e2633 100644 --- a/promkit/src/preset/json.rs +++ b/promkit/src/preset/json.rs @@ -1,4 +1,4 @@ -use std::cell::RefCell; +use std::{cell::RefCell, io}; use crate::{ crossterm::style::{Attribute, Attributes, Color, ContentStyle}, @@ -17,6 +17,8 @@ pub struct Json { keymap: ActiveKeySwitcher, title_state: text::State, json_state: json::State, + /// Writer to which promptkit write its contents + writer: Box, } impl Json { @@ -47,6 +49,7 @@ impl Json { indent: 2, }, keymap: ActiveKeySwitcher::new("default", self::keymap::default), + writer: Box::new(io::stdout()), } } @@ -91,18 +94,21 @@ impl Json { self } + /// Sets writer. + pub fn writer(mut self, writer: W) -> Self { + self.writer = Box::new(writer); + self + } + /// Creates a prompt based on the current configuration of the `Json` instance. - pub fn prompt( - self, - writer: W, - ) -> anyhow::Result> { + pub fn prompt(self) -> anyhow::Result> { Ok(Prompt { renderer: render::Renderer { keymap: RefCell::new(self.keymap), title_snapshot: Snapshot::::new(self.title_state), json_snapshot: Snapshot::::new(self.json_state), }, - writer, + writer: self.writer, }) } } diff --git a/promkit/src/preset/listbox.rs b/promkit/src/preset/listbox.rs index 152bb9f4..f1fd27d9 100644 --- a/promkit/src/preset/listbox.rs +++ b/promkit/src/preset/listbox.rs @@ -1,4 +1,4 @@ -use std::{cell::RefCell, fmt::Display}; +use std::{cell::RefCell, fmt::Display, io}; use crate::{ crossterm::style::{Attribute, Attributes, Color, ContentStyle}, @@ -19,6 +19,8 @@ pub struct Listbox { title_state: text::State, /// State for the selectable list itself. listbox_state: listbox::State, + /// Writer to which promptkit write its contents + writer: Box, } impl Listbox { @@ -45,6 +47,7 @@ impl Listbox { lines: Default::default(), }, keymap: ActiveKeySwitcher::new("default", self::keymap::default), + writer: Box::new(io::stdout()), } } @@ -89,17 +92,23 @@ impl Listbox { self } + /// Sets writer. + pub fn writer(mut self, writer: W) -> Self { + self.writer = Box::new(writer); + self + } + /// Displays the select prompt and waits for user input. /// Returns a `Result` containing the `Prompt` result, /// which is the selected option. - pub fn prompt(self, writer: W) -> anyhow::Result> { + pub fn prompt(self) -> anyhow::Result> { Ok(Prompt { renderer: render::Renderer { keymap: RefCell::new(self.keymap), title_snapshot: Snapshot::::new(self.title_state), listbox_snapshot: Snapshot::::new(self.listbox_state), }, - writer, + writer: self.writer, }) } } diff --git a/promkit/src/preset/query_selector.rs b/promkit/src/preset/query_selector.rs index c6e8bca1..f17673b5 100644 --- a/promkit/src/preset/query_selector.rs +++ b/promkit/src/preset/query_selector.rs @@ -1,4 +1,4 @@ -use std::{cell::RefCell, fmt::Display}; +use std::{cell::RefCell, fmt::Display, io}; use crate::{ crossterm::style::{Attribute, Attributes, Color, ContentStyle}, @@ -28,6 +28,8 @@ pub struct QuerySelector { /// A filter function to apply to the list box items /// based on the text editor input. filter: render::Filter, + /// Writer to which promptkit write its contents + writer: Box, } impl QuerySelector { @@ -74,6 +76,7 @@ impl QuerySelector { }, keymap: ActiveKeySwitcher::new("default", self::keymap::default), filter, + writer: Box::new(io::stdout()), } } @@ -154,13 +157,16 @@ impl QuerySelector { self } + /// Sets writer. + pub fn writer(mut self, writer: W) -> Self { + self.writer = Box::new(writer); + self + } + /// Displays the query select prompt and waits for user input. /// Returns a `Result` containing the `Prompt` result, /// which is the selected option. - pub fn prompt( - self, - writer: W, - ) -> anyhow::Result> { + pub fn prompt(self) -> anyhow::Result> { Ok(Prompt { renderer: render::Renderer { keymap: RefCell::new(self.keymap), @@ -169,7 +175,7 @@ impl QuerySelector { listbox_snapshot: Snapshot::::new(self.listbox_state), filter: self.filter, }, - writer, + writer: self.writer, }) } } diff --git a/promkit/src/preset/readline.rs b/promkit/src/preset/readline.rs index 5fb01f66..84e6e960 100644 --- a/promkit/src/preset/readline.rs +++ b/promkit/src/preset/readline.rs @@ -1,4 +1,4 @@ -use std::{cell::RefCell, collections::HashSet}; +use std::{cell::RefCell, collections::HashSet, io}; use crate::{ crossterm::style::{Attribute, Attributes, Color, ContentStyle}, @@ -34,6 +34,8 @@ pub struct Readline { validator: Option>, /// State for displaying error messages based on input validation. error_message_state: text::State, + /// Writer to which promptkit write its contents + writer: Box, } impl Default for Readline { @@ -80,6 +82,7 @@ impl Default for Readline { .attrs(Attributes::from(Attribute::Bold)) .build(), }, + writer: Box::new(io::stdout()), } } } @@ -172,12 +175,15 @@ impl Readline { self } + /// Sets writer. + pub fn writer(mut self, writer: W) -> Self { + self.writer = Box::new(writer); + self + } + /// Initiates the prompt process, /// displaying the configured UI elements and handling user input. - pub fn prompt( - self, - writer: W, - ) -> anyhow::Result> { + pub fn prompt(self) -> anyhow::Result> { Ok(Prompt { renderer: render::Renderer { keymap: RefCell::new(self.keymap), @@ -188,7 +194,7 @@ impl Readline { validator: self.validator, error_message_snapshot: Snapshot::::new(self.error_message_state), }, - writer, + writer: self.writer, }) } } diff --git a/promkit/src/preset/readline/confirm.rs b/promkit/src/preset/readline/confirm.rs index 0b12f947..f183fc89 100644 --- a/promkit/src/preset/readline/confirm.rs +++ b/promkit/src/preset/readline/confirm.rs @@ -31,10 +31,7 @@ impl Confirm { /// Displays the confirmation prompt and waits for user input. /// Returns a `Result` containing the `Prompt` result, /// which is the user's input. - pub fn prompt( - self, - writer: W, - ) -> anyhow::Result> { - self.0.prompt(writer) + pub fn prompt(self) -> anyhow::Result> { + self.0.prompt() } } diff --git a/promkit/src/preset/readline/password.rs b/promkit/src/preset/readline/password.rs index ec8338f8..65246c42 100644 --- a/promkit/src/preset/readline/password.rs +++ b/promkit/src/preset/readline/password.rs @@ -67,10 +67,7 @@ impl Password { /// Displays the password prompt and waits for user input. /// Returns a `Result` containing the `Prompt` result, /// which is the user's input. - pub fn prompt( - self, - writer: W, - ) -> anyhow::Result> { - self.0.prompt(writer) + pub fn prompt(self) -> anyhow::Result> { + self.0.prompt() } } diff --git a/promkit/src/preset/tree.rs b/promkit/src/preset/tree.rs index 7c088faa..99a067de 100644 --- a/promkit/src/preset/tree.rs +++ b/promkit/src/preset/tree.rs @@ -1,4 +1,4 @@ -use std::cell::RefCell; +use std::{cell::RefCell, io}; use crate::{ crossterm::style::{Attribute, Attributes, Color, ContentStyle}, @@ -21,6 +21,8 @@ pub struct Tree { title_state: text::State, /// State for the tree itself. tree_state: tree::State, + /// Writer to which promptkit write its contents + writer: Box, } impl Tree { @@ -47,6 +49,7 @@ impl Tree { lines: Default::default(), indent: 2, }, + writer: Box::new(io::stdout()), } } @@ -103,20 +106,23 @@ impl Tree { self } + /// Sets writer. + pub fn writer(mut self, writer: W) -> Self { + self.writer = Box::new(writer); + self + } + /// Displays the tree prompt and waits for user input. /// Returns a `Result` containing the `Prompt` result, /// which is a list of selected options. - pub fn prompt( - self, - writer: W, - ) -> anyhow::Result> { + pub fn prompt(self) -> anyhow::Result> { Ok(Prompt { renderer: render::Renderer { keymap: RefCell::new(self.keymap), title_snapshot: Snapshot::::new(self.title_state), tree_snapshot: Snapshot::::new(self.tree_state), }, - writer, + writer: self.writer, }) } } From 42075a926624e24049f8ecd39688f2fa5c2cc121 Mon Sep 17 00:00:00 2001 From: Inhyuk Cho Date: Thu, 5 Dec 2024 15:44:18 +0900 Subject: [PATCH 4/4] chore: remove unused use --- promkit/examples/checkbox.rs | 1 - promkit/examples/confirm.rs | 1 - promkit/examples/form.rs | 1 - promkit/examples/json.rs | 1 - promkit/examples/listbox.rs | 1 - promkit/examples/password.rs | 1 - promkit/examples/query_selector.rs | 1 - promkit/examples/readline.rs | 1 - promkit/examples/readline_loop.rs | 1 - promkit/examples/tree.rs | 1 - 10 files changed, 10 deletions(-) diff --git a/promkit/examples/checkbox.rs b/promkit/examples/checkbox.rs index 8d27e6e4..b3a2238f 100644 --- a/promkit/examples/checkbox.rs +++ b/promkit/examples/checkbox.rs @@ -1,5 +1,4 @@ use promkit::preset::checkbox::Checkbox; -use std::io; fn main() -> anyhow::Result<()> { let mut p = Checkbox::new(vec![ diff --git a/promkit/examples/confirm.rs b/promkit/examples/confirm.rs index 9c482386..8d08de6a 100644 --- a/promkit/examples/confirm.rs +++ b/promkit/examples/confirm.rs @@ -1,5 +1,4 @@ use promkit::preset::confirm::Confirm; -use std::io; fn main() -> anyhow::Result<()> { let mut p = Confirm::new("Do you have a pet?").prompt()?; diff --git a/promkit/examples/form.rs b/promkit/examples/form.rs index 4fef50f0..3d206fc8 100644 --- a/promkit/examples/form.rs +++ b/promkit/examples/form.rs @@ -1,5 +1,4 @@ use promkit::{crossterm::style::Color, preset::form::Form, style::StyleBuilder, text_editor}; -use std::io; fn main() -> anyhow::Result<()> { let mut p = Form::new([ diff --git a/promkit/examples/json.rs b/promkit/examples/json.rs index 8ec8b809..b02b0d89 100644 --- a/promkit/examples/json.rs +++ b/promkit/examples/json.rs @@ -1,5 +1,4 @@ use promkit::{json::JsonStream, preset::json::Json, serde_json::Deserializer}; -use std::io; fn main() -> anyhow::Result<()> { let stream = JsonStream::new( diff --git a/promkit/examples/listbox.rs b/promkit/examples/listbox.rs index 0d135e17..126e53b4 100644 --- a/promkit/examples/listbox.rs +++ b/promkit/examples/listbox.rs @@ -1,5 +1,4 @@ use promkit::preset::listbox::Listbox; -use std::io; fn main() -> anyhow::Result<()> { let mut p = Listbox::new(0..100) diff --git a/promkit/examples/password.rs b/promkit/examples/password.rs index 3374929e..c4492391 100644 --- a/promkit/examples/password.rs +++ b/promkit/examples/password.rs @@ -1,5 +1,4 @@ use promkit::preset::password::Password; -use std::io; fn main() -> anyhow::Result<()> { let mut p = Password::default() diff --git a/promkit/examples/query_selector.rs b/promkit/examples/query_selector.rs index ceb41d6c..7549b8e0 100644 --- a/promkit/examples/query_selector.rs +++ b/promkit/examples/query_selector.rs @@ -1,5 +1,4 @@ use promkit::preset::query_selector::QuerySelector; -use std::io; fn main() -> anyhow::Result<()> { let mut p = QuerySelector::new(0..100, |text, items| -> Vec { diff --git a/promkit/examples/readline.rs b/promkit/examples/readline.rs index 02513610..d187a627 100644 --- a/promkit/examples/readline.rs +++ b/promkit/examples/readline.rs @@ -1,5 +1,4 @@ use promkit::{preset::readline::Readline, suggest::Suggest}; -use std::io; fn main() -> anyhow::Result<()> { let mut p = Readline::default() diff --git a/promkit/examples/readline_loop.rs b/promkit/examples/readline_loop.rs index abda3f0c..8fe9f223 100644 --- a/promkit/examples/readline_loop.rs +++ b/promkit/examples/readline_loop.rs @@ -1,5 +1,4 @@ use promkit::preset::readline::Readline; -use std::io; fn main() -> anyhow::Result<()> { let mut p = Readline::default().prompt()?; diff --git a/promkit/examples/tree.rs b/promkit/examples/tree.rs index 3045ca3e..81123d6b 100644 --- a/promkit/examples/tree.rs +++ b/promkit/examples/tree.rs @@ -1,5 +1,4 @@ use promkit::{preset::tree::Tree, tree::Node}; -use std::io; fn main() -> anyhow::Result<()> { let mut p = Tree::new(Node::try_from(&std::env::current_dir()?.join("src"))?)