mirror of
https://github.com/YGGverse/Yoda.git
synced 2026-03-31 08:35:28 +00:00
implement italic tag
This commit is contained in:
parent
2ef5e52079
commit
ca9c2058ed
4 changed files with 153 additions and 5 deletions
|
|
@ -2,6 +2,7 @@ mod bold;
|
|||
mod code;
|
||||
mod header;
|
||||
mod hr;
|
||||
mod italic;
|
||||
mod list;
|
||||
mod pre;
|
||||
mod quote;
|
||||
|
|
@ -18,6 +19,7 @@ use gtk::{
|
|||
prelude::{TextBufferExt, TextViewExt},
|
||||
};
|
||||
use header::Header;
|
||||
use italic::Italic;
|
||||
use pre::Pre;
|
||||
use quote::Quote;
|
||||
use std::collections::HashMap;
|
||||
|
|
@ -28,6 +30,7 @@ pub struct Tags {
|
|||
pub bold: Bold,
|
||||
pub code: Code,
|
||||
pub header: Header,
|
||||
pub italic: Italic,
|
||||
pub pre: Pre,
|
||||
pub quote: Quote,
|
||||
pub strike: Strike,
|
||||
|
|
@ -47,6 +50,7 @@ impl Tags {
|
|||
bold: Bold::new(),
|
||||
code: Code::new(),
|
||||
header: Header::new(),
|
||||
italic: Italic::new(),
|
||||
pre: Pre::new(),
|
||||
quote: Quote::new(),
|
||||
strike: Strike::new(),
|
||||
|
|
@ -75,6 +79,7 @@ impl Tags {
|
|||
self.quote.render(&buffer);
|
||||
|
||||
self.bold.render(&buffer);
|
||||
self.italic.render(&buffer);
|
||||
self.pre.render(&buffer);
|
||||
self.strike.render(&buffer);
|
||||
self.underline.render(&buffer);
|
||||
|
|
@ -102,6 +107,7 @@ impl Tags {
|
|||
title.map(|mut s| {
|
||||
s = bold::strip_tags(&s);
|
||||
s = hr::strip_tags(&s);
|
||||
s = italic::strip_tags(&s);
|
||||
s = pre::strip_tags(&s);
|
||||
s = reference::strip_tags(&s);
|
||||
s = strike::strip_tags(&s);
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ impl Bold {
|
|||
Self(TextTag::builder().weight(600).wrap_mode(Word).build())
|
||||
}
|
||||
|
||||
/// Apply **bold** `Tag` to given `TextBuffer`
|
||||
/// Apply **bold**/__bold__ `Tag` to given `TextBuffer`
|
||||
pub fn render(&self, buffer: &TextBuffer) {
|
||||
assert!(buffer.tag_table().add(&self.0));
|
||||
|
||||
|
|
@ -72,7 +72,8 @@ pub fn strip_tags(value: &str) -> String {
|
|||
|
||||
#[test]
|
||||
fn test_strip_tags() {
|
||||
const VALUE: &str = "Some **bold 1** and **bold 2** and __bold 3__ and *italic 1* and _italic 2_ with ";
|
||||
const VALUE: &str =
|
||||
"Some **bold 1** and **bold 2** and __bold 3__ and *italic 1* and _italic 2_";
|
||||
let mut result = String::from(VALUE);
|
||||
for cap in Regex::new(REGEX_BOLD).unwrap().captures_iter(VALUE) {
|
||||
if let Some(m) = cap.get(0) {
|
||||
|
|
@ -81,7 +82,7 @@ fn test_strip_tags() {
|
|||
}
|
||||
assert_eq!(
|
||||
result,
|
||||
"Some bold 1 and bold 2 and bold 3 and *italic 1* and _italic 2_ with "
|
||||
"Some bold 1 and bold 2 and bold 3 and *italic 1* and _italic 2_"
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -90,7 +91,7 @@ fn test_regex() {
|
|||
let cap: Vec<_> = Regex::new(REGEX_BOLD)
|
||||
.unwrap()
|
||||
.captures_iter(
|
||||
"Some **bold 1** and **bold 2** and __bold 3__ and *italic 1* and _italic 2_ with "
|
||||
"Some **bold 1** and **bold 2** and __bold 3__ and *italic 1* and _italic 2_",
|
||||
)
|
||||
.collect();
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,141 @@
|
|||
use gtk::{
|
||||
TextBuffer, TextTag,
|
||||
WrapMode::Word,
|
||||
pango::Style,
|
||||
prelude::{TextBufferExt, TextBufferExtManual},
|
||||
};
|
||||
use regex::Regex;
|
||||
|
||||
const REGEX_ITALIC_1: &str = r"\*(?P<text>[^\*]*)\*";
|
||||
const REGEX_ITALIC_2: &str = r"\b_(?P<text>[^_]*)_\b";
|
||||
|
||||
pub struct Italic(TextTag);
|
||||
|
||||
impl Italic {
|
||||
pub fn new() -> Self {
|
||||
Self(
|
||||
TextTag::builder()
|
||||
.style(Style::Italic)
|
||||
.wrap_mode(Word)
|
||||
.build(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Apply *italic*/_italic_ `Tag` to given `TextBuffer`
|
||||
/// * run after `Bold` tag!
|
||||
pub fn render(&self, buffer: &TextBuffer) {
|
||||
assert!(buffer.tag_table().add(&self.0));
|
||||
|
||||
render(self, buffer, REGEX_ITALIC_1);
|
||||
render(self, buffer, REGEX_ITALIC_2);
|
||||
}
|
||||
}
|
||||
|
||||
fn render(this: &Italic, buffer: &TextBuffer, regex: &str) {
|
||||
let (start, end) = buffer.bounds();
|
||||
let full_content = buffer.text(&start, &end, true).to_string();
|
||||
|
||||
let matches: Vec<_> = Regex::new(regex)
|
||||
.unwrap()
|
||||
.captures_iter(&full_content)
|
||||
.collect();
|
||||
|
||||
for cap in matches.into_iter().rev() {
|
||||
let full_match = cap.get(0).unwrap();
|
||||
|
||||
let start_char_offset = full_content[..full_match.start()].chars().count() as i32;
|
||||
let end_char_offset = full_content[..full_match.end()].chars().count() as i32;
|
||||
|
||||
let mut start_iter = buffer.iter_at_offset(start_char_offset);
|
||||
let mut end_iter = buffer.iter_at_offset(end_char_offset);
|
||||
|
||||
if start_char_offset > 0
|
||||
&& buffer
|
||||
.text(
|
||||
&buffer.iter_at_offset(start_char_offset - 1),
|
||||
&end_iter,
|
||||
false,
|
||||
)
|
||||
.contains("\\")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut tags = start_iter.tags();
|
||||
tags.push(this.0.clone());
|
||||
|
||||
buffer.delete(&mut start_iter, &mut end_iter);
|
||||
buffer.insert_with_tags(
|
||||
&mut start_iter,
|
||||
&cap["text"],
|
||||
&tags.iter().collect::<Vec<&TextTag>>(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// * run after `Bold` tag!
|
||||
pub fn strip_tags(value: &str) -> String {
|
||||
let mut s = String::from(value);
|
||||
for cap in Regex::new(REGEX_ITALIC_1).unwrap().captures_iter(value) {
|
||||
if let Some(m) = cap.get(0) {
|
||||
s = s.replace(m.as_str(), &cap["text"]);
|
||||
}
|
||||
}
|
||||
for cap in Regex::new(REGEX_ITALIC_2).unwrap().captures_iter(value) {
|
||||
if let Some(m) = cap.get(0) {
|
||||
s = s.replace(m.as_str(), &cap["text"]);
|
||||
}
|
||||
}
|
||||
s
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_strip_tags() {
|
||||
const S: &str = "Some *italic 1*\nand *italic 2* and _italic 3_";
|
||||
{
|
||||
let mut result = String::from(S);
|
||||
for cap in Regex::new(REGEX_ITALIC_1).unwrap().captures_iter(S) {
|
||||
if let Some(m) = cap.get(0) {
|
||||
result = result.replace(m.as_str(), &cap["text"]);
|
||||
}
|
||||
}
|
||||
assert_eq!(result, "Some italic 1\nand italic 2 and _italic 3_")
|
||||
}
|
||||
{
|
||||
let mut result = String::from(S);
|
||||
for cap in Regex::new(REGEX_ITALIC_2).unwrap().captures_iter(S) {
|
||||
if let Some(m) = cap.get(0) {
|
||||
result = result.replace(m.as_str(), &cap["text"]);
|
||||
}
|
||||
}
|
||||
assert_eq!(result, "Some *italic 1*\nand *italic 2* and italic 3")
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_regex() {
|
||||
const S: &str = "Some *italic 1*\nand *italic 2* and _italic 3_";
|
||||
{
|
||||
let cap: Vec<_> = Regex::new(REGEX_ITALIC_1)
|
||||
.unwrap()
|
||||
.captures_iter(S)
|
||||
.collect();
|
||||
|
||||
assert_eq!(cap.len(), 2);
|
||||
|
||||
let mut c = cap.into_iter();
|
||||
assert_eq!(&c.next().unwrap()["text"], "italic 1");
|
||||
assert_eq!(&c.next().unwrap()["text"], "italic 2");
|
||||
}
|
||||
{
|
||||
let cap: Vec<_> = Regex::new(REGEX_ITALIC_2)
|
||||
.unwrap()
|
||||
.captures_iter(S)
|
||||
.collect();
|
||||
|
||||
assert_eq!(cap.len(), 1);
|
||||
|
||||
let mut c = cap.into_iter();
|
||||
assert_eq!(&c.next().unwrap()["text"], "italic 3");
|
||||
}
|
||||
}
|
||||
|
|
@ -16,7 +16,7 @@ impl Quote {
|
|||
TextTag::builder()
|
||||
.left_margin(28)
|
||||
.wrap_mode(Word)
|
||||
.style(Italic) // what about the italic tags decoration? @TODO
|
||||
.style(Italic) // conflicts the italic tags decoration @TODO
|
||||
.build(),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue