mirror of
https://github.com/YGGverse/Yoda.git
synced 2026-03-31 08:35:28 +00:00
use shared code widget, remove duplicated components (used in gemtext and markdown renderers)
This commit is contained in:
parent
45e8824a2e
commit
f4b1aa9ecc
19 changed files with 166 additions and 741 deletions
|
|
@ -1,12 +1,12 @@
|
||||||
|
mod common;
|
||||||
mod gemini;
|
mod gemini;
|
||||||
mod markdown;
|
mod markdown;
|
||||||
mod nex;
|
mod nex;
|
||||||
mod plain;
|
mod plain;
|
||||||
mod source;
|
mod source;
|
||||||
|
|
||||||
use crate::{app::browser::window::tab::item::page::Page, profile::Profile};
|
|
||||||
|
|
||||||
use super::{ItemAction, WindowAction};
|
use super::{ItemAction, WindowAction};
|
||||||
|
use crate::{app::browser::window::tab::item::page::Page, profile::Profile};
|
||||||
use adw::ClampScrollable;
|
use adw::ClampScrollable;
|
||||||
use gemini::Gemini;
|
use gemini::Gemini;
|
||||||
use gtk::{ScrolledWindow, TextView, glib::Uri};
|
use gtk::{ScrolledWindow, TextView, glib::Uri};
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
139
src/app/browser/window/tab/item/page/content/text/common/code.rs
Normal file
139
src/app/browser/window/tab/item/page/content/text/common/code.rs
Normal file
|
|
@ -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;
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
mod ansi;
|
|
||||||
pub mod error;
|
pub mod error;
|
||||||
mod gutter;
|
mod gutter;
|
||||||
mod icon;
|
mod icon;
|
||||||
mod syntax;
|
|
||||||
mod tag;
|
mod tag;
|
||||||
|
|
||||||
use super::{ItemAction, WindowAction};
|
use super::{ItemAction, WindowAction};
|
||||||
|
|
@ -20,7 +18,6 @@ use gutter::Gutter;
|
||||||
use icon::Icon;
|
use icon::Icon;
|
||||||
use sourceview::prelude::{ActionExt, ActionMapExt, DisplayExt, ToVariant};
|
use sourceview::prelude::{ActionExt, ActionMapExt, DisplayExt, ToVariant};
|
||||||
use std::{cell::Cell, collections::HashMap, rc::Rc};
|
use std::{cell::Cell, collections::HashMap, rc::Rc};
|
||||||
use syntax::Syntax;
|
|
||||||
use tag::Tag;
|
use tag::Tag;
|
||||||
|
|
||||||
pub const NEW_LINE: &str = "\n";
|
pub const NEW_LINE: &str = "\n";
|
||||||
|
|
@ -63,9 +60,6 @@ impl Gemini {
|
||||||
RGBA::new(0.208, 0.518, 0.894, 0.9),
|
RGBA::new(0.208, 0.518, 0.894, 0.9),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Init syntect highlight features
|
|
||||||
let syntax = Syntax::new();
|
|
||||||
|
|
||||||
// Init icons
|
// Init icons
|
||||||
let icon = Icon::new();
|
let icon = Icon::new();
|
||||||
|
|
||||||
|
|
@ -125,60 +119,17 @@ impl Gemini {
|
||||||
Some(ref mut c) => {
|
Some(ref mut c) => {
|
||||||
match c.continue_from(line) {
|
match c.continue_from(line) {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
// Close tag found:
|
// Closing tag found:
|
||||||
if c.is_completed {
|
if c.is_completed {
|
||||||
// Is alt provided
|
text_view.add_child_at_anchor(
|
||||||
let alt = match c.alt {
|
&super::common::Code::init(
|
||||||
Some(ref alt) => {
|
&text_view,
|
||||||
// Insert alt value to the main buffer
|
c.value.trim_end(),
|
||||||
buffer.insert_with_tags(
|
c.alt.as_ref(),
|
||||||
&mut buffer.end_iter(),
|
)
|
||||||
alt.as_str(),
|
.widget,
|
||||||
&[&tag.title],
|
&buffer.create_child_anchor(&mut buffer.end_iter()),
|
||||||
);
|
);
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset
|
// Reset
|
||||||
code = None;
|
code = None;
|
||||||
|
|
|
||||||
|
|
@ -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(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -2,14 +2,12 @@ mod header;
|
||||||
mod list;
|
mod list;
|
||||||
mod plain;
|
mod plain;
|
||||||
mod quote;
|
mod quote;
|
||||||
mod title;
|
|
||||||
|
|
||||||
use gtk::{TextTag, TextTagTable};
|
use gtk::{TextTag, TextTagTable};
|
||||||
use header::Header;
|
use header::Header;
|
||||||
use list::List;
|
use list::List;
|
||||||
use plain::Plain;
|
use plain::Plain;
|
||||||
use quote::Quote;
|
use quote::Quote;
|
||||||
use title::Title;
|
|
||||||
|
|
||||||
pub struct Tag {
|
pub struct Tag {
|
||||||
pub text_tag_table: TextTagTable,
|
pub text_tag_table: TextTagTable,
|
||||||
|
|
@ -19,7 +17,6 @@ pub struct Tag {
|
||||||
pub h3: TextTag,
|
pub h3: TextTag,
|
||||||
pub list: TextTag,
|
pub list: TextTag,
|
||||||
pub quote: TextTag,
|
pub quote: TextTag,
|
||||||
pub title: TextTag,
|
|
||||||
pub plain: TextTag,
|
pub plain: TextTag,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -38,7 +35,6 @@ impl Tag {
|
||||||
let h3 = TextTag::h3();
|
let h3 = TextTag::h3();
|
||||||
let list = TextTag::list();
|
let list = TextTag::list();
|
||||||
let quote = TextTag::quote();
|
let quote = TextTag::quote();
|
||||||
let title = TextTag::title();
|
|
||||||
let plain = TextTag::plain();
|
let plain = TextTag::plain();
|
||||||
|
|
||||||
// Init tag table
|
// Init tag table
|
||||||
|
|
@ -47,7 +43,6 @@ impl Tag {
|
||||||
text_tag_table.add(&h1);
|
text_tag_table.add(&h1);
|
||||||
text_tag_table.add(&h2);
|
text_tag_table.add(&h2);
|
||||||
text_tag_table.add(&h3);
|
text_tag_table.add(&h3);
|
||||||
text_tag_table.add(&title);
|
|
||||||
text_tag_table.add(&list);
|
text_tag_table.add(&list);
|
||||||
text_tag_table.add("e);
|
text_tag_table.add("e);
|
||||||
text_tag_table.add(&plain);
|
text_tag_table.add(&plain);
|
||||||
|
|
@ -60,7 +55,6 @@ impl Tag {
|
||||||
h3,
|
h3,
|
||||||
list,
|
list,
|
||||||
quote,
|
quote,
|
||||||
title,
|
|
||||||
plain,
|
plain,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,18 +1,10 @@
|
||||||
mod ansi;
|
|
||||||
mod syntax;
|
|
||||||
|
|
||||||
use gtk::{
|
use gtk::{
|
||||||
Align, Box, Button, Label, Orientation, PolicyType, ScrolledWindow, Separator, TextBuffer,
|
TextBuffer, TextSearchFlags, TextView,
|
||||||
TextSearchFlags, TextTagTable, TextView, WrapMode,
|
glib::{GString, uuid_string_random},
|
||||||
gdk::Display,
|
prelude::{TextBufferExt, TextBufferExtManual, TextViewExt},
|
||||||
glib::{ControlFlow, GString, idle_add_local, uuid_string_random},
|
|
||||||
prelude::{
|
|
||||||
BoxExt, ButtonExt, DisplayExt, TextBufferExt, TextBufferExtManual, TextViewExt, WidgetExt,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use std::{cell::Cell, collections::HashMap, rc::Rc};
|
use std::collections::HashMap;
|
||||||
use syntax::Syntax;
|
|
||||||
|
|
||||||
const REGEX_CODE: &str = r"(?s)```[ \t]*(?P<alt>.*?)\n(?P<data>.*?)```";
|
const REGEX_CODE: &str = r"(?s)```[ \t]*(?P<alt>.*?)\n(?P<data>.*?)```";
|
||||||
|
|
||||||
|
|
@ -69,8 +61,6 @@ impl Code {
|
||||||
/// Apply code `Tag` to given `TextView` using `Self.index`
|
/// Apply code `Tag` to given `TextView` using `Self.index`
|
||||||
pub fn render(&mut self, text_view: &TextView) {
|
pub fn render(&mut self, text_view: &TextView) {
|
||||||
let buffer = text_view.buffer();
|
let buffer = text_view.buffer();
|
||||||
let syntax = Syntax::new();
|
|
||||||
let copied = Rc::new(Cell::new(None));
|
|
||||||
for (k, v) in self.0.iter() {
|
for (k, v) in self.0.iter() {
|
||||||
while let Some((mut m_start, mut m_end)) =
|
while let Some((mut m_start, mut m_end)) =
|
||||||
buffer
|
buffer
|
||||||
|
|
@ -79,134 +69,14 @@ impl Code {
|
||||||
{
|
{
|
||||||
buffer.delete(&mut m_start, &mut m_end);
|
buffer.delete(&mut m_start, &mut m_end);
|
||||||
text_view.add_child_at_anchor(
|
text_view.add_child_at_anchor(
|
||||||
&{
|
&super::super::super::common::Code::init(
|
||||||
const MARGIN: i32 = 16;
|
text_view,
|
||||||
let widget = Box::builder()
|
v.data.as_ref(),
|
||||||
.css_classes(["card"])
|
v.alt.as_ref(),
|
||||||
.halign(Align::Fill)
|
)
|
||||||
.hexpand(true)
|
.widget,
|
||||||
.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
|
|
||||||
},
|
|
||||||
&buffer.create_child_anchor(&mut m_end),
|
&buffer.create_child_anchor(&mut m_end),
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -1,256 +0,0 @@
|
||||||
use gtk::gdk::RGBA;
|
|
||||||
|
|
||||||
/// Default RGBa palette for ANSI terminal emulation
|
|
||||||
pub fn default(color: u8) -> Option<RGBA> {
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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<Vec<(TextTag, String)>, 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<Vec<(TextTag, String)>, 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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue