feat: support custom theme (#187)

pull/188/head
sigoden 7 months ago committed by GitHub
parent 84004fd576
commit 680670e834
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

83
Cargo.lock generated

@ -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"

@ -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

@ -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<RenderOptions> {
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) {

@ -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());

@ -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<Theme>,
code_color: Option<Color>,
md_syntax: SyntaxReference,
code_syntax: Option<SyntaxReference>,
@ -38,18 +34,7 @@ impl MarkdownRender {
let syntax_set: SyntaxSet = bincode::deserialize_from(SYNTAXES)
.with_context(|| "MarkdownRender: invalid syntaxes binary")?;
let md_theme: Option<Theme> = 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<Theme>,
pub wrap: Option<String>,
pub wrap_code: bool,
}
impl RenderOptions {
pub(crate) fn new(
highlight: bool,
light_theme: bool,
wrap: Option<String>,
wrap_code: bool,
) -> Self {
pub(crate) fn new(theme: Option<Theme>, wrap: Option<String>, 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();

@ -24,7 +24,7 @@ pub fn render_stream(
abort: SharedAbortSignal,
wg: WaitGroup,
) -> Result<String> {
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();

@ -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 {

@ -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<String>,
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<String>) -> Self {
pub fn new(external_commands: Vec<String>, 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()

@ -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<dyn EditMode> = if config.read().keybindings.is_vi() {
let mut normal_keybindings = default_vi_normal_keybindings();

@ -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)?;

@ -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
}
}

Loading…
Cancel
Save