From 70dfac115f289c5cea936ce1660c35f9c8cc582d Mon Sep 17 00:00:00 2001 From: yggverse Date: Tue, 3 Dec 2024 01:23:23 +0200 Subject: [PATCH] draft highlight tag implementation --- .../item/page/content/text/gemini/reader.rs | 52 +++++++---- .../page/content/text/gemini/reader/syntax.rs | 91 ++++++++++++++++--- 2 files changed, 115 insertions(+), 28 deletions(-) diff --git a/src/app/browser/window/tab/item/page/content/text/gemini/reader.rs b/src/app/browser/window/tab/item/page/content/text/gemini/reader.rs index 21c8ae21..eaed8588 100644 --- a/src/app/browser/window/tab/item/page/content/text/gemini/reader.rs +++ b/src/app/browser/window/tab/item/page/content/text/gemini/reader.rs @@ -69,14 +69,22 @@ impl Reader { // Is inline code if let Some(code) = Code::inline_from(line) { // Append value to buffer - buffer.insert_with_tags( - &mut buffer.end_iter(), - &match syntax.auto_highlight(&code.value, None) { - Ok(highlight) => highlight, - Err(_) => code.value.to_string(), // @TODO handle - }, - &[&tag.code.text_tag], - ); + match syntax.highlight(&code.value, &tag.code.text_tag, None) { + Ok(highlight) => { + for (text_tag, entity) in highlight { + buffer.insert_with_tags(&mut buffer.end_iter(), &entity, &[&text_tag]); + } + } + Err(_) => { + buffer.insert_with_tags( + &mut buffer.end_iter(), + &code.value, + &[&tag.code.text_tag], + ); + } // @TODO handle + } + + // Append new line buffer.insert(&mut buffer.end_iter(), NEW_LINE); // Skip other actions for this line @@ -119,15 +127,25 @@ impl Reader { None => None, }; - // Insert multiline code buffer into main buffer - buffer.insert_with_tags( - &mut buffer.end_iter(), - &match syntax.auto_highlight(&this.value, alt) { - Ok(highlight) => highlight, - Err(_) => this.value.to_string(), // @TODO handle - }, - &[&tag.code.text_tag], - ); + // Insert multiline code into main buffer + match syntax.highlight(&this.value, &tag.code.text_tag, alt) { + Ok(highlight) => { + for (text_tag, entity) in highlight { + buffer.insert_with_tags( + &mut buffer.end_iter(), + &entity, + &[&text_tag], + ); + } + } + Err(_) => { + buffer.insert_with_tags( + &mut buffer.end_iter(), + &this.value, + &[&tag.code.text_tag], + ); + } // @TODO handle + } // Reset multiline = None; diff --git a/src/app/browser/window/tab/item/page/content/text/gemini/reader/syntax.rs b/src/app/browser/window/tab/item/page/content/text/gemini/reader/syntax.rs index 642c7162..f1a74759 100644 --- a/src/app/browser/window/tab/item/page/content/text/gemini/reader/syntax.rs +++ b/src/app/browser/window/tab/item/page/content/text/gemini/reader/syntax.rs @@ -1,8 +1,13 @@ +use gtk::{ + gdk::RGBA, + pango::{Style, Underline}, + prelude::TextTagExt, + TextTag, +}; use syntect::{ easy::HighlightLines, - highlighting::ThemeSet, + highlighting::{Color, FontStyle, ThemeSet}, parsing::{SyntaxReference, SyntaxSet}, - util::as_24_bit_terminal_escaped, Error, }; @@ -21,28 +26,92 @@ impl Syntax { } } - pub fn auto_highlight(&self, source: &str, alt: Option<&String>) -> Result { + pub fn highlight( + &self, + source_code: &str, + source_tag: &TextTag, + alt: Option<&String>, + ) -> Result, Error> { if let Some(name) = alt { - if let Some(syntax_reference) = self.syntax_set.find_syntax_by_name(name) { - return self.highlight(source, syntax_reference); + if let Some(reference) = self.syntax_set.find_syntax_by_extension(name) { + return self.syntect_buffer(source_code, source_tag, reference); } } - if let Some(syntax_reference) = self.syntax_set.find_syntax_by_first_line(source) { - return self.highlight(source, syntax_reference); + if let Some(reference) = self.syntax_set.find_syntax_by_first_line(source_code) { + return self.syntect_buffer(source_code, source_tag, reference); } - Ok(source.to_string()) + Ok(self.default_buffer(source_code, source_tag)) } - pub fn highlight( + fn default_buffer(&self, source: &str, source_tag: &TextTag) -> Vec<(TextTag, String)> { + // Init new line buffer + let mut buffer = Vec::new(); + + // Create new tag from source preset + let mut tag = TextTag::new(None); + tag.clone_from(source_tag); + + // Append + buffer.push((tag, source.to_string())); + buffer + } + + fn syntect_buffer( &self, source: &str, + source_tag: &TextTag, syntax_reference: &SyntaxReference, - ) -> Result { + ) -> Result, Error> { + // Init new line buffer + let mut buffer = Vec::new(); + + // Apply syntect decorator let ranges = HighlightLines::new(syntax_reference, &self.theme_set.themes[DEFAULT_THEME]) .highlight_line(&source, &self.syntax_set)?; - Ok(as_24_bit_terminal_escaped(&ranges[..], true)) + // Build tags + for (style, entity) in ranges { + // Create new tag from source preset + let mut tag = TextTag::new(None); + tag.clone_from(source_tag); + + // Tuneup using syntect conversion + tag.set_background_rgba(Some(&color_to_rgba(style.background))); + tag.set_foreground_rgba(Some(&color_to_rgba(style.foreground))); + tag.set_style(font_style_to_style(style.font_style)); + tag.set_underline(font_style_to_underline(style.font_style)); + + // Append + buffer.push((tag, entity.to_string())); + } + + Ok(buffer) + } +} + +// Tools + +fn color_to_rgba(color: Color) -> RGBA { + RGBA::new( + color.r.into(), + color.g.into(), + color.b.into(), + color.a.into(), + ) +} + +fn font_style_to_style(font_style: FontStyle) -> Style { + match font_style { + FontStyle::ITALIC => Style::Italic, + _ => Style::Normal, + } +} + +fn font_style_to_underline(font_style: FontStyle) -> Underline { + match font_style { + FontStyle::UNDERLINE => Underline::Single, + _ => Underline::None, } }