summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorsigoden <sigoden@gmail.com>2023-03-08 07:25:32 +0800
committerGitHub <noreply@github.com>2023-03-08 07:25:32 +0800
commitec1c4bcf8f94f973ddf05d35956a40f0c42b5d9d (patch)
treeb31840dfe7969fc7353a278febc3a5f665e644f6 /src
parent360264121ca2931cb7f2fa40594ba590b245ace5 (diff)
downloadaichat-ec1c4bcf8f94f973ddf05d35956a40f0c42b5d9d.tar.gz
refactor: theme and code color (#32)
Diffstat (limited to 'src')
-rw-r--r--src/render/markdown.rs110
1 files changed, 85 insertions, 25 deletions
diff --git a/src/render/markdown.rs b/src/render/markdown.rs
index 5316653..f0cb64a 100644
--- a/src/render/markdown.rs
+++ b/src/render/markdown.rs
@@ -1,37 +1,37 @@
-use syntect::highlighting::Theme;
+// use colored::{Color, Colorize};
+use crossterm::style::{Color, Stylize};
+use syntect::highlighting::{Color as SyntectColor, FontStyle, Style, Theme};
use syntect::parsing::SyntaxSet;
-use syntect::util::as_24_bit_terminal_escaped;
use syntect::{easy::HighlightLines, parsing::SyntaxReference};
-/// Comms from https://github.com/jonschlinkert/sublime-monokai-extended/tree/0ca4e75291515c4d47e2d455e598e03e0dc53745
-const THEME: &[u8] = include_bytes!("../../assets/theme.yaml");
+/// Monokai Extended
+const MD_THEME: &[u8] = include_bytes!("../../assets/monokai-extended.theme.bin");
/// Comes from https://github.com/sharkdp/bat/raw/5e77ca37e89c873e4490b42ff556370dc5c6ba4f/assets/syntaxes.bin
const SYNTAXES: &[u8] = include_bytes!("../../assets/syntaxes.bin");
pub struct MarkdownRender {
syntax_set: SyntaxSet,
- theme: Theme,
+ md_theme: Theme,
+ code_color: Color,
md_syntax: SyntaxReference,
- txt_syntax: SyntaxReference,
- code_syntax: SyntaxReference,
+ code_syntax: Option<SyntaxReference>,
line_type: LineType,
}
impl MarkdownRender {
pub fn new() -> Self {
let syntax_set: SyntaxSet =
- bincode::deserialize_from(SYNTAXES).expect("invalid syntaxes.bin");
- let theme: Theme = serde_yaml::from_slice(THEME).unwrap();
+ bincode::deserialize_from(SYNTAXES).expect("invalid syntaxes binary");
+ let md_theme: Theme = bincode::deserialize_from(MD_THEME).expect("invalid md_theme binary");
+ let code_color = get_code_color(&md_theme);
let md_syntax = syntax_set.find_syntax_by_extension("md").unwrap().clone();
- let txt_syntax = syntax_set.find_syntax_by_extension("txt").unwrap().clone();
- let code_syntax = txt_syntax.clone();
let line_type = LineType::Normal;
Self {
syntax_set,
- theme,
+ md_theme,
+ code_color,
md_syntax,
- code_syntax,
- txt_syntax,
+ code_syntax: None,
line_type,
}
}
@@ -49,16 +49,14 @@ impl MarkdownRender {
LineType::Normal | LineType::CodeEnd => {
self.line_type = LineType::CodeBegin;
self.code_syntax = if lang.is_empty() {
- self.txt_syntax.clone()
+ None
} else {
- self.find_syntax(&lang)
- .cloned()
- .unwrap_or_else(|| self.txt_syntax.clone())
+ self.find_syntax(&lang).cloned()
};
}
LineType::CodeBegin | LineType::CodeInner => {
self.line_type = LineType::CodeEnd;
- self.code_syntax = self.txt_syntax.clone();
+ self.code_syntax = None;
}
}
self.render_line_inner(line, &self.md_syntax)
@@ -71,9 +69,9 @@ impl MarkdownRender {
}
LineType::CodeBegin => {
self.line_type = LineType::CodeInner;
- self.render_line_inner(line, &self.code_syntax)
+ self.render_code_line(line)
}
- LineType::CodeInner => self.render_line_inner(line, &self.code_syntax),
+ LineType::CodeInner => self.render_code_line(line),
}
}
}
@@ -86,7 +84,7 @@ impl MarkdownRender {
LineType::Normal | LineType::CodeEnd => {
self.render_line_inner(line, &self.md_syntax)
}
- _ => self.render_line_inner(line, &self.code_syntax),
+ _ => self.render_code_line(line),
}
};
@@ -94,9 +92,16 @@ impl MarkdownRender {
}
fn render_line_inner(&self, line: &str, syntax: &SyntaxReference) -> Option<String> {
- let mut highlighter = HighlightLines::new(syntax, &self.theme);
+ let mut highlighter = HighlightLines::new(syntax, &self.md_theme);
let ranges = highlighter.highlight_line(line, &self.syntax_set).ok()?;
- Some(as_24_bit_terminal_escaped(&ranges[..], false))
+ Some(as_terminal_escaped(&ranges))
+ }
+
+ fn render_code_line(&self, line: &str) -> Option<String> {
+ self.code_syntax
+ .as_ref()
+ .map(|syntax| self.render_line_inner(line, syntax))
+ .unwrap_or_else(|| Some(format!("{}", line.with(self.code_color))))
}
fn find_syntax(&self, lang: &str) -> Option<&SyntaxReference> {
@@ -114,6 +119,46 @@ enum LineType {
CodeEnd,
}
+fn as_terminal_escaped(ranges: &[(Style, &str)]) -> String {
+ let mut output = String::new();
+ for (style, text) in ranges {
+ let fg = blend_fg_color(style.foreground, style.background);
+ let mut text = text.with(convert_color(fg));
+ if style.font_style.contains(FontStyle::BOLD) {
+ text = text.bold()
+ }
+ if style.font_style.contains(FontStyle::UNDERLINE) {
+ text = text.underlined()
+ }
+ output.push_str(&text.to_string());
+ }
+ output
+}
+
+fn convert_color(c: SyntectColor) -> Color {
+ Color::Rgb {
+ r: c.r,
+ g: c.g,
+ b: c.b,
+ }
+}
+
+fn blend_fg_color(fg: SyntectColor, bg: SyntectColor) -> SyntectColor {
+ if fg.a == 0xff {
+ return fg;
+ }
+ let ratio = fg.a as u32;
+ let r = (fg.r as u32 * ratio + bg.r as u32 * (255 - ratio)) / 255;
+ let g = (fg.g as u32 * ratio + bg.g as u32 * (255 - ratio)) / 255;
+ let b = (fg.b as u32 * ratio + bg.b as u32 * (255 - ratio)) / 255;
+ SyntectColor {
+ r: r as u8,
+ g: g as u8,
+ b: b as u8,
+ a: 255,
+ }
+}
+
fn detect_code_block(line: &str) -> Option<String> {
if !line.starts_with("```") {
return None;
@@ -126,8 +171,23 @@ fn detect_code_block(line: &str) -> Option<String> {
Some(lang)
}
+fn get_code_color(theme: &Theme) -> Color {
+ let scope = theme.scopes.iter().find(|v| {
+ v.scope
+ .selectors
+ .iter()
+ .any(|v| v.path.scopes.iter().any(|v| v.to_string() == "string"))
+ });
+ scope
+ .and_then(|v| v.style.foreground)
+ .map(convert_color)
+ .unwrap_or_else(|| Color::Yellow)
+}
+
#[test]
-fn feature() {
+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()));
}