feat: support line breaks

This commit is contained in:
PoiScript 2023-11-21 16:33:30 +08:00
parent 6598095a9f
commit 58dfb022c2
No known key found for this signature in database
GPG key ID: 22C2B1249D99985E
10 changed files with 199 additions and 60 deletions

View file

@ -260,6 +260,10 @@ const nodes = [
struct: "Entity",
kind: ["ENTITY"],
},
{
struct: "LineBreak",
kind: ["LINE_BREAK"],
},
];
let content = `//! generated file, do not modify it directly

View file

@ -1691,3 +1691,28 @@ impl Entity {
self.syntax.text_range().end().into()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct LineBreak {
pub(crate) syntax: SyntaxNode,
}
impl AstNode for LineBreak {
type Language = OrgLanguage;
fn can_cast(kind: SyntaxKind) -> bool {
kind == LINE_BREAK
}
fn cast(node: SyntaxNode) -> Option<LineBreak> {
Self::can_cast(node.kind()).then(|| LineBreak { syntax: node })
}
fn syntax(&self) -> &SyntaxNode {
&self.syntax
}
}
impl LineBreak {
pub fn begin(&self) -> u32 {
self.syntax.text_range().start().into()
}
pub fn end(&self) -> u32 {
self.syntax.text_range().end().into()
}
}

View file

@ -49,7 +49,7 @@
/// special_block quote_block center_block verse_block comment_block example_block export_block
/// source_block babel_call clock cookie radio_target drawer dyn_block fn_def fn_ref macros
/// snippet timestamp target fixed_width org_table org_table_row org_table_cell latex_fragment
/// latex_environment entity
/// latex_environment entity line_break
/// }
/// }
///
@ -200,6 +200,9 @@ macro_rules! forward_handler {
(@method $handler:ty, entity) => {
forward_handler!(@method $handler, entity, WalkEvent<&$crate::ast::Entity>);
};
(@method $handler:ty, line_break) => {
forward_handler!(@method $handler, line_break, WalkEvent<&$crate::ast::LineBreak>);
};
(@method $handler:ty, $x:ident) => {
std::compile_error!(std::concat!(std::stringify!($x), " is not a method"));
};

View file

@ -510,4 +510,12 @@ impl Traverser for HtmlExport {
ctx.skip();
}
}
#[tracing::instrument(skip(self, ctx))]
fn line_break(&mut self, event: WalkEvent<&LineBreak>, ctx: &mut TraversalContext) {
if let WalkEvent::Enter(_) = event {
self.output += "<br/>";
ctx.skip();
}
}
}

View file

@ -140,6 +140,7 @@ pub trait Traverser {
LATEX_FRAGMENT => traverse!(LatexFragment, latex_fragment),
LATEX_ENVIRONMENT => traverse!(LatexEnvironment, latex_environment),
ENTITY => traverse!(Entity, entity),
LINE_BREAK => traverse!(LineBreak, line_break),
BLOCK_CONTENT | LIST_ITEM_CONTENT => traverse_children!(node),
@ -156,97 +157,99 @@ pub trait Traverser {
}
/// Called when visiting `Text` token
fn text(&mut self, _token: SyntaxToken, _ctx: &mut TraversalContext);
fn text(&mut self, token: SyntaxToken, ctx: &mut TraversalContext);
/// Called when entering or leaving `Document` node
fn document(&mut self, _event: WalkEvent<&Document>, _ctx: &mut TraversalContext);
fn document(&mut self, event: WalkEvent<&Document>, ctx: &mut TraversalContext);
/// Called when entering or leaving `Headline` node
fn headline(&mut self, _event: WalkEvent<&Headline>, _ctx: &mut TraversalContext);
fn headline(&mut self, event: WalkEvent<&Headline>, ctx: &mut TraversalContext);
/// Called when entering or leaving `Paragraph` node
fn paragraph(&mut self, _event: WalkEvent<&Paragraph>, _ctx: &mut TraversalContext);
fn paragraph(&mut self, event: WalkEvent<&Paragraph>, ctx: &mut TraversalContext);
/// Called when entering or leaving `Section` node
fn section(&mut self, _event: WalkEvent<&Section>, _ctx: &mut TraversalContext);
fn section(&mut self, event: WalkEvent<&Section>, ctx: &mut TraversalContext);
/// Called when entering or leaving `Rule` node
fn rule(&mut self, _event: WalkEvent<&Rule>, _ctx: &mut TraversalContext);
fn rule(&mut self, event: WalkEvent<&Rule>, ctx: &mut TraversalContext);
/// Called when entering or leaving `Comment` node
fn comment(&mut self, _event: WalkEvent<&Comment>, _ctx: &mut TraversalContext);
fn comment(&mut self, event: WalkEvent<&Comment>, ctx: &mut TraversalContext);
/// Called when entering or leaving `InlineSrc` node
fn inline_src(&mut self, _event: WalkEvent<&InlineSrc>, _ctx: &mut TraversalContext);
fn inline_src(&mut self, event: WalkEvent<&InlineSrc>, ctx: &mut TraversalContext);
/// Called when entering or leaving `InlineCall` node
fn inline_call(&mut self, _event: WalkEvent<&InlineCall>, _ctx: &mut TraversalContext);
fn inline_call(&mut self, event: WalkEvent<&InlineCall>, ctx: &mut TraversalContext);
/// Called when entering or leaving `Code` node
fn code(&mut self, _event: WalkEvent<&Code>, _ctx: &mut TraversalContext);
fn code(&mut self, event: WalkEvent<&Code>, ctx: &mut TraversalContext);
/// Called when entering or leaving `Bold` node
fn bold(&mut self, _event: WalkEvent<&Bold>, _ctx: &mut TraversalContext);
fn bold(&mut self, event: WalkEvent<&Bold>, ctx: &mut TraversalContext);
/// Called when entering or leaving `Verbatim` node
fn verbatim(&mut self, _event: WalkEvent<&Verbatim>, _ctx: &mut TraversalContext);
fn verbatim(&mut self, event: WalkEvent<&Verbatim>, ctx: &mut TraversalContext);
/// Called when entering or leaving `Italic` node
fn italic(&mut self, _event: WalkEvent<&Italic>, _ctx: &mut TraversalContext);
fn italic(&mut self, event: WalkEvent<&Italic>, ctx: &mut TraversalContext);
/// Called when entering or leaving `Strike` node
fn strike(&mut self, _event: WalkEvent<&Strike>, _ctx: &mut TraversalContext);
fn strike(&mut self, event: WalkEvent<&Strike>, ctx: &mut TraversalContext);
/// Called when entering or leaving `Underline` node
fn underline(&mut self, _event: WalkEvent<&Underline>, _ctx: &mut TraversalContext);
fn underline(&mut self, event: WalkEvent<&Underline>, ctx: &mut TraversalContext);
/// Called when entering or leaving `List` node
fn list(&mut self, _event: WalkEvent<&List>, _ctx: &mut TraversalContext);
fn list(&mut self, event: WalkEvent<&List>, ctx: &mut TraversalContext);
/// Called when entering or leaving `ListItem` node
fn list_item(&mut self, _event: WalkEvent<&ListItem>, _ctx: &mut TraversalContext);
fn list_item(&mut self, event: WalkEvent<&ListItem>, ctx: &mut TraversalContext);
/// Called when entering or leaving `SpecialBlock` node
fn special_block(&mut self, _event: WalkEvent<&SpecialBlock>, _ctx: &mut TraversalContext);
fn special_block(&mut self, event: WalkEvent<&SpecialBlock>, ctx: &mut TraversalContext);
/// Called when entering or leaving `QuoteBlock` node
fn quote_block(&mut self, _event: WalkEvent<&QuoteBlock>, _ctx: &mut TraversalContext);
fn quote_block(&mut self, event: WalkEvent<&QuoteBlock>, ctx: &mut TraversalContext);
/// Called when entering or leaving `CenterBlock` node
fn center_block(&mut self, _event: WalkEvent<&CenterBlock>, _ctx: &mut TraversalContext);
fn center_block(&mut self, event: WalkEvent<&CenterBlock>, ctx: &mut TraversalContext);
/// Called when entering or leaving `VerseBlock` node
fn verse_block(&mut self, _event: WalkEvent<&VerseBlock>, _ctx: &mut TraversalContext);
fn verse_block(&mut self, event: WalkEvent<&VerseBlock>, ctx: &mut TraversalContext);
/// Called when entering or leaving `CommentBlock` node
fn comment_block(&mut self, _event: WalkEvent<&CommentBlock>, _ctx: &mut TraversalContext);
fn comment_block(&mut self, event: WalkEvent<&CommentBlock>, ctx: &mut TraversalContext);
/// Called when entering or leaving `ExampleBlock` node
fn example_block(&mut self, _event: WalkEvent<&ExampleBlock>, _ctx: &mut TraversalContext);
fn example_block(&mut self, event: WalkEvent<&ExampleBlock>, ctx: &mut TraversalContext);
/// Called when entering or leaving `ExportBlock` node
fn export_block(&mut self, _event: WalkEvent<&ExportBlock>, _ctx: &mut TraversalContext);
fn export_block(&mut self, event: WalkEvent<&ExportBlock>, ctx: &mut TraversalContext);
/// Called when entering or leaving `SourceBlock` node
fn source_block(&mut self, _event: WalkEvent<&SourceBlock>, _ctx: &mut TraversalContext);
fn source_block(&mut self, event: WalkEvent<&SourceBlock>, ctx: &mut TraversalContext);
/// Called when entering or leaving `BabelCall` node
fn babel_call(&mut self, _event: WalkEvent<&BabelCall>, _ctx: &mut TraversalContext);
fn babel_call(&mut self, event: WalkEvent<&BabelCall>, ctx: &mut TraversalContext);
/// Called when entering or leaving `Clock` node
fn clock(&mut self, _event: WalkEvent<&Clock>, _ctx: &mut TraversalContext);
fn clock(&mut self, event: WalkEvent<&Clock>, ctx: &mut TraversalContext);
/// Called when entering or leaving `Cookie` node
fn cookie(&mut self, _event: WalkEvent<&Cookie>, _ctx: &mut TraversalContext);
fn cookie(&mut self, event: WalkEvent<&Cookie>, ctx: &mut TraversalContext);
/// Called when entering or leaving `RadioTarget` node
fn radio_target(&mut self, _event: WalkEvent<&RadioTarget>, _ctx: &mut TraversalContext);
fn radio_target(&mut self, event: WalkEvent<&RadioTarget>, ctx: &mut TraversalContext);
/// Called when entering or leaving `Drawer` node
fn drawer(&mut self, _event: WalkEvent<&Drawer>, _ctx: &mut TraversalContext);
fn drawer(&mut self, event: WalkEvent<&Drawer>, ctx: &mut TraversalContext);
/// Called when entering or leaving `DynBlock` node
fn dyn_block(&mut self, _event: WalkEvent<&DynBlock>, _ctx: &mut TraversalContext);
fn dyn_block(&mut self, event: WalkEvent<&DynBlock>, ctx: &mut TraversalContext);
/// Called when entering or leaving `FnDef` node
fn fn_def(&mut self, _event: WalkEvent<&FnDef>, _ctx: &mut TraversalContext);
fn fn_def(&mut self, event: WalkEvent<&FnDef>, ctx: &mut TraversalContext);
/// Called when entering or leaving `FnRef` node
fn fn_ref(&mut self, _event: WalkEvent<&FnRef>, _ctx: &mut TraversalContext);
fn fn_ref(&mut self, event: WalkEvent<&FnRef>, ctx: &mut TraversalContext);
/// Called when entering or leaving `Macros` node
fn macros(&mut self, _event: WalkEvent<&Macros>, _ctx: &mut TraversalContext);
fn macros(&mut self, event: WalkEvent<&Macros>, ctx: &mut TraversalContext);
/// Called when entering or leaving `Snippet` node
fn snippet(&mut self, _event: WalkEvent<&Snippet>, _ctx: &mut TraversalContext);
fn snippet(&mut self, event: WalkEvent<&Snippet>, ctx: &mut TraversalContext);
/// Called when entering or leaving `Timestamp` node
fn timestamp(&mut self, _event: WalkEvent<&Timestamp>, _ctx: &mut TraversalContext);
fn timestamp(&mut self, event: WalkEvent<&Timestamp>, ctx: &mut TraversalContext);
/// Called when entering or leaving `Target` node
fn target(&mut self, _event: WalkEvent<&Target>, _ctx: &mut TraversalContext);
fn target(&mut self, event: WalkEvent<&Target>, ctx: &mut TraversalContext);
/// Called when entering or leaving `FixedWidth` node
fn fixed_width(&mut self, _event: WalkEvent<&FixedWidth>, _ctx: &mut TraversalContext);
fn fixed_width(&mut self, event: WalkEvent<&FixedWidth>, ctx: &mut TraversalContext);
/// Called when entering or leaving `OrgTable` node
fn org_table(&mut self, _event: WalkEvent<&OrgTable>, _ctx: &mut TraversalContext);
fn org_table(&mut self, event: WalkEvent<&OrgTable>, ctx: &mut TraversalContext);
/// Called when entering or leaving `OrgTableRow` node
fn org_table_row(&mut self, _event: WalkEvent<&OrgTableRow>, _ctx: &mut TraversalContext);
fn org_table_row(&mut self, event: WalkEvent<&OrgTableRow>, ctx: &mut TraversalContext);
/// Called when entering or leaving `OrgTableCell` node
fn org_table_cell(&mut self, _event: WalkEvent<&OrgTableCell>, _ctx: &mut TraversalContext);
fn org_table_cell(&mut self, event: WalkEvent<&OrgTableCell>, ctx: &mut TraversalContext);
/// Called when entering or leaving `Link` node
fn link(&mut self, _event: WalkEvent<&Link>, _ctx: &mut TraversalContext);
fn link(&mut self, event: WalkEvent<&Link>, ctx: &mut TraversalContext);
/// Called when entering or leaving `LatexFragment` node
fn latex_fragment(&mut self, _event: WalkEvent<&LatexFragment>, _ctx: &mut TraversalContext);
fn latex_fragment(&mut self, event: WalkEvent<&LatexFragment>, ctx: &mut TraversalContext);
/// Called when entering or leaving `LatexEnvironment` node
fn latex_environment(
&mut self,
_event: WalkEvent<&LatexEnvironment>,
_ctx: &mut TraversalContext,
event: WalkEvent<&LatexEnvironment>,
ctx: &mut TraversalContext,
);
/// Called when entering or leaving `Entity` node
fn entity(&mut self, _event: WalkEvent<&Entity>, _ctx: &mut TraversalContext);
fn entity(&mut self, event: WalkEvent<&Entity>, ctx: &mut TraversalContext);
/// Called when entering or leaving `LineBreak` node
fn line_break(&mut self, event: WalkEvent<&LineBreak>, ctx: &mut TraversalContext);
}

82
src/syntax/line_break.rs Normal file
View file

@ -0,0 +1,82 @@
use nom::{
branch::alt,
character::complete::{line_ending, space0},
combinator::{eof, map},
sequence::tuple,
IResult,
};
use crate::{
syntax::combinator::{backslash_token, node},
SyntaxKind,
};
use super::{combinator::GreenElement, input::Input};
pub fn line_break_node(input: Input) -> IResult<Input, GreenElement, ()> {
debug_assert!(input.s.starts_with('\\'));
let mut parser = map(
tuple((
backslash_token,
backslash_token,
space0,
alt((line_ending, eof)),
)),
|(b1, b2, ws, nl)| {
node(
SyntaxKind::LINE_BREAK,
[b1, b2, ws.ws_token(), nl.nl_token()],
)
},
);
crate::lossless_parser!(parser, input)
}
#[test]
fn parse() {
use crate::ast::LineBreak;
use crate::tests::to_ast;
let to_line_break = to_ast::<LineBreak>(line_break_node);
insta::assert_debug_snapshot!(
to_line_break("\\\\\n").syntax,
@r###"
LINE_BREAK@0..3
BACKSLASH@0..1 "\\"
BACKSLASH@1..2 "\\"
WHITESPACE@2..2 ""
NEW_LINE@2..3 "\n"
"###
);
insta::assert_debug_snapshot!(
to_line_break("\\\\ \n").syntax,
@r###"
LINE_BREAK@0..6
BACKSLASH@0..1 "\\"
BACKSLASH@1..2 "\\"
WHITESPACE@2..5 " "
NEW_LINE@5..6 "\n"
"###
);
insta::assert_debug_snapshot!(
to_line_break("\\\\\r\n").syntax,
@r###"
LINE_BREAK@0..4
BACKSLASH@0..1 "\\"
BACKSLASH@1..2 "\\"
WHITESPACE@2..2 ""
NEW_LINE@2..4 "\r\n"
"###
);
insta::assert_debug_snapshot!(
to_line_break("\\\\ ").syntax,
@r###"
LINE_BREAK@0..6
BACKSLASH@0..1 "\\"
BACKSLASH@1..2 "\\"
WHITESPACE@2..6 " "
NEW_LINE@6..6 ""
"###
);
}

View file

@ -21,6 +21,7 @@ pub mod input;
pub mod keyword;
pub mod latex_environment;
pub mod latex_fragment;
pub mod line_break;
pub mod link;
pub mod list;
pub mod macros;
@ -184,6 +185,7 @@ pub enum SyntaxKind {
INLINE_SRC,
LINK,
LINK_PATH,
LINE_BREAK,
COOKIE,
RADIO_TARGET,
FN_REF,
@ -234,6 +236,7 @@ impl SyntaxKind {
| SyntaxKind::FN_REF
| SyntaxKind::INLINE_CALL
| SyntaxKind::INLINE_SRC
| SyntaxKind::LINE_BREAK
| SyntaxKind::LINK
| SyntaxKind::MACROS
| SyntaxKind::RADIO_TARGET

View file

@ -10,6 +10,7 @@ use super::{
inline_src::inline_src_node,
input::Input,
latex_fragment::latex_fragment_node,
line_break::line_break_node,
link::link_node,
macros::macros_node,
radio_target::radio_target_node,
@ -110,10 +111,10 @@ impl<'a> Iterator for ObjectPositions<'a> {
/// - Statistics Cookies
/// - Timestamps
/// - Text Markup (bold code strike verbatim underline italic)
/// - Line Breaks
///
/// // todo:
/// - Citations
/// - Line Breaks
/// - Subscript and Superscript
pub fn object_nodes(input: Input) -> Vec<GreenElement> {
// TODO:
@ -124,11 +125,6 @@ pub fn object_nodes(input: Input) -> Vec<GreenElement> {
'l: while !i.is_empty() {
for (input, head) in ObjectPositions::standard(i) {
debug_assert!(
input.s.len() >= 2,
"object must have at least two characters: {:?}",
input.s
);
if let Ok((input, node)) = standard_object_node(input) {
if !head.is_empty() {
nodes.push(head.text_token())
@ -170,11 +166,6 @@ pub fn minimal_object_nodes(input: Input) -> Vec<GreenElement> {
'l: while !i.is_empty() {
for (input, head) in ObjectPositions::minimal(i) {
debug_assert!(
input.s.len() >= 2,
"object must have at least two characters: {:?}",
input.s
);
if let Ok((input, node)) = minimal_object_node(input) {
if !head.is_empty() {
nodes.push(head.text_token())
@ -205,6 +196,12 @@ pub fn minimal_object_nodes(input: Input) -> Vec<GreenElement> {
/// parse an object from standard sets
fn standard_object_node(i: Input) -> IResult<Input, GreenElement, ()> {
debug_assert!(
i.s.len() >= 2,
"object must have at least two characters: {:?}",
i.s
);
match &i.as_bytes()[0] {
b'*' => bold_node(i),
b'+' => strike_node(i),
@ -225,7 +222,13 @@ fn standard_object_node(i: Input) -> IResult<Input, GreenElement, ()> {
b'c' => inline_call_node(i),
b's' => inline_src_node(i),
b'$' => latex_fragment_node(i),
b'\\' => entity_node(i).or_else(|_| latex_fragment_node(i)),
b'\\' => {
if i.as_bytes()[1] == b'\\' {
line_break_node(i)
} else {
entity_node(i).or_else(|_| latex_fragment_node(i))
}
}
_ => Err(nom::Err::Error(())),
}
}