diff options
Diffstat (limited to 'crates/ra_syntax/src/lib.rs')
-rw-r--r-- | crates/ra_syntax/src/lib.rs | 94 |
1 files changed, 64 insertions, 30 deletions
diff --git a/crates/ra_syntax/src/lib.rs b/crates/ra_syntax/src/lib.rs index 0ceabc203..930a643b7 100644 --- a/crates/ra_syntax/src/lib.rs +++ b/crates/ra_syntax/src/lib.rs | |||
@@ -31,6 +31,12 @@ pub mod ast; | |||
31 | #[doc(hidden)] | 31 | #[doc(hidden)] |
32 | pub mod fuzz; | 32 | pub mod fuzz; |
33 | 33 | ||
34 | use std::{sync::Arc, fmt::Write}; | ||
35 | |||
36 | use ra_text_edit::AtomTextEdit; | ||
37 | |||
38 | use crate::syntax_node::GreenNode; | ||
39 | |||
34 | pub use rowan::{SmolStr, TextRange, TextUnit}; | 40 | pub use rowan::{SmolStr, TextRange, TextUnit}; |
35 | pub use ra_parser::SyntaxKind; | 41 | pub use ra_parser::SyntaxKind; |
36 | pub use ra_parser::T; | 42 | pub use ra_parser::T; |
@@ -43,45 +49,72 @@ pub use crate::{ | |||
43 | parsing::{tokenize, classify_literal, Token}, | 49 | parsing::{tokenize, classify_literal, Token}, |
44 | }; | 50 | }; |
45 | 51 | ||
46 | use ra_text_edit::AtomTextEdit; | 52 | /// `Parse` is the result of the parsing: a syntax tree and a collection of |
47 | use crate::syntax_node::GreenNode; | 53 | /// errors. |
48 | 54 | /// | |
49 | /// `SourceFile` represents a parse tree for a single Rust file. | 55 | /// Note that we always produce a syntax tree, even for completely invalid |
50 | pub use crate::ast::SourceFile; | 56 | /// files. |
57 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
58 | pub struct Parse { | ||
59 | pub tree: TreeArc<SourceFile>, | ||
60 | pub errors: Arc<Vec<SyntaxError>>, | ||
61 | } | ||
51 | 62 | ||
52 | impl SourceFile { | 63 | impl Parse { |
53 | fn new(green: GreenNode, errors: Vec<SyntaxError>) -> TreeArc<SourceFile> { | 64 | pub fn ok(self) -> Result<TreeArc<SourceFile>, Arc<Vec<SyntaxError>>> { |
54 | let root = SyntaxNode::new(green, errors); | 65 | if self.errors.is_empty() { |
55 | if cfg!(debug_assertions) { | 66 | Ok(self.tree) |
56 | validation::validate_block_structure(&root); | 67 | } else { |
68 | Err(self.errors) | ||
57 | } | 69 | } |
58 | assert_eq!(root.kind(), SyntaxKind::SOURCE_FILE); | ||
59 | TreeArc::cast(root) | ||
60 | } | 70 | } |
61 | 71 | ||
62 | pub fn parse(text: &str) -> TreeArc<SourceFile> { | 72 | pub fn reparse(&self, edit: &AtomTextEdit) -> Parse { |
63 | let (green, errors) = parsing::parse_text(text); | 73 | self.incremental_reparse(edit).unwrap_or_else(|| self.full_reparse(edit)) |
64 | SourceFile::new(green, errors) | ||
65 | } | 74 | } |
66 | 75 | ||
67 | pub fn reparse(&self, edit: &AtomTextEdit) -> TreeArc<SourceFile> { | 76 | pub fn debug_dump(&self) -> String { |
68 | self.incremental_reparse(edit).unwrap_or_else(|| self.full_reparse(edit)) | 77 | let mut buf = self.tree.syntax().debug_dump(); |
78 | for err in self.errors.iter() { | ||
79 | writeln!(buf, "err: `{}`", err).unwrap(); | ||
80 | } | ||
81 | buf | ||
69 | } | 82 | } |
70 | 83 | ||
71 | pub fn incremental_reparse(&self, edit: &AtomTextEdit) -> Option<TreeArc<SourceFile>> { | 84 | fn incremental_reparse(&self, edit: &AtomTextEdit) -> Option<Parse> { |
72 | parsing::incremental_reparse(self.syntax(), edit, self.errors()) | 85 | // FIXME: validation errors are not handled here |
73 | .map(|(green_node, errors, _reparsed_range)| SourceFile::new(green_node, errors)) | 86 | parsing::incremental_reparse(self.tree.syntax(), edit, self.errors.to_vec()).map( |
87 | |(green_node, errors, _reparsed_range)| Parse { | ||
88 | tree: SourceFile::new(green_node), | ||
89 | errors: Arc::new(errors), | ||
90 | }, | ||
91 | ) | ||
74 | } | 92 | } |
75 | 93 | ||
76 | fn full_reparse(&self, edit: &AtomTextEdit) -> TreeArc<SourceFile> { | 94 | fn full_reparse(&self, edit: &AtomTextEdit) -> Parse { |
77 | let text = edit.apply(self.syntax().text().to_string()); | 95 | let text = edit.apply(self.tree.syntax().text().to_string()); |
78 | SourceFile::parse(&text) | 96 | SourceFile::parse(&text) |
79 | } | 97 | } |
98 | } | ||
99 | |||
100 | /// `SourceFile` represents a parse tree for a single Rust file. | ||
101 | pub use crate::ast::SourceFile; | ||
102 | |||
103 | impl SourceFile { | ||
104 | fn new(green: GreenNode) -> TreeArc<SourceFile> { | ||
105 | let root = SyntaxNode::new(green); | ||
106 | if cfg!(debug_assertions) { | ||
107 | validation::validate_block_structure(&root); | ||
108 | } | ||
109 | assert_eq!(root.kind(), SyntaxKind::SOURCE_FILE); | ||
110 | TreeArc::cast(root) | ||
111 | } | ||
80 | 112 | ||
81 | pub fn errors(&self) -> Vec<SyntaxError> { | 113 | pub fn parse(text: &str) -> Parse { |
82 | let mut errors = self.syntax.root_data().to_vec(); | 114 | let (green, mut errors) = parsing::parse_text(text); |
83 | errors.extend(validation::validate(self)); | 115 | let tree = SourceFile::new(green); |
84 | errors | 116 | errors.extend(validation::validate(&tree)); |
117 | Parse { tree, errors: Arc::new(errors) } | ||
85 | } | 118 | } |
86 | } | 119 | } |
87 | 120 | ||
@@ -98,14 +131,15 @@ fn api_walkthrough() { | |||
98 | "; | 131 | "; |
99 | // `SourceFile` is the main entry point. | 132 | // `SourceFile` is the main entry point. |
100 | // | 133 | // |
101 | // Note how `parse` does not return a `Result`: even completely invalid | 134 | // The `parse` method returns a `Parse` -- a pair of syntax tree and a list |
102 | // source code might be parsed. | 135 | // of errors. That is, syntax tree is constructed even in presence of errors. |
103 | let file = SourceFile::parse(source_code); | 136 | let parse = SourceFile::parse(source_code); |
137 | assert!(parse.errors.is_empty()); | ||
104 | 138 | ||
105 | // Due to the way ownership is set up, owned syntax Nodes always live behind | 139 | // Due to the way ownership is set up, owned syntax Nodes always live behind |
106 | // a `TreeArc` smart pointer. `TreeArc` is roughly an `std::sync::Arc` which | 140 | // a `TreeArc` smart pointer. `TreeArc` is roughly an `std::sync::Arc` which |
107 | // points to the whole file instead of an individual node. | 141 | // points to the whole file instead of an individual node. |
108 | let file: TreeArc<SourceFile> = file; | 142 | let file: TreeArc<SourceFile> = parse.tree; |
109 | 143 | ||
110 | // `SourceFile` is the root of the syntax tree. We can iterate file's items: | 144 | // `SourceFile` is the root of the syntax tree. We can iterate file's items: |
111 | let mut func = None; | 145 | let mut func = None; |