Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refractor: group colors #802

Draft
wants to merge 11 commits into
base: main
Choose a base branch
from
82 changes: 64 additions & 18 deletions src/engine.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::path::PathBuf;

use itertools::Itertools;
use nu_ansi_term::{Color, Style};
use nu_ansi_term::Style;

use crate::{enums::ReedlineRawEvent, CursorConfig};
#[cfg(feature = "bashisms")]
Expand Down Expand Up @@ -39,6 +39,7 @@ use {
cursor::{SetCursorStyle, Show},
event,
event::{Event, KeyCode, KeyEvent, KeyModifiers},
style::Color,
terminal, QueueableCommand,
},
std::{
Expand Down Expand Up @@ -132,15 +133,12 @@ pub struct Reedline {
// Highlight the edit buffer
highlighter: Box<dyn Highlighter>,

// Style used for visual selection
visual_selection_style: Style,

// Showcase hints based on various strategies (history, language-completion, spellcheck, etc)
hinter: Option<Box<dyn Hinter>>,
hide_hints: bool,

// Use ansi coloring or not
use_ansi_coloring: bool,
theme: ReedlineTheme,

// Current working directory as defined by the application. If set, it will
// override the actual working directory of the process.
Expand Down Expand Up @@ -186,6 +184,31 @@ impl Drop for Reedline {
}
}

pub struct ReedlineTheme {
pub visual_selection: Style,
pub use_ansi_coloring: bool,
/// The color for the prompt, indicator, and right prompt
pub prompt: Color,
pub prompt_multiline: nu_ansi_term::Color,
pub indicator: Color,
pub prompt_right: Color,
}

impl Default for ReedlineTheme {
fn default() -> Self {
Self {
visual_selection: Style::new()
.fg(nu_ansi_term::Color::Black)
.on(nu_ansi_term::Color::LightGray),
use_ansi_coloring: true,
prompt: Color::Green,
prompt_multiline: nu_ansi_term::Color::LightBlue,
indicator: Color::Cyan,
prompt_right: Color::AnsiValue(5),
}
}
}

impl Reedline {
const FILTERED_ITEM_ID: HistoryItemId = HistoryItemId(i64::MAX);

Expand All @@ -195,7 +218,6 @@ impl Reedline {
let history = Box::<FileBackedHistory>::default();
let painter = Painter::new(std::io::BufWriter::new(std::io::stderr()));
let buffer_highlighter = Box::<ExampleHighlighter>::default();
let visual_selection_style = Style::new().on(Color::LightGray);
let completer = Box::<DefaultCompleter>::default();
let hinter = None;
let validator = None;
Expand Down Expand Up @@ -223,11 +245,10 @@ impl Reedline {
quick_completions: false,
partial_completions: false,
highlighter: buffer_highlighter,
visual_selection_style,
hinter,
hide_hints: false,
validator,
use_ansi_coloring: true,
theme: ReedlineTheme::default(),
cwd: None,
menus: Vec::new(),
buffer_editor: None,
Expand Down Expand Up @@ -361,10 +382,37 @@ impl Reedline {
/// and in the command line syntax highlighting.
#[must_use]
pub fn with_ansi_colors(mut self, use_ansi_coloring: bool) -> Self {
self.use_ansi_coloring = use_ansi_coloring;
self.theme.use_ansi_coloring = use_ansi_coloring;
self
}

/// A builder which sets the color to use for the prompt.
#[must_use]
pub fn with_prompt_color(mut self, color: Color) -> Self {
self.theme.prompt = color;
self
}

/// A builder which sets the color to use for the multiline prompt.
#[must_use]
pub fn with_prompt_multiline_color(mut self, color: nu_ansi_term::Color) -> Self {
self.theme.prompt_multiline = color;
self
}

/// A builder which sets the indicator color to use for the prompt.
#[must_use]
pub fn with_indicator_color(mut self, color: Color) -> Self {
self.theme.indicator = color;
self
}

/// A builder which sets the color to use for the right side of the prompt.
#[must_use]
pub fn with_prompt_right_color(mut self, color: Color) -> Self {
self.theme.prompt_right = color;
self
}
/// Update current working directory.
#[must_use]
pub fn with_cwd(mut self, cwd: Option<String>) -> Self {
Expand Down Expand Up @@ -397,7 +445,7 @@ impl Reedline {
/// A builder that configures the style used for visual selection
#[must_use]
pub fn with_visual_selection_style(mut self, style: Style) -> Self {
self.visual_selection_style = style;
self.theme.visual_selection = style;
self
}

Expand Down Expand Up @@ -1700,7 +1748,7 @@ impl Reedline {
let res_string = self.history_cursor.string_at_cursor().unwrap_or_default();

// Highlight matches
let res_string = if self.use_ansi_coloring {
let res_string = if self.theme.use_ansi_coloring {
let match_highlighter = SimpleMatchHighlighter::new(substring);
let styled = match_highlighter.highlight(&res_string, 0);
styled.render_simple()
Expand All @@ -1718,11 +1766,10 @@ impl Reedline {
);

self.painter.repaint_buffer(
prompt,
&lines,
self.prompt_edit_mode(),
None,
self.use_ansi_coloring,
&self.theme,
&self.cursor_shapes,
)?;
}
Expand All @@ -1741,13 +1788,13 @@ impl Reedline {
.highlighter
.highlight(buffer_to_paint, cursor_position_in_buffer);
if let Some((from, to)) = self.editor.get_selection() {
styled_text.style_range(from, to, self.visual_selection_style);
styled_text.style_range(from, to, self.theme.visual_selection);
}

let (before_cursor, after_cursor) = styled_text.render_around_insertion_point(
cursor_position_in_buffer,
prompt,
self.use_ansi_coloring,
&self.theme,
);

let hint: String = if self.hints_active() {
Expand All @@ -1756,7 +1803,7 @@ impl Reedline {
buffer_to_paint,
cursor_position_in_buffer,
self.history.as_ref(),
self.use_ansi_coloring,
self.theme.use_ansi_coloring,
&self.cwd.clone().unwrap_or_else(|| {
std::env::current_dir()
.unwrap_or_default()
Expand Down Expand Up @@ -1801,11 +1848,10 @@ impl Reedline {
let menu = self.menus.iter().find(|menu| menu.is_active());

self.painter.repaint_buffer(
prompt,
&lines,
self.prompt_edit_mode(),
menu,
self.use_ansi_coloring,
&self.theme,
&self.cursor_shapes,
)
}
Expand Down
56 changes: 23 additions & 33 deletions src/painting/painter.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
use crate::{CursorConfig, PromptEditMode, PromptViMode};
use crate::{engine::ReedlineTheme, CursorConfig, PromptEditMode, PromptViMode};

use {
super::utils::{coerce_crlf, line_width},
crate::{
menu::{Menu, ReedlineMenu},
painting::PromptLines,
Prompt,
},
crossterm::{
cursor::{self, MoveTo, RestorePosition, SavePosition},
Expand All @@ -17,7 +16,7 @@ use {
std::ops::RangeInclusive,
};
#[cfg(feature = "external_printer")]
use {crate::LineBuffer, crossterm::cursor::MoveUp};
use {crate::LineBuffer, crate::Prompt, crossterm::cursor::MoveUp};

// Returns a string that skips N number of lines with the next offset of lines
// An offset of 0 would return only one line after skipping the required lines
Expand Down Expand Up @@ -185,11 +184,10 @@ impl Painter {
/// the screen.
pub(crate) fn repaint_buffer(
&mut self,
prompt: &dyn Prompt,
lines: &PromptLines,
prompt_mode: PromptEditMode,
menu: Option<&ReedlineMenu>,
use_ansi_coloring: bool,
theme: &ReedlineTheme,
cursor_config: &Option<CursorConfig>,
) -> Result<()> {
self.stdout.queue(cursor::Hide)?;
Expand Down Expand Up @@ -229,9 +227,9 @@ impl Painter {
.queue(Clear(ClearType::FromCursorDown))?;

if self.large_buffer {
self.print_large_buffer(prompt, lines, menu, use_ansi_coloring)?;
self.print_large_buffer(lines, menu, theme)?;
} else {
self.print_small_buffer(prompt, lines, menu, use_ansi_coloring)?;
self.print_small_buffer(lines, menu, theme)?;
}

// The last_required_lines is used to calculate safe range of the current prompt.
Expand Down Expand Up @@ -315,36 +313,32 @@ impl Painter {

fn print_small_buffer(
&mut self,
prompt: &dyn Prompt,
lines: &PromptLines,
menu: Option<&ReedlineMenu>,
use_ansi_coloring: bool,
theme: &ReedlineTheme,
) -> Result<()> {
// print our prompt with color
if use_ansi_coloring {
self.stdout
.queue(SetForegroundColor(prompt.get_prompt_color()))?;
if theme.use_ansi_coloring {
self.stdout.queue(SetForegroundColor(theme.prompt))?;
}

self.stdout
.queue(Print(&coerce_crlf(&lines.prompt_str_left)))?;

if use_ansi_coloring {
self.stdout
.queue(SetForegroundColor(prompt.get_indicator_color()))?;
if theme.use_ansi_coloring {
self.stdout.queue(SetForegroundColor(theme.indicator))?;
}

self.stdout
.queue(Print(&coerce_crlf(&lines.prompt_indicator)))?;

if use_ansi_coloring {
self.stdout
.queue(SetForegroundColor(prompt.get_prompt_right_color()))?;
if theme.use_ansi_coloring {
self.stdout.queue(SetForegroundColor(theme.prompt_right))?;
}

self.print_right_prompt(lines)?;

if use_ansi_coloring {
if theme.use_ansi_coloring {
self.stdout
.queue(SetAttribute(Attribute::Reset))?
.queue(ResetColor)?;
Expand All @@ -356,7 +350,7 @@ impl Painter {
.queue(Print(&lines.after_cursor))?;

if let Some(menu) = menu {
self.print_menu(menu, lines, use_ansi_coloring)?;
self.print_menu(menu, lines, theme.use_ansi_coloring)?;
} else {
self.stdout.queue(Print(&lines.hint))?;
}
Expand All @@ -366,10 +360,9 @@ impl Painter {

fn print_large_buffer(
&mut self,
prompt: &dyn Prompt,
lines: &PromptLines,
menu: Option<&ReedlineMenu>,
use_ansi_coloring: bool,
theme: &ReedlineTheme,
) -> Result<()> {
let screen_width = self.screen_width();
let screen_height = self.screen_height();
Expand All @@ -389,9 +382,8 @@ impl Painter {
let extra_rows = (total_lines_before).saturating_sub(screen_height as usize);

// print our prompt with color
if use_ansi_coloring {
self.stdout
.queue(SetForegroundColor(prompt.get_prompt_color()))?;
if theme.use_ansi_coloring {
self.stdout.queue(SetForegroundColor(theme.prompt))?;
}

// In case the prompt is made out of multiple lines, the prompt is split by
Expand All @@ -400,9 +392,8 @@ impl Painter {
self.stdout.queue(Print(&coerce_crlf(prompt_skipped)))?;

if extra_rows == 0 {
if use_ansi_coloring {
self.stdout
.queue(SetForegroundColor(prompt.get_prompt_right_color()))?;
if theme.use_ansi_coloring {
self.stdout.queue(SetForegroundColor(theme.prompt_right))?;
}

self.print_right_prompt(lines)?;
Expand All @@ -411,14 +402,13 @@ impl Painter {
// Adjusting extra_rows base on the calculated prompt line size
let extra_rows = extra_rows.saturating_sub(prompt_lines);

if use_ansi_coloring {
self.stdout
.queue(SetForegroundColor(prompt.get_indicator_color()))?;
if theme.use_ansi_coloring {
self.stdout.queue(SetForegroundColor(theme.indicator))?;
}
let indicator_skipped = skip_buffer_lines(&lines.prompt_indicator, extra_rows, None);
self.stdout.queue(Print(&coerce_crlf(indicator_skipped)))?;

if use_ansi_coloring {
if theme.use_ansi_coloring {
self.stdout.queue(ResetColor)?;
}

Expand Down Expand Up @@ -453,7 +443,7 @@ impl Painter {
} else {
self.stdout.queue(Print(&lines.after_cursor))?;
}
self.print_menu(menu, lines, use_ansi_coloring)?;
self.print_menu(menu, lines, theme.use_ansi_coloring)?;
} else {
// Selecting lines for the hint
// The -1 subtraction is done because the remaining lines consider the line where the
Expand Down
8 changes: 4 additions & 4 deletions src/painting/styled_text.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use nu_ansi_term::Style;

use crate::Prompt;
use crate::{engine::ReedlineTheme, Prompt};

use super::utils::strip_ansi;

Expand Down Expand Up @@ -101,14 +101,14 @@ impl StyledText {
insertion_point: usize,
prompt: &dyn Prompt,
// multiline_prompt: &str,
use_ansi_coloring: bool,
theme: &ReedlineTheme,
) -> (String, String) {
let mut current_idx = 0;
let mut left_string = String::new();
let mut right_string = String::new();

let multiline_prompt = prompt.render_prompt_multiline_indicator();
let prompt_style = Style::new().fg(prompt.get_prompt_multiline_color());
let prompt_style = Style::new().fg(theme.prompt_multiline);

for pair in &self.buffer {
if current_idx >= insertion_point {
Expand All @@ -135,7 +135,7 @@ impl StyledText {
current_idx += pair.1.len();
}

if use_ansi_coloring {
if theme.use_ansi_coloring {
(left_string, right_string)
} else {
(strip_ansi(&left_string), strip_ansi(&right_string))
Expand Down
Loading
Loading