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
55 changes: 37 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: Theme,

// Engine Menus
menus: Vec<ReedlineMenu>,
Expand Down Expand Up @@ -182,6 +180,31 @@ impl Drop for Reedline {
}
}

pub struct Theme {
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 Theme {
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 @@ -191,7 +214,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 @@ -219,11 +241,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: Theme::default(),
menus: Vec::new(),
buffer_editor: None,
cursor_shapes: None,
Expand Down Expand Up @@ -356,7 +377,7 @@ 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
}

Expand Down Expand Up @@ -385,7 +406,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 @@ -1682,7 +1703,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 @@ -1700,11 +1721,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 @@ -1723,13 +1743,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 @@ -1738,7 +1758,7 @@ impl Reedline {
buffer_to_paint,
cursor_position_in_buffer,
self.history.as_ref(),
self.use_ansi_coloring,
self.theme.use_ansi_coloring,
)
})
} else {
Expand Down Expand Up @@ -1777,11 +1797,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
54 changes: 22 additions & 32 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::Theme, CursorConfig, PromptEditMode, PromptViMode};

use {
super::utils::{coerce_crlf, line_width},
crate::{
menu::{Menu, ReedlineMenu},
painting::PromptLines,
Prompt,
},
crossterm::{
cursor::{self, MoveTo, RestorePosition, SavePosition},
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: &Theme,
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: &Theme,
) -> 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: &Theme,
) -> 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::Theme, 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: &Theme,
) -> (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
23 changes: 0 additions & 23 deletions src/prompt/base.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use {
crossterm::style::Color,
serde::{Deserialize, Serialize},
std::{
borrow::Cow,
Expand All @@ -8,12 +7,6 @@ use {
strum_macros::EnumIter,
};

/// The default color for the prompt, indicator, and right prompt
pub static DEFAULT_PROMPT_COLOR: Color = Color::Green;
pub static DEFAULT_PROMPT_MULTILINE_COLOR: nu_ansi_term::Color = nu_ansi_term::Color::LightBlue;
pub static DEFAULT_INDICATOR_COLOR: Color = Color::Cyan;
pub static DEFAULT_PROMPT_RIGHT_COLOR: Color = Color::AnsiValue(5);

/// The current success/failure of the history search
pub enum PromptHistorySearchStatus {
/// Success for the search
Expand Down Expand Up @@ -97,22 +90,6 @@ pub trait Prompt: Send {
&self,
history_search: PromptHistorySearch,
) -> Cow<str>;
/// Get the default prompt color
fn get_prompt_color(&self) -> Color {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm just wondering if there's a way to do this without creating a breaking change. Removing these 4 functions will break everyone who's using reedline and making custom prompts, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see a way to keep these functions.

These 4 functions have default implementations and are the current way to change the default colors of reedline. Looking at the reverse dependancies of reedline I picked a few to check if they have there own implementations ans so this would be a breaking change:

That don't:

That do:

Since changing the colors are already used by existing users, at the very least I would need to expose Theme record for diatom. If having a breaking change isn't a show stopper, I will add that.

Copy link
Collaborator

@fdncred fdncred Jul 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

of course, I'm most concerned about nushell with this change. I see you say that nu-cli doesn't break.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Glade this isn't a blocker. Added builder methods so diathom-cli (and other users) can still change the colors.

DEFAULT_PROMPT_COLOR
}
/// Get the default multiline prompt color
fn get_prompt_multiline_color(&self) -> nu_ansi_term::Color {
DEFAULT_PROMPT_MULTILINE_COLOR
}
/// Get the default indicator color
fn get_indicator_color(&self) -> Color {
DEFAULT_INDICATOR_COLOR
}
/// Get the default right prompt color
fn get_prompt_right_color(&self) -> Color {
DEFAULT_PROMPT_RIGHT_COLOR
}

/// Whether to render right prompt on the last line
fn right_prompt_on_last_line(&self) -> bool {
Expand Down
Loading