implement code tag parser

This commit is contained in:
yggverse 2024-10-15 03:38:23 +03:00
parent bb9ddda109
commit 9e9b40e3ac
5 changed files with 193 additions and 4 deletions

View File

@ -1,6 +1,7 @@
mod parser;
mod widget;
use parser::code::Code;
use parser::header::Header;
use parser::link::Link;
use parser::list::List;
@ -44,8 +45,95 @@ impl Reader {
// Init new text buffer
let buffer = TextBuffer::new(None);
// Init multiline code builder features
let mut multiline = None;
// Parse gemtext lines
for line in gemtext.lines() {
// Is inline code
if let Some(code) = Code::inline_from(line) {
// Build tag from level parsed
let tag = TextTag::builder()
.family("monospace")
.scale(0.8)
.wrap_mode(WrapMode::None)
.build();
// Register tag in buffer
buffer.tag_table().add(&tag);
// Append value to buffer
buffer.insert_with_tags(&mut buffer.end_iter(), code.value.as_str(), &[&tag]);
buffer.insert(&mut buffer.end_iter(), "\n");
// Skip other actions for this line
continue;
}
// Is multiline code
match multiline {
None => {
// Open tag found
if let Some(code) = Code::multiline_begin_from(line) {
// Begin next lines collection into the code buffer
multiline = Some(code);
// Skip other actions for this line
continue;
}
}
Some(ref mut this) => {
Code::multiline_continue_from(this, line);
// Close tag found:
if this.completed {
// Is alt provided
if let Some(alt) = &this.alt {
// Build tag for code alt description
let tag = TextTag::builder()
.pixels_above_lines(4)
.pixels_below_lines(8)
.weight(500)
.wrap_mode(WrapMode::None)
.build();
// Register tag in buffer
buffer.tag_table().add(&tag);
// Insert alt value to the main buffer
buffer.insert_with_tags(&mut buffer.end_iter(), alt.as_str(), &[&tag]);
buffer.insert(&mut buffer.end_iter(), "\n");
}
// Build tag container for multiline code result
let tag = TextTag::builder()
.family("monospace") // @TODO does not work
.left_margin(28)
.scale(0.8)
.wrap_mode(WrapMode::None)
.build();
// Register tag in buffer
buffer.tag_table().add(&tag);
// Insert multiline code buffer into main buffer
buffer.insert_with_tags(
&mut buffer.end_iter(),
&this.buffer.join("\n"),
&[&tag],
);
buffer.insert(&mut buffer.end_iter(), "\n");
// Reset
multiline = None;
}
// Skip other actions for this line
continue;
}
};
// Is header
if let Some(header) = Header::from(line) {
// Build tag from level parsed
@ -54,13 +142,13 @@ impl Reader {
.scale(1.6)
.sentence(true)
.weight(500)
.wrap_mode(gtk::WrapMode::Word)
.wrap_mode(WrapMode::Word)
.build(),
parser::header::Level::H2 => TextTag::builder()
.scale(1.4)
.sentence(true)
.weight(400)
.wrap_mode(gtk::WrapMode::Word)
.wrap_mode(WrapMode::Word)
.build(),
parser::header::Level::H3 => TextTag::builder()
.scale(1.2)
@ -140,7 +228,7 @@ impl Reader {
.left_margin(28)
.pixels_above_lines(4)
.pixels_below_lines(4)
.wrap_mode(gtk::WrapMode::Word)
.wrap_mode(WrapMode::Word)
.build();
// Register tag in buffer
@ -163,7 +251,7 @@ impl Reader {
// Build tag from level parsed
let tag = TextTag::builder()
.style(Style::Italic)
.wrap_mode(gtk::WrapMode::Word)
.wrap_mode(WrapMode::Word)
.build();
// Register tag in buffer

View File

@ -1,3 +1,4 @@
pub mod code;
pub mod header;
pub mod link;
pub mod list;

View File

@ -0,0 +1,25 @@
pub mod inline;
pub mod multiline;
use inline::Inline;
use multiline::Multiline;
pub struct Code {
// nothing yet..
}
impl Code {
// Inline
pub fn inline_from(line: &str) -> Option<Inline> {
Inline::from(line)
}
// Multiline
pub fn multiline_begin_from(line: &str) -> Option<Multiline> {
Multiline::begin_from(line)
}
pub fn multiline_continue_from(this: &mut Multiline, line: &str) {
Multiline::continue_from(this, line)
}
}

View File

@ -0,0 +1,29 @@
use gtk::glib::{GString, Regex, RegexCompileFlags, RegexMatchFlags};
pub struct Inline {
pub value: GString,
}
impl Inline {
pub fn from(line: &str) -> Option<Self> {
// Parse line
let regex = Regex::split_simple(
r"^`{3}([^`]*)`{3}$",
line,
RegexCompileFlags::DEFAULT,
RegexMatchFlags::DEFAULT,
);
// Detect value
let value = regex.get(1)?;
if value.trim().is_empty() {
return None;
}
// Result
Some(Self {
value: GString::from(value.as_str()),
})
}
}

View File

@ -0,0 +1,46 @@
use gtk::glib::GString;
pub struct Multiline {
pub alt: Option<GString>,
pub buffer: Vec<GString>,
pub completed: bool,
}
impl Multiline {
// Search in line for tag open,
// return Self constructed on success or None
pub fn begin_from(line: &str) -> Option<Self> {
if line.starts_with("```") {
let alt = line.trim_start_matches("```").trim();
return Some(Self {
alt: match alt.is_empty() {
true => None,
false => Some(GString::from(alt)),
},
buffer: Vec::new(),
completed: false,
});
}
None
}
// Continue preformatted buffer from line,
// set `completed` as True on close tag found
pub fn continue_from(&mut self, line: &str) {
// Make sure buffer not completed yet
if self.completed {
panic!("Could not continue as completed") // @TODO handle
}
// Line contain close tag
if line.ends_with("```") {
self.completed = true;
}
// Append data to the buffer, trim close tag on exists
self.buffer
.push(GString::from(line.trim_end_matches("```").trim()));
}
}