fix code blocks
This commit is contained in:
parent
38e7781c8d
commit
e552764d12
|
@ -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"
|
||||||
|
|
|
@ -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)]
|
||||||
|
|
|
@ -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,8 +92,31 @@ 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 {
|
||||||
|
if let Some(range) = &code_block {
|
||||||
|
self.eat_while(|c| c == ' ');
|
||||||
|
if self.current_starts_with("```") {
|
||||||
|
code_block = None;
|
||||||
|
self.position += 3;
|
||||||
|
self.eat_until(|c| c == '\n');
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
self.eat_until(|c| c == '\n');
|
||||||
|
|
||||||
|
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');
|
self.eat_until(|c| c == '\n');
|
||||||
let before_indentation = self.position;
|
let before_indentation = self.position;
|
||||||
let line_indent_level = self.eat_as_long_as(' ');
|
let line_indent_level = self.eat_as_long_as(' ');
|
||||||
|
@ -85,7 +124,8 @@ impl<'a> Parser<'a> {
|
||||||
if self.current().map(&cond).is_some_and(identity) || self.current().is_none() {
|
if self.current().map(&cond).is_some_and(identity) || self.current().is_none() {
|
||||||
self.position = before_indentation;
|
self.position = before_indentation;
|
||||||
break;
|
break;
|
||||||
} else if !matches!(self.current(), Some('\n')) && line_indent_level < indent_level {
|
} else if !matches!(self.current(), Some('\n')) && line_indent_level < indent_level
|
||||||
|
{
|
||||||
return Err(ParseErrorKind::InconsistentIndentation {
|
return Err(ParseErrorKind::InconsistentIndentation {
|
||||||
got: line_indent_level,
|
got: line_indent_level,
|
||||||
expected: indent_level,
|
expected: indent_level,
|
||||||
|
@ -93,6 +133,7 @@ impl<'a> Parser<'a> {
|
||||||
.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 {
|
||||||
|
|
Loading…
Reference in a new issue