diff options
author | Aleksey Kladov <[email protected]> | 2019-02-12 15:41:57 +0000 |
---|---|---|
committer | Aleksey Kladov <[email protected]> | 2019-02-12 16:02:30 +0000 |
commit | 4e91c23c796988e3934afabf619185333f85c116 (patch) | |
tree | f81bff262a32170f2677c5c60fe6ddba766f042a /crates/ra_syntax/src/syntax_node | |
parent | 72a122092bb37249a5d04ca3b8b5c8b78f9b84dd (diff) |
rename yellow -> syntax_node
why yellow in the first place? Its red + green.
Diffstat (limited to 'crates/ra_syntax/src/syntax_node')
-rw-r--r-- | crates/ra_syntax/src/syntax_node/builder.rs | 41 | ||||
-rw-r--r-- | crates/ra_syntax/src/syntax_node/syntax_error.rs | 142 | ||||
-rw-r--r-- | crates/ra_syntax/src/syntax_node/syntax_text.rs | 144 |
3 files changed, 327 insertions, 0 deletions
diff --git a/crates/ra_syntax/src/syntax_node/builder.rs b/crates/ra_syntax/src/syntax_node/builder.rs new file mode 100644 index 000000000..8abd0f051 --- /dev/null +++ b/crates/ra_syntax/src/syntax_node/builder.rs | |||
@@ -0,0 +1,41 @@ | |||
1 | use crate::{ | ||
2 | parser_impl::Sink, | ||
3 | syntax_node::{GreenNode, RaTypes, SyntaxError}, | ||
4 | SmolStr, SyntaxKind, | ||
5 | }; | ||
6 | use rowan::GreenNodeBuilder; | ||
7 | |||
8 | pub(crate) struct GreenBuilder { | ||
9 | errors: Vec<SyntaxError>, | ||
10 | inner: GreenNodeBuilder<RaTypes>, | ||
11 | } | ||
12 | |||
13 | impl GreenBuilder { | ||
14 | pub(crate) fn new() -> GreenBuilder { | ||
15 | GreenBuilder { errors: Vec::new(), inner: GreenNodeBuilder::new() } | ||
16 | } | ||
17 | } | ||
18 | |||
19 | impl Sink for GreenBuilder { | ||
20 | type Tree = (GreenNode, Vec<SyntaxError>); | ||
21 | |||
22 | fn leaf(&mut self, kind: SyntaxKind, text: SmolStr) { | ||
23 | self.inner.leaf(kind, text); | ||
24 | } | ||
25 | |||
26 | fn start_branch(&mut self, kind: SyntaxKind) { | ||
27 | self.inner.start_internal(kind) | ||
28 | } | ||
29 | |||
30 | fn finish_branch(&mut self) { | ||
31 | self.inner.finish_internal(); | ||
32 | } | ||
33 | |||
34 | fn error(&mut self, error: SyntaxError) { | ||
35 | self.errors.push(error) | ||
36 | } | ||
37 | |||
38 | fn finish(self) -> (GreenNode, Vec<SyntaxError>) { | ||
39 | (self.inner.finish(), self.errors) | ||
40 | } | ||
41 | } | ||
diff --git a/crates/ra_syntax/src/syntax_node/syntax_error.rs b/crates/ra_syntax/src/syntax_node/syntax_error.rs new file mode 100644 index 000000000..412cf82cc --- /dev/null +++ b/crates/ra_syntax/src/syntax_node/syntax_error.rs | |||
@@ -0,0 +1,142 @@ | |||
1 | use std::fmt; | ||
2 | |||
3 | use crate::{TextRange, TextUnit}; | ||
4 | |||
5 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
6 | pub struct SyntaxError { | ||
7 | kind: SyntaxErrorKind, | ||
8 | location: Location, | ||
9 | } | ||
10 | |||
11 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
12 | pub enum Location { | ||
13 | Offset(TextUnit), | ||
14 | Range(TextRange), | ||
15 | } | ||
16 | |||
17 | impl Into<Location> for TextUnit { | ||
18 | fn into(self) -> Location { | ||
19 | Location::Offset(self) | ||
20 | } | ||
21 | } | ||
22 | |||
23 | impl Into<Location> for TextRange { | ||
24 | fn into(self) -> Location { | ||
25 | Location::Range(self) | ||
26 | } | ||
27 | } | ||
28 | |||
29 | impl SyntaxError { | ||
30 | pub fn new<L: Into<Location>>(kind: SyntaxErrorKind, loc: L) -> SyntaxError { | ||
31 | SyntaxError { kind, location: loc.into() } | ||
32 | } | ||
33 | |||
34 | pub fn kind(&self) -> SyntaxErrorKind { | ||
35 | self.kind.clone() | ||
36 | } | ||
37 | |||
38 | pub fn location(&self) -> Location { | ||
39 | self.location.clone() | ||
40 | } | ||
41 | |||
42 | pub fn offset(&self) -> TextUnit { | ||
43 | match self.location { | ||
44 | Location::Offset(offset) => offset, | ||
45 | Location::Range(range) => range.start(), | ||
46 | } | ||
47 | } | ||
48 | |||
49 | pub fn add_offset(mut self, plus_offset: TextUnit) -> SyntaxError { | ||
50 | self.location = match self.location { | ||
51 | Location::Range(range) => Location::Range(range + plus_offset), | ||
52 | Location::Offset(offset) => Location::Offset(offset + plus_offset), | ||
53 | }; | ||
54 | |||
55 | self | ||
56 | } | ||
57 | } | ||
58 | |||
59 | impl fmt::Display for SyntaxError { | ||
60 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
61 | self.kind.fmt(f) | ||
62 | } | ||
63 | } | ||
64 | |||
65 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
66 | pub enum SyntaxErrorKind { | ||
67 | ParseError(ParseError), | ||
68 | UnescapedCodepoint, | ||
69 | EmptyChar, | ||
70 | UnclosedChar, | ||
71 | OverlongChar, | ||
72 | EmptyByte, | ||
73 | UnclosedByte, | ||
74 | OverlongByte, | ||
75 | ByteOutOfRange, | ||
76 | UnescapedByte, | ||
77 | EmptyByteEscape, | ||
78 | InvalidByteEscape, | ||
79 | TooShortByteCodeEscape, | ||
80 | MalformedByteCodeEscape, | ||
81 | UnicodeEscapeForbidden, | ||
82 | EmptyAsciiEscape, | ||
83 | InvalidAsciiEscape, | ||
84 | TooShortAsciiCodeEscape, | ||
85 | AsciiCodeEscapeOutOfRange, | ||
86 | MalformedAsciiCodeEscape, | ||
87 | UnclosedUnicodeEscape, | ||
88 | MalformedUnicodeEscape, | ||
89 | EmptyUnicodeEcape, | ||
90 | OverlongUnicodeEscape, | ||
91 | UnicodeEscapeOutOfRange, | ||
92 | UnclosedString, | ||
93 | InvalidSuffix, | ||
94 | InvalidBlockAttr, | ||
95 | } | ||
96 | |||
97 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
98 | pub struct ParseError(pub String); | ||
99 | |||
100 | impl fmt::Display for SyntaxErrorKind { | ||
101 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
102 | use self::SyntaxErrorKind::*; | ||
103 | match self { | ||
104 | UnescapedCodepoint => write!(f, "This codepoint should always be escaped"), | ||
105 | EmptyAsciiEscape => write!(f, "Empty escape sequence"), | ||
106 | InvalidAsciiEscape => write!(f, "Invalid escape sequence"), | ||
107 | EmptyChar => write!(f, "Empty char literal"), | ||
108 | UnclosedChar => write!(f, "Unclosed char literal"), | ||
109 | OverlongChar => write!(f, "Char literal should be one character long"), | ||
110 | EmptyByte => write!(f, "Empty byte literal"), | ||
111 | UnclosedByte => write!(f, "Unclosed byte literal"), | ||
112 | OverlongByte => write!(f, "Byte literal should be one character long"), | ||
113 | ByteOutOfRange => write!(f, "Byte should be a valid ASCII character"), | ||
114 | UnescapedByte => write!(f, "This byte should always be escaped"), | ||
115 | EmptyByteEscape => write!(f, "Empty escape sequence"), | ||
116 | InvalidByteEscape => write!(f, "Invalid escape sequence"), | ||
117 | TooShortByteCodeEscape => write!(f, "Escape sequence should have two digits"), | ||
118 | MalformedByteCodeEscape => write!(f, "Escape sequence should be a hexadecimal number"), | ||
119 | UnicodeEscapeForbidden => { | ||
120 | write!(f, "Unicode escapes are not allowed in byte literals or byte strings") | ||
121 | } | ||
122 | TooShortAsciiCodeEscape => write!(f, "Escape sequence should have two digits"), | ||
123 | AsciiCodeEscapeOutOfRange => { | ||
124 | write!(f, "Escape sequence should be between \\x00 and \\x7F") | ||
125 | } | ||
126 | MalformedAsciiCodeEscape => write!(f, "Escape sequence should be a hexadecimal number"), | ||
127 | UnclosedUnicodeEscape => write!(f, "Missing `}}`"), | ||
128 | MalformedUnicodeEscape => write!(f, "Malformed unicode escape sequence"), | ||
129 | EmptyUnicodeEcape => write!(f, "Empty unicode escape sequence"), | ||
130 | OverlongUnicodeEscape => { | ||
131 | write!(f, "Unicode escape sequence should have at most 6 digits") | ||
132 | } | ||
133 | UnicodeEscapeOutOfRange => write!(f, "Unicode escape code should be at most 0x10FFFF"), | ||
134 | UnclosedString => write!(f, "Unclosed string literal"), | ||
135 | InvalidSuffix => write!(f, "Invalid literal suffix"), | ||
136 | InvalidBlockAttr => { | ||
137 | write!(f, "A block in this position cannot accept inner attributes") | ||
138 | } | ||
139 | ParseError(msg) => write!(f, "{}", msg.0), | ||
140 | } | ||
141 | } | ||
142 | } | ||
diff --git a/crates/ra_syntax/src/syntax_node/syntax_text.rs b/crates/ra_syntax/src/syntax_node/syntax_text.rs new file mode 100644 index 000000000..84e5b231a --- /dev/null +++ b/crates/ra_syntax/src/syntax_node/syntax_text.rs | |||
@@ -0,0 +1,144 @@ | |||
1 | use std::{fmt, ops}; | ||
2 | |||
3 | use crate::{SyntaxNode, TextRange, TextUnit}; | ||
4 | |||
5 | #[derive(Clone)] | ||
6 | pub struct SyntaxText<'a> { | ||
7 | node: &'a SyntaxNode, | ||
8 | range: TextRange, | ||
9 | } | ||
10 | |||
11 | impl<'a> SyntaxText<'a> { | ||
12 | pub(crate) fn new(node: &'a SyntaxNode) -> SyntaxText<'a> { | ||
13 | SyntaxText { node, range: node.range() } | ||
14 | } | ||
15 | |||
16 | pub fn chunks(&self) -> impl Iterator<Item = &'a str> { | ||
17 | let range = self.range; | ||
18 | self.node.descendants().filter_map(move |node| { | ||
19 | let text = node.leaf_text()?; | ||
20 | let range = range.intersection(&node.range())?; | ||
21 | let range = range - node.range().start(); | ||
22 | Some(&text[range]) | ||
23 | }) | ||
24 | } | ||
25 | |||
26 | pub fn push_to(&self, buf: &mut String) { | ||
27 | self.chunks().for_each(|it| buf.push_str(it)); | ||
28 | } | ||
29 | |||
30 | pub fn to_string(&self) -> String { | ||
31 | self.chunks().collect() | ||
32 | } | ||
33 | |||
34 | pub fn contains(&self, c: char) -> bool { | ||
35 | self.chunks().any(|it| it.contains(c)) | ||
36 | } | ||
37 | |||
38 | pub fn find(&self, c: char) -> Option<TextUnit> { | ||
39 | let mut acc: TextUnit = 0.into(); | ||
40 | for chunk in self.chunks() { | ||
41 | if let Some(pos) = chunk.find(c) { | ||
42 | let pos: TextUnit = (pos as u32).into(); | ||
43 | return Some(acc + pos); | ||
44 | } | ||
45 | acc += TextUnit::of_str(chunk); | ||
46 | } | ||
47 | None | ||
48 | } | ||
49 | |||
50 | pub fn len(&self) -> TextUnit { | ||
51 | self.range.len() | ||
52 | } | ||
53 | |||
54 | pub fn slice(&self, range: impl SyntaxTextSlice) -> SyntaxText<'a> { | ||
55 | let range = range.restrict(self.range).unwrap_or_else(|| { | ||
56 | panic!("invalid slice, range: {:?}, slice: {:?}", self.range, range) | ||
57 | }); | ||
58 | SyntaxText { node: self.node, range } | ||
59 | } | ||
60 | |||
61 | pub fn char_at(&self, offset: impl Into<TextUnit>) -> Option<char> { | ||
62 | let mut start: TextUnit = 0.into(); | ||
63 | let offset = offset.into(); | ||
64 | for chunk in self.chunks() { | ||
65 | let end = start + TextUnit::of_str(chunk); | ||
66 | if start <= offset && offset < end { | ||
67 | let off: usize = u32::from(offset - start) as usize; | ||
68 | return Some(chunk[off..].chars().next().unwrap()); | ||
69 | } | ||
70 | start = end; | ||
71 | } | ||
72 | None | ||
73 | } | ||
74 | } | ||
75 | |||
76 | impl<'a> fmt::Debug for SyntaxText<'a> { | ||
77 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
78 | fmt::Debug::fmt(&self.to_string(), f) | ||
79 | } | ||
80 | } | ||
81 | |||
82 | impl<'a> fmt::Display for SyntaxText<'a> { | ||
83 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
84 | fmt::Display::fmt(&self.to_string(), f) | ||
85 | } | ||
86 | } | ||
87 | |||
88 | pub trait SyntaxTextSlice: fmt::Debug { | ||
89 | fn restrict(&self, range: TextRange) -> Option<TextRange>; | ||
90 | } | ||
91 | |||
92 | impl SyntaxTextSlice for TextRange { | ||
93 | fn restrict(&self, range: TextRange) -> Option<TextRange> { | ||
94 | self.intersection(&range) | ||
95 | } | ||
96 | } | ||
97 | |||
98 | impl SyntaxTextSlice for ops::RangeTo<TextUnit> { | ||
99 | fn restrict(&self, range: TextRange) -> Option<TextRange> { | ||
100 | if !range.contains_inclusive(self.end) { | ||
101 | return None; | ||
102 | } | ||
103 | Some(TextRange::from_to(range.start(), self.end)) | ||
104 | } | ||
105 | } | ||
106 | |||
107 | impl SyntaxTextSlice for ops::RangeFrom<TextUnit> { | ||
108 | fn restrict(&self, range: TextRange) -> Option<TextRange> { | ||
109 | if !range.contains_inclusive(self.start) { | ||
110 | return None; | ||
111 | } | ||
112 | Some(TextRange::from_to(self.start, range.end())) | ||
113 | } | ||
114 | } | ||
115 | |||
116 | impl SyntaxTextSlice for ops::Range<TextUnit> { | ||
117 | fn restrict(&self, range: TextRange) -> Option<TextRange> { | ||
118 | TextRange::from_to(self.start, self.end).restrict(range) | ||
119 | } | ||
120 | } | ||
121 | |||
122 | impl From<SyntaxText<'_>> for String { | ||
123 | fn from(text: SyntaxText) -> String { | ||
124 | text.to_string() | ||
125 | } | ||
126 | } | ||
127 | |||
128 | impl PartialEq<str> for SyntaxText<'_> { | ||
129 | fn eq(&self, mut rhs: &str) -> bool { | ||
130 | for chunk in self.chunks() { | ||
131 | if !rhs.starts_with(chunk) { | ||
132 | return false; | ||
133 | } | ||
134 | rhs = &rhs[chunk.len()..]; | ||
135 | } | ||
136 | rhs.is_empty() | ||
137 | } | ||
138 | } | ||
139 | |||
140 | impl PartialEq<&'_ str> for SyntaxText<'_> { | ||
141 | fn eq(&self, rhs: &&str) -> bool { | ||
142 | self == *rhs | ||
143 | } | ||
144 | } | ||