fix code blocks

This commit is contained in:
liquidex 2023-08-27 16:22:22 +02:00
parent 38e7781c8d
commit e552764d12
3 changed files with 74 additions and 22 deletions

View file

@ -224,18 +224,18 @@
% id = "01H8V55APD5686J8GTXP118V8E" % id = "01H8V55APD5686J8GTXP118V8E"
- ``` - ```
\% id = "root" % id = "root"
\- this is a branch - this is a branch
\% id = "child" % id = "child"
\- this is a child branch - this is a child branch
\+ this is a branch that is collapsed + this is a branch that is collapsed
\- and this is a child of that branch - and this is a child of that branch
\% content.link = "some-other-tree" % content.link = "some-other-tree"
\- and this branch links to another tree - and this branch links to another tree
``` ```
% id = "01H8V55APDB49SPHPPMV2BCMW3" % id = "01H8V55APDB49SPHPPMV2BCMW3"

View file

@ -13,6 +13,9 @@ pub enum ParseErrorKind {
#[error("at least {expected} spaces of indentation were expected, but got {got}")] #[error("at least {expected} spaces of indentation were expected, but got {got}")]
InconsistentIndentation { got: usize, expected: usize }, InconsistentIndentation { got: usize, expected: usize },
#[error("unterminated code block")]
UnterminatedCodeBlock,
} }
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)] #[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]

View file

@ -40,11 +40,21 @@ pub struct Parser<'a> {
pub position: usize, pub position: usize,
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum AllowCodeBlocks {
No,
Yes,
}
impl<'a> Parser<'a> { impl<'a> Parser<'a> {
fn current(&self) -> Option<char> { fn current(&self) -> Option<char> {
self.input[self.position..].chars().next() self.input[self.position..].chars().next()
} }
fn current_starts_with(&self, s: &str) -> bool {
self.input[self.position..].starts_with(s)
}
fn advance(&mut self) { fn advance(&mut self) {
self.position += self.current().map(|c| c.len_utf8()).unwrap_or(0); self.position += self.current().map(|c| c.len_utf8()).unwrap_or(0);
} }
@ -58,6 +68,12 @@ impl<'a> Parser<'a> {
count count
} }
fn eat_while(&mut self, cond: impl Fn(char) -> bool) {
while self.current().map(&cond).is_some_and(|x| x) {
self.advance();
}
}
fn eat_until(&mut self, cond: impl Fn(char) -> bool) { fn eat_until(&mut self, cond: impl Fn(char) -> bool) {
while self.current().map(&cond).is_some_and(|x| !x) { while self.current().map(&cond).is_some_and(|x| !x) {
self.advance(); self.advance();
@ -76,21 +92,46 @@ impl<'a> Parser<'a> {
&mut self, &mut self,
indent_level: usize, indent_level: usize,
cond: impl Fn(char) -> bool, cond: impl Fn(char) -> bool,
allow_code_blocks: AllowCodeBlocks,
) -> Result<(), ParseError> { ) -> Result<(), ParseError> {
let mut code_block: Option<Range<usize>> = None;
loop { loop {
self.eat_until(|c| c == '\n'); if let Some(range) = &code_block {
let before_indentation = self.position; self.eat_while(|c| c == ' ');
let line_indent_level = self.eat_as_long_as(' '); if self.current_starts_with("```") {
let after_indentation = self.position; code_block = None;
if self.current().map(&cond).is_some_and(identity) || self.current().is_none() { self.position += 3;
self.position = before_indentation; self.eat_until(|c| c == '\n');
break; continue;
} else if !matches!(self.current(), Some('\n')) && line_indent_level < indent_level { }
return Err(ParseErrorKind::InconsistentIndentation { self.eat_until(|c| c == '\n');
got: line_indent_level,
expected: indent_level, if self.current().is_none() {
return Err(ParseErrorKind::UnterminatedCodeBlock.at(range.clone()));
}
} else {
self.eat_while(|c| c == ' ');
if allow_code_blocks == AllowCodeBlocks::Yes && self.current_starts_with("```") {
code_block = Some(self.position..self.position + 3);
self.position += 3;
continue;
}
self.eat_until(|c| c == '\n');
let before_indentation = self.position;
let line_indent_level = self.eat_as_long_as(' ');
let after_indentation = self.position;
if self.current().map(&cond).is_some_and(identity) || self.current().is_none() {
self.position = before_indentation;
break;
} else if !matches!(self.current(), Some('\n')) && line_indent_level < indent_level
{
return Err(ParseErrorKind::InconsistentIndentation {
got: line_indent_level,
expected: indent_level,
}
.at(before_indentation..after_indentation));
} }
.at(before_indentation..after_indentation));
} }
} }
Ok(()) Ok(())
@ -107,7 +148,11 @@ impl<'a> Parser<'a> {
let start = self.position; let start = self.position;
self.advance(); self.advance();
let after_percent = self.position; let after_percent = self.position;
self.eat_indented_lines_until(indent_level, |c| c == '-' || c == '+')?; self.eat_indented_lines_until(
indent_level,
|c| c == '-' || c == '+',
AllowCodeBlocks::No,
)?;
self.eat_as_long_as(' '); self.eat_as_long_as(' ');
let end = self.position; let end = self.position;
Some(Attributes { Some(Attributes {
@ -128,7 +173,11 @@ impl<'a> Parser<'a> {
let kind_end = self.position; let kind_end = self.position;
let content_start = self.position; let content_start = self.position;
self.eat_indented_lines_until(indent_level, |c| c == '-' || c == '+' || c == '%')?; self.eat_indented_lines_until(
indent_level,
|c| c == '-' || c == '+' || c == '%',
AllowCodeBlocks::Yes,
)?;
let content_end = self.position; let content_end = self.position;
Ok(Some(BranchEvent { Ok(Some(BranchEvent {