From 94a589942ce0962c51ebf505b93c7b49c8624402 Mon Sep 17 00:00:00 2001 From: junzhuo Date: Fri, 20 Sep 2024 06:06:37 +0800 Subject: [PATCH] Reorganize `split-highlight` feature gating logic --- Cargo.toml | 8 +- examples/continuation_prompt.rs | 11 +- examples/example.rs | 4 +- examples/highlight_python.rs | 66 ++++++++ examples/read_password.rs | 12 +- rustyline-derive/src/lib.rs | 22 ++- src/highlight.rs | 267 +++++++++++++++++--------------- src/tty/unix.rs | 29 +++- src/tty/windows.rs | 4 +- 9 files changed, 265 insertions(+), 158 deletions(-) create mode 100644 examples/highlight_python.rs diff --git a/Cargo.toml b/Cargo.toml index 2f70f86f0..ea722c55a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ members = ["rustyline-derive"] ansi-str = { version = "0.8.0", optional = true } # ansi_str::Style is immutable so we use anstyle::Style instead anstyle = { version = "1.0.8", optional = true } +syntect = { version = "5.2.0", optional = true } bitflags = "2.6" cfg-if = "1.0" # For file completion @@ -71,7 +72,7 @@ with-sqlite-history = ["rusqlite"] with-fuzzy = ["skim"] case_insensitive_history_search = ["regex"] # For continuation prompt, indentation, scrolling -split-highlight = [] +split-highlight = ["ansi-str", "anstyle", "syntect"] continuation-prompt = ["split-highlight"] [[example]] name = "custom_key_bindings" @@ -87,7 +88,10 @@ name = "input_multiline" required-features = ["custom-bindings", "derive"] [[example]] name = "continuation_prompt" -required-features = ["custom-bindings", "derive", "continuation-prompt", "anstyle"] +required-features = ["custom-bindings", "derive", "continuation-prompt"] +[[example]] +name = "highlight_python" +required-features = ["custom-bindings", "derive", "continuation-prompt"] [[example]] name = "input_validation" required-features = ["derive"] diff --git a/examples/continuation_prompt.rs b/examples/continuation_prompt.rs index e0c6c31ba..4169ee621 100644 --- a/examples/continuation_prompt.rs +++ b/examples/continuation_prompt.rs @@ -1,4 +1,4 @@ -use rustyline::highlight::{AnsiStyle, StyledBlock}; +use rustyline::highlight::StyledBlock; use rustyline::{Cmd, Editor, EventHandler, KeyCode, KeyEvent, Modifiers, Result}; use rustyline::{Completer, Helper, Hinter, Validator}; use std::borrow::Cow; @@ -6,6 +6,7 @@ use std::borrow::Cow; struct InputHelper; impl rustyline::highlight::Highlighter for InputHelper { + #[cfg(feature = "continuation-prompt")] fn continuation_prompt<'b, 's: 'b, 'p: 'b>( &'s self, prompt: &'p str, @@ -18,14 +19,6 @@ impl rustyline::highlight::Highlighter for InputHelper { Cow::Owned(prompt.replace('>', ".")) } } - fn highlight_line<'l>( - &self, - line: &'l str, - _pos: usize, - ) -> impl Iterator> { - line.split('\n') - .map(|l| core::iter::once((AnsiStyle::default(), l))) - } } fn main() -> Result<()> { let h = InputHelper {}; diff --git a/examples/example.rs b/examples/example.rs index f2f5c79af..132f369d3 100644 --- a/examples/example.rs +++ b/examples/example.rs @@ -37,12 +37,12 @@ impl Highlighter for MyHelper { Owned("\x1b[1m".to_owned() + hint + "\x1b[m") } - #[cfg(any(not(feature = "split-highlight"), feature = "ansi-str"))] + #[cfg(not(feature = "split-highlight"))] fn highlight<'l>(&self, line: &'l str, pos: usize) -> Cow<'l, str> { self.highlighter.highlight(line, pos) } - #[cfg(all(feature = "split-highlight", not(feature = "ansi-str")))] + #[cfg(all(feature = "split-highlight", not(feature = "continuation-prompt")))] fn highlight_line<'l>( &self, line: &'l str, diff --git a/examples/highlight_python.rs b/examples/highlight_python.rs new file mode 100644 index 000000000..be063f85a --- /dev/null +++ b/examples/highlight_python.rs @@ -0,0 +1,66 @@ +use rustyline::highlight::StyledBlock; +use rustyline::{Cmd, Editor, EventHandler, KeyCode, KeyEvent, Modifiers, Result}; +use rustyline::{Completer, Helper, Hinter, Validator}; +use syntect::highlighting::{Theme, ThemeSet}; +use syntect::parsing::SyntaxSet; + +use std::borrow::Cow; +#[derive(Completer, Helper, Hinter, Validator)] +struct InputHelper { + theme: Theme, + syntax_set: SyntaxSet, +} + +impl rustyline::highlight::Highlighter for InputHelper { + fn highlight_char(&self, _line: &str, _pos: usize, _forced: bool) -> bool { + true + } + #[cfg(feature = "continuation-prompt")] + fn continuation_prompt<'b, 's: 'b, 'p: 'b>( + &'s self, + _prompt: &'p str, + _default: bool, + ) -> Cow<'b, str> { + Cow::Borrowed("... ") + } + #[cfg(all(feature = "split-highlight", feature = "continuation-prompt",))] + fn highlight_lines<'l>( + &self, + lines: &'l str, + _pos: usize, + ) -> impl Iterator> { + use syntect::easy::HighlightLines; + use syntect::highlighting::Style; + let syntax = self.syntax_set.find_syntax_by_extension("py").unwrap(); + let mut highlighter = HighlightLines::new(syntax, &self.theme); + lines.split('\n').map(move |line| { + highlighter + .highlight_line(line, &self.syntax_set) + .unwrap_or(vec![(Style::default(), line)]) + .into_iter() + .map(|(mut s, token)| { + s.background.a = 0; + (s, token) + }) + }) + } +} +fn main() -> Result<()> { + let h = InputHelper { + theme: ThemeSet::load_defaults() + .themes + .remove("base16-ocean.dark") + .unwrap(), + syntax_set: SyntaxSet::load_defaults_newlines(), + }; + let mut rl = Editor::new()?; + rl.set_helper(Some(h)); + rl.bind_sequence( + KeyEvent(KeyCode::Char('s'), Modifiers::CTRL), + EventHandler::Simple(Cmd::Newline), + ); + let input = rl.readline("python REPL example\n>>> ")?; + println!("Input: \n{input}"); + + Ok(()) +} diff --git a/examples/read_password.rs b/examples/read_password.rs index 249b7d8ed..8676734f4 100644 --- a/examples/read_password.rs +++ b/examples/read_password.rs @@ -9,7 +9,7 @@ struct MaskingHighlighter { } impl Highlighter for MaskingHighlighter { - #[cfg(any(not(feature = "split-highlight"), feature = "ansi-str"))] + #[cfg(not(feature = "split-highlight"))] fn highlight<'l>(&self, line: &'l str, _pos: usize) -> std::borrow::Cow<'l, str> { use unicode_width::UnicodeWidthStr; if self.masking { @@ -19,7 +19,7 @@ impl Highlighter for MaskingHighlighter { } } - #[cfg(all(feature = "split-highlight", not(feature = "ansi-str")))] + #[cfg(all(feature = "split-highlight", not(feature = "continuation-prompt")))] fn highlight_line<'l>( &self, line: &'l str, @@ -27,13 +27,9 @@ impl Highlighter for MaskingHighlighter { ) -> impl Iterator { use unicode_width::UnicodeWidthStr; if self.masking { - vec![( - rustyline::highlight::AnsiStyle::default(), - " ".repeat(line.width()), - )] - .into_iter() + vec![(anstyle::Style::default(), " ".repeat(line.width()))].into_iter() } else { - vec![(rustyline::highlight::AnsiStyle::default(), line.to_owned())].into_iter() + vec![(anstyle::Style::default(), line.to_owned())].into_iter() } } diff --git a/rustyline-derive/src/lib.rs b/rustyline-derive/src/lib.rs index 2672136f7..ee5488a2a 100644 --- a/rustyline-derive/src/lib.rs +++ b/rustyline-derive/src/lib.rs @@ -102,12 +102,12 @@ pub fn highlighter_macro_derive(input: TokenStream) -> TokenStream { quote! { #[automatically_derived] impl #impl_generics ::rustyline::highlight::Highlighter for #name #ty_generics #where_clause { - #[cfg(any(not(feature = "split-highlight"), feature = "ansi-str"))] + #[cfg(not(feature = "split-highlight"))] fn highlight<'l>(&self, line: &'l str, pos: usize) -> ::std::borrow::Cow<'l, str> { ::rustyline::highlight::Highlighter::highlight(&self.#field_name_or_index, line, pos) } - #[cfg(all(feature = "split-highlight", not(feature = "ansi-str")))] + #[cfg(all(feature = "split-highlight", not(feature = "continuation-prompt")))] fn highlight_line<'l>( &self, line: &'l str, @@ -116,6 +116,24 @@ pub fn highlighter_macro_derive(input: TokenStream) -> TokenStream { ::rustyline::highlight::Highlighter::highlight_line(&self.#field_name_or_index, line, pos) } + #[cfg(all(feature = "split-highlight", feature = "continuation-prompt"))] + fn highlight_lines<'l>( + &self, + lines: &'l str, + pos: usize, + ) -> impl Iterator> { + ::rustyline::highlight::Highlighter::highlight_lines(&self.#field_name_or_index, lines, pos) + } + + #[cfg(feature = "continuation-prompt")] + fn continuation_prompt<'b, 's: 'b, 'p: 'b>( + &'s self, + prompt: &'p str, + default: bool, + ) -> ::std::borrow::Cow<'b, str> { + ::rustyline::highlight::Highlighter::continuation_prompt(&self.#field_name_or_index, prompt, default) + } + fn highlight_prompt<'b, 's: 'b, 'p: 'b>( &'s self, prompt: &'p str, diff --git a/src/highlight.rs b/src/highlight.rs index 8bed05a0b..8be350d72 100644 --- a/src/highlight.rs +++ b/src/highlight.rs @@ -1,15 +1,16 @@ //! Syntax highlighting use crate::config::CompletionType; +#[cfg(feature = "split-highlight")] +use core::fmt::Display; use std::borrow::Cow::{self, Borrowed}; use std::cell::Cell; #[cfg(feature = "split-highlight")] -use std::fmt::Display; - +pub use {ansi_str, anstyle, syntect}; /// ANSI style #[cfg(feature = "split-highlight")] #[cfg_attr(docsrs, doc(cfg(feature = "split-highlight")))] -pub trait Style: Default { +pub trait Style { /// Produce a ansi sequences which sets the graphic mode fn start(&self) -> impl Display; /// Produce a ansi sequences which ends the graphic mode @@ -27,8 +28,8 @@ impl Style for () { } } -/*#[cfg(feature = "ansi-str")] -#[cfg_attr(docsrs, doc(cfg(feature = "ansi-str")))] +#[cfg(feature = "split-highlight")] +#[cfg_attr(docsrs, doc(cfg(feature = "split-highlight")))] impl Style for ansi_str::Style { fn start(&self) -> impl Display { self.start() @@ -37,9 +38,10 @@ impl Style for ansi_str::Style { fn end(&self) -> impl Display { self.end() } -}*/ -#[cfg(feature = "anstyle")] -#[cfg_attr(docsrs, doc(cfg(feature = "anstyle")))] +} + +#[cfg(feature = "split-highlight")] +#[cfg_attr(docsrs, doc(cfg(feature = "split-highlight")))] impl Style for anstyle::Style { fn start(&self) -> impl Display { self.render() @@ -50,13 +52,47 @@ impl Style for anstyle::Style { } } -/// ANSI Style -#[cfg(feature = "anstyle")] -#[cfg_attr(docsrs, doc(cfg(feature = "anstyle")))] -pub type AnsiStyle = anstyle::Style; -/// ANSI Style -#[cfg(not(feature = "anstyle"))] -pub type AnsiStyle = (); +#[cfg(feature = "split-highlight")] +#[cfg_attr(docsrs, doc(cfg(feature = "split-highlight")))] +impl Style for syntect::highlighting::Style { + fn start(&self) -> impl Display { + /// Display wrapper for `syntect::highlighting::Style` + struct StyleDisplay<'a>(&'a syntect::highlighting::Style); + impl Display for StyleDisplay<'_> { + #[inline] + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use syntect::highlighting::FontStyle; + let fg = &self.0.foreground; + let bg = &self.0.background; + if fg.a != 0 { + write!(f, "\x1b[38;2;{};{};{}m", fg.r, fg.g, fg.b)?; + } + if bg.a != 0 { + write!(f, "\x1b[48;2;{};{};{}m", bg.r, bg.g, bg.b)?; + } + if self.0.font_style.contains(FontStyle::BOLD) { + f.write_str("\x1b[1m")?; + } + if self.0.font_style.contains(FontStyle::UNDERLINE) { + f.write_str("\x1b[4m")?; + } + if self.0.font_style.contains(FontStyle::ITALIC) { + f.write_str("\x1b[3m")?; + } + Ok(()) + } + } + StyleDisplay(self) + } + + fn end(&self) -> impl Display { + if self != &Self::default() { + "\x1B[0m" + } else { + "" + } + } +} /// Styled text #[cfg(feature = "split-highlight")] @@ -73,8 +109,9 @@ pub trait StyledBlock { where Self: Sized; } -/*#[cfg(feature = "ansi-str")] -#[cfg_attr(docsrs, doc(cfg(feature = "ansi-str")))] + +#[cfg(feature = "split-highlight")] +#[cfg_attr(docsrs, doc(cfg(feature = "split-highlight")))] impl StyledBlock for ansi_str::AnsiBlock<'_> { type Style = ansi_str::Style; @@ -85,11 +122,12 @@ impl StyledBlock for ansi_str::AnsiBlock<'_> { fn style(&self) -> &Self::Style { self.style() } -}*/ +} + #[cfg(feature = "split-highlight")] #[cfg_attr(docsrs, doc(cfg(feature = "split-highlight")))] -impl StyledBlock for (AnsiStyle, &str) { - type Style = AnsiStyle; +impl StyledBlock for (S, &str) { + type Style = S; fn text(&self) -> &str { self.1 @@ -99,9 +137,10 @@ impl StyledBlock for (AnsiStyle, &str) { &self.0 } } -#[cfg(all(feature = "split-highlight", not(feature = "ansi-str")))] -impl StyledBlock for (AnsiStyle, String) { - type Style = AnsiStyle; +#[cfg(feature = "split-highlight")] +#[cfg_attr(docsrs, doc(cfg(feature = "split-highlight")))] +impl StyledBlock for (S, String) { + type Style = S; fn text(&self) -> &str { &self.1 @@ -122,74 +161,37 @@ pub trait Highlighter { /// /// For example, you can implement /// [blink-matching-paren](https://www.gnu.org/software/bash/manual/html_node/Readline-Init-File-Syntax.html). - #[cfg(any(not(feature = "split-highlight"), feature = "ansi-str"))] - #[cfg_attr( - docsrs, - doc(cfg(any(not(feature = "split-highlight"), feature = "ansi-str"))) - )] + #[cfg(not(feature = "split-highlight"))] + #[cfg_attr(docsrs, doc(cfg(not(feature = "split-highlight"))))] fn highlight<'l>(&self, line: &'l str, pos: usize) -> Cow<'l, str> { let _ = pos; Borrowed(line) } - /// Takes the `prompt` and - /// returns the highlighted continuation-prompt (with ANSI color). - /// - /// The continuation-prompt should be a single line, and - /// the same length as the last line of prompt - #[cfg(feature = "continuation-prompt")] - fn continuation_prompt<'b, 's: 'b, 'p: 'b>( - &'s self, - prompt: &'p str, - default: bool, - ) -> Cow<'b, str> { - let _ = default; - if let Some(pos) = prompt.rfind('\n') { - Borrowed(&prompt[pos + 1..]) - } else { - Borrowed(prompt) - } - } - - /// Takes the currently edited `line` with the cursor `pos`ition and - /// returns the styled blocks. - #[cfg(all( - feature = "split-highlight", - feature = "continuation-prompt", - not(feature = "ansi-str") - ))] + /// Takes the currently edited `lines` with the cursor `pos`ition and + /// returns the styled blocks of each line. + #[cfg(all(feature = "split-highlight", feature = "continuation-prompt",))] #[cfg_attr( docsrs, - doc(cfg(all( - feature = "split-highlight", - feature = "continuation-prompt", - not(feature = "ansi-str") - ))) + doc(cfg(all(feature = "split-highlight", feature = "continuation-prompt",))) )] - fn highlight_line<'l>( + fn highlight_lines<'l>( &self, - line: &'l str, + lines: &'l str, pos: usize, ) -> impl Iterator> { - let _ = (line, pos); - line.split('\n') - .map(|l| core::iter::once((AnsiStyle::default(), l))) + let _ = pos; + lines + .split('\n') + .map(|l| core::iter::once((anstyle::Style::new(), l))) } /// Takes the currently edited `line` with the cursor `pos`ition and /// returns the styled blocks. - #[cfg(all( - feature = "split-highlight", - not(feature = "continuation-prompt"), - not(feature = "ansi-str") - ))] + #[cfg(all(feature = "split-highlight", not(feature = "continuation-prompt"),))] #[cfg_attr( docsrs, - doc(cfg(all( - feature = "split-highlight", - not(feature = "continuation-prompt"), - not(feature = "ansi-str") - ))) + doc(cfg(all(feature = "split-highlight", not(feature = "continuation-prompt"),))) )] fn highlight_line<'l>( &self, @@ -197,7 +199,26 @@ pub trait Highlighter { pos: usize, ) -> impl Iterator { let _ = (line, pos); - core::iter::once((AnsiStyle::default(), line)) + core::iter::once((anstyle::Style::new(), line)) + } + + /// Takes the `prompt` and + /// returns the highlighted continuation-prompt (with ANSI color). + /// + /// The continuation-prompt should be a single line, and + /// the same length as the last line of prompt + #[cfg(feature = "continuation-prompt")] + fn continuation_prompt<'b, 's: 'b, 'p: 'b>( + &'s self, + prompt: &'p str, + default: bool, + ) -> Cow<'b, str> { + let _ = default; + if let Some(pos) = prompt.rfind('\n') { + Borrowed(&prompt[pos + 1..]) + } else { + Borrowed(prompt) + } } /// Takes the `prompt` and @@ -243,16 +264,12 @@ pub trait Highlighter { impl Highlighter for () {} impl<'r, H: Highlighter> Highlighter for &'r H { - #[cfg(any(not(feature = "split-highlight"), feature = "ansi-str"))] + #[cfg(not(feature = "split-highlight"))] fn highlight<'l>(&self, line: &'l str, pos: usize) -> Cow<'l, str> { (**self).highlight(line, pos) } - #[cfg(all( - feature = "split-highlight", - not(feature = "continuation-prompt"), - not(feature = "ansi-str") - ))] + #[cfg(all(feature = "split-highlight", not(feature = "continuation-prompt"),))] fn highlight_line<'l>( &self, line: &'l str, @@ -261,20 +278,15 @@ impl<'r, H: Highlighter> Highlighter for &'r H { (**self).highlight_line(line, pos) } - /// Takes the currently edited `line` with the cursor `pos`ition and - /// returns the styled blocks. - #[cfg(all( - feature = "split-highlight", - feature = "continuation-prompt", - feature = "continuation-prompt", - not(feature = "ansi-str") - ))] - fn highlight_line<'l>( + /// Takes the currently edited `lines` with the cursor `pos`ition and + /// returns the styled blocks of each line. + #[cfg(all(feature = "split-highlight", feature = "continuation-prompt",))] + fn highlight_lines<'l>( &self, - line: &'l str, + lines: &'l str, pos: usize, ) -> impl Iterator> { - (**self).highlight_line(line, pos) + (**self).highlight_lines(lines, pos) } fn highlight_prompt<'b, 's: 'b, 'p: 'b>( @@ -326,13 +338,8 @@ impl MatchingBracketHighlighter { } } -#[cfg(any( - not(feature = "split-highlight"), - feature = "anstyle", - feature = "ansi-str" -))] impl Highlighter for MatchingBracketHighlighter { - #[cfg(any(not(feature = "split-highlight"), feature = "ansi-str"))] + #[cfg(not(feature = "split-highlight"))] fn highlight<'l>(&self, line: &'l str, _pos: usize) -> Cow<'l, str> { if line.len() <= 1 { return Borrowed(line); @@ -348,58 +355,66 @@ impl Highlighter for MatchingBracketHighlighter { Borrowed(line) } - #[cfg(all( - feature = "split-highlight", - not(feature = "continuation-prompt"), - not(feature = "ansi-str") - ))] + #[cfg(all(feature = "split-highlight", not(feature = "continuation-prompt"),))] fn highlight_line<'l>( &self, line: &'l str, _pos: usize, ) -> impl Iterator { if line.len() <= 1 { - return vec![(AnsiStyle::default(), line)].into_iter(); + return vec![(anstyle::Style::new(), line)].into_iter(); } if let Some((bracket, pos)) = self.bracket.get() { if let Some((_, idx)) = find_matching_bracket(line, pos, bracket) { return vec![ - (AnsiStyle::default(), &line[0..idx]), + (anstyle::Style::new(), &line[0..idx]), (self.style, &line[idx..=idx]), - (AnsiStyle::default(), &line[idx + 1..]), + (anstyle::Style::new(), &line[idx + 1..]), ] .into_iter(); } } - vec![(AnsiStyle::default(), line)].into_iter() + vec![(anstyle::Style::new(), line)].into_iter() } - #[cfg(all( - feature = "split-highlight", - feature = "continuation-prompt", - feature = "continuation-prompt", - not(feature = "ansi-str") - ))] - fn highlight_line<'l>( + #[cfg(all(feature = "split-highlight", feature = "continuation-prompt",))] + fn highlight_lines<'l>( &self, - line: &'l str, + lines: &'l str, _pos: usize, ) -> impl Iterator> { - if line.len() <= 1 { - return vec![vec![(AnsiStyle::default(), line)].into_iter()].into_iter(); + if lines.len() <= 1 { + return vec![vec![(anstyle::Style::new(), lines)].into_iter()].into_iter(); } if let Some((bracket, pos)) = self.bracket.get() { - if let Some((_, idx)) = find_matching_bracket(line, pos, bracket) { - return vec![vec![ - (AnsiStyle::default(), &line[0..idx]), - (self.style, &line[idx..=idx]), - (AnsiStyle::default(), &line[idx + 1..]), - ] - .into_iter()] - .into_iter(); + if let Some((_, idx)) = find_matching_bracket(lines, pos, bracket) { + let mut line_bgn_idx = 0; + return lines + .split('\n') + .map(|line| { + let line_end_idx = line_bgn_idx + line.len(); + let v = if line_bgn_idx <= idx && line_end_idx >= idx { + let shifted_idx = idx - line_bgn_idx; + vec![ + (anstyle::Style::new(), &line[0..shifted_idx]), + (self.style, &line[shifted_idx..=shifted_idx]), + (anstyle::Style::new(), &line[shifted_idx + 1..]), + ] + } else { + vec![(anstyle::Style::new(), line)] + }; + line_bgn_idx = line_end_idx + 1; + v.into_iter() + }) + .collect::>() + .into_iter(); } } - vec![vec![(AnsiStyle::default(), line)].into_iter()].into_iter() + lines + .split('\n') + .map(|line| vec![(anstyle::Style::new(), line)].into_iter()) + .collect::>() + .into_iter() } fn highlight_char(&self, line: &str, pos: usize, forced: bool) -> bool { diff --git a/src/tty/unix.rs b/src/tty/unix.rs index 8a479d85d..269860b2f 100644 --- a/src/tty/unix.rs +++ b/src/tty/unix.rs @@ -1003,12 +1003,12 @@ impl Renderer for PosixRenderer { .push_str(&highlighter.highlight_prompt(prompt, default_prompt)); // display the input line cfg_if::cfg_if! { - if #[cfg(any(not(feature = "split-highlight"), feature = "ansi-str"))] { + if #[cfg(not(feature = "split-highlight"))] { self.buffer .push_str(&highlighter.highlight(line, line.pos())); } else if #[cfg(feature = "continuation-prompt")] { use crate::highlight::{Style, StyledBlock}; - let mut iter = highlighter.highlight_line(line, line.pos()); + let mut iter = highlighter.highlight_lines(line, line.pos()); if let Some(fisrt_line_blocks) = iter.next() { for sb in fisrt_line_blocks { let style = sb.style(); @@ -1735,14 +1735,29 @@ mod test { line.insert('a', out.cols - prompt_size.col + 1, &mut NoListener) ); let new_layout = out.compute_layout(prompt_size, default_prompt, &line, None); - assert_eq!(Position { col: 1, row: 1 }, new_layout.cursor); + cfg_if::cfg_if! { + if #[cfg(feature = "continuation-prompt")] { + assert_eq!(Position { col: 3, row: 1 }, new_layout.cursor); + } else { + assert_eq!(Position { col: 1, row: 1 }, new_layout.cursor); + } + } assert_eq!(new_layout.cursor, new_layout.end); out.refresh_line::<()>(prompt, &line, None, &old_layout, &new_layout, None) .unwrap(); #[rustfmt::skip] - assert_eq!( - "\r\u{1b}[K> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\u{1b}[1C", - out.buffer - ); + cfg_if::cfg_if!{ + if #[cfg(feature = "continuation-prompt")] { + assert_eq!( + "\r\u{1b}[K> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\u{1b}[3C", + out.buffer + ); + } else { + assert_eq!( + "\r\u{1b}[K> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\u{1b}[1C", + out.buffer + ); + } + } } } diff --git a/src/tty/windows.rs b/src/tty/windows.rs index f5bbc80fe..178c565ee 100644 --- a/src/tty/windows.rs +++ b/src/tty/windows.rs @@ -450,12 +450,12 @@ impl Renderer for ConsoleRenderer { col = self.wrap_at_eol(&highlighter.highlight_prompt(prompt, default_prompt), col); // append the input line cfg_if::cfg_if! { - if #[cfg(any(not(feature = "split-highlight"), feature = "ansi-str"))] { + if #[cfg(not(feature = "split-highlight"))] { col = self.wrap_at_eol(&highlighter.highlight(line, line.pos()), col); } else if #[cfg(feature = "continuation-prompt")] { use std::fmt::Write; use crate::highlight::{Style, StyledBlock}; - let mut iter = highlighter.highlight_line(line, line.pos()); + let mut iter = highlighter.highlight_lines(line, line.pos()); if let Some(fisrt_line_blocks) = iter.next() { for sb in fisrt_line_blocks { let style = sb.style();