summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorsigoden <sigoden@gmail.com>2023-10-31 09:06:41 +0800
committerGitHub <noreply@github.com>2023-10-31 09:06:41 +0800
commit680670e834ff57cf6eb15c373870d1fd478f8edb (patch)
tree5b3ef0436532560c35da9959d3e603397a1d8c0d /src
parent84004fd5767abc94a5360b742e7fd0c8a79c6c55 (diff)
downloadaichat-680670e834ff57cf6eb15c373870d1fd478f8edb.tar.gz
feat: support custom theme (#187)
Diffstat (limited to 'src')
-rw-r--r--src/config/mod.rs28
-rw-r--r--src/main.rs2
-rw-r--r--src/render/markdown.rs42
-rw-r--r--src/render/mod.rs2
-rw-r--r--src/repl/handler.rs2
-rw-r--r--src/repl/highlighter.rs16
-rw-r--r--src/repl/init.rs2
-rw-r--r--src/repl/mod.rs1
-rw-r--r--src/repl/prompt.rs56
9 files changed, 45 insertions, 106 deletions
diff --git a/src/config/mod.rs b/src/config/mod.rs
index 9f4a11a..c84a6fb 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<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) {
diff --git a/src/main.rs b/src/main.rs
index 8e1835a..5cd82e4 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 9fc3545..8513a43 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<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,
}
@@ -344,15 +321,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();
let render = MarkdownRender::init(options).unwrap();
diff --git a/src/render/mod.rs b/src/render/mod.rs
index 2d4cbfb..1748137 100644
--- a/src/render/mod.rs
+++ b/src/render/mod.rs
@@ -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();
diff --git a/src/repl/handler.rs b/src/repl/handler.rs
index afe016a..94f91a1 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 103d198..cca6297 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<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()
diff --git a/src/repl/init.rs b/src/repl/init.rs
index 420f596..fbbcf22 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<dyn EditMode> = 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 4cf59f7..eb99fda 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 afc4bee..c73e586 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
}
}