diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_editor/src/typing.rs | 52 | ||||
-rw-r--r-- | crates/ra_syntax/src/ast/generated.rs | 18 | ||||
-rw-r--r-- | crates/ra_syntax/src/ast/mod.rs | 43 | ||||
-rw-r--r-- | crates/ra_syntax/src/grammar.ron | 1 |
4 files changed, 78 insertions, 36 deletions
diff --git a/crates/ra_editor/src/typing.rs b/crates/ra_editor/src/typing.rs index 6c1a91ffb..ae82ff89b 100644 --- a/crates/ra_editor/src/typing.rs +++ b/crates/ra_editor/src/typing.rs | |||
@@ -58,14 +58,19 @@ pub fn join_lines(file: &File, range: TextRange) -> LocalEdit { | |||
58 | } | 58 | } |
59 | 59 | ||
60 | pub fn on_enter(file: &File, offset: TextUnit) -> Option<LocalEdit> { | 60 | pub fn on_enter(file: &File, offset: TextUnit) -> Option<LocalEdit> { |
61 | let comment = find_leaf_at_offset(file.syntax(), offset).left_biased().filter(|it| it.kind() == COMMENT)?; | 61 | let comment = find_leaf_at_offset(file.syntax(), offset).left_biased().and_then(|it| ast::Comment::cast(it))?; |
62 | let prefix = comment_preffix(comment)?; | 62 | |
63 | if offset < comment.range().start() + TextUnit::of_str(prefix) { | 63 | if let ast::CommentFlavor::Multiline = comment.flavor() { |
64 | return None; | ||
65 | } | ||
66 | |||
67 | let prefix = comment.prefix(); | ||
68 | if offset < comment.syntax().range().start() + TextUnit::of_str(prefix) + TextUnit::from(1) { | ||
64 | return None; | 69 | return None; |
65 | } | 70 | } |
66 | 71 | ||
67 | let indent = node_indent(file, comment)?; | 72 | let indent = node_indent(file, comment.syntax())?; |
68 | let inserted = format!("\n{}{}", indent, prefix); | 73 | let inserted = format!("\n{}{} ", indent, prefix); |
69 | let cursor_position = offset + TextUnit::of_str(&inserted); | 74 | let cursor_position = offset + TextUnit::of_str(&inserted); |
70 | let mut edit = EditBuilder::new(); | 75 | let mut edit = EditBuilder::new(); |
71 | edit.insert(offset, inserted); | 76 | edit.insert(offset, inserted); |
@@ -75,20 +80,6 @@ pub fn on_enter(file: &File, offset: TextUnit) -> Option<LocalEdit> { | |||
75 | }) | 80 | }) |
76 | } | 81 | } |
77 | 82 | ||
78 | fn comment_preffix(comment: SyntaxNodeRef) -> Option<&'static str> { | ||
79 | let text = comment.leaf_text().unwrap(); | ||
80 | let res = if text.starts_with("///") { | ||
81 | "/// " | ||
82 | } else if text.starts_with("//!") { | ||
83 | "//! " | ||
84 | } else if text.starts_with("//") { | ||
85 | "// " | ||
86 | } else { | ||
87 | return None; | ||
88 | }; | ||
89 | Some(res) | ||
90 | } | ||
91 | |||
92 | fn node_indent<'a>(file: &'a File, node: SyntaxNodeRef) -> Option<&'a str> { | 83 | fn node_indent<'a>(file: &'a File, node: SyntaxNodeRef) -> Option<&'a str> { |
93 | let ws = match find_leaf_at_offset(file.syntax(), node.range().start()) { | 84 | let ws = match find_leaf_at_offset(file.syntax(), node.range().start()) { |
94 | LeafAtOffset::Between(l, r) => { | 85 | LeafAtOffset::Between(l, r) => { |
@@ -166,31 +157,27 @@ fn remove_newline( | |||
166 | // Removes: comma, newline (incl. surrounding whitespace) | 157 | // Removes: comma, newline (incl. surrounding whitespace) |
167 | // Adds: a single whitespace | 158 | // Adds: a single whitespace |
168 | edit.replace(range, " ".to_string()); | 159 | edit.replace(range, " ".to_string()); |
169 | } else if prev.kind() == COMMENT && next.kind() == COMMENT { | 160 | } else if let (Some(_), Some(next)) = (ast::Comment::cast(prev), ast::Comment::cast(next)) { |
170 | // Removes: newline (incl. surrounding whitespace), start of the next comment | 161 | // Removes: newline (incl. surrounding whitespace), start of the next comment |
171 | 162 | let comment_text = next.text(); | |
172 | // FIXME: I guess it is safe to unwrap here? A comment always has text, right? | ||
173 | let comment_text = next.leaf_text().unwrap().as_str(); | ||
174 | let comment_start_length = comment_start_length(comment_text); | ||
175 | |||
176 | if let Some(newline_pos) = comment_text.find('\n') { | 163 | if let Some(newline_pos) = comment_text.find('\n') { |
177 | // Special case for multi-line c-like comments: join the comment content but | 164 | // Special case for multi-line c-like comments: join the comment content but |
178 | // keep the leading `/*` | 165 | // keep the leading `/*` |
179 | 166 | ||
180 | let newline_offset = next.range().start() | 167 | let newline_offset = next.syntax().range().start() |
181 | + TextUnit::from(newline_pos as u32) | 168 | + TextUnit::from(newline_pos as u32) |
182 | + TextUnit::of_char('\n'); | 169 | + TextUnit::of_char('\n'); |
183 | 170 | ||
184 | edit.insert(newline_offset, "/*".to_string()); | 171 | edit.insert(newline_offset, "/*".to_string()); |
185 | edit.delete(TextRange::from_to( | 172 | edit.delete(TextRange::from_to( |
186 | node.range().start(), | 173 | node.range().start(), |
187 | next.range().start() + comment_start_length | 174 | next.syntax().range().start() + TextUnit::of_str(next.prefix()) |
188 | )); | 175 | )); |
189 | } else { | 176 | } else { |
190 | // Single-line comments | 177 | // Single-line comments |
191 | edit.delete(TextRange::from_to( | 178 | edit.delete(TextRange::from_to( |
192 | node.range().start(), | 179 | node.range().start(), |
193 | next.range().start() + comment_start_length | 180 | next.syntax().range().start() + TextUnit::of_str(next.prefix()) |
194 | )); | 181 | )); |
195 | } | 182 | } |
196 | } else { | 183 | } else { |
@@ -205,7 +192,7 @@ fn remove_newline( | |||
205 | } | 192 | } |
206 | } | 193 | } |
207 | 194 | ||
208 | // FIXME: do we ever reach this point? What does it mean to be here? I think we should document it | 195 | // The node is either the first or the last in the file |
209 | let suff = &node_text[TextRange::from_to( | 196 | let suff = &node_text[TextRange::from_to( |
210 | offset - node.range().start() + TextUnit::of_char('\n'), | 197 | offset - node.range().start() + TextUnit::of_char('\n'), |
211 | TextUnit::of_str(node_text), | 198 | TextUnit::of_str(node_text), |
@@ -218,13 +205,6 @@ fn remove_newline( | |||
218 | ); | 205 | ); |
219 | } | 206 | } |
220 | 207 | ||
221 | // Return the start length of the comment (e.g. 2 for `//` and 3 for `//!`) | ||
222 | fn comment_start_length(_text: &str) -> TextUnit { | ||
223 | // TODO: use the parser here instead of reimplementing comment parsing? | ||
224 | // Otherwise, reimplement comment parsing :) | ||
225 | return TextUnit::from(2); | ||
226 | } | ||
227 | |||
228 | fn is_trailing_comma(left: SyntaxKind, right: SyntaxKind) -> bool { | 208 | fn is_trailing_comma(left: SyntaxKind, right: SyntaxKind) -> bool { |
229 | match (left, right) { | 209 | match (left, right) { |
230 | (COMMA, R_PAREN) | (COMMA, R_BRACK) => true, | 210 | (COMMA, R_PAREN) | (COMMA, R_BRACK) => true, |
diff --git a/crates/ra_syntax/src/ast/generated.rs b/crates/ra_syntax/src/ast/generated.rs index 2db6dff1b..b0b855eb4 100644 --- a/crates/ra_syntax/src/ast/generated.rs +++ b/crates/ra_syntax/src/ast/generated.rs | |||
@@ -227,6 +227,24 @@ impl<'a> AstNode<'a> for CastExpr<'a> { | |||
227 | 227 | ||
228 | impl<'a> CastExpr<'a> {} | 228 | impl<'a> CastExpr<'a> {} |
229 | 229 | ||
230 | // Comment | ||
231 | #[derive(Debug, Clone, Copy)] | ||
232 | pub struct Comment<'a> { | ||
233 | syntax: SyntaxNodeRef<'a>, | ||
234 | } | ||
235 | |||
236 | impl<'a> AstNode<'a> for Comment<'a> { | ||
237 | fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> { | ||
238 | match syntax.kind() { | ||
239 | COMMENT => Some(Comment { syntax }), | ||
240 | _ => None, | ||
241 | } | ||
242 | } | ||
243 | fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax } | ||
244 | } | ||
245 | |||
246 | impl<'a> Comment<'a> {} | ||
247 | |||
230 | // Condition | 248 | // Condition |
231 | #[derive(Debug, Clone, Copy)] | 249 | #[derive(Debug, Clone, Copy)] |
232 | pub struct Condition<'a> { | 250 | pub struct Condition<'a> { |
diff --git a/crates/ra_syntax/src/ast/mod.rs b/crates/ra_syntax/src/ast/mod.rs index c1570b868..10dac72e5 100644 --- a/crates/ra_syntax/src/ast/mod.rs +++ b/crates/ra_syntax/src/ast/mod.rs | |||
@@ -99,6 +99,49 @@ impl<'a> Lifetime<'a> { | |||
99 | } | 99 | } |
100 | } | 100 | } |
101 | 101 | ||
102 | impl<'a> Comment<'a> { | ||
103 | pub fn text(&self) -> SmolStr { | ||
104 | self.syntax().leaf_text().unwrap().clone() | ||
105 | } | ||
106 | |||
107 | pub fn flavor(&self) -> CommentFlavor { | ||
108 | let text = self.text(); | ||
109 | if text.starts_with("///") { | ||
110 | CommentFlavor::Doc | ||
111 | } else if text.starts_with("//!") { | ||
112 | CommentFlavor::ModuleDoc | ||
113 | } else if text.starts_with("//") { | ||
114 | CommentFlavor::Line | ||
115 | } else { | ||
116 | CommentFlavor::Multiline | ||
117 | } | ||
118 | } | ||
119 | |||
120 | pub fn prefix(&self) -> &'static str { | ||
121 | self.flavor().prefix() | ||
122 | } | ||
123 | } | ||
124 | |||
125 | #[derive(Debug)] | ||
126 | pub enum CommentFlavor { | ||
127 | Line, | ||
128 | Doc, | ||
129 | ModuleDoc, | ||
130 | Multiline | ||
131 | } | ||
132 | |||
133 | impl CommentFlavor { | ||
134 | pub fn prefix(&self) -> &'static str { | ||
135 | use self::CommentFlavor::*; | ||
136 | match *self { | ||
137 | Line => "//", | ||
138 | Doc => "///", | ||
139 | ModuleDoc => "//!", | ||
140 | Multiline => "/*" | ||
141 | } | ||
142 | } | ||
143 | } | ||
144 | |||
102 | impl<'a> Name<'a> { | 145 | impl<'a> Name<'a> { |
103 | pub fn text(&self) -> SmolStr { | 146 | pub fn text(&self) -> SmolStr { |
104 | let ident = self.syntax().first_child() | 147 | let ident = self.syntax().first_child() |
diff --git a/crates/ra_syntax/src/grammar.ron b/crates/ra_syntax/src/grammar.ron index 4b990fd8d..9da0c2c13 100644 --- a/crates/ra_syntax/src/grammar.ron +++ b/crates/ra_syntax/src/grammar.ron | |||
@@ -537,5 +537,6 @@ Grammar( | |||
537 | "PathSegment": ( | 537 | "PathSegment": ( |
538 | options: [ "NameRef" ] | 538 | options: [ "NameRef" ] |
539 | ), | 539 | ), |
540 | "Comment": (), | ||
540 | }, | 541 | }, |
541 | ) | 542 | ) |