diff --git a/Cargo.lock b/Cargo.lock index 38630e15..45ab516f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -503,6 +503,15 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "deranged" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3" +dependencies = [ + "powerfmt", +] + [[package]] name = "dirs" version = "5.0.1" @@ -953,6 +962,15 @@ version = "0.2.149" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" +[[package]] +name = "line-wrap" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f30344350a2a51da54c1d53be93fade8a237e545dbcc4bdbe635413f2117cab9" +dependencies = [ + "safemem", +] + [[package]] name = "linux-raw-sys" version = "0.4.10" @@ -1226,6 +1244,26 @@ version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +[[package]] +name = "plist" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdc0001cfea3db57a2e24bc0d818e9e20e554b5f97fabb9bc231dc240269ae06" +dependencies = [ + "base64", + "indexmap 1.9.3", + "line-wrap", + "quick-xml", + "serde", + "time", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "proc-macro2" version = "1.0.69" @@ -1235,6 +1273,15 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "quick-xml" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81b9228215d82c7b61490fec1de287136b5de6f5700f6e58ea9ad61a7964ca51" +dependencies = [ + "memchr", +] + [[package]] name = "quote" version = "1.0.33" @@ -1475,6 +1522,12 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" +[[package]] +name = "safemem" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" + [[package]] name = "same-file" version = "1.0.6" @@ -1739,6 +1792,7 @@ dependencies = [ "fnv", "once_cell", "onig", + "plist", "regex-syntax 0.7.5", "serde", "serde_json", @@ -1798,6 +1852,35 @@ dependencies = [ "syn 2.0.38", ] +[[package]] +name = "time" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5" +dependencies = [ + "deranged", + "itoa", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20" +dependencies = [ + "time-core", +] + [[package]] name = "tinyvec" version = "1.6.0" diff --git a/Cargo.toml b/Cargo.toml index 6a2246b1..121d222c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,7 +49,7 @@ default-features = false [dependencies.syntect] version = "5.0.0" default-features = false -features = ["parsing", "regex-onig"] +features = ["parsing", "regex-onig", "plist-load"] [profile.release] lto = true diff --git a/src/config/mod.rs b/src/config/mod.rs index 9f4a11a3..c84a6fb6 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -25,6 +25,11 @@ use std::{ process::exit, sync::Arc, }; +use syntect::highlighting::ThemeSet; + +/// Monokai Extended +const DARK_THEME: &[u8] = include_bytes!("../../assets/monokai-extended.theme.bin"); +const LIGHT_THEME: &[u8] = include_bytes!("../../assets/monokai-extended-light.theme.bin"); const CONFIG_FILE_NAME: &str = "config.yaml"; const ROLES_FILE_NAME: &str = "roles.yaml"; @@ -522,13 +527,32 @@ impl Config { } } - pub fn get_render_options(&self) -> RenderOptions { + pub fn get_render_options(&self) -> Result { + let theme = if self.highlight { + let theme_mode = if self.light_theme { "light" } else { "dark" }; + let theme_filename = format!("{theme_mode}.tmTheme"); + let theme_path = Self::local_path(&theme_filename)?; + if theme_path.exists() { + let theme = ThemeSet::get_theme(&theme_path) + .with_context(|| format!("Invalid theme at {}", theme_path.display()))?; + Some(theme) + } else { + let theme = if self.light_theme { + bincode::deserialize_from(LIGHT_THEME).expect("Invalid builtin light theme") + } else { + bincode::deserialize_from(DARK_THEME).expect("Invalid builtin dark theme") + }; + Some(theme) + } + } else { + None + }; let wrap = if stdout().is_terminal() { self.wrap.clone() } else { None }; - RenderOptions::new(self.highlight, self.light_theme, wrap, self.wrap_code) + Ok(RenderOptions::new(theme, wrap, self.wrap_code)) } pub fn maybe_print_send_tokens(&self, input: &str) { diff --git a/src/main.rs b/src/main.rs index 8e1835ab..5cd82e41 100644 --- a/src/main.rs +++ b/src/main.rs @@ -109,7 +109,7 @@ fn start_directive( } config.read().maybe_print_send_tokens(input); let output = if no_stream { - let render_options = config.read().get_render_options(); + let render_options = config.read().get_render_options()?; let output = client.send_message(input)?; let mut markdown_render = MarkdownRender::init(render_options)?; println!("{}", markdown_render.render(&output).trim()); diff --git a/src/render/markdown.rs b/src/render/markdown.rs index 9fc35458..8513a431 100644 --- a/src/render/markdown.rs +++ b/src/render/markdown.rs @@ -7,9 +7,6 @@ use syntect::highlighting::{Color as SyntectColor, FontStyle, Style, Theme}; use syntect::parsing::SyntaxSet; use syntect::{easy::HighlightLines, parsing::SyntaxReference}; -/// Monokai Extended -const MD_THEME: &[u8] = include_bytes!("../../assets/monokai-extended.theme.bin"); -const MD_THEME_LIGHT: &[u8] = include_bytes!("../../assets/monokai-extended-light.theme.bin"); /// Comes from https://github.com/sharkdp/bat/raw/5e77ca37e89c873e4490b42ff556370dc5c6ba4f/assets/syntaxes.bin const SYNTAXES: &[u8] = include_bytes!("../../assets/syntaxes.bin"); @@ -25,7 +22,6 @@ lazy_static! { pub struct MarkdownRender { options: RenderOptions, syntax_set: SyntaxSet, - md_theme: Option, code_color: Option, md_syntax: SyntaxReference, code_syntax: Option, @@ -38,18 +34,7 @@ impl MarkdownRender { let syntax_set: SyntaxSet = bincode::deserialize_from(SYNTAXES) .with_context(|| "MarkdownRender: invalid syntaxes binary")?; - let md_theme: Option = match (options.highlight, options.light_theme) { - (false, _) => None, - (true, false) => Some( - bincode::deserialize_from(MD_THEME) - .with_context(|| "MarkdownRender: invalid theme binary")?, - ), - (true, true) => Some( - bincode::deserialize_from(MD_THEME_LIGHT) - .expect("MarkdownRender: invalid theme binary"), - ), - }; - let code_color = md_theme.as_ref().map(get_code_color); + let code_color = options.theme.as_ref().map(get_code_color); let md_syntax = syntax_set.find_syntax_by_extension("md").unwrap().clone(); let line_type = LineType::Normal; let wrap_width = match options.wrap.as_deref() { @@ -70,7 +55,6 @@ impl MarkdownRender { }; Ok(Self { syntax_set, - md_theme, code_color, md_syntax, code_syntax: None, @@ -161,7 +145,7 @@ impl MarkdownRender { let ws: String = line.chars().take_while(|c| c.is_whitespace()).collect(); let trimed_line: &str = &line[ws.len()..]; let mut line_highlighted = None; - if let Some(theme) = &self.md_theme { + if let Some(theme) = &self.options.theme { let mut highlighter = HighlightLines::new(syntax, theme); if let Ok(ranges) = highlighter.highlight_line(trimed_line, &self.syntax_set) { line_highlighted = Some(format!("{ws}{}", as_terminal_escaped(&ranges))) @@ -207,22 +191,15 @@ impl MarkdownRender { #[derive(Debug, Clone, Default)] pub struct RenderOptions { - pub highlight: bool, - pub light_theme: bool, + pub theme: Option, pub wrap: Option, pub wrap_code: bool, } impl RenderOptions { - pub(crate) fn new( - highlight: bool, - light_theme: bool, - wrap: Option, - wrap_code: bool, - ) -> Self { + pub(crate) fn new(theme: Option, wrap: Option, wrap_code: bool) -> Self { Self { - highlight, - light_theme, + theme, wrap, wrap_code, } @@ -343,15 +320,6 @@ std::error::Error>> { ``` "#; - #[test] - fn test_assets() { - let syntax_set: SyntaxSet = - bincode::deserialize_from(SYNTAXES).expect("invalid syntaxes.bin"); - assert!(syntax_set.find_syntax_by_extension("md").is_some()); - let md_theme: Theme = bincode::deserialize_from(MD_THEME).expect("invalid md_theme binary"); - assert_eq!(md_theme.name, Some("Monokai Extended".into())); - } - #[test] fn test_render() { let options = RenderOptions::default(); diff --git a/src/render/mod.rs b/src/render/mod.rs index 2d4cbfb0..1748137d 100644 --- a/src/render/mod.rs +++ b/src/render/mod.rs @@ -24,7 +24,7 @@ pub fn render_stream( abort: SharedAbortSignal, wg: WaitGroup, ) -> Result { - let render_options = config.read().get_render_options(); + let render_options = config.read().get_render_options()?; let mut stream_handler = { let (tx, rx) = unbounded(); let abort_clone = abort.clone(); diff --git a/src/repl/handler.rs b/src/repl/handler.rs index afe016a1..94f91a1e 100644 --- a/src/repl/handler.rs +++ b/src/repl/handler.rs @@ -97,7 +97,7 @@ impl ReplCmdHandler { } ReplCmd::SessionInfo => { if let Some(session) = &self.config.read().session { - let render_options = self.config.read().get_render_options(); + let render_options = self.config.read().get_render_options()?; let mut markdown_render = MarkdownRender::init(render_options)?; print_now!("{}\n\n", session.render(&mut markdown_render)?); } else { diff --git a/src/repl/highlighter.rs b/src/repl/highlighter.rs index 103d1988..cca62977 100644 --- a/src/repl/highlighter.rs +++ b/src/repl/highlighter.rs @@ -3,16 +3,13 @@ use crate::config::SharedConfig; use nu_ansi_term::{Color, Style}; use reedline::{Highlighter, StyledText}; -const MATCH_COLOR: Color = Color::Green; - pub struct ReplHighlighter { external_commands: Vec, config: SharedConfig, } impl ReplHighlighter { - /// Construct the default highlighter with a given set of extern commands/keywords to detect and highlight - pub fn new(config: SharedConfig, external_commands: Vec) -> Self { + pub fn new(external_commands: Vec, config: SharedConfig) -> Self { Self { external_commands, config, @@ -22,18 +19,15 @@ impl ReplHighlighter { impl Highlighter for ReplHighlighter { fn highlight(&self, line: &str, _cursor: usize) -> StyledText { - let mut styled_text = StyledText::new(); - let color = if self.config.read().light_theme { - Color::Black - } else { - Color::White - }; + let color = Color::Default; let match_color = if self.config.read().highlight { - MATCH_COLOR + Color::Green } else { color }; + let mut styled_text = StyledText::new(); + if self .external_commands .clone() diff --git a/src/repl/init.rs b/src/repl/init.rs index 420f5969..fbbcf222 100644 --- a/src/repl/init.rs +++ b/src/repl/init.rs @@ -26,7 +26,7 @@ impl Repl { .collect(); let completer = Self::create_completer(&config, &commands); - let highlighter = ReplHighlighter::new(config.clone(), commands); + let highlighter = ReplHighlighter::new(commands, config.clone()); let menu = Self::create_menu(); let edit_mode: Box = if config.read().keybindings.is_vi() { let mut normal_keybindings = default_vi_normal_keybindings(); diff --git a/src/repl/mod.rs b/src/repl/mod.rs index 4cf59f7e..eb99fda1 100644 --- a/src/repl/mod.rs +++ b/src/repl/mod.rs @@ -123,7 +123,6 @@ impl Repl { } ".set" => { handler.handle(ReplCmd::Set(args.unwrap_or_default().to_string()))?; - self.prompt.sync_config(); } ".copy" => { handler.handle(ReplCmd::Copy)?; diff --git a/src/repl/prompt.rs b/src/repl/prompt.rs index afc4bee8..c73e5863 100644 --- a/src/repl/prompt.rs +++ b/src/repl/prompt.rs @@ -12,57 +12,11 @@ const PROMPT_RIGHT_COLOR: Color = Color::AnsiValue(5); #[derive(Clone)] pub struct ReplPrompt { config: SharedConfig, - prompt_color: Color, - prompt_multiline_color: nu_ansi_term::Color, - indicator_color: Color, - prompt_right_color: Color, } impl ReplPrompt { pub fn new(config: SharedConfig) -> Self { - let (prompt_color, prompt_multiline_color, indicator_color, prompt_right_color) = - Self::get_colors(&config); - Self { - config, - prompt_color, - prompt_multiline_color, - indicator_color, - prompt_right_color, - } - } - pub fn sync_config(&mut self) { - let (prompt_color, prompt_multiline_color, indicator_color, prompt_right_color) = - Self::get_colors(&self.config); - self.prompt_color = prompt_color; - self.prompt_multiline_color = prompt_multiline_color; - self.indicator_color = indicator_color; - self.prompt_right_color = prompt_right_color; - } - - pub fn get_colors(config: &SharedConfig) -> (Color, nu_ansi_term::Color, Color, Color) { - let render_options = config.read().get_render_options(); - if render_options.highlight { - ( - PROMPT_COLOR, - PROMPT_MULTILINE_COLOR, - INDICATOR_COLOR, - PROMPT_RIGHT_COLOR, - ) - } else if render_options.light_theme { - ( - Color::Black, - nu_ansi_term::Color::Black, - Color::Black, - Color::Black, - ) - } else { - ( - Color::White, - nu_ansi_term::Color::White, - Color::White, - Color::White, - ) - } + Self { config } } } @@ -114,18 +68,18 @@ impl Prompt for ReplPrompt { } fn get_prompt_color(&self) -> Color { - self.prompt_color + PROMPT_COLOR } /// Get the default multiline prompt color fn get_prompt_multiline_color(&self) -> nu_ansi_term::Color { - self.prompt_multiline_color + PROMPT_MULTILINE_COLOR } /// Get the default indicator color fn get_indicator_color(&self) -> Color { - self.indicator_color + INDICATOR_COLOR } /// Get the default right prompt color fn get_prompt_right_color(&self) -> Color { - self.prompt_right_color + PROMPT_RIGHT_COLOR } }