diff --git a/src/main.rs b/src/main.rs index d73d4c1b..de798ec1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -116,7 +116,7 @@ fn start_directive( let output = client.send_message(input)?; if highlight { let mut markdown_render = MarkdownRender::new(light_theme); - println!("{}", markdown_render.render(&output).trim()); + println!("{}", markdown_render.render_block(&output).trim()); } else { println!("{}", output.trim()); } diff --git a/src/render/cmd.rs b/src/render/cmd.rs index bf7a006a..527c1940 100644 --- a/src/render/cmd.rs +++ b/src/render/cmd.rs @@ -26,7 +26,7 @@ pub fn cmd_render_stream( let mut lines: Vec<&str> = text.split('\n').collect(); buffer = lines.pop().unwrap_or_default().to_string(); let output = lines.join("\n"); - print_now!("{}\n", markdown_render.render(&output)); + print_now!("{}\n", markdown_render.render_block(&output)); } else { buffer = format!("{buffer}{text}"); if !(markdown_render.is_code_block() @@ -36,14 +36,14 @@ pub fn cmd_render_stream( || buffer.starts_with('|')) { if let Some((output, remain)) = split_line(&buffer) { - print_now!("{}", markdown_render.render_line_stateless(&output)); + print_now!("{}", markdown_render.render_line(&output)); buffer = remain; } } } } ReplyStreamEvent::Done => { - let output = markdown_render.render(&buffer); + let output = markdown_render.render_block(&buffer); print_now!("{}\n", output.trim_end()); break; } diff --git a/src/render/markdown.rs b/src/render/markdown.rs index 75904afb..66bb6013 100644 --- a/src/render/markdown.rs +++ b/src/render/markdown.rs @@ -53,14 +53,17 @@ impl MarkdownRender { } } - pub fn render(&mut self, src: &str) -> String { + pub fn render_block(&mut self, src: &str) -> String { src.split('\n') - .map(|line| self.render_line(line).unwrap_or_else(|| line.to_string())) + .map(|line| { + self.render_line_impl(line) + .unwrap_or_else(|| line.to_string()) + }) .collect::>() .join("\n") } - pub fn render_line_stateless(&self, line: &str) -> String { + pub fn render_line(&self, line: &str) -> String { let output = if self.is_code_block() && detect_code_block(line).is_none() { self.render_code_line(line) } else { @@ -76,7 +79,7 @@ impl MarkdownRender { ) } - fn render_line(&mut self, line: &str) -> Option { + fn render_line_impl(&mut self, line: &str) -> Option { if let Some(lang) = detect_code_block(line) { match self.prev_line_type { LineType::Normal | LineType::CodeEnd => { diff --git a/src/render/repl.rs b/src/render/repl.rs index 2bdf3b25..eb507503 100644 --- a/src/render/repl.rs +++ b/src/render/repl.rs @@ -39,51 +39,62 @@ fn repl_render_stream_inner( writer: &mut Stdout, ) -> Result<()> { let mut last_tick = Instant::now(); - let tick_rate = Duration::from_millis(100); + let tick_rate = Duration::from_millis(50); let mut buffer = String::new(); let mut markdown_render = MarkdownRender::new(light_theme); - let terminal_columns = terminal::size()?.0; + let columns = terminal::size()?.0; loop { if abort.aborted() { return Ok(()); } if let Ok(evt) = rx.try_recv() { - recover_cursor(writer, terminal_columns, &buffer)?; - match evt { ReplyStreamEvent::Text(text) => { + if !buffer.is_empty() { + let buffer_width = buffer.width() as u16; + let need_rows = (buffer_width + columns - 1) / columns; + let (col, row) = cursor::position()?; + + if row + 1 >= need_rows { + if col == 0 { + queue!(writer, cursor::MoveTo(0, row - need_rows))?; + } else { + queue!(writer, cursor::MoveTo(0, row + 1 - need_rows))?; + } + } else { + queue!( + writer, + terminal::ScrollUp(need_rows - 1 - row), + cursor::MoveTo(0, 0) + )?; + } + } + if text.contains('\n') { let text = format!("{buffer}{text}"); let mut lines: Vec<&str> = text.split('\n').collect(); buffer = lines.pop().unwrap_or_default().to_string(); - let output = markdown_render.render(&lines.join("\n")); + let output = markdown_render.render_block(&lines.join("\n")); for line in output.split('\n') { queue!( writer, style::Print(line), style::Print("\n"), - cursor::MoveLeft(terminal_columns), + cursor::MoveLeft(columns), )?; } queue!(writer, style::Print(&buffer),)?; } else { buffer = format!("{buffer}{text}"); - let output = markdown_render.render_line_stateless(&buffer); + let output = markdown_render.render_line(&buffer); queue!(writer, style::Print(&output))?; } + writer.flush()?; } ReplyStreamEvent::Done => { - let output = markdown_render.render_line_stateless(&buffer); - let trimed_output = output.trim_end(); - if !trimed_output.is_empty() { - queue!(writer, style::Print(output.trim_end()))?; - writer.flush()?; - } - - let (_, row) = cursor::position()?; - queue!(writer, cursor::MoveTo(0, row), style::Print("\n\n"))?; + queue!(writer, style::Print("\n"))?; writer.flush()?; break; @@ -117,21 +128,3 @@ fn repl_render_stream_inner( } Ok(()) } - -fn recover_cursor(writer: &mut Stdout, terminal_columns: u16, buffer: &str) -> Result<()> { - let buffer_rows = (u16::try_from(buffer.width()).unwrap_or(u16::MAX) + terminal_columns - 1) - / terminal_columns; - let (_, row) = cursor::position()?; - if buffer_rows == 0 { - queue!(writer, cursor::MoveTo(0, row))?; - } else if row + 1 >= buffer_rows { - queue!(writer, cursor::MoveTo(0, row + 1 - buffer_rows))?; - } else { - queue!( - writer, - terminal::ScrollUp(buffer_rows - 1 - row), - cursor::MoveTo(0, 0) - )?; - } - Ok(()) -}