diff options
| author | sigoden <sigoden@gmail.com> | 2023-11-08 18:26:38 +0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-11-08 18:26:38 +0800 |
| commit | eb30d90391ef73429589a98f34dfe4d3d88d0a38 (patch) | |
| tree | 3ba569b16048a289d729262ffce475d610e51187 /src/render | |
| parent | a8a504e0f07c2d1c725bace7f0affb468cf0622d (diff) | |
| download | aichat-eb30d90391ef73429589a98f34dfe4d3d88d0a38.tar.gz | |
refactor: improve render (#235)
* refactor: redesign render
- if stdout is not terminal, just write reply text to stdout
- rename repl_render_stream to markdown_stream
- deprecate cmd_render_stream
- use raw_stream to just print streaming reply text
* optimize rendering error
* optimize render_stream
Diffstat (limited to 'src/render')
| -rw-r--r-- | src/render/cmd.rs | 64 | ||||
| -rw-r--r-- | src/render/markdown.rs | 40 | ||||
| -rw-r--r-- | src/render/mod.rs | 43 | ||||
| -rw-r--r-- | src/render/stream.rs (renamed from src/render/repl.rs) | 42 |
4 files changed, 58 insertions, 131 deletions
diff --git a/src/render/cmd.rs b/src/render/cmd.rs deleted file mode 100644 index e942025..0000000 --- a/src/render/cmd.rs +++ /dev/null @@ -1,64 +0,0 @@ -use super::{MarkdownRender, ReplyEvent}; - -use crate::utils::{split_line_sematic, split_line_tail, AbortSignal}; - -use anyhow::Result; -use crossbeam::channel::Receiver; -use textwrap::core::display_width; - -pub fn cmd_render_stream( - rx: &Receiver<ReplyEvent>, - render: &mut MarkdownRender, - abort: &AbortSignal, -) -> Result<()> { - let mut buffer = String::new(); - let mut indent = 0; - loop { - if abort.aborted() { - return Ok(()); - } - if let Ok(evt) = rx.try_recv() { - match evt { - ReplyEvent::Text(text) => { - if text.contains('\n') { - let text = format!("{buffer}{text}"); - let (head, tail) = split_line_tail(&text); - buffer = tail.to_string(); - let output = render.render_with_indent(head, indent); - println!("{}", output); - indent = 0; - } else { - buffer = format!("{buffer}{text}"); - if !(render.is_code() - || buffer.len() < 40 - || buffer.starts_with('#') - || buffer.starts_with('>') - || buffer.starts_with('|')) - { - if let Some((head, remain)) = split_line_sematic(&buffer) { - buffer = remain; - let output = render.render_with_indent(&head, indent); - let (_, tail) = split_line_tail(&output); - if let Some(width) = render.wrap_width() { - if output.contains('\n') { - indent = display_width(tail); - } else { - indent += display_width(&output); - } - indent %= width as usize; - } - print!("{}", output); - } - } - } - } - ReplyEvent::Done => { - let output = render.render_with_indent(&buffer, indent); - println!("{}", output); - break; - } - } - } - } - Ok(()) -} diff --git a/src/render/markdown.rs b/src/render/markdown.rs index f300854..44e24b3 100644 --- a/src/render/markdown.rs +++ b/src/render/markdown.rs @@ -64,17 +64,6 @@ impl MarkdownRender { }) } - pub(crate) const fn is_code(&self) -> bool { - matches!( - self.prev_line_type, - LineType::CodeBegin | LineType::CodeInner - ) - } - - pub(crate) const fn wrap_width(&self) -> Option<u16> { - self.wrap_width - } - pub fn render(&mut self, text: &str) -> String { text.split('\n') .map(|line| self.render_line_mut(line)) @@ -82,16 +71,6 @@ impl MarkdownRender { .join("\n") } - pub fn render_with_indent(&mut self, text: &str, indent: usize) -> String { - let text = format!("{}{}", " ".repeat(indent), text); - let output = self.render(&text); - if output.starts_with('\n') { - output - } else { - output.chars().skip(indent).collect() - } - } - pub fn render_line(&self, line: &str) -> String { let (_, code_syntax, is_code) = self.check_line(line); if is_code { @@ -377,23 +356,4 @@ std::error::Error>> { let output = render.render(TEXT); assert_eq!(TEXT_WRAP_ALL, output); } - - #[test] - fn wrap_with_indent() { - let options = RenderOptions::default(); - let mut render = MarkdownRender::init(options).unwrap(); - render.wrap_width = Some(80); - - let input = "To unzip a file in Rust, you can use the `zip` crate. Here's an example code"; - let output = render.render_with_indent(input, 40); - let expect = - "To unzip a file in Rust, you can use the\n`zip` crate. Here's an example code"; - assert_eq!(output, expect); - - let input = "Unzip a file"; - let output = render.render_with_indent(input, 76); - let expect = "\nUnzip a file"; - - assert_eq!(output, expect); - } } diff --git a/src/render/mod.rs b/src/render/mod.rs index 2b97557..1e20ccc 100644 --- a/src/render/mod.rs +++ b/src/render/mod.rs @@ -1,10 +1,8 @@ -mod cmd; mod markdown; -mod repl; +mod stream; -use self::cmd::cmd_render_stream; pub use self::markdown::{MarkdownRender, RenderOptions}; -use self::repl::repl_render_stream; +use self::stream::{markdown_stream, raw_stream}; use crate::client::Client; use crate::config::GlobalConfig; @@ -13,17 +11,19 @@ use crate::utils::AbortSignal; use anyhow::{Context, Result}; use crossbeam::channel::{unbounded, Sender}; use crossbeam::sync::WaitGroup; +use is_terminal::IsTerminal; use nu_ansi_term::{Color, Style}; +use std::io::stdout; use std::thread::spawn; pub fn render_stream( input: &str, client: &dyn Client, config: &GlobalConfig, - repl: bool, abort: AbortSignal, - wg: WaitGroup, ) -> Result<String> { + let wg = WaitGroup::new(); + let wg_cloned = wg.clone(); let render_options = config.read().get_render_options()?; let mut stream_handler = { let (tx, rx) = unbounded(); @@ -31,33 +31,44 @@ pub fn render_stream( let highlight = config.read().highlight; spawn(move || { let run = move || { - if repl { + if stdout().is_terminal() { let mut render = MarkdownRender::init(render_options)?; - repl_render_stream(&rx, &mut render, &abort) + markdown_stream(&rx, &mut render, &abort) } else { - let mut render = MarkdownRender::init(render_options)?; - cmd_render_stream(&rx, &mut render, &abort) + raw_stream(&rx, &abort) } }; if let Err(err) = run() { render_error(err, highlight); } - drop(wg); + drop(wg_cloned); }); ReplyHandler::new(tx, abort_clone) }; - client.send_message_streaming(input, &mut stream_handler)?; - let buffer = stream_handler.get_buffer(); - Ok(buffer.to_string()) + let ret = client.send_message_streaming(input, &mut stream_handler); + wg.wait(); + let output = stream_handler.get_buffer().to_string(); + match ret { + Ok(_) => { + println!(); + Ok(output) + } + Err(err) => { + if !output.is_empty() { + println!(); + } + Err(err) + } + } } pub fn render_error(err: anyhow::Error, highlight: bool) { let err = format!("{err:?}"); if highlight { let style = Style::new().fg(Color::Red); - println!("{}", style.paint(err.trim())); + eprintln!("{}", style.paint(err)); } else { - println!("{}", err.trim()); + eprintln!("{err}"); } } diff --git a/src/render/repl.rs b/src/render/stream.rs index eeb5420..189862d 100644 --- a/src/render/repl.rs +++ b/src/render/stream.rs @@ -1,6 +1,6 @@ use super::{MarkdownRender, ReplyEvent}; -use crate::utils::{split_line_tail, AbortSignal}; +use crate::utils::AbortSignal; use anyhow::Result; use crossbeam::channel::Receiver; @@ -16,7 +16,7 @@ use std::{ }; use textwrap::core::display_width; -pub fn repl_render_stream( +pub fn markdown_stream( rx: &Receiver<ReplyEvent>, render: &mut MarkdownRender, abort: &AbortSignal, @@ -24,14 +24,33 @@ pub fn repl_render_stream( enable_raw_mode()?; let mut stdout = io::stdout(); - let ret = repl_render_stream_inner(rx, render, abort, &mut stdout); + let ret = markdown_stream_inner(rx, render, abort, &mut stdout); disable_raw_mode()?; ret } -fn repl_render_stream_inner( +pub fn raw_stream(rx: &Receiver<ReplyEvent>, abort: &AbortSignal) -> Result<()> { + loop { + if abort.aborted() { + return Ok(()); + } + if let Ok(evt) = rx.try_recv() { + match evt { + ReplyEvent::Text(text) => { + print!("{}", text); + } + ReplyEvent::Done => { + break; + } + } + } + } + Ok(()) +} + +fn markdown_stream_inner( rx: &Receiver<ReplyEvent>, render: &mut MarkdownRender, abort: &AbortSignal, @@ -104,13 +123,6 @@ fn repl_render_stream_inner( writer.flush()?; } ReplyEvent::Done => { - #[cfg(target_os = "windows")] - let eol = "\n\n"; - #[cfg(not(target_os = "windows"))] - let eol = "\n"; - queue!(writer, style::Print(eol))?; - writer.flush()?; - break; } } @@ -157,6 +169,14 @@ fn print_block(writer: &mut Stdout, text: &str, columns: u16) -> Result<u16> { Ok(num) } +fn split_line_tail(text: &str) -> (&str, &str) { + if let Some((head, tail)) = text.rsplit_once('\n') { + (head, tail) + } else { + ("", text) + } +} + fn need_rows(text: &str, columns: u16) -> u16 { let buffer_width = display_width(text).max(1) as u16; (buffer_width + columns - 1) / columns |
