diff --git a/src/app/browser/window/tab/item/page/content/text.rs b/src/app/browser/window/tab/item/page/content/text.rs index f400591c..ad2c9fdb 100644 --- a/src/app/browser/window/tab/item/page/content/text.rs +++ b/src/app/browser/window/tab/item/page/content/text.rs @@ -1,12 +1,12 @@ +mod common; mod gemini; mod markdown; mod nex; mod plain; mod source; -use crate::{app::browser::window::tab::item::page::Page, profile::Profile}; - use super::{ItemAction, WindowAction}; +use crate::{app::browser::window::tab::item::page::Page, profile::Profile}; use adw::ClampScrollable; use gemini::Gemini; use gtk::{ScrolledWindow, TextView, glib::Uri}; diff --git a/src/app/browser/window/tab/item/page/content/text/common.rs b/src/app/browser/window/tab/item/page/content/text/common.rs new file mode 100644 index 00000000..8bef633b --- /dev/null +++ b/src/app/browser/window/tab/item/page/content/text/common.rs @@ -0,0 +1,4 @@ +/// Shared widgets: +/// e.g. Gemtext and Markdown renderers have single implementation for the code blocks +pub mod code; +pub use code::Code; diff --git a/src/app/browser/window/tab/item/page/content/text/common/code.rs b/src/app/browser/window/tab/item/page/content/text/common/code.rs new file mode 100644 index 00000000..6af5f997 --- /dev/null +++ b/src/app/browser/window/tab/item/page/content/text/common/code.rs @@ -0,0 +1,139 @@ +mod ansi; +mod syntax; + +use gtk::{ + Align, Box, Button, Label, Orientation, PolicyType, ScrolledWindow, Separator, TextBuffer, + TextTagTable, TextView, WrapMode, + gdk::Display, + glib::{ControlFlow, idle_add_local}, + prelude::{BoxExt, ButtonExt, DisplayExt, TextBufferExt, TextBufferExtManual, WidgetExt}, +}; +use std::{cell::Cell, rc::Rc}; +use syntax::Syntax; + +pub struct Code { + pub widget: Box, +} + +impl Code { + pub fn init(parent: &TextView, source: &str, alt: Option<&String>) -> Self { + let syntax = Syntax::new(); + let copied = Rc::new(Cell::new(None)); + + let widget = Box::builder() + .css_classes(["card"]) + .halign(Align::Fill) + .hexpand(true) + .margin_bottom(MARGIN / 2) + .orientation(Orientation::Vertical) + .build(); + widget.append(&{ + let header = Box::builder() + .halign(Align::Fill) + .hexpand(true) + .orientation(Orientation::Horizontal) + .build(); + if let Some(label) = alt { + header.append( + &Label::builder() + .halign(Align::Start) + .hexpand(true) + .label(label) + .margin_bottom(MARGIN) + .margin_end(MARGIN) + .margin_start(MARGIN) + .margin_top(MARGIN) + .selectable(true) + .build(), + ); + } + header.append(&{ + const TOGGLE_BUTTON_CLASS: &str = "dimmed"; + const TOGGLE_BUTTON_TOOLTIP: (&str, &str) = ("Copy", "Copied"); + let copy = Button::builder() + .css_classes(["circular", "flat", TOGGLE_BUTTON_CLASS]) + .halign(Align::End) + .icon_name("edit-copy-symbolic") + .margin_bottom(MARGIN / 2) + .margin_end(MARGIN / 2) + .margin_start(MARGIN / 2) + .margin_top(MARGIN / 2) + .tooltip_text(TOGGLE_BUTTON_TOOLTIP.0) + .valign(Align::Center) + .build(); + copy.set_cursor_from_name(Some("pointer")); + copy.connect_clicked({ + let source = String::from(source); + let copied = copied.clone(); + move |this| { + if let Some(prev) = copied.replace(Some(this.clone())) { + prev.set_tooltip_text(Some(TOGGLE_BUTTON_TOOLTIP.0)); + prev.add_css_class(TOGGLE_BUTTON_CLASS) + } + this.set_tooltip_text(Some(TOGGLE_BUTTON_TOOLTIP.1)); + this.remove_css_class(TOGGLE_BUTTON_CLASS); + + Display::default().unwrap().clipboard().set_text(&source) + } + }); + copy + }); + header + }); + widget.append( + &Separator::builder() + .orientation(Orientation::Horizontal) + .build(), + ); + widget.append(&{ + ScrolledWindow::builder() + .child( + &TextView::builder() + .buffer(&{ + let b = TextBuffer::new(Some(&TextTagTable::new())); + let mut start = b.start_iter(); + match syntax.highlight(source, alt) { + Ok(highlight) => { + for (syntax_tag, entity) in highlight { + assert!(b.tag_table().add(&syntax_tag)); + b.insert_with_tags(&mut start, &entity, &[&syntax_tag]) + } + } + Err(_) => { + // Try ANSI/SGR format (terminal emulation) @TODO optional + for (syntax_tag, entity) in ansi::format(source) { + assert!(b.tag_table().add(&syntax_tag)); + b.insert_with_tags(&mut start, &entity, &[&syntax_tag]) + } + } + } + b + }) + .css_classes(["code-block"]) + .cursor_visible(false) + .editable(false) + .wrap_mode(WrapMode::None) + .build(), + ) + .margin_bottom(MARGIN) + .margin_end(MARGIN) + .margin_start(MARGIN) + .margin_top(MARGIN) + .vscrollbar_policy(PolicyType::Never) + .hscrollbar_policy(PolicyType::Automatic) + .propagate_natural_height(true) + .build() + }); + idle_add_local({ + let widget = widget.clone(); + let parent = parent.clone(); + move || { + widget.set_width_request(parent.width() - 22); + ControlFlow::Break + } + }); + Self { widget } + } +} + +const MARGIN: i32 = 16; diff --git a/src/app/browser/window/tab/item/page/content/text/gemini/ansi.rs b/src/app/browser/window/tab/item/page/content/text/common/code/ansi.rs similarity index 100% rename from src/app/browser/window/tab/item/page/content/text/gemini/ansi.rs rename to src/app/browser/window/tab/item/page/content/text/common/code/ansi.rs diff --git a/src/app/browser/window/tab/item/page/content/text/gemini/ansi/rgba.rs b/src/app/browser/window/tab/item/page/content/text/common/code/ansi/rgba.rs similarity index 100% rename from src/app/browser/window/tab/item/page/content/text/gemini/ansi/rgba.rs rename to src/app/browser/window/tab/item/page/content/text/common/code/ansi/rgba.rs diff --git a/src/app/browser/window/tab/item/page/content/text/markdown/tags/code/ansi/tag.rs b/src/app/browser/window/tab/item/page/content/text/common/code/ansi/tag.rs similarity index 100% rename from src/app/browser/window/tab/item/page/content/text/markdown/tags/code/ansi/tag.rs rename to src/app/browser/window/tab/item/page/content/text/common/code/ansi/tag.rs diff --git a/src/app/browser/window/tab/item/page/content/text/gemini/syntax.rs b/src/app/browser/window/tab/item/page/content/text/common/code/syntax.rs similarity index 100% rename from src/app/browser/window/tab/item/page/content/text/gemini/syntax.rs rename to src/app/browser/window/tab/item/page/content/text/common/code/syntax.rs diff --git a/src/app/browser/window/tab/item/page/content/text/gemini/syntax/error.rs b/src/app/browser/window/tab/item/page/content/text/common/code/syntax/error.rs similarity index 100% rename from src/app/browser/window/tab/item/page/content/text/gemini/syntax/error.rs rename to src/app/browser/window/tab/item/page/content/text/common/code/syntax/error.rs diff --git a/src/app/browser/window/tab/item/page/content/text/markdown/tags/code/syntax/tag.rs b/src/app/browser/window/tab/item/page/content/text/common/code/syntax/tag.rs similarity index 100% rename from src/app/browser/window/tab/item/page/content/text/markdown/tags/code/syntax/tag.rs rename to src/app/browser/window/tab/item/page/content/text/common/code/syntax/tag.rs diff --git a/src/app/browser/window/tab/item/page/content/text/gemini.rs b/src/app/browser/window/tab/item/page/content/text/gemini.rs index 6a09513c..4eb00890 100644 --- a/src/app/browser/window/tab/item/page/content/text/gemini.rs +++ b/src/app/browser/window/tab/item/page/content/text/gemini.rs @@ -1,8 +1,6 @@ -mod ansi; pub mod error; mod gutter; mod icon; -mod syntax; mod tag; use super::{ItemAction, WindowAction}; @@ -20,7 +18,6 @@ use gutter::Gutter; use icon::Icon; use sourceview::prelude::{ActionExt, ActionMapExt, DisplayExt, ToVariant}; use std::{cell::Cell, collections::HashMap, rc::Rc}; -use syntax::Syntax; use tag::Tag; pub const NEW_LINE: &str = "\n"; @@ -63,9 +60,6 @@ impl Gemini { RGBA::new(0.208, 0.518, 0.894, 0.9), ); - // Init syntect highlight features - let syntax = Syntax::new(); - // Init icons let icon = Icon::new(); @@ -125,60 +119,17 @@ impl Gemini { Some(ref mut c) => { match c.continue_from(line) { Ok(()) => { - // Close tag found: + // Closing tag found: if c.is_completed { - // Is alt provided - let alt = match c.alt { - Some(ref alt) => { - // Insert alt value to the main buffer - buffer.insert_with_tags( - &mut buffer.end_iter(), - alt.as_str(), - &[&tag.title], - ); - - // Append new line after alt text - buffer.insert(&mut buffer.end_iter(), NEW_LINE); - - // Return value as wanted also for syntax highlight detection - Some(alt) - } - None => None, - }; - - // Begin code block construction - // Try auto-detect code syntax for given `value` and `alt` @TODO optional - match syntax.highlight(&c.value, alt) { - Ok(highlight) => { - for (syntax_tag, entity) in highlight { - // Register new tag - if !tag.text_tag_table.add(&syntax_tag) { - todo!() - } - // Append tag to buffer - buffer.insert_with_tags( - &mut buffer.end_iter(), - &entity, - &[&syntax_tag], - ); - } - } - Err(_) => { - // Try ANSI/SGR format (terminal emulation) @TODO optional - for (syntax_tag, entity) in ansi::format(&c.value) { - // Register new tag - if !tag.text_tag_table.add(&syntax_tag) { - todo!() - } - // Append tag to buffer - buffer.insert_with_tags( - &mut buffer.end_iter(), - &entity, - &[&syntax_tag], - ); - } - } // @TODO handle - } + text_view.add_child_at_anchor( + &super::common::Code::init( + &text_view, + c.value.trim_end(), + c.alt.as_ref(), + ) + .widget, + &buffer.create_child_anchor(&mut buffer.end_iter()), + ); // Reset code = None; diff --git a/src/app/browser/window/tab/item/page/content/text/gemini/ansi/tag.rs b/src/app/browser/window/tab/item/page/content/text/gemini/ansi/tag.rs deleted file mode 100644 index 7154b1f3..00000000 --- a/src/app/browser/window/tab/item/page/content/text/gemini/ansi/tag.rs +++ /dev/null @@ -1,29 +0,0 @@ -use gtk::{TextTag, WrapMode}; - -/// Default [TextTag](https://docs.gtk.org/gtk4/class.TextTag.html) preset -/// for ANSI buffer -pub struct Tag { - pub text_tag: TextTag, -} - -impl Default for Tag { - fn default() -> Self { - Self::new() - } -} - -impl Tag { - // Constructors - - /// Create new `Self` - pub fn new() -> Self { - Self { - text_tag: TextTag::builder() - .family("monospace") // @TODO - .left_margin(28) - .scale(0.81) // * the rounded `0.8` value crops text for some reason @TODO - .wrap_mode(WrapMode::None) - .build(), - } - } -} diff --git a/src/app/browser/window/tab/item/page/content/text/gemini/syntax/tag.rs b/src/app/browser/window/tab/item/page/content/text/gemini/syntax/tag.rs deleted file mode 100644 index 4b2011b8..00000000 --- a/src/app/browser/window/tab/item/page/content/text/gemini/syntax/tag.rs +++ /dev/null @@ -1,29 +0,0 @@ -use gtk::{TextTag, WrapMode}; - -/// Default [TextTag](https://docs.gtk.org/gtk4/class.TextTag.html) preset -/// for syntax highlight buffer -pub struct Tag { - pub text_tag: TextTag, -} - -impl Default for Tag { - fn default() -> Self { - Self::new() - } -} - -impl Tag { - // Constructors - - /// Create new `Self` - pub fn new() -> Self { - Self { - text_tag: TextTag::builder() - .family("monospace") // @TODO - .left_margin(28) - .scale(0.81) // * the rounded `0.8` value crops text for some reason @TODO - .wrap_mode(WrapMode::None) - .build(), - } - } -} diff --git a/src/app/browser/window/tab/item/page/content/text/gemini/tag.rs b/src/app/browser/window/tab/item/page/content/text/gemini/tag.rs index f917b5f7..599621c1 100644 --- a/src/app/browser/window/tab/item/page/content/text/gemini/tag.rs +++ b/src/app/browser/window/tab/item/page/content/text/gemini/tag.rs @@ -2,14 +2,12 @@ mod header; mod list; mod plain; mod quote; -mod title; use gtk::{TextTag, TextTagTable}; use header::Header; use list::List; use plain::Plain; use quote::Quote; -use title::Title; pub struct Tag { pub text_tag_table: TextTagTable, @@ -19,7 +17,6 @@ pub struct Tag { pub h3: TextTag, pub list: TextTag, pub quote: TextTag, - pub title: TextTag, pub plain: TextTag, } @@ -38,7 +35,6 @@ impl Tag { let h3 = TextTag::h3(); let list = TextTag::list(); let quote = TextTag::quote(); - let title = TextTag::title(); let plain = TextTag::plain(); // Init tag table @@ -47,7 +43,6 @@ impl Tag { text_tag_table.add(&h1); text_tag_table.add(&h2); text_tag_table.add(&h3); - text_tag_table.add(&title); text_tag_table.add(&list); text_tag_table.add("e); text_tag_table.add(&plain); @@ -60,7 +55,6 @@ impl Tag { h3, list, quote, - title, plain, } } diff --git a/src/app/browser/window/tab/item/page/content/text/gemini/tag/title.rs b/src/app/browser/window/tab/item/page/content/text/gemini/tag/title.rs deleted file mode 100644 index ed0072fe..00000000 --- a/src/app/browser/window/tab/item/page/content/text/gemini/tag/title.rs +++ /dev/null @@ -1,16 +0,0 @@ -use gtk::{TextTag, WrapMode}; - -pub trait Title { - fn title() -> Self; -} - -impl Title for TextTag { - fn title() -> Self { - TextTag::builder() - .pixels_above_lines(4) - .pixels_below_lines(8) - .weight(500) - .wrap_mode(WrapMode::None) - .build() - } -} diff --git a/src/app/browser/window/tab/item/page/content/text/markdown/tags/code.rs b/src/app/browser/window/tab/item/page/content/text/markdown/tags/code.rs index 0255769a..3ef93de6 100644 --- a/src/app/browser/window/tab/item/page/content/text/markdown/tags/code.rs +++ b/src/app/browser/window/tab/item/page/content/text/markdown/tags/code.rs @@ -1,18 +1,10 @@ -mod ansi; -mod syntax; - use gtk::{ - Align, Box, Button, Label, Orientation, PolicyType, ScrolledWindow, Separator, TextBuffer, - TextSearchFlags, TextTagTable, TextView, WrapMode, - gdk::Display, - glib::{ControlFlow, GString, idle_add_local, uuid_string_random}, - prelude::{ - BoxExt, ButtonExt, DisplayExt, TextBufferExt, TextBufferExtManual, TextViewExt, WidgetExt, - }, + TextBuffer, TextSearchFlags, TextView, + glib::{GString, uuid_string_random}, + prelude::{TextBufferExt, TextBufferExtManual, TextViewExt}, }; use regex::Regex; -use std::{cell::Cell, collections::HashMap, rc::Rc}; -use syntax::Syntax; +use std::collections::HashMap; const REGEX_CODE: &str = r"(?s)```[ \t]*(?P.*?)\n(?P.*?)```"; @@ -69,8 +61,6 @@ impl Code { /// Apply code `Tag` to given `TextView` using `Self.index` pub fn render(&mut self, text_view: &TextView) { let buffer = text_view.buffer(); - let syntax = Syntax::new(); - let copied = Rc::new(Cell::new(None)); for (k, v) in self.0.iter() { while let Some((mut m_start, mut m_end)) = buffer @@ -79,134 +69,14 @@ impl Code { { buffer.delete(&mut m_start, &mut m_end); text_view.add_child_at_anchor( - &{ - const MARGIN: i32 = 16; - let widget = Box::builder() - .css_classes(["card"]) - .halign(Align::Fill) - .hexpand(true) - .margin_bottom(MARGIN / 2) - .orientation(Orientation::Vertical) - .build(); - widget.append(&{ - let header = Box::builder() - .halign(Align::Fill) - .hexpand(true) - .orientation(Orientation::Horizontal) - .build(); - if let Some(ref alt) = v.alt { - header.append( - &Label::builder() - .halign(Align::Start) - .hexpand(true) - .label(alt) - .margin_bottom(MARGIN) - .margin_end(MARGIN) - .margin_start(MARGIN) - .margin_top(MARGIN) - .selectable(true) - .build(), - ); - } - header.append(&{ - const TOGGLE_BUTTON_CLASS: &str = "dimmed"; - const TOGGLE_BUTTON_TOOLTIP: (&str, &str) = ("Copy", "Copied"); - let copy = Button::builder() - .css_classes(["circular", "flat", TOGGLE_BUTTON_CLASS]) - .halign(Align::End) - .icon_name("edit-copy-symbolic") - .margin_bottom(MARGIN / 2) - .margin_end(MARGIN / 2) - .margin_start(MARGIN / 2) - .margin_top(MARGIN / 2) - .tooltip_text(TOGGLE_BUTTON_TOOLTIP.0) - .valign(Align::Center) - .build(); - copy.set_cursor_from_name(Some("pointer")); - copy.connect_clicked({ - let source = v.data.clone(); - let copied = copied.clone(); - move |this| { - if let Some(prev) = copied.replace(Some(this.clone())) { - prev.set_tooltip_text(Some(TOGGLE_BUTTON_TOOLTIP.0)); - prev.add_css_class(TOGGLE_BUTTON_CLASS) - } - this.set_tooltip_text(Some(TOGGLE_BUTTON_TOOLTIP.1)); - this.remove_css_class(TOGGLE_BUTTON_CLASS); - - Display::default().unwrap().clipboard().set_text(&source) - } - }); - copy - }); - header - }); - widget.append( - &Separator::builder() - .orientation(Orientation::Horizontal) - .build(), - ); - widget.append(&{ - ScrolledWindow::builder() - .child( - &TextView::builder() - .buffer(&{ - let b = TextBuffer::new(Some(&TextTagTable::new())); - let mut start = b.start_iter(); - match syntax.highlight(&v.data, v.alt.as_ref()) { - Ok(highlight) => { - for (syntax_tag, entity) in highlight { - assert!(b.tag_table().add(&syntax_tag)); - b.insert_with_tags( - &mut start, - &entity, - &[&syntax_tag], - ) - } - } - Err(_) => { - // Try ANSI/SGR format (terminal emulation) @TODO optional - for (syntax_tag, entity) in - ansi::format(&v.data) - { - assert!(b.tag_table().add(&syntax_tag)); - b.insert_with_tags( - &mut start, - &entity, - &[&syntax_tag], - ) - } - } - } - b - }) - .css_classes(["code-block"]) - .cursor_visible(false) - .editable(false) - .wrap_mode(WrapMode::None) - .build(), - ) - .margin_bottom(MARGIN) - .margin_end(MARGIN) - .margin_start(MARGIN) - .margin_top(MARGIN) - .vscrollbar_policy(PolicyType::Never) - .hscrollbar_policy(PolicyType::Automatic) - .propagate_natural_height(true) - .build() - }); - idle_add_local({ - let widget = widget.clone(); - let text_view = text_view.clone(); - move || { - widget.set_width_request(text_view.width() - 22); - ControlFlow::Break - } - }); - widget - }, + &super::super::super::common::Code::init( + text_view, + v.data.as_ref(), + v.alt.as_ref(), + ) + .widget, &buffer.create_child_anchor(&mut m_end), - ); + ) } } } diff --git a/src/app/browser/window/tab/item/page/content/text/markdown/tags/code/ansi.rs b/src/app/browser/window/tab/item/page/content/text/markdown/tags/code/ansi.rs deleted file mode 100644 index b617b69a..00000000 --- a/src/app/browser/window/tab/item/page/content/text/markdown/tags/code/ansi.rs +++ /dev/null @@ -1,33 +0,0 @@ -mod rgba; -mod tag; - -use tag::Tag; - -use ansi_parser::{AnsiParser, AnsiSequence, Output}; -use gtk::{TextTag, prelude::TextTagExt}; - -/// Apply ANSI/SGR format to new buffer -pub fn format(source_code: &str) -> Vec<(TextTag, String)> { - let mut buffer = Vec::new(); - let mut tag = Tag::new(); - - for ref entity in source_code.ansi_parse() { - if let Output::Escape(AnsiSequence::SetGraphicsMode(color)) = entity - && color.len() > 1 - { - if color[0] == 38 { - tag.text_tag - .set_foreground_rgba(rgba::default(*color.last().unwrap()).as_ref()); - } else { - tag.text_tag - .set_background_rgba(rgba::default(*color.last().unwrap()).as_ref()); - } - } - if let Output::TextBlock(text) = entity { - buffer.push((tag.text_tag, text.to_string())); - tag = Tag::new(); - } - } - - buffer -} diff --git a/src/app/browser/window/tab/item/page/content/text/markdown/tags/code/ansi/rgba.rs b/src/app/browser/window/tab/item/page/content/text/markdown/tags/code/ansi/rgba.rs deleted file mode 100644 index d1398d2f..00000000 --- a/src/app/browser/window/tab/item/page/content/text/markdown/tags/code/ansi/rgba.rs +++ /dev/null @@ -1,256 +0,0 @@ -use gtk::gdk::RGBA; - -/// Default RGBa palette for ANSI terminal emulation -pub fn default(color: u8) -> Option { - match color { - 7 => Some(RGBA::new(0.854, 0.854, 0.854, 1.0)), - 8 => Some(RGBA::new(0.424, 0.424, 0.424, 1.0)), - 10 => Some(RGBA::new(0.0, 1.0, 0.0, 1.0)), - 11 => Some(RGBA::new(1.0, 1.0, 0.0, 1.0)), - 12 => Some(RGBA::new(0.0, 0.0, 1.0, 1.0)), - 13 => Some(RGBA::new(1.0, 0.0, 1.0, 1.0)), - 14 => Some(RGBA::new(0.0, 1.0, 1.0, 1.0)), - 15 => Some(RGBA::new(1.0, 1.0, 1.0, 1.0)), - 16 => Some(RGBA::new(0.0, 0.0, 0.0, 1.0)), - 17 => Some(RGBA::new(0.0, 0.020, 0.373, 1.0)), - 18 => Some(RGBA::new(0.0, 0.031, 0.529, 1.0)), - 19 => Some(RGBA::new(0.0, 0.0, 0.686, 1.0)), - 20 => Some(RGBA::new(0.0, 0.0, 0.823, 1.0)), - 21 => Some(RGBA::new(0.0, 0.0, 1.0, 1.0)), - 22 => Some(RGBA::new(0.0, 0.373, 0.0, 1.0)), - 23 => Some(RGBA::new(0.0, 0.373, 0.373, 1.0)), - 24 => Some(RGBA::new(0.0, 0.373, 0.529, 1.0)), - 25 => Some(RGBA::new(0.0, 0.373, 0.686, 1.0)), - 26 => Some(RGBA::new(0.0, 0.373, 0.823, 1.0)), - 27 => Some(RGBA::new(0.0, 0.373, 1.0, 1.0)), - 28 => Some(RGBA::new(0.0, 0.533, 0.0, 1.0)), - 29 => Some(RGBA::new(0.0, 0.533, 0.373, 1.0)), - 30 => Some(RGBA::new(0.0, 0.533, 0.533, 1.0)), - 31 => Some(RGBA::new(0.0, 0.533, 0.686, 1.0)), - 32 => Some(RGBA::new(0.0, 0.533, 0.823, 1.0)), - 33 => Some(RGBA::new(0.0, 0.533, 1.0, 1.0)), - 34 => Some(RGBA::new(0.039, 0.686, 0.0, 1.0)), - 35 => Some(RGBA::new(0.039, 0.686, 0.373, 1.0)), - 36 => Some(RGBA::new(0.039, 0.686, 0.529, 1.0)), - 37 => Some(RGBA::new(0.039, 0.686, 0.686, 1.0)), - 38 => Some(RGBA::new(0.039, 0.686, 0.823, 1.0)), - 39 => Some(RGBA::new(0.039, 0.686, 1.0, 1.0)), - 40 => Some(RGBA::new(0.0, 0.843, 0.0, 1.0)), - 41 => Some(RGBA::new(0.0, 0.843, 0.373, 1.0)), - 42 => Some(RGBA::new(0.0, 0.843, 0.529, 1.0)), - 43 => Some(RGBA::new(0.0, 0.843, 0.686, 1.0)), - 44 => Some(RGBA::new(0.0, 0.843, 0.843, 1.0)), - 45 => Some(RGBA::new(0.0, 0.843, 1.0, 1.0)), - 46 => Some(RGBA::new(0.0, 1.0, 0.0, 1.0)), - 47 => Some(RGBA::new(0.0, 1.0, 0.373, 1.0)), - 48 => Some(RGBA::new(0.0, 1.0, 0.529, 1.0)), - 49 => Some(RGBA::new(0.0, 1.0, 0.686, 1.0)), - 50 => Some(RGBA::new(0.0, 1.0, 0.843, 1.0)), - 51 => Some(RGBA::new(0.0, 1.0, 1.0, 1.0)), - 52 => Some(RGBA::new(0.373, 0.0, 0.0, 1.0)), - 53 => Some(RGBA::new(0.373, 0.0, 0.373, 1.0)), - 54 => Some(RGBA::new(0.373, 0.0, 0.529, 1.0)), - 55 => Some(RGBA::new(0.373, 0.0, 0.686, 1.0)), - 56 => Some(RGBA::new(0.373, 0.0, 0.843, 1.0)), - 57 => Some(RGBA::new(0.373, 0.0, 1.0, 1.0)), - 58 => Some(RGBA::new(0.373, 0.373, 0.0, 1.0)), - 59 => Some(RGBA::new(0.373, 0.373, 0.373, 1.0)), - 60 => Some(RGBA::new(0.373, 0.373, 0.529, 1.0)), - 61 => Some(RGBA::new(0.373, 0.373, 0.686, 1.0)), - 62 => Some(RGBA::new(0.373, 0.373, 0.843, 1.0)), - 63 => Some(RGBA::new(0.373, 0.373, 1.0, 1.0)), - 64 => Some(RGBA::new(0.373, 0.529, 0.0, 1.0)), - 65 => Some(RGBA::new(0.373, 0.529, 0.373, 1.0)), - 66 => Some(RGBA::new(0.373, 0.529, 0.529, 1.0)), - 67 => Some(RGBA::new(0.373, 0.529, 0.686, 1.0)), - 68 => Some(RGBA::new(0.373, 0.529, 0.843, 1.0)), - 69 => Some(RGBA::new(0.373, 0.529, 1.0, 1.0)), - 70 => Some(RGBA::new(0.373, 0.686, 0.0, 1.0)), - 71 => Some(RGBA::new(0.373, 0.686, 0.373, 1.0)), - 72 => Some(RGBA::new(0.373, 0.686, 0.529, 1.0)), - 73 => Some(RGBA::new(0.373, 0.686, 0.686, 1.0)), - 74 => Some(RGBA::new(0.373, 0.686, 0.843, 1.0)), - 75 => Some(RGBA::new(0.373, 0.686, 1.0, 1.0)), - 76 => Some(RGBA::new(0.373, 0.843, 0.0, 1.0)), - 77 => Some(RGBA::new(0.373, 0.843, 0.373, 1.0)), - 78 => Some(RGBA::new(0.373, 0.843, 0.529, 1.0)), - 79 => Some(RGBA::new(0.373, 0.843, 0.686, 1.0)), - 80 => Some(RGBA::new(0.373, 0.843, 0.843, 1.0)), - 81 => Some(RGBA::new(0.373, 0.843, 1.0, 1.0)), - 82 => Some(RGBA::new(0.373, 1.0, 0.0, 1.0)), - 83 => Some(RGBA::new(0.373, 1.0, 0.373, 1.0)), - 84 => Some(RGBA::new(0.373, 1.0, 0.529, 1.0)), - 85 => Some(RGBA::new(0.373, 1.0, 0.686, 1.0)), - 86 => Some(RGBA::new(0.373, 1.0, 0.843, 1.0)), - 87 => Some(RGBA::new(0.373, 1.0, 1.0, 1.0)), - 88 => Some(RGBA::new(0.529, 0.0, 0.0, 1.0)), - 89 => Some(RGBA::new(0.529, 0.0, 0.373, 1.0)), - 90 => Some(RGBA::new(0.529, 0.0, 0.529, 1.0)), - 91 => Some(RGBA::new(0.529, 0.0, 0.686, 1.0)), - 92 => Some(RGBA::new(0.529, 0.0, 0.843, 1.0)), - 93 => Some(RGBA::new(0.529, 0.0, 1.0, 1.0)), - 94 => Some(RGBA::new(0.529, 0.373, 0.0, 1.0)), - 95 => Some(RGBA::new(0.529, 0.373, 0.373, 1.0)), - 96 => Some(RGBA::new(0.529, 0.373, 0.529, 1.0)), - 97 => Some(RGBA::new(0.529, 0.373, 0.686, 1.0)), - 98 => Some(RGBA::new(0.529, 0.373, 0.843, 1.0)), - 99 => Some(RGBA::new(0.529, 0.373, 1.0, 1.0)), - 100 => Some(RGBA::new(0.529, 0.529, 0.0, 1.0)), - 101 => Some(RGBA::new(0.529, 0.529, 0.373, 1.0)), - 102 => Some(RGBA::new(0.529, 0.529, 0.529, 1.0)), - 103 => Some(RGBA::new(0.529, 0.529, 0.686, 1.0)), - 104 => Some(RGBA::new(0.529, 0.529, 0.843, 1.0)), - 105 => Some(RGBA::new(0.529, 0.529, 1.0, 1.0)), - 106 => Some(RGBA::new(0.533, 0.686, 0.0, 1.0)), - 107 => Some(RGBA::new(0.533, 0.686, 0.373, 1.0)), - 108 => Some(RGBA::new(0.533, 0.686, 0.529, 1.0)), - 109 => Some(RGBA::new(0.533, 0.686, 0.686, 1.0)), - 110 => Some(RGBA::new(0.533, 0.686, 0.843, 1.0)), - 111 => Some(RGBA::new(0.533, 0.686, 1.0, 1.0)), - 112 => Some(RGBA::new(0.533, 0.843, 0.0, 1.0)), - 113 => Some(RGBA::new(0.533, 0.843, 0.373, 1.0)), - 114 => Some(RGBA::new(0.533, 0.843, 0.529, 1.0)), - 115 => Some(RGBA::new(0.533, 0.843, 0.686, 1.0)), - 116 => Some(RGBA::new(0.533, 0.843, 0.843, 1.0)), - 117 => Some(RGBA::new(0.533, 0.843, 1.0, 1.0)), - 118 => Some(RGBA::new(0.533, 1.0, 0.0, 1.0)), - 119 => Some(RGBA::new(0.533, 1.0, 0.373, 1.0)), - 120 => Some(RGBA::new(0.533, 1.0, 0.529, 1.0)), - 121 => Some(RGBA::new(0.533, 1.0, 0.686, 1.0)), - 122 => Some(RGBA::new(0.533, 1.0, 0.843, 1.0)), - 123 => Some(RGBA::new(0.533, 1.0, 1.0, 1.0)), - 124 => Some(RGBA::new(0.686, 0.0, 0.0, 1.0)), - 125 => Some(RGBA::new(0.686, 0.0, 0.373, 1.0)), - 126 => Some(RGBA::new(0.686, 0.0, 0.529, 1.0)), - 127 => Some(RGBA::new(0.686, 0.0, 0.686, 1.0)), - 128 => Some(RGBA::new(0.686, 0.0, 0.843, 1.0)), - 129 => Some(RGBA::new(0.686, 0.0, 1.0, 1.0)), - 130 => Some(RGBA::new(0.686, 0.373, 0.0, 1.0)), - 131 => Some(RGBA::new(0.686, 0.373, 0.373, 1.0)), - 132 => Some(RGBA::new(0.686, 0.373, 0.529, 1.0)), - 133 => Some(RGBA::new(0.686, 0.373, 0.686, 1.0)), - 134 => Some(RGBA::new(0.686, 0.373, 0.843, 1.0)), - 135 => Some(RGBA::new(0.686, 0.373, 1.0, 1.0)), - 136 => Some(RGBA::new(0.686, 0.529, 0.0, 1.0)), - 137 => Some(RGBA::new(0.686, 0.529, 0.373, 1.0)), - 138 => Some(RGBA::new(0.686, 0.529, 0.529, 1.0)), - 139 => Some(RGBA::new(0.686, 0.529, 0.686, 1.0)), - 140 => Some(RGBA::new(0.686, 0.529, 0.843, 1.0)), - 141 => Some(RGBA::new(0.686, 0.529, 1.0, 1.0)), - 142 => Some(RGBA::new(0.686, 0.686, 0.0, 1.0)), - 143 => Some(RGBA::new(0.686, 0.686, 0.373, 1.0)), - 144 => Some(RGBA::new(0.686, 0.686, 0.529, 1.0)), - 145 => Some(RGBA::new(0.686, 0.686, 0.686, 1.0)), - 146 => Some(RGBA::new(0.686, 0.686, 0.843, 1.0)), - 147 => Some(RGBA::new(0.686, 0.686, 1.0, 1.0)), - 148 => Some(RGBA::new(0.686, 0.843, 0.0, 1.0)), - 149 => Some(RGBA::new(0.686, 0.843, 0.373, 1.0)), - 150 => Some(RGBA::new(0.686, 0.843, 0.529, 1.0)), - 151 => Some(RGBA::new(0.686, 0.843, 0.686, 1.0)), - 152 => Some(RGBA::new(0.686, 0.843, 0.843, 1.0)), - 153 => Some(RGBA::new(0.686, 0.843, 1.0, 1.0)), - 154 => Some(RGBA::new(0.686, 1.0, 0.0, 1.0)), - 155 => Some(RGBA::new(0.686, 1.0, 0.373, 1.0)), - 156 => Some(RGBA::new(0.686, 1.0, 0.529, 1.0)), - 157 => Some(RGBA::new(0.686, 1.0, 0.686, 1.0)), - 158 => Some(RGBA::new(0.686, 1.0, 0.843, 1.0)), - 159 => Some(RGBA::new(0.686, 1.0, 1.0, 1.0)), - 160 => Some(RGBA::new(0.847, 0.0, 0.0, 1.0)), - 161 => Some(RGBA::new(0.847, 0.0, 0.373, 1.0)), - 162 => Some(RGBA::new(0.847, 0.0, 0.529, 1.0)), - 163 => Some(RGBA::new(0.847, 0.0, 0.686, 1.0)), - 164 => Some(RGBA::new(0.847, 0.0, 0.843, 1.0)), - 165 => Some(RGBA::new(0.847, 0.0, 1.0, 1.0)), - 166 => Some(RGBA::new(0.847, 0.373, 0.0, 1.0)), - 167 => Some(RGBA::new(0.847, 0.373, 0.373, 1.0)), - 168 => Some(RGBA::new(0.847, 0.373, 0.529, 1.0)), - 169 => Some(RGBA::new(0.847, 0.373, 0.686, 1.0)), - 170 => Some(RGBA::new(0.847, 0.373, 0.843, 1.0)), - 171 => Some(RGBA::new(0.847, 0.373, 1.0, 1.0)), - 172 => Some(RGBA::new(0.847, 0.529, 0.0, 1.0)), - 173 => Some(RGBA::new(0.847, 0.529, 0.373, 1.0)), - 174 => Some(RGBA::new(0.847, 0.529, 0.529, 1.0)), - 175 => Some(RGBA::new(0.847, 0.529, 0.686, 1.0)), - 176 => Some(RGBA::new(0.847, 0.529, 0.843, 1.0)), - 177 => Some(RGBA::new(0.847, 0.529, 1.0, 1.0)), - 178 => Some(RGBA::new(0.847, 0.686, 0.0, 1.0)), - 179 => Some(RGBA::new(0.847, 0.686, 0.373, 1.0)), - 180 => Some(RGBA::new(0.847, 0.686, 0.529, 1.0)), - 181 => Some(RGBA::new(0.847, 0.686, 0.686, 1.0)), - 182 => Some(RGBA::new(0.847, 0.686, 0.843, 1.0)), - 183 => Some(RGBA::new(0.847, 0.686, 1.0, 1.0)), - 184 => Some(RGBA::new(0.847, 0.843, 0.0, 1.0)), - 185 => Some(RGBA::new(0.847, 0.843, 0.373, 1.0)), - 186 => Some(RGBA::new(0.847, 0.843, 0.529, 1.0)), - 187 => Some(RGBA::new(0.847, 0.843, 0.686, 1.0)), - 188 => Some(RGBA::new(0.847, 0.843, 0.843, 1.0)), - 189 => Some(RGBA::new(0.847, 0.843, 1.0, 1.0)), - 190 => Some(RGBA::new(0.847, 1.0, 0.0, 1.0)), - 191 => Some(RGBA::new(0.847, 1.0, 0.373, 1.0)), - 192 => Some(RGBA::new(0.847, 1.0, 0.529, 1.0)), - 193 => Some(RGBA::new(0.847, 1.0, 0.686, 1.0)), - 194 => Some(RGBA::new(0.847, 1.0, 0.843, 1.0)), - 195 => Some(RGBA::new(0.847, 1.0, 1.0, 1.0)), - 196 => Some(RGBA::new(1.0, 0.0, 0.0, 1.0)), - 197 => Some(RGBA::new(1.0, 0.0, 0.373, 1.0)), - 198 => Some(RGBA::new(1.0, 0.0, 0.529, 1.0)), - 199 => Some(RGBA::new(1.0, 0.0, 0.686, 1.0)), - 200 => Some(RGBA::new(1.0, 0.0, 0.843, 1.0)), - 201 => Some(RGBA::new(1.0, 0.0, 1.0, 1.0)), - 202 => Some(RGBA::new(1.0, 0.373, 0.0, 1.0)), - 203 => Some(RGBA::new(1.0, 0.373, 0.373, 1.0)), - 204 => Some(RGBA::new(1.0, 0.373, 0.529, 1.0)), - 205 => Some(RGBA::new(1.0, 0.373, 0.686, 1.0)), - 206 => Some(RGBA::new(1.0, 0.373, 0.843, 1.0)), - 207 => Some(RGBA::new(1.0, 0.373, 1.0, 1.0)), - 208 => Some(RGBA::new(1.0, 0.529, 0.0, 1.0)), - 209 => Some(RGBA::new(1.0, 0.529, 0.373, 1.0)), - 210 => Some(RGBA::new(1.0, 0.529, 0.529, 1.0)), - 211 => Some(RGBA::new(1.0, 0.529, 0.686, 1.0)), - 212 => Some(RGBA::new(1.0, 0.529, 0.843, 1.0)), - 213 => Some(RGBA::new(1.0, 0.529, 1.0, 1.0)), - 214 => Some(RGBA::new(1.0, 0.686, 0.0, 1.0)), - 215 => Some(RGBA::new(1.0, 0.686, 0.373, 1.0)), - 216 => Some(RGBA::new(1.0, 0.686, 0.529, 1.0)), - 217 => Some(RGBA::new(1.0, 0.686, 0.686, 1.0)), - 218 => Some(RGBA::new(1.0, 0.686, 0.843, 1.0)), - 219 => Some(RGBA::new(1.0, 0.686, 1.0, 1.0)), - 220 => Some(RGBA::new(1.0, 0.843, 0.0, 1.0)), - 221 => Some(RGBA::new(1.0, 0.843, 0.373, 1.0)), - 222 => Some(RGBA::new(1.0, 0.843, 0.529, 1.0)), - 223 => Some(RGBA::new(1.0, 0.843, 0.686, 1.0)), - 224 => Some(RGBA::new(1.0, 0.843, 0.843, 1.0)), - 225 => Some(RGBA::new(1.0, 0.843, 1.0, 1.0)), - 226 => Some(RGBA::new(1.0, 1.0, 0.0, 1.0)), - 227 => Some(RGBA::new(1.0, 1.0, 0.373, 1.0)), - 228 => Some(RGBA::new(1.0, 1.0, 0.529, 1.0)), - 229 => Some(RGBA::new(1.0, 1.0, 0.686, 1.0)), - 230 => Some(RGBA::new(1.0, 1.0, 0.843, 1.0)), - 231 => Some(RGBA::new(1.0, 1.0, 1.0, 1.0)), - 232 => Some(RGBA::new(0.031, 0.031, 0.031, 1.0)), - 233 => Some(RGBA::new(0.071, 0.071, 0.071, 1.0)), - 234 => Some(RGBA::new(0.110, 0.110, 0.110, 1.0)), - 235 => Some(RGBA::new(0.149, 0.149, 0.149, 1.0)), - 236 => Some(RGBA::new(0.188, 0.188, 0.188, 1.0)), - 237 => Some(RGBA::new(0.227, 0.227, 0.227, 1.0)), - 238 => Some(RGBA::new(0.267, 0.267, 0.267, 1.0)), - 239 => Some(RGBA::new(0.306, 0.306, 0.306, 1.0)), - 240 => Some(RGBA::new(0.345, 0.345, 0.345, 1.0)), - 241 => Some(RGBA::new(0.384, 0.384, 0.384, 1.0)), - 242 => Some(RGBA::new(0.424, 0.424, 0.424, 1.0)), - 243 => Some(RGBA::new(0.462, 0.462, 0.462, 1.0)), - 244 => Some(RGBA::new(0.502, 0.502, 0.502, 1.0)), - 245 => Some(RGBA::new(0.541, 0.541, 0.541, 1.0)), - 246 => Some(RGBA::new(0.580, 0.580, 0.580, 1.0)), - 247 => Some(RGBA::new(0.620, 0.620, 0.620, 1.0)), - 248 => Some(RGBA::new(0.659, 0.659, 0.659, 1.0)), - 249 => Some(RGBA::new(0.694, 0.694, 0.694, 1.0)), - 250 => Some(RGBA::new(0.733, 0.733, 0.733, 1.0)), - 251 => Some(RGBA::new(0.777, 0.777, 0.777, 1.0)), - 252 => Some(RGBA::new(0.816, 0.816, 0.816, 1.0)), - 253 => Some(RGBA::new(0.855, 0.855, 0.855, 1.0)), - 254 => Some(RGBA::new(0.890, 0.890, 0.890, 1.0)), - 255 => Some(RGBA::new(0.933, 0.933, 0.933, 1.0)), - _ => None, - } -} diff --git a/src/app/browser/window/tab/item/page/content/text/markdown/tags/code/syntax.rs b/src/app/browser/window/tab/item/page/content/text/markdown/tags/code/syntax.rs deleted file mode 100644 index 50de853d..00000000 --- a/src/app/browser/window/tab/item/page/content/text/markdown/tags/code/syntax.rs +++ /dev/null @@ -1,152 +0,0 @@ -pub mod error; -mod tag; - -pub use error::Error; -use tag::Tag; - -use adw::StyleManager; -use gtk::{ - TextTag, - gdk::RGBA, - pango::{Style, Underline}, - prelude::TextTagExt, -}; -use syntect::{ - easy::HighlightLines, - highlighting::{Color, FontStyle, ThemeSet}, - parsing::{SyntaxReference, SyntaxSet}, -}; - -/* Default theme - @TODO make optional - base16-ocean.dark - base16-eighties.dark - base16-mocha.dark - base16-ocean.light - InspiredGitHub - Solarized (dark) - Solarized (light) -*/ -pub const DEFAULT_THEME_DARK: &str = "base16-eighties.dark"; -pub const DEFAULT_THEME_LIGHT: &str = "InspiredGitHub"; - -pub struct Syntax { - syntax_set: SyntaxSet, - theme_set: ThemeSet, -} - -impl Default for Syntax { - fn default() -> Self { - Self::new() - } -} - -impl Syntax { - // Constructors - - /// Create new `Self` - pub fn new() -> Self { - Self { - syntax_set: SyntaxSet::load_defaults_newlines(), - theme_set: ThemeSet::load_defaults(), - } - } - - // Actions - - /// Apply `Syntect` highlight to new buffer returned, - /// according to given `alt` and `source_code` content - pub fn highlight( - &self, - source_code: &str, - alt: Option<&String>, - ) -> Result, Error> { - if let Some(value) = alt { - if let Some(reference) = self.syntax_set.find_syntax_by_name(value) { - return self.buffer(source_code, reference); - } - - if let Some(reference) = self.syntax_set.find_syntax_by_token(value) { - return self.buffer(source_code, reference); - } - - if let Some(reference) = self.syntax_set.find_syntax_by_path(value) { - return self.buffer(source_code, reference); - } - } - - if let Some(reference) = self.syntax_set.find_syntax_by_first_line(source_code) { - return self.buffer(source_code, reference); - } - - Err(Error::Parse) - } - - fn buffer( - &self, - source: &str, - syntax_reference: &SyntaxReference, - ) -> Result, Error> { - // Init new line buffer - let mut buffer = Vec::new(); - - // Apply syntect decorator - let mut ranges = HighlightLines::new( - syntax_reference, - &self.theme_set.themes[if StyleManager::default().is_dark() { - DEFAULT_THEME_DARK - } else { - DEFAULT_THEME_LIGHT - }], // @TODO apply on env change - ); - - match ranges.highlight_line(source, &self.syntax_set) { - Ok(result) => { - // Build tags - for (style, entity) in result { - // Create new tag from default preset - let tag = Tag::new(); - - // Tuneup using syntect conversion - // tag.set_background_rgba(Some(&color_to_rgba(style.background))); - tag.text_tag - .set_foreground_rgba(Some(&color_to_rgba(style.foreground))); - tag.text_tag - .set_style(font_style_to_style(style.font_style)); - tag.text_tag - .set_underline(font_style_to_underline(style.font_style)); - - // Append - buffer.push((tag.text_tag, entity.to_string())); - } - Ok(buffer) - } - Err(e) => Err(Error::Syntect(e)), - } - } -} - -// Tools - -fn color_to_rgba(color: Color) -> RGBA { - RGBA::new( - color.r as f32 / 255.0, - color.g as f32 / 255.0, - color.b as f32 / 255.0, - color.a as f32 / 255.0, - ) -} - -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, - } -} diff --git a/src/app/browser/window/tab/item/page/content/text/markdown/tags/code/syntax/error.rs b/src/app/browser/window/tab/item/page/content/text/markdown/tags/code/syntax/error.rs deleted file mode 100644 index ae9bfdb6..00000000 --- a/src/app/browser/window/tab/item/page/content/text/markdown/tags/code/syntax/error.rs +++ /dev/null @@ -1,18 +0,0 @@ -use std::fmt::{Display, Formatter, Result}; - -#[derive(Debug)] -pub enum Error { - Parse, - Syntect(syntect::Error), -} - -impl Display for Error { - fn fmt(&self, f: &mut Formatter) -> Result { - match self { - Self::Parse => write!(f, "Parse error"), - Self::Syntect(e) => { - write!(f, "Syntect error: {e}") - } - } - } -}