1use core::fmt;
11use core::fmt::Write;
12
13use crate::text::TextRange;
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
24#[allow(non_camel_case_types)]
25pub enum SyntaxKind {
26 NAME,
29 TYPE,
31 COLON,
33 DESCRIPTION,
35 OPEN_BRACKET,
37 CLOSE_BRACKET,
39 OPTIONAL,
41 BODY_TEXT,
43 SUMMARY,
45 EXTENDED_SUMMARY,
47 STRAY_LINE,
49
50 WARNING_TYPE,
53
54 UNDERLINE,
57 DIRECTIVE_MARKER,
59 KEYWORD,
61 DOUBLE_COLON,
63 VERSION,
65 RETURN_TYPE,
67 DEFAULT_KEYWORD,
69 DEFAULT_SEPARATOR,
71 DEFAULT_VALUE,
73 NUMBER,
75 CONTENT,
77
78 GOOGLE_DOCSTRING,
81 GOOGLE_SECTION,
83 GOOGLE_SECTION_HEADER,
85 GOOGLE_ARG,
87 GOOGLE_RETURNS,
89 GOOGLE_EXCEPTION,
91 GOOGLE_WARNING,
93 GOOGLE_SEE_ALSO_ITEM,
95 GOOGLE_ATTRIBUTE,
97 GOOGLE_METHOD,
99
100 NUMPY_DOCSTRING,
103 NUMPY_SECTION,
105 NUMPY_SECTION_HEADER,
107 NUMPY_DEPRECATION,
109 NUMPY_PARAMETER,
111 NUMPY_RETURNS,
113 NUMPY_EXCEPTION,
115 NUMPY_WARNING,
117 NUMPY_SEE_ALSO_ITEM,
119 NUMPY_REFERENCE,
121 NUMPY_ATTRIBUTE,
123 NUMPY_METHOD,
125}
126
127impl SyntaxKind {
128 pub const fn is_node(self) -> bool {
130 matches!(
131 self,
132 Self::GOOGLE_DOCSTRING
133 | Self::GOOGLE_SECTION
134 | Self::GOOGLE_SECTION_HEADER
135 | Self::GOOGLE_ARG
136 | Self::GOOGLE_RETURNS
137 | Self::GOOGLE_EXCEPTION
138 | Self::GOOGLE_WARNING
139 | Self::GOOGLE_SEE_ALSO_ITEM
140 | Self::GOOGLE_ATTRIBUTE
141 | Self::GOOGLE_METHOD
142 | Self::NUMPY_DOCSTRING
143 | Self::NUMPY_SECTION
144 | Self::NUMPY_SECTION_HEADER
145 | Self::NUMPY_DEPRECATION
146 | Self::NUMPY_PARAMETER
147 | Self::NUMPY_RETURNS
148 | Self::NUMPY_EXCEPTION
149 | Self::NUMPY_WARNING
150 | Self::NUMPY_SEE_ALSO_ITEM
151 | Self::NUMPY_REFERENCE
152 | Self::NUMPY_ATTRIBUTE
153 | Self::NUMPY_METHOD
154 )
155 }
156
157 pub const fn is_token(self) -> bool {
159 !self.is_node()
160 }
161
162 pub const fn name(self) -> &'static str {
164 match self {
165 Self::NAME => "NAME",
167 Self::TYPE => "TYPE",
168 Self::COLON => "COLON",
169 Self::DESCRIPTION => "DESCRIPTION",
170 Self::OPEN_BRACKET => "OPEN_BRACKET",
171 Self::CLOSE_BRACKET => "CLOSE_BRACKET",
172 Self::OPTIONAL => "OPTIONAL",
173 Self::BODY_TEXT => "BODY_TEXT",
174 Self::SUMMARY => "SUMMARY",
175 Self::EXTENDED_SUMMARY => "EXTENDED_SUMMARY",
176 Self::STRAY_LINE => "STRAY_LINE",
177 Self::WARNING_TYPE => "WARNING_TYPE",
179 Self::UNDERLINE => "UNDERLINE",
181 Self::DIRECTIVE_MARKER => "DIRECTIVE_MARKER",
182 Self::KEYWORD => "KEYWORD",
183 Self::DOUBLE_COLON => "DOUBLE_COLON",
184 Self::VERSION => "VERSION",
185 Self::RETURN_TYPE => "RETURN_TYPE",
186 Self::DEFAULT_KEYWORD => "DEFAULT_KEYWORD",
187 Self::DEFAULT_SEPARATOR => "DEFAULT_SEPARATOR",
188 Self::DEFAULT_VALUE => "DEFAULT_VALUE",
189 Self::NUMBER => "NUMBER",
190 Self::CONTENT => "CONTENT",
191 Self::GOOGLE_DOCSTRING => "GOOGLE_DOCSTRING",
193 Self::GOOGLE_SECTION => "GOOGLE_SECTION",
194 Self::GOOGLE_SECTION_HEADER => "GOOGLE_SECTION_HEADER",
195 Self::GOOGLE_ARG => "GOOGLE_ARG",
196 Self::GOOGLE_RETURNS => "GOOGLE_RETURNS",
197 Self::GOOGLE_EXCEPTION => "GOOGLE_EXCEPTION",
198 Self::GOOGLE_WARNING => "GOOGLE_WARNING",
199 Self::GOOGLE_SEE_ALSO_ITEM => "GOOGLE_SEE_ALSO_ITEM",
200 Self::GOOGLE_ATTRIBUTE => "GOOGLE_ATTRIBUTE",
201 Self::GOOGLE_METHOD => "GOOGLE_METHOD",
202 Self::NUMPY_DOCSTRING => "NUMPY_DOCSTRING",
204 Self::NUMPY_SECTION => "NUMPY_SECTION",
205 Self::NUMPY_SECTION_HEADER => "NUMPY_SECTION_HEADER",
206 Self::NUMPY_DEPRECATION => "NUMPY_DEPRECATION",
207 Self::NUMPY_PARAMETER => "NUMPY_PARAMETER",
208 Self::NUMPY_RETURNS => "NUMPY_RETURNS",
209 Self::NUMPY_EXCEPTION => "NUMPY_EXCEPTION",
210 Self::NUMPY_WARNING => "NUMPY_WARNING",
211 Self::NUMPY_SEE_ALSO_ITEM => "NUMPY_SEE_ALSO_ITEM",
212 Self::NUMPY_REFERENCE => "NUMPY_REFERENCE",
213 Self::NUMPY_ATTRIBUTE => "NUMPY_ATTRIBUTE",
214 Self::NUMPY_METHOD => "NUMPY_METHOD",
215 }
216 }
217}
218
219impl fmt::Display for SyntaxKind {
220 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
221 f.write_str(self.name())
222 }
223}
224
225#[derive(Debug, Clone, PartialEq, Eq)]
233pub struct SyntaxNode {
234 kind: SyntaxKind,
235 range: TextRange,
236 children: Vec<SyntaxElement>,
237}
238
239impl SyntaxNode {
240 pub fn new(kind: SyntaxKind, range: TextRange, children: Vec<SyntaxElement>) -> Self {
242 Self {
243 kind,
244 range,
245 children,
246 }
247 }
248
249 pub fn kind(&self) -> SyntaxKind {
251 self.kind
252 }
253
254 pub fn range(&self) -> &TextRange {
256 &self.range
257 }
258
259 pub fn children(&self) -> &[SyntaxElement] {
261 &self.children
262 }
263
264 pub fn children_mut(&mut self) -> &mut [SyntaxElement] {
266 &mut self.children
267 }
268
269 pub fn push_child(&mut self, child: SyntaxElement) {
271 self.children.push(child);
272 }
273
274 pub fn extend_range_to(&mut self, end: crate::text::TextSize) {
276 self.range = TextRange::new(self.range.start(), end);
277 }
278
279 pub fn find_token(&self, kind: SyntaxKind) -> Option<&SyntaxToken> {
281 self.children.iter().find_map(|c| match c {
282 SyntaxElement::Token(t) if t.kind() == kind => Some(t),
283 _ => None,
284 })
285 }
286
287 pub fn required_token(&self, kind: SyntaxKind) -> &SyntaxToken {
294 self.find_token(kind)
295 .unwrap_or_else(|| panic!("required token {:?} not found in {:?}", kind, self.kind))
296 }
297
298 pub fn tokens(&self, kind: SyntaxKind) -> impl Iterator<Item = &SyntaxToken> {
300 self.children.iter().filter_map(move |c| match c {
301 SyntaxElement::Token(t) if t.kind() == kind => Some(t),
302 _ => None,
303 })
304 }
305
306 pub fn find_node(&self, kind: SyntaxKind) -> Option<&SyntaxNode> {
308 self.children.iter().find_map(|c| match c {
309 SyntaxElement::Node(n) if n.kind() == kind => Some(n),
310 _ => None,
311 })
312 }
313
314 pub fn nodes(&self, kind: SyntaxKind) -> impl Iterator<Item = &SyntaxNode> {
316 self.children.iter().filter_map(move |c| match c {
317 SyntaxElement::Node(n) if n.kind() == kind => Some(n),
318 _ => None,
319 })
320 }
321
322 pub fn pretty_fmt(&self, src: &str, indent: usize, out: &mut String) {
324 for _ in 0..indent {
325 out.push_str(" ");
326 }
327 let _ = writeln!(out, "{}@{} {{", self.kind.name(), self.range);
328 for child in &self.children {
329 match child {
330 SyntaxElement::Node(n) => n.pretty_fmt(src, indent + 1, out),
331 SyntaxElement::Token(t) => t.pretty_fmt(src, indent + 1, out),
332 }
333 }
334 for _ in 0..indent {
335 out.push_str(" ");
336 }
337 out.push_str("}\n");
338 }
339}
340
341#[derive(Debug, Clone, PartialEq, Eq)]
345pub struct SyntaxToken {
346 kind: SyntaxKind,
347 range: TextRange,
348}
349
350impl SyntaxToken {
351 pub fn new(kind: SyntaxKind, range: TextRange) -> Self {
353 Self { kind, range }
354 }
355
356 pub fn kind(&self) -> SyntaxKind {
358 self.kind
359 }
360
361 pub fn range(&self) -> &TextRange {
363 &self.range
364 }
365
366 pub fn text<'a>(&self, source: &'a str) -> &'a str {
368 self.range.source_text(source)
369 }
370
371 pub fn extend_range(&mut self, other: TextRange) {
373 self.range.extend(other);
374 }
375
376 pub fn pretty_fmt(&self, src: &str, indent: usize, out: &mut String) {
378 for _ in 0..indent {
379 out.push_str(" ");
380 }
381 let _ = writeln!(
382 out,
383 "{}: {:?}@{}",
384 self.kind.name(),
385 self.text(src),
386 self.range
387 );
388 }
389}
390
391#[derive(Debug, Clone, PartialEq, Eq)]
393pub enum SyntaxElement {
394 Node(SyntaxNode),
396 Token(SyntaxToken),
398}
399
400impl SyntaxElement {
401 pub fn range(&self) -> &TextRange {
403 match self {
404 Self::Node(n) => n.range(),
405 Self::Token(t) => t.range(),
406 }
407 }
408
409 pub fn kind(&self) -> SyntaxKind {
411 match self {
412 Self::Node(n) => n.kind(),
413 Self::Token(t) => t.kind(),
414 }
415 }
416}
417
418#[derive(Debug, Clone, PartialEq, Eq)]
426pub struct Parsed {
427 source: String,
428 root: SyntaxNode,
429}
430
431impl Parsed {
432 pub fn new(source: String, root: SyntaxNode) -> Self {
434 Self { source, root }
435 }
436
437 pub fn source(&self) -> &str {
439 &self.source
440 }
441
442 pub fn root(&self) -> &SyntaxNode {
444 &self.root
445 }
446
447 pub fn pretty_print(&self) -> String {
449 let mut out = String::new();
450 self.root.pretty_fmt(&self.source, 0, &mut out);
451 out
452 }
453}
454
455pub trait Visitor {
463 fn enter(&mut self, _node: &SyntaxNode) {}
465 fn leave(&mut self, _node: &SyntaxNode) {}
467 fn visit_token(&mut self, _token: &SyntaxToken) {}
469}
470
471pub fn walk(node: &SyntaxNode, visitor: &mut dyn Visitor) {
473 visitor.enter(node);
474 for child in node.children() {
475 match child {
476 SyntaxElement::Node(n) => walk(n, visitor),
477 SyntaxElement::Token(t) => visitor.visit_token(t),
478 }
479 }
480 visitor.leave(node);
481}
482
483#[cfg(test)]
488mod tests {
489 use super::*;
490 use crate::text::{TextRange, TextSize};
491
492 #[test]
493 fn test_syntax_kind_name() {
494 assert_eq!(SyntaxKind::GOOGLE_ARG.name(), "GOOGLE_ARG");
495 assert_eq!(SyntaxKind::NAME.name(), "NAME");
496 assert_eq!(SyntaxKind::NUMPY_PARAMETER.name(), "NUMPY_PARAMETER");
497 }
498
499 #[test]
500 fn test_syntax_kind_is_node_is_token() {
501 assert!(SyntaxKind::GOOGLE_DOCSTRING.is_node());
502 assert!(!SyntaxKind::GOOGLE_DOCSTRING.is_token());
503 assert!(SyntaxKind::NAME.is_token());
504 assert!(!SyntaxKind::NAME.is_node());
505 }
506
507 #[test]
508 fn test_syntax_token_text() {
509 let source = "hello world";
510 let token = SyntaxToken::new(
511 SyntaxKind::NAME,
512 TextRange::new(TextSize::new(0), TextSize::new(5)),
513 );
514 assert_eq!(token.text(source), "hello");
515 }
516
517 #[test]
518 fn test_syntax_node_find_token() {
519 let node = SyntaxNode::new(
520 SyntaxKind::GOOGLE_ARG,
521 TextRange::new(TextSize::new(0), TextSize::new(10)),
522 vec![
523 SyntaxElement::Token(SyntaxToken::new(
524 SyntaxKind::NAME,
525 TextRange::new(TextSize::new(0), TextSize::new(3)),
526 )),
527 SyntaxElement::Token(SyntaxToken::new(
528 SyntaxKind::COLON,
529 TextRange::new(TextSize::new(3), TextSize::new(4)),
530 )),
531 SyntaxElement::Token(SyntaxToken::new(
532 SyntaxKind::DESCRIPTION,
533 TextRange::new(TextSize::new(5), TextSize::new(10)),
534 )),
535 ],
536 );
537
538 assert!(node.find_token(SyntaxKind::NAME).is_some());
539 assert!(node.find_token(SyntaxKind::COLON).is_some());
540 assert!(node.find_token(SyntaxKind::TYPE).is_none());
541 assert_eq!(node.tokens(SyntaxKind::NAME).count(), 1);
542 }
543
544 #[test]
545 fn test_syntax_node_find_node() {
546 let child = SyntaxNode::new(
547 SyntaxKind::GOOGLE_SECTION_HEADER,
548 TextRange::new(TextSize::new(0), TextSize::new(5)),
549 vec![SyntaxElement::Token(SyntaxToken::new(
550 SyntaxKind::NAME,
551 TextRange::new(TextSize::new(0), TextSize::new(4)),
552 ))],
553 );
554 let parent = SyntaxNode::new(
555 SyntaxKind::GOOGLE_SECTION,
556 TextRange::new(TextSize::new(0), TextSize::new(20)),
557 vec![SyntaxElement::Node(child)],
558 );
559
560 assert!(
561 parent
562 .find_node(SyntaxKind::GOOGLE_SECTION_HEADER)
563 .is_some()
564 );
565 assert!(parent.find_node(SyntaxKind::GOOGLE_ARG).is_none());
566 assert_eq!(parent.nodes(SyntaxKind::GOOGLE_SECTION_HEADER).count(), 1);
567 }
568
569 #[test]
570 fn test_pretty_print() {
571 let source = "Args:\n x: int";
572 let root = SyntaxNode::new(
573 SyntaxKind::GOOGLE_DOCSTRING,
574 TextRange::new(TextSize::new(0), TextSize::new(source.len() as u32)),
575 vec![SyntaxElement::Node(SyntaxNode::new(
576 SyntaxKind::GOOGLE_SECTION,
577 TextRange::new(TextSize::new(0), TextSize::new(source.len() as u32)),
578 vec![
579 SyntaxElement::Node(SyntaxNode::new(
580 SyntaxKind::GOOGLE_SECTION_HEADER,
581 TextRange::new(TextSize::new(0), TextSize::new(5)),
582 vec![
583 SyntaxElement::Token(SyntaxToken::new(
584 SyntaxKind::NAME,
585 TextRange::new(TextSize::new(0), TextSize::new(4)),
586 )),
587 SyntaxElement::Token(SyntaxToken::new(
588 SyntaxKind::COLON,
589 TextRange::new(TextSize::new(4), TextSize::new(5)),
590 )),
591 ],
592 )),
593 SyntaxElement::Node(SyntaxNode::new(
594 SyntaxKind::GOOGLE_ARG,
595 TextRange::new(TextSize::new(10), TextSize::new(source.len() as u32)),
596 vec![
597 SyntaxElement::Token(SyntaxToken::new(
598 SyntaxKind::NAME,
599 TextRange::new(TextSize::new(10), TextSize::new(11)),
600 )),
601 SyntaxElement::Token(SyntaxToken::new(
602 SyntaxKind::COLON,
603 TextRange::new(TextSize::new(11), TextSize::new(12)),
604 )),
605 SyntaxElement::Token(SyntaxToken::new(
606 SyntaxKind::DESCRIPTION,
607 TextRange::new(
608 TextSize::new(13),
609 TextSize::new(source.len() as u32),
610 ),
611 )),
612 ],
613 )),
614 ],
615 ))],
616 );
617
618 let parsed = Parsed::new(source.to_string(), root);
619 let output = parsed.pretty_print();
620
621 assert!(output.contains("GOOGLE_DOCSTRING@"));
623 assert!(output.contains("GOOGLE_SECTION@"));
624 assert!(output.contains("GOOGLE_SECTION_HEADER@"));
625 assert!(output.contains("GOOGLE_ARG@"));
626 assert!(output.contains("NAME: \"Args\"@"));
627 assert!(output.contains("COLON: \":\"@"));
628 assert!(output.contains("NAME: \"x\"@"));
629 assert!(output.contains("DESCRIPTION: \"int\"@"));
630 }
631
632 #[test]
633 fn test_visitor_walk() {
634 let source = "hello";
635 let root = SyntaxNode::new(
636 SyntaxKind::GOOGLE_DOCSTRING,
637 TextRange::new(TextSize::new(0), TextSize::new(5)),
638 vec![SyntaxElement::Token(SyntaxToken::new(
639 SyntaxKind::SUMMARY,
640 TextRange::new(TextSize::new(0), TextSize::new(5)),
641 ))],
642 );
643
644 struct Counter {
645 nodes: usize,
646 tokens: usize,
647 }
648 impl Visitor for Counter {
649 fn enter(&mut self, _node: &SyntaxNode) {
650 self.nodes += 1;
651 }
652 fn visit_token(&mut self, _token: &SyntaxToken) {
653 self.tokens += 1;
654 }
655 }
656
657 let mut counter = Counter {
658 nodes: 0,
659 tokens: 0,
660 };
661 walk(&root, &mut counter);
662 assert_eq!(counter.nodes, 1);
663 assert_eq!(counter.tokens, 1);
664
665 let tok = root.required_token(SyntaxKind::SUMMARY);
667 assert_eq!(tok.text(source), "hello");
668 }
669}