Skip to main content

oak_notedown/parser/
mod.rs

1/// Element type definitions for the Notedown parser.
2pub mod element_type;
3
4use crate::{
5    language::NotedownLanguage,
6    lexer::{NotedownLexer, token_type::NoteTokenType},
7    parser::element_type::NoteElementType,
8};
9use oak_core::{
10    TextEdit,
11    errors::OakError,
12    parser::{ParseCache, ParseOutput, Parser, ParserState},
13    source::Source,
14    tree::GreenNode,
15};
16
17pub(crate) type State<'a, S> = ParserState<'a, NotedownLanguage, S>;
18
19/// Notedown parser implementation
20pub struct NoteParser<'a> {
21    /// Reference to the language configuration
22    pub language: &'a NotedownLanguage,
23}
24
25impl<'a> NoteParser<'a> {
26    /// Create a new parser with the given language configuration
27    pub fn new(language: &'a NotedownLanguage) -> Self {
28        Self { language }
29    }
30}
31
32impl<'p> Parser<NotedownLanguage> for NoteParser<'p> {
33    fn parse<'a, S: Source + ?Sized>(&self, source: &'a S, edits: &[TextEdit], cache: &'a mut impl ParseCache<NotedownLanguage>) -> ParseOutput<'a, NotedownLanguage> {
34        let lexer = NotedownLexer::new(self.language);
35        oak_core::parser::parse_with_lexer(&lexer, source, edits, cache, |state| {
36            let checkpoint = state.checkpoint();
37            while state.not_at_end() {
38                self.parse_block(state);
39            }
40
41            Ok(state.finish_at(checkpoint, NoteElementType::Root))
42        })
43    }
44}
45
46impl<'p> NoteParser<'p> {
47    fn parse_block<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) {
48        let kind = state.peek_kind();
49        match kind {
50            Some(NoteTokenType::Hash) => self.parse_heading(state),
51            Some(NoteTokenType::Asterisk) | Some(NoteTokenType::Dash) | Some(NoteTokenType::Plus) => self.parse_list_item(state),
52            Some(NoteTokenType::Pipe) => self.parse_table(state),
53            Some(NoteTokenType::Backtick) => self.parse_code_block(state),
54            _ => self.parse_paragraph(state),
55        }
56    }
57
58    fn parse_heading<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) {
59        let checkpoint = state.checkpoint();
60        let mut level = 0;
61        while state.at(NoteTokenType::Hash) {
62            state.bump();
63            level += 1;
64        }
65
66        self.parse_inline_content(state);
67
68        let kind = match level {
69            1..=6 => NoteElementType::Heading,
70            _ => NoteElementType::Paragraph,
71        };
72        state.finish_at(checkpoint, kind);
73    }
74
75    fn parse_list_item<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) {
76        let checkpoint = state.checkpoint();
77        state.bump(); // marker
78        self.parse_inline_content(state);
79        state.finish_at(checkpoint, NoteElementType::ListItem);
80    }
81
82    fn parse_table<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) {
83        let checkpoint = state.checkpoint();
84        while state.not_at_end() && state.at(NoteTokenType::Pipe) {
85            self.parse_table_row(state);
86            while state.at(NoteTokenType::Newline) || state.at(NoteTokenType::Whitespace) {
87                state.bump();
88            }
89        }
90        state.finish_at(checkpoint, NoteElementType::Table);
91    }
92
93    fn parse_table_row<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) {
94        let checkpoint = state.checkpoint();
95        while state.at(NoteTokenType::Pipe) {
96            self.parse_table_cell(state);
97        }
98        if state.at(NoteTokenType::Newline) {
99            state.bump();
100        }
101        state.finish_at(checkpoint, NoteElementType::TableRow);
102    }
103
104    fn parse_table_cell<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) {
105        let checkpoint = state.checkpoint();
106        state.bump(); // |
107        while state.not_at_end() && !state.at(NoteTokenType::Pipe) && !state.at(NoteTokenType::Newline) {
108            self.parse_inline_content(state);
109        }
110        // ElementType for cell is not explicitly in NoteElementType, using Token(Pipe) as placeholder or just Root
111        state.finish_at(checkpoint, NoteElementType::Root);
112    }
113
114    fn parse_paragraph<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) {
115        let checkpoint = state.checkpoint();
116        self.parse_inline_content(state);
117        if state.at(NoteTokenType::Newline) {
118            state.bump();
119        }
120        state.finish_at(checkpoint, NoteElementType::Paragraph);
121    }
122
123    fn parse_inline_content<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) {
124        while state.not_at_end() && !state.at(NoteTokenType::Newline) {
125            let checkpoint = state.checkpoint();
126            let kind = state.peek_kind();
127            match kind {
128                Some(NoteTokenType::Asterisk) | Some(NoteTokenType::Underscore) => {
129                    let marker = kind.unwrap();
130                    state.bump();
131                    while state.not_at_end() && !state.at(marker) && !state.at(NoteTokenType::Newline) {
132                        self.parse_inline_content(state);
133                    }
134                    if state.at(marker) {
135                        state.bump();
136                    }
137                    state.finish_at(checkpoint, NoteElementType::Root);
138                }
139                Some(NoteTokenType::LeftBracket) => {
140                    state.bump(); // [
141                    while state.not_at_end() && !state.at(NoteTokenType::RightBracket) && !state.at(NoteTokenType::Newline) {
142                        self.parse_inline_content(state);
143                    }
144                    if state.at(NoteTokenType::RightBracket) {
145                        state.bump();
146                    }
147                    state.finish_at(checkpoint, NoteElementType::Link);
148                }
149                _ => {
150                    state.bump();
151                }
152            }
153        }
154    }
155
156    fn parse_code_block<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) {
157        let checkpoint = state.checkpoint();
158        state.bump(); // ```
159        while state.not_at_end() && !state.at(NoteTokenType::Backtick) {
160            state.bump();
161        }
162        if state.at(NoteTokenType::Backtick) {
163            state.bump();
164        }
165        state.finish_at(checkpoint, NoteElementType::CodeBlock);
166    }
167}