Skip to content

Commit

Permalink
feat: add rendering for textbox and hyperlink (#119)
Browse files Browse the repository at this point in the history
* fix: fix spans for ambiguous tokens

* fix: reset peeked_cache on cache update

* feat: add textbox & hyperlink rendering

* feat: handle open formats without Vec
  • Loading branch information
mhatzl authored Nov 19, 2023
1 parent c1a4827 commit 10bbdf0
Show file tree
Hide file tree
Showing 20 changed files with 315 additions and 128 deletions.
22 changes: 19 additions & 3 deletions commons/src/lexer/position.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,14 @@ impl AddAssign<Option<SpanLen>> for Position {
}
}

impl AddAssign<usize> for Position {
fn add_assign(&mut self, rhs: usize) {
self.col_utf8 += rhs;
self.col_utf16 += rhs;
self.col_grapheme += rhs;
}
}

impl<T> Add<T> for Position
where
Position: AddAssign<T>,
Expand Down Expand Up @@ -122,13 +130,21 @@ impl SubAssign<SpanLen> for Position {
impl SubAssign<Option<SpanLen>> for Position {
fn sub_assign(&mut self, rhs: Option<SpanLen>) {
if let Some(rhs) = rhs {
self.col_utf8 += rhs.len_utf8;
self.col_utf16 += rhs.len_utf16;
self.col_grapheme += rhs.len_grapheme;
self.col_utf8 -= rhs.len_utf8;
self.col_utf16 -= rhs.len_utf16;
self.col_grapheme -= rhs.len_grapheme;
}
}
}

impl SubAssign<usize> for Position {
fn sub_assign(&mut self, rhs: usize) {
self.col_utf8 -= rhs;
self.col_utf16 -= rhs;
self.col_grapheme -= rhs;
}
}

impl<T> Sub<T> for Position
where
Position: SubAssign<T>,
Expand Down
1 change: 1 addition & 0 deletions inline/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ unimarkup-commons = { path = "../commons/", version = "0" }

[dev-dependencies]
unimarkup-commons = { path ="../commons/", version = "0", features = ["test_runner"] }
unimarkup-core = { path = "../core/", version = "0" }
serde.workspace = true
serde_yaml.workspace = true
libtest-mimic = "0.6.1"
10 changes: 5 additions & 5 deletions inline/src/element/formatting/ambiguous.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ fn resolve_closing<'slice, 'input>(
close_token.kind,
inner,
None, // check for optional attributes here
open_token.start,
open_token.start + counterpart(close_token.kind).len(), // Because inner token gets closed
close_token.end,
false,
));
Expand Down Expand Up @@ -271,12 +271,12 @@ fn to_inline<'input>(
inner,
None,
inner_token.start,
inner_token.end,
end - outer_token.kind.len(), // Because the outer token is at "end"
implicit_end,
)],
attributes,
outer_token.start,
outer_token.end,
end,
implicit_end,
)
} else {
Expand Down Expand Up @@ -320,7 +320,7 @@ fn main_part(kind: InlineTokenKind) -> InlineTokenKind {
InlineTokenKind::Italic | InlineTokenKind::BoldItalic => InlineTokenKind::Bold,
InlineTokenKind::Underline => kind,
InlineTokenKind::Subscript | InlineTokenKind::UnderlineSubscript => {
InlineTokenKind::Underline
InlineTokenKind::Subscript
}
_ => kind,
}
Expand All @@ -334,7 +334,7 @@ fn sub_part(kind: InlineTokenKind) -> InlineTokenKind {
InlineTokenKind::Bold | InlineTokenKind::BoldItalic => InlineTokenKind::Italic,
InlineTokenKind::Subscript => kind,
InlineTokenKind::Underline | InlineTokenKind::UnderlineSubscript => {
InlineTokenKind::Subscript
InlineTokenKind::Underline
}
_ => kind,
}
Expand Down
19 changes: 17 additions & 2 deletions inline/src/element/formatting/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -215,10 +215,25 @@ const STRIKETHROUGH_INDEX: usize = 5;
const HIGHLIGHT_INDEX: usize = 6;
const OVERLINE_INDEX: usize = 7;
const QUOTE_INDEX: usize = 8;
pub(crate) const NR_OF_UNSCOPED_FORMATS: usize = 9;
const NR_OF_UNSCOPED_FORMATS: usize = 9;

/// Type used to keep track of open formats that do not open their own scope.
pub(crate) type OpenFormatMap = [bool; NR_OF_UNSCOPED_FORMATS];
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub(crate) struct OpenFormatMap([bool; NR_OF_UNSCOPED_FORMATS]);

impl OpenFormatMap {
pub(crate) fn is_open(&self, index: usize) -> bool {
self.0[index]
}

pub(crate) fn open(&mut self, index: usize) {
self.0[index] = true;
}

pub(crate) fn close(&mut self, index: usize) {
self.0[index] = false;
}
}

/// Returns the index in the open format map for the given unscoped format.
pub(crate) fn map_index(kind: &InlineTokenKind) -> usize {
Expand Down
4 changes: 2 additions & 2 deletions inline/src/element/formatting/scoped.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ macro_rules! scoped_parser {
// ignore implicits, because only escapes and logic elements are allowed in following inline verbatim
let prev_context_flags = parser.context.flags;

let mut scoped_parser =
let (mut scoped_parser, outer_open_formats) =
parser.nest_scoped(Some(Rc::new(|matcher: &mut dyn EndMatcher| {
!matcher.prev_is_space()
&& matcher.consumed_matches(&[InlineTokenKind::$kind.into()])
Expand All @@ -41,7 +41,7 @@ macro_rules! scoped_parser {
scoped_parser = updated_parser;

let end_reached = scoped_parser.iter.end_reached();
parser = scoped_parser.unfold();
parser = scoped_parser.unfold(outer_open_formats);
parser.context.flags = prev_context_flags;

let prev_token = parser.iter.prev_token().expect(
Expand Down
21 changes: 12 additions & 9 deletions inline/src/element/textbox/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,20 +73,22 @@ pub(crate) fn parse<'slice, 'input>(
open_token.kind
);

let mut scoped_parser = parser.nest_scoped(Some(Rc::new(|matcher: &mut dyn EndMatcher| {
matcher.consumed_matches(&[TokenKind::CloseBracket])
})));
let (mut scoped_parser, outer_open_formats) =
parser.nest_scoped(Some(Rc::new(|matcher: &mut dyn EndMatcher| {
matcher.consumed_matches(&[TokenKind::CloseBracket])
})));

let checkpoint = scoped_parser.iter.checkpoint();
let (updated_parser, box_variant_opt) = parse_box_variant(scoped_parser);
scoped_parser = updated_parser;

match box_variant_opt {
Some(box_variant) => {
return (scoped_parser.unfold(), Some(box_variant));
return (scoped_parser.unfold(outer_open_formats), Some(box_variant));
}
None => {
scoped_parser.iter.rollback(checkpoint);
scoped_parser.iter.next(); // Consume open bracket
}
}

Expand All @@ -104,17 +106,18 @@ pub(crate) fn parse<'slice, 'input>(
.expect("Inlines in textbox => previous token must exist.")
};
let end_reached = scoped_parser.iter.end_reached();
parser = scoped_parser.unfold();
parser = scoped_parser.unfold(outer_open_formats);

// check for `()`
if end_reached && parser.iter.peek_kind() == Some(InlineTokenKind::OpenParenthesis) {
parser
.iter
.next()
.expect("Peeked before, so `next` must return Some."); // Consume open parenthesis
let mut link_parser = parser.nest_scoped(Some(Rc::new(|matcher: &mut dyn EndMatcher| {
matcher.consumed_matches(&[TokenKind::CloseParenthesis])
})));
let (mut link_parser, outer_open_formats) =
parser.nest_scoped(Some(Rc::new(|matcher: &mut dyn EndMatcher| {
matcher.consumed_matches(&[TokenKind::CloseParenthesis])
})));

let link = link_parser
.iter
Expand Down Expand Up @@ -142,7 +145,7 @@ pub(crate) fn parse<'slice, 'input>(
)
};

parser = link_parser.unfold();
parser = link_parser.unfold(outer_open_formats);

return (
parser,
Expand Down
4 changes: 3 additions & 1 deletion inline/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
//! Crate for parsing Unimarkup inline elements.

pub mod element;
pub mod parser;

mod tokenize;

pub mod parser;
pub use tokenize::kind::InlineTokenKind;
38 changes: 15 additions & 23 deletions inline/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
use unimarkup_commons::lexer::token::iterator::{IteratorEndFn, IteratorPrefixFn, TokenIterator};

use crate::{
element::Inline,
element::{formatting::OpenFormatMap, Inline},
tokenize::{iterator::InlineTokenIterator, kind::InlineTokenKind},
};

Expand Down Expand Up @@ -32,7 +32,7 @@ pub fn parse_inlines<'slice, 'input>(
end_reached: updated_parser.iter.end_reached(),
prefix_mismatch: updated_parser.iter.prefix_mismatch(),
};
inline_parser = updated_parser.unfold();
inline_parser = updated_parser.unfold(OpenFormatMap::default());

(
inline_parser.iter.into(),
Expand Down Expand Up @@ -99,9 +99,6 @@ impl<'slice, 'input> InlineParser<'slice, 'input> {
let mut inlines = Vec::default();
let mut format_closes = false;

#[cfg(debug_assertions)]
let mut curr_len = parser.iter.max_len();

parser.iter.reset_peek();

'outer: while let Some(kind) = parser.iter.peek_kind() {
Expand Down Expand Up @@ -142,8 +139,8 @@ impl<'slice, 'input> InlineParser<'slice, 'input> {
let success = parser.iter.rollback(checkpoint);
debug_assert!(
success,
"Rollback was not successful for checkpoint '{:?}'",
checkpoint
"Inline rollback was not successful at '{:?}'",
parser.iter.peek()
)
}
}
Expand All @@ -154,15 +151,6 @@ impl<'slice, 'input> InlineParser<'slice, 'input> {

parser = updated_parser;
inlines = updated_inlines;

#[cfg(debug_assertions)]
{
assert!(
parser.iter.max_len() < curr_len,
"Parser consumed no token in iteration."
);
curr_len = parser.iter.max_len();
}
}

if !format_closes {
Expand All @@ -174,15 +162,19 @@ impl<'slice, 'input> InlineParser<'slice, 'input> {
}

/// Create an inline parser that has this parser as parent.
pub fn nest_scoped(mut self, end_match: Option<IteratorEndFn>) -> Self {
self.iter = self.iter.nest_scoped(end_match);
self
/// Returns the nested parser, and the [`OpenFormatMap`] of the outer scope.
/// This [`OpenFormatMap`] must be used when calling `unfold()` to get correct inline formatting.
pub fn nest_scoped(mut self, end_match: Option<IteratorEndFn>) -> (Self, OpenFormatMap) {
let (scoped_iter, outer_open_formats) = self.iter.nest_scoped(end_match);
self.iter = scoped_iter;

(self, outer_open_formats)
}

/// Returns the parent parser if this parser is nested.
/// Otherwise, self is returned unchanged.
pub fn unfold(mut self) -> Self {
self.iter = self.iter.unfold();
/// Overrides the internal [`OpenFormatMap`] with the given one.
pub fn unfold(mut self, outer_open_formats: OpenFormatMap) -> Self {
self.iter = self.iter.unfold(outer_open_formats);
self
}
}
Expand Down Expand Up @@ -230,7 +222,7 @@ mod test {

#[test]
fn dummy_for_debugging() {
let tokens = unimarkup_commons::lexer::token::lex_str("`a`");
let tokens = unimarkup_commons::lexer::token::lex_str("[Simple textbox]");
let mut inline_parser = InlineParser {
iter: InlineTokenIterator::from(TokenIterator::from(&*tokens)),
context: InlineContext::default(),
Expand Down
Loading

0 comments on commit 10bbdf0

Please sign in to comment.