diff options
Diffstat (limited to 'crates/ra_editor/src')
-rw-r--r-- | crates/ra_editor/src/code_actions.rs | 19 | ||||
-rw-r--r-- | crates/ra_editor/src/extend_selection.rs | 6 | ||||
-rw-r--r-- | crates/ra_editor/src/folding_ranges.rs | 6 | ||||
-rw-r--r-- | crates/ra_editor/src/lib.rs | 18 | ||||
-rw-r--r-- | crates/ra_editor/src/line_index.rs | 219 | ||||
-rw-r--r-- | crates/ra_editor/src/symbols.rs | 40 | ||||
-rw-r--r-- | crates/ra_editor/src/test_utils.rs | 14 | ||||
-rw-r--r-- | crates/ra_editor/src/typing.rs | 16 |
8 files changed, 278 insertions, 60 deletions
diff --git a/crates/ra_editor/src/code_actions.rs b/crates/ra_editor/src/code_actions.rs index ef6df0d53..0139b19d3 100644 --- a/crates/ra_editor/src/code_actions.rs +++ b/crates/ra_editor/src/code_actions.rs | |||
@@ -3,7 +3,7 @@ use join_to_string::join; | |||
3 | use ra_syntax::{ | 3 | use ra_syntax::{ |
4 | algo::{find_covering_node, find_leaf_at_offset}, | 4 | algo::{find_covering_node, find_leaf_at_offset}, |
5 | ast::{self, AstNode, AttrsOwner, NameOwner, TypeParamsOwner}, | 5 | ast::{self, AstNode, AttrsOwner, NameOwner, TypeParamsOwner}, |
6 | Direction, File, | 6 | Direction, SourceFileNode, |
7 | SyntaxKind::{COMMA, WHITESPACE}, | 7 | SyntaxKind::{COMMA, WHITESPACE}, |
8 | SyntaxNodeRef, TextRange, TextUnit, | 8 | SyntaxNodeRef, TextRange, TextUnit, |
9 | }; | 9 | }; |
@@ -16,7 +16,10 @@ pub struct LocalEdit { | |||
16 | pub cursor_position: Option<TextUnit>, | 16 | pub cursor_position: Option<TextUnit>, |
17 | } | 17 | } |
18 | 18 | ||
19 | pub fn flip_comma<'a>(file: &'a File, offset: TextUnit) -> Option<impl FnOnce() -> LocalEdit + 'a> { | 19 | pub fn flip_comma<'a>( |
20 | file: &'a SourceFileNode, | ||
21 | offset: TextUnit, | ||
22 | ) -> Option<impl FnOnce() -> LocalEdit + 'a> { | ||
20 | let syntax = file.syntax(); | 23 | let syntax = file.syntax(); |
21 | 24 | ||
22 | let comma = find_leaf_at_offset(syntax, offset).find(|leaf| leaf.kind() == COMMA)?; | 25 | let comma = find_leaf_at_offset(syntax, offset).find(|leaf| leaf.kind() == COMMA)?; |
@@ -33,7 +36,10 @@ pub fn flip_comma<'a>(file: &'a File, offset: TextUnit) -> Option<impl FnOnce() | |||
33 | }) | 36 | }) |
34 | } | 37 | } |
35 | 38 | ||
36 | pub fn add_derive<'a>(file: &'a File, offset: TextUnit) -> Option<impl FnOnce() -> LocalEdit + 'a> { | 39 | pub fn add_derive<'a>( |
40 | file: &'a SourceFileNode, | ||
41 | offset: TextUnit, | ||
42 | ) -> Option<impl FnOnce() -> LocalEdit + 'a> { | ||
37 | let nominal = find_node_at_offset::<ast::NominalDef>(file.syntax(), offset)?; | 43 | let nominal = find_node_at_offset::<ast::NominalDef>(file.syntax(), offset)?; |
38 | Some(move || { | 44 | Some(move || { |
39 | let derive_attr = nominal | 45 | let derive_attr = nominal |
@@ -58,7 +64,10 @@ pub fn add_derive<'a>(file: &'a File, offset: TextUnit) -> Option<impl FnOnce() | |||
58 | }) | 64 | }) |
59 | } | 65 | } |
60 | 66 | ||
61 | pub fn add_impl<'a>(file: &'a File, offset: TextUnit) -> Option<impl FnOnce() -> LocalEdit + 'a> { | 67 | pub fn add_impl<'a>( |
68 | file: &'a SourceFileNode, | ||
69 | offset: TextUnit, | ||
70 | ) -> Option<impl FnOnce() -> LocalEdit + 'a> { | ||
62 | let nominal = find_node_at_offset::<ast::NominalDef>(file.syntax(), offset)?; | 71 | let nominal = find_node_at_offset::<ast::NominalDef>(file.syntax(), offset)?; |
63 | let name = nominal.name()?; | 72 | let name = nominal.name()?; |
64 | 73 | ||
@@ -98,7 +107,7 @@ pub fn add_impl<'a>(file: &'a File, offset: TextUnit) -> Option<impl FnOnce() -> | |||
98 | } | 107 | } |
99 | 108 | ||
100 | pub fn introduce_variable<'a>( | 109 | pub fn introduce_variable<'a>( |
101 | file: &'a File, | 110 | file: &'a SourceFileNode, |
102 | range: TextRange, | 111 | range: TextRange, |
103 | ) -> Option<impl FnOnce() -> LocalEdit + 'a> { | 112 | ) -> Option<impl FnOnce() -> LocalEdit + 'a> { |
104 | let node = find_covering_node(file.syntax(), range); | 113 | let node = find_covering_node(file.syntax(), range); |
diff --git a/crates/ra_editor/src/extend_selection.rs b/crates/ra_editor/src/extend_selection.rs index 9d8df25c3..8f11d5364 100644 --- a/crates/ra_editor/src/extend_selection.rs +++ b/crates/ra_editor/src/extend_selection.rs | |||
@@ -1,11 +1,11 @@ | |||
1 | use ra_syntax::{ | 1 | use ra_syntax::{ |
2 | algo::{find_covering_node, find_leaf_at_offset, LeafAtOffset}, | 2 | algo::{find_covering_node, find_leaf_at_offset, LeafAtOffset}, |
3 | Direction, File, | 3 | Direction, SourceFileNode, |
4 | SyntaxKind::*, | 4 | SyntaxKind::*, |
5 | SyntaxNodeRef, TextRange, TextUnit, | 5 | SyntaxNodeRef, TextRange, TextUnit, |
6 | }; | 6 | }; |
7 | 7 | ||
8 | pub fn extend_selection(file: &File, range: TextRange) -> Option<TextRange> { | 8 | pub fn extend_selection(file: &SourceFileNode, range: TextRange) -> Option<TextRange> { |
9 | let syntax = file.syntax(); | 9 | let syntax = file.syntax(); |
10 | extend(syntax.borrowed(), range) | 10 | extend(syntax.borrowed(), range) |
11 | } | 11 | } |
@@ -120,7 +120,7 @@ mod tests { | |||
120 | 120 | ||
121 | fn do_check(before: &str, afters: &[&str]) { | 121 | fn do_check(before: &str, afters: &[&str]) { |
122 | let (cursor, before) = extract_offset(before); | 122 | let (cursor, before) = extract_offset(before); |
123 | let file = File::parse(&before); | 123 | let file = SourceFileNode::parse(&before); |
124 | let mut range = TextRange::offset_len(cursor, 0.into()); | 124 | let mut range = TextRange::offset_len(cursor, 0.into()); |
125 | for &after in afters { | 125 | for &after in afters { |
126 | range = extend_selection(&file, range).unwrap(); | 126 | range = extend_selection(&file, range).unwrap(); |
diff --git a/crates/ra_editor/src/folding_ranges.rs b/crates/ra_editor/src/folding_ranges.rs index 0803c8891..2a8fa3cda 100644 --- a/crates/ra_editor/src/folding_ranges.rs +++ b/crates/ra_editor/src/folding_ranges.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | use rustc_hash::FxHashSet; | 1 | use rustc_hash::FxHashSet; |
2 | 2 | ||
3 | use ra_syntax::{ | 3 | use ra_syntax::{ |
4 | ast, AstNode, Direction, File, | 4 | ast, AstNode, Direction, SourceFileNode, |
5 | SyntaxKind::{self, *}, | 5 | SyntaxKind::{self, *}, |
6 | SyntaxNodeRef, TextRange, | 6 | SyntaxNodeRef, TextRange, |
7 | }; | 7 | }; |
@@ -18,7 +18,7 @@ pub struct Fold { | |||
18 | pub kind: FoldKind, | 18 | pub kind: FoldKind, |
19 | } | 19 | } |
20 | 20 | ||
21 | pub fn folding_ranges(file: &File) -> Vec<Fold> { | 21 | pub fn folding_ranges(file: &SourceFileNode) -> Vec<Fold> { |
22 | let mut res = vec![]; | 22 | let mut res = vec![]; |
23 | let mut visited_comments = FxHashSet::default(); | 23 | let mut visited_comments = FxHashSet::default(); |
24 | let mut visited_imports = FxHashSet::default(); | 24 | let mut visited_imports = FxHashSet::default(); |
@@ -171,7 +171,7 @@ mod tests { | |||
171 | 171 | ||
172 | fn do_check(text: &str, fold_kinds: &[FoldKind]) { | 172 | fn do_check(text: &str, fold_kinds: &[FoldKind]) { |
173 | let (ranges, text) = extract_ranges(text); | 173 | let (ranges, text) = extract_ranges(text); |
174 | let file = File::parse(&text); | 174 | let file = SourceFileNode::parse(&text); |
175 | let folds = folding_ranges(&file); | 175 | let folds = folding_ranges(&file); |
176 | 176 | ||
177 | assert_eq!( | 177 | assert_eq!( |
diff --git a/crates/ra_editor/src/lib.rs b/crates/ra_editor/src/lib.rs index f92181b86..ff4e8303d 100644 --- a/crates/ra_editor/src/lib.rs +++ b/crates/ra_editor/src/lib.rs | |||
@@ -30,7 +30,7 @@ pub use ra_syntax::AtomEdit; | |||
30 | use ra_syntax::{ | 30 | use ra_syntax::{ |
31 | algo::find_leaf_at_offset, | 31 | algo::find_leaf_at_offset, |
32 | ast::{self, AstNode, NameOwner}, | 32 | ast::{self, AstNode, NameOwner}, |
33 | File, | 33 | SourceFileNode, |
34 | Location, | 34 | Location, |
35 | SyntaxKind::{self, *}, | 35 | SyntaxKind::{self, *}, |
36 | SyntaxNodeRef, TextRange, TextUnit, | 36 | SyntaxNodeRef, TextRange, TextUnit, |
@@ -60,7 +60,7 @@ pub enum RunnableKind { | |||
60 | Bin, | 60 | Bin, |
61 | } | 61 | } |
62 | 62 | ||
63 | pub fn matching_brace(file: &File, offset: TextUnit) -> Option<TextUnit> { | 63 | pub fn matching_brace(file: &SourceFileNode, offset: TextUnit) -> Option<TextUnit> { |
64 | const BRACES: &[SyntaxKind] = &[ | 64 | const BRACES: &[SyntaxKind] = &[ |
65 | L_CURLY, R_CURLY, L_BRACK, R_BRACK, L_PAREN, R_PAREN, L_ANGLE, R_ANGLE, | 65 | L_CURLY, R_CURLY, L_BRACK, R_BRACK, L_PAREN, R_PAREN, L_ANGLE, R_ANGLE, |
66 | ]; | 66 | ]; |
@@ -78,7 +78,7 @@ pub fn matching_brace(file: &File, offset: TextUnit) -> Option<TextUnit> { | |||
78 | Some(matching_node.range().start()) | 78 | Some(matching_node.range().start()) |
79 | } | 79 | } |
80 | 80 | ||
81 | pub fn highlight(file: &File) -> Vec<HighlightedRange> { | 81 | pub fn highlight(file: &SourceFileNode) -> Vec<HighlightedRange> { |
82 | let mut res = Vec::new(); | 82 | let mut res = Vec::new(); |
83 | for node in file.syntax().descendants() { | 83 | for node in file.syntax().descendants() { |
84 | let tag = match node.kind() { | 84 | let tag = match node.kind() { |
@@ -100,7 +100,7 @@ pub fn highlight(file: &File) -> Vec<HighlightedRange> { | |||
100 | res | 100 | res |
101 | } | 101 | } |
102 | 102 | ||
103 | pub fn diagnostics(file: &File) -> Vec<Diagnostic> { | 103 | pub fn diagnostics(file: &SourceFileNode) -> Vec<Diagnostic> { |
104 | fn location_to_range(location: Location) -> TextRange { | 104 | fn location_to_range(location: Location) -> TextRange { |
105 | match location { | 105 | match location { |
106 | Location::Offset(offset) => TextRange::offset_len(offset, 1.into()), | 106 | Location::Offset(offset) => TextRange::offset_len(offset, 1.into()), |
@@ -117,11 +117,11 @@ pub fn diagnostics(file: &File) -> Vec<Diagnostic> { | |||
117 | .collect() | 117 | .collect() |
118 | } | 118 | } |
119 | 119 | ||
120 | pub fn syntax_tree(file: &File) -> String { | 120 | pub fn syntax_tree(file: &SourceFileNode) -> String { |
121 | ::ra_syntax::utils::dump_tree(file.syntax()) | 121 | ::ra_syntax::utils::dump_tree(file.syntax()) |
122 | } | 122 | } |
123 | 123 | ||
124 | pub fn runnables(file: &File) -> Vec<Runnable> { | 124 | pub fn runnables(file: &SourceFileNode) -> Vec<Runnable> { |
125 | file.syntax() | 125 | file.syntax() |
126 | .descendants() | 126 | .descendants() |
127 | .filter_map(ast::FnDef::cast) | 127 | .filter_map(ast::FnDef::cast) |
@@ -163,7 +163,7 @@ mod tests { | |||
163 | 163 | ||
164 | #[test] | 164 | #[test] |
165 | fn test_highlighting() { | 165 | fn test_highlighting() { |
166 | let file = File::parse( | 166 | let file = SourceFileNode::parse( |
167 | r#" | 167 | r#" |
168 | // comment | 168 | // comment |
169 | fn main() {} | 169 | fn main() {} |
@@ -184,7 +184,7 @@ fn main() {} | |||
184 | 184 | ||
185 | #[test] | 185 | #[test] |
186 | fn test_runnables() { | 186 | fn test_runnables() { |
187 | let file = File::parse( | 187 | let file = SourceFileNode::parse( |
188 | r#" | 188 | r#" |
189 | fn main() {} | 189 | fn main() {} |
190 | 190 | ||
@@ -209,7 +209,7 @@ fn test_foo() {} | |||
209 | fn test_matching_brace() { | 209 | fn test_matching_brace() { |
210 | fn do_check(before: &str, after: &str) { | 210 | fn do_check(before: &str, after: &str) { |
211 | let (pos, before) = extract_offset(before); | 211 | let (pos, before) = extract_offset(before); |
212 | let file = File::parse(&before); | 212 | let file = SourceFileNode::parse(&before); |
213 | let new_pos = match matching_brace(&file, pos) { | 213 | let new_pos = match matching_brace(&file, pos) { |
214 | None => pos, | 214 | None => pos, |
215 | Some(pos) => pos, | 215 | Some(pos) => pos, |
diff --git a/crates/ra_editor/src/line_index.rs b/crates/ra_editor/src/line_index.rs index 9abbb0d09..aab7e4081 100644 --- a/crates/ra_editor/src/line_index.rs +++ b/crates/ra_editor/src/line_index.rs | |||
@@ -1,43 +1,124 @@ | |||
1 | use crate::TextUnit; | 1 | use crate::TextUnit; |
2 | use rustc_hash::FxHashMap; | ||
2 | use superslice::Ext; | 3 | use superslice::Ext; |
3 | 4 | ||
4 | #[derive(Clone, Debug, Hash, PartialEq, Eq)] | 5 | #[derive(Clone, Debug, PartialEq, Eq)] |
5 | pub struct LineIndex { | 6 | pub struct LineIndex { |
6 | newlines: Vec<TextUnit>, | 7 | newlines: Vec<TextUnit>, |
8 | utf16_lines: FxHashMap<u32, Vec<Utf16Char>>, | ||
7 | } | 9 | } |
8 | 10 | ||
9 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | 11 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] |
10 | pub struct LineCol { | 12 | pub struct LineCol { |
11 | pub line: u32, | 13 | pub line: u32, |
12 | pub col: TextUnit, | 14 | pub col_utf16: u32, |
15 | } | ||
16 | |||
17 | #[derive(Clone, Debug, Hash, PartialEq, Eq)] | ||
18 | struct Utf16Char { | ||
19 | start: TextUnit, | ||
20 | end: TextUnit, | ||
21 | } | ||
22 | |||
23 | impl Utf16Char { | ||
24 | fn len(&self) -> TextUnit { | ||
25 | self.end - self.start | ||
26 | } | ||
13 | } | 27 | } |
14 | 28 | ||
15 | impl LineIndex { | 29 | impl LineIndex { |
16 | pub fn new(text: &str) -> LineIndex { | 30 | pub fn new(text: &str) -> LineIndex { |
31 | let mut utf16_lines = FxHashMap::default(); | ||
32 | let mut utf16_chars = Vec::new(); | ||
33 | |||
17 | let mut newlines = vec![0.into()]; | 34 | let mut newlines = vec![0.into()]; |
18 | let mut curr = 0.into(); | 35 | let mut curr_row = 0.into(); |
36 | let mut curr_col = 0.into(); | ||
37 | let mut line = 0; | ||
19 | for c in text.chars() { | 38 | for c in text.chars() { |
20 | curr += TextUnit::of_char(c); | 39 | curr_row += TextUnit::of_char(c); |
21 | if c == '\n' { | 40 | if c == '\n' { |
22 | newlines.push(curr); | 41 | newlines.push(curr_row); |
42 | |||
43 | // Save any utf-16 characters seen in the previous line | ||
44 | if utf16_chars.len() > 0 { | ||
45 | utf16_lines.insert(line, utf16_chars); | ||
46 | utf16_chars = Vec::new(); | ||
47 | } | ||
48 | |||
49 | // Prepare for processing the next line | ||
50 | curr_col = 0.into(); | ||
51 | line += 1; | ||
52 | continue; | ||
23 | } | 53 | } |
54 | |||
55 | let char_len = TextUnit::of_char(c); | ||
56 | if char_len.to_usize() > 1 { | ||
57 | utf16_chars.push(Utf16Char { | ||
58 | start: curr_col, | ||
59 | end: curr_col + char_len, | ||
60 | }); | ||
61 | } | ||
62 | |||
63 | curr_col += char_len; | ||
64 | } | ||
65 | LineIndex { | ||
66 | newlines, | ||
67 | utf16_lines, | ||
24 | } | 68 | } |
25 | LineIndex { newlines } | ||
26 | } | 69 | } |
27 | 70 | ||
28 | pub fn line_col(&self, offset: TextUnit) -> LineCol { | 71 | pub fn line_col(&self, offset: TextUnit) -> LineCol { |
29 | let line = self.newlines.upper_bound(&offset) - 1; | 72 | let line = self.newlines.upper_bound(&offset) - 1; |
30 | let line_start_offset = self.newlines[line]; | 73 | let line_start_offset = self.newlines[line]; |
31 | let col = offset - line_start_offset; | 74 | let col = offset - line_start_offset; |
75 | |||
32 | LineCol { | 76 | LineCol { |
33 | line: line as u32, | 77 | line: line as u32, |
34 | col, | 78 | col_utf16: self.utf8_to_utf16_col(line as u32, col) as u32, |
35 | } | 79 | } |
36 | } | 80 | } |
37 | 81 | ||
38 | pub fn offset(&self, line_col: LineCol) -> TextUnit { | 82 | pub fn offset(&self, line_col: LineCol) -> TextUnit { |
39 | //TODO: return Result | 83 | //TODO: return Result |
40 | self.newlines[line_col.line as usize] + line_col.col | 84 | let col = self.utf16_to_utf8_col(line_col.line, line_col.col_utf16); |
85 | self.newlines[line_col.line as usize] + col | ||
86 | } | ||
87 | |||
88 | fn utf8_to_utf16_col(&self, line: u32, mut col: TextUnit) -> usize { | ||
89 | if let Some(utf16_chars) = self.utf16_lines.get(&line) { | ||
90 | let mut correction = TextUnit::from_usize(0); | ||
91 | for c in utf16_chars { | ||
92 | if col >= c.end { | ||
93 | correction += c.len() - TextUnit::from_usize(1); | ||
94 | } else { | ||
95 | // From here on, all utf16 characters come *after* the character we are mapping, | ||
96 | // so we don't need to take them into account | ||
97 | break; | ||
98 | } | ||
99 | } | ||
100 | |||
101 | col -= correction; | ||
102 | } | ||
103 | |||
104 | col.to_usize() | ||
105 | } | ||
106 | |||
107 | fn utf16_to_utf8_col(&self, line: u32, col: u32) -> TextUnit { | ||
108 | let mut col: TextUnit = col.into(); | ||
109 | if let Some(utf16_chars) = self.utf16_lines.get(&line) { | ||
110 | for c in utf16_chars { | ||
111 | if col >= c.start { | ||
112 | col += c.len() - TextUnit::from_usize(1); | ||
113 | } else { | ||
114 | // From here on, all utf16 characters come *after* the character we are mapping, | ||
115 | // so we don't need to take them into account | ||
116 | break; | ||
117 | } | ||
118 | } | ||
119 | } | ||
120 | |||
121 | col | ||
41 | } | 122 | } |
42 | } | 123 | } |
43 | 124 | ||
@@ -49,63 +130,63 @@ fn test_line_index() { | |||
49 | index.line_col(0.into()), | 130 | index.line_col(0.into()), |
50 | LineCol { | 131 | LineCol { |
51 | line: 0, | 132 | line: 0, |
52 | col: 0.into() | 133 | col_utf16: 0 |
53 | } | 134 | } |
54 | ); | 135 | ); |
55 | assert_eq!( | 136 | assert_eq!( |
56 | index.line_col(1.into()), | 137 | index.line_col(1.into()), |
57 | LineCol { | 138 | LineCol { |
58 | line: 0, | 139 | line: 0, |
59 | col: 1.into() | 140 | col_utf16: 1 |
60 | } | 141 | } |
61 | ); | 142 | ); |
62 | assert_eq!( | 143 | assert_eq!( |
63 | index.line_col(5.into()), | 144 | index.line_col(5.into()), |
64 | LineCol { | 145 | LineCol { |
65 | line: 0, | 146 | line: 0, |
66 | col: 5.into() | 147 | col_utf16: 5 |
67 | } | 148 | } |
68 | ); | 149 | ); |
69 | assert_eq!( | 150 | assert_eq!( |
70 | index.line_col(6.into()), | 151 | index.line_col(6.into()), |
71 | LineCol { | 152 | LineCol { |
72 | line: 1, | 153 | line: 1, |
73 | col: 0.into() | 154 | col_utf16: 0 |
74 | } | 155 | } |
75 | ); | 156 | ); |
76 | assert_eq!( | 157 | assert_eq!( |
77 | index.line_col(7.into()), | 158 | index.line_col(7.into()), |
78 | LineCol { | 159 | LineCol { |
79 | line: 1, | 160 | line: 1, |
80 | col: 1.into() | 161 | col_utf16: 1 |
81 | } | 162 | } |
82 | ); | 163 | ); |
83 | assert_eq!( | 164 | assert_eq!( |
84 | index.line_col(8.into()), | 165 | index.line_col(8.into()), |
85 | LineCol { | 166 | LineCol { |
86 | line: 1, | 167 | line: 1, |
87 | col: 2.into() | 168 | col_utf16: 2 |
88 | } | 169 | } |
89 | ); | 170 | ); |
90 | assert_eq!( | 171 | assert_eq!( |
91 | index.line_col(10.into()), | 172 | index.line_col(10.into()), |
92 | LineCol { | 173 | LineCol { |
93 | line: 1, | 174 | line: 1, |
94 | col: 4.into() | 175 | col_utf16: 4 |
95 | } | 176 | } |
96 | ); | 177 | ); |
97 | assert_eq!( | 178 | assert_eq!( |
98 | index.line_col(11.into()), | 179 | index.line_col(11.into()), |
99 | LineCol { | 180 | LineCol { |
100 | line: 1, | 181 | line: 1, |
101 | col: 5.into() | 182 | col_utf16: 5 |
102 | } | 183 | } |
103 | ); | 184 | ); |
104 | assert_eq!( | 185 | assert_eq!( |
105 | index.line_col(12.into()), | 186 | index.line_col(12.into()), |
106 | LineCol { | 187 | LineCol { |
107 | line: 1, | 188 | line: 1, |
108 | col: 6.into() | 189 | col_utf16: 6 |
109 | } | 190 | } |
110 | ); | 191 | ); |
111 | 192 | ||
@@ -115,35 +196,129 @@ fn test_line_index() { | |||
115 | index.line_col(0.into()), | 196 | index.line_col(0.into()), |
116 | LineCol { | 197 | LineCol { |
117 | line: 0, | 198 | line: 0, |
118 | col: 0.into() | 199 | col_utf16: 0 |
119 | } | 200 | } |
120 | ); | 201 | ); |
121 | assert_eq!( | 202 | assert_eq!( |
122 | index.line_col(1.into()), | 203 | index.line_col(1.into()), |
123 | LineCol { | 204 | LineCol { |
124 | line: 1, | 205 | line: 1, |
125 | col: 0.into() | 206 | col_utf16: 0 |
126 | } | 207 | } |
127 | ); | 208 | ); |
128 | assert_eq!( | 209 | assert_eq!( |
129 | index.line_col(2.into()), | 210 | index.line_col(2.into()), |
130 | LineCol { | 211 | LineCol { |
131 | line: 1, | 212 | line: 1, |
132 | col: 1.into() | 213 | col_utf16: 1 |
133 | } | 214 | } |
134 | ); | 215 | ); |
135 | assert_eq!( | 216 | assert_eq!( |
136 | index.line_col(6.into()), | 217 | index.line_col(6.into()), |
137 | LineCol { | 218 | LineCol { |
138 | line: 1, | 219 | line: 1, |
139 | col: 5.into() | 220 | col_utf16: 5 |
140 | } | 221 | } |
141 | ); | 222 | ); |
142 | assert_eq!( | 223 | assert_eq!( |
143 | index.line_col(7.into()), | 224 | index.line_col(7.into()), |
144 | LineCol { | 225 | LineCol { |
145 | line: 2, | 226 | line: 2, |
146 | col: 0.into() | 227 | col_utf16: 0 |
147 | } | 228 | } |
148 | ); | 229 | ); |
149 | } | 230 | } |
231 | |||
232 | #[cfg(test)] | ||
233 | mod test_utf8_utf16_conv { | ||
234 | use super::*; | ||
235 | |||
236 | #[test] | ||
237 | fn test_char_len() { | ||
238 | assert_eq!('メ'.len_utf8(), 3); | ||
239 | assert_eq!('メ'.len_utf16(), 1); | ||
240 | } | ||
241 | |||
242 | #[test] | ||
243 | fn test_empty_index() { | ||
244 | let col_index = LineIndex::new( | ||
245 | " | ||
246 | const C: char = 'x'; | ||
247 | ", | ||
248 | ); | ||
249 | assert_eq!(col_index.utf16_lines.len(), 0); | ||
250 | } | ||
251 | |||
252 | #[test] | ||
253 | fn test_single_char() { | ||
254 | let col_index = LineIndex::new( | ||
255 | " | ||
256 | const C: char = 'メ'; | ||
257 | ", | ||
258 | ); | ||
259 | |||
260 | assert_eq!(col_index.utf16_lines.len(), 1); | ||
261 | assert_eq!(col_index.utf16_lines[&1].len(), 1); | ||
262 | assert_eq!( | ||
263 | col_index.utf16_lines[&1][0], | ||
264 | Utf16Char { | ||
265 | start: 17.into(), | ||
266 | end: 20.into() | ||
267 | } | ||
268 | ); | ||
269 | |||
270 | // UTF-8 to UTF-16, no changes | ||
271 | assert_eq!(col_index.utf8_to_utf16_col(1, 15.into()), 15); | ||
272 | |||
273 | // UTF-8 to UTF-16 | ||
274 | assert_eq!(col_index.utf8_to_utf16_col(1, 22.into()), 20); | ||
275 | |||
276 | // UTF-16 to UTF-8, no changes | ||
277 | assert_eq!(col_index.utf16_to_utf8_col(1, 15), TextUnit::from(15)); | ||
278 | |||
279 | // UTF-16 to UTF-8 | ||
280 | assert_eq!(col_index.utf16_to_utf8_col(1, 19), TextUnit::from(21)); | ||
281 | } | ||
282 | |||
283 | #[test] | ||
284 | fn test_string() { | ||
285 | let col_index = LineIndex::new( | ||
286 | " | ||
287 | const C: char = \"メ メ\"; | ||
288 | ", | ||
289 | ); | ||
290 | |||
291 | assert_eq!(col_index.utf16_lines.len(), 1); | ||
292 | assert_eq!(col_index.utf16_lines[&1].len(), 2); | ||
293 | assert_eq!( | ||
294 | col_index.utf16_lines[&1][0], | ||
295 | Utf16Char { | ||
296 | start: 17.into(), | ||
297 | end: 20.into() | ||
298 | } | ||
299 | ); | ||
300 | assert_eq!( | ||
301 | col_index.utf16_lines[&1][1], | ||
302 | Utf16Char { | ||
303 | start: 21.into(), | ||
304 | end: 24.into() | ||
305 | } | ||
306 | ); | ||
307 | |||
308 | // UTF-8 to UTF-16 | ||
309 | assert_eq!(col_index.utf8_to_utf16_col(1, 15.into()), 15); | ||
310 | |||
311 | assert_eq!(col_index.utf8_to_utf16_col(1, 21.into()), 19); | ||
312 | assert_eq!(col_index.utf8_to_utf16_col(1, 25.into()), 21); | ||
313 | |||
314 | assert!(col_index.utf8_to_utf16_col(2, 15.into()) == 15); | ||
315 | |||
316 | // UTF-16 to UTF-8 | ||
317 | assert_eq!(col_index.utf16_to_utf8_col(1, 15), TextUnit::from_usize(15)); | ||
318 | |||
319 | assert_eq!(col_index.utf16_to_utf8_col(1, 18), TextUnit::from_usize(20)); | ||
320 | assert_eq!(col_index.utf16_to_utf8_col(1, 19), TextUnit::from_usize(23)); | ||
321 | |||
322 | assert_eq!(col_index.utf16_to_utf8_col(2, 15), TextUnit::from_usize(15)); | ||
323 | } | ||
324 | } | ||
diff --git a/crates/ra_editor/src/symbols.rs b/crates/ra_editor/src/symbols.rs index 4e602d0e3..6d3b0514a 100644 --- a/crates/ra_editor/src/symbols.rs +++ b/crates/ra_editor/src/symbols.rs | |||
@@ -2,8 +2,8 @@ use crate::TextRange; | |||
2 | 2 | ||
3 | use ra_syntax::{ | 3 | use ra_syntax::{ |
4 | algo::visit::{visitor, Visitor}, | 4 | algo::visit::{visitor, Visitor}, |
5 | ast::{self, NameOwner}, | 5 | ast::{self, DocCommentsOwner, NameOwner}, |
6 | AstNode, File, SmolStr, SyntaxKind, SyntaxNodeRef, WalkEvent, | 6 | AstNode, SourceFileNode, SmolStr, SyntaxKind, SyntaxNodeRef, WalkEvent, |
7 | }; | 7 | }; |
8 | 8 | ||
9 | #[derive(Debug, Clone)] | 9 | #[derive(Debug, Clone)] |
@@ -22,7 +22,37 @@ pub struct FileSymbol { | |||
22 | pub kind: SyntaxKind, | 22 | pub kind: SyntaxKind, |
23 | } | 23 | } |
24 | 24 | ||
25 | pub fn file_symbols(file: &File) -> Vec<FileSymbol> { | 25 | impl FileSymbol { |
26 | pub fn docs(&self, file: &SourceFileNode) -> Option<String> { | ||
27 | file.syntax() | ||
28 | .descendants() | ||
29 | .filter(|node| node.kind() == self.kind && node.range() == self.node_range) | ||
30 | .filter_map(|node: SyntaxNodeRef| { | ||
31 | fn doc_comments<'a, N: DocCommentsOwner<'a>>(node: N) -> Option<String> { | ||
32 | let comments = node.doc_comment_text(); | ||
33 | if comments.is_empty() { | ||
34 | None | ||
35 | } else { | ||
36 | Some(comments) | ||
37 | } | ||
38 | } | ||
39 | |||
40 | visitor() | ||
41 | .visit(doc_comments::<ast::FnDef>) | ||
42 | .visit(doc_comments::<ast::StructDef>) | ||
43 | .visit(doc_comments::<ast::EnumDef>) | ||
44 | .visit(doc_comments::<ast::TraitDef>) | ||
45 | .visit(doc_comments::<ast::Module>) | ||
46 | .visit(doc_comments::<ast::TypeDef>) | ||
47 | .visit(doc_comments::<ast::ConstDef>) | ||
48 | .visit(doc_comments::<ast::StaticDef>) | ||
49 | .accept(node)? | ||
50 | }) | ||
51 | .nth(0) | ||
52 | } | ||
53 | } | ||
54 | |||
55 | pub fn file_symbols(file: &SourceFileNode) -> Vec<FileSymbol> { | ||
26 | file.syntax().descendants().filter_map(to_symbol).collect() | 56 | file.syntax().descendants().filter_map(to_symbol).collect() |
27 | } | 57 | } |
28 | 58 | ||
@@ -47,7 +77,7 @@ fn to_symbol(node: SyntaxNodeRef) -> Option<FileSymbol> { | |||
47 | .accept(node)? | 77 | .accept(node)? |
48 | } | 78 | } |
49 | 79 | ||
50 | pub fn file_structure(file: &File) -> Vec<StructureNode> { | 80 | pub fn file_structure(file: &SourceFileNode) -> Vec<StructureNode> { |
51 | let mut res = Vec::new(); | 81 | let mut res = Vec::new(); |
52 | let mut stack = Vec::new(); | 82 | let mut stack = Vec::new(); |
53 | 83 | ||
@@ -123,7 +153,7 @@ mod tests { | |||
123 | 153 | ||
124 | #[test] | 154 | #[test] |
125 | fn test_file_structure() { | 155 | fn test_file_structure() { |
126 | let file = File::parse( | 156 | let file = SourceFileNode::parse( |
127 | r#" | 157 | r#" |
128 | struct Foo { | 158 | struct Foo { |
129 | x: i32 | 159 | x: i32 |
diff --git a/crates/ra_editor/src/test_utils.rs b/crates/ra_editor/src/test_utils.rs index bc3d700f6..cbeb6433b 100644 --- a/crates/ra_editor/src/test_utils.rs +++ b/crates/ra_editor/src/test_utils.rs | |||
@@ -1,10 +1,14 @@ | |||
1 | use crate::LocalEdit; | 1 | use crate::LocalEdit; |
2 | pub use crate::_test_utils::*; | 2 | pub use crate::_test_utils::*; |
3 | use ra_syntax::{File, TextRange, TextUnit}; | 3 | use ra_syntax::{SourceFileNode, TextRange, TextUnit}; |
4 | 4 | ||
5 | pub fn check_action<F: Fn(&File, TextUnit) -> Option<LocalEdit>>(before: &str, after: &str, f: F) { | 5 | pub fn check_action<F: Fn(&SourceFileNode, TextUnit) -> Option<LocalEdit>>( |
6 | before: &str, | ||
7 | after: &str, | ||
8 | f: F, | ||
9 | ) { | ||
6 | let (before_cursor_pos, before) = extract_offset(before); | 10 | let (before_cursor_pos, before) = extract_offset(before); |
7 | let file = File::parse(&before); | 11 | let file = SourceFileNode::parse(&before); |
8 | let result = f(&file, before_cursor_pos).expect("code action is not applicable"); | 12 | let result = f(&file, before_cursor_pos).expect("code action is not applicable"); |
9 | let actual = result.edit.apply(&before); | 13 | let actual = result.edit.apply(&before); |
10 | let actual_cursor_pos = match result.cursor_position { | 14 | let actual_cursor_pos = match result.cursor_position { |
@@ -15,13 +19,13 @@ pub fn check_action<F: Fn(&File, TextUnit) -> Option<LocalEdit>>(before: &str, a | |||
15 | assert_eq_text!(after, &actual); | 19 | assert_eq_text!(after, &actual); |
16 | } | 20 | } |
17 | 21 | ||
18 | pub fn check_action_range<F: Fn(&File, TextRange) -> Option<LocalEdit>>( | 22 | pub fn check_action_range<F: Fn(&SourceFileNode, TextRange) -> Option<LocalEdit>>( |
19 | before: &str, | 23 | before: &str, |
20 | after: &str, | 24 | after: &str, |
21 | f: F, | 25 | f: F, |
22 | ) { | 26 | ) { |
23 | let (range, before) = extract_range(before); | 27 | let (range, before) = extract_range(before); |
24 | let file = File::parse(&before); | 28 | let file = SourceFileNode::parse(&before); |
25 | let result = f(&file, range).expect("code action is not applicable"); | 29 | let result = f(&file, range).expect("code action is not applicable"); |
26 | let actual = result.edit.apply(&before); | 30 | let actual = result.edit.apply(&before); |
27 | let actual_cursor_pos = match result.cursor_position { | 31 | let actual_cursor_pos = match result.cursor_position { |
diff --git a/crates/ra_editor/src/typing.rs b/crates/ra_editor/src/typing.rs index 5a457d148..f894d8392 100644 --- a/crates/ra_editor/src/typing.rs +++ b/crates/ra_editor/src/typing.rs | |||
@@ -4,14 +4,14 @@ use ra_syntax::{ | |||
4 | algo::{find_covering_node, find_leaf_at_offset, LeafAtOffset}, | 4 | algo::{find_covering_node, find_leaf_at_offset, LeafAtOffset}, |
5 | ast, | 5 | ast, |
6 | text_utils::{contains_offset_nonstrict, intersect}, | 6 | text_utils::{contains_offset_nonstrict, intersect}, |
7 | AstNode, File, SyntaxKind, | 7 | AstNode, SourceFileNode, SyntaxKind, |
8 | SyntaxKind::*, | 8 | SyntaxKind::*, |
9 | SyntaxNodeRef, TextRange, TextUnit, | 9 | SyntaxNodeRef, TextRange, TextUnit, |
10 | }; | 10 | }; |
11 | 11 | ||
12 | use crate::{find_node_at_offset, EditBuilder, LocalEdit}; | 12 | use crate::{find_node_at_offset, EditBuilder, LocalEdit}; |
13 | 13 | ||
14 | pub fn join_lines(file: &File, range: TextRange) -> LocalEdit { | 14 | pub fn join_lines(file: &SourceFileNode, range: TextRange) -> LocalEdit { |
15 | let range = if range.is_empty() { | 15 | let range = if range.is_empty() { |
16 | let syntax = file.syntax(); | 16 | let syntax = file.syntax(); |
17 | let text = syntax.text().slice(range.start()..); | 17 | let text = syntax.text().slice(range.start()..); |
@@ -55,7 +55,7 @@ pub fn join_lines(file: &File, range: TextRange) -> LocalEdit { | |||
55 | } | 55 | } |
56 | } | 56 | } |
57 | 57 | ||
58 | pub fn on_enter(file: &File, offset: TextUnit) -> Option<LocalEdit> { | 58 | pub fn on_enter(file: &SourceFileNode, offset: TextUnit) -> Option<LocalEdit> { |
59 | let comment = find_leaf_at_offset(file.syntax(), offset) | 59 | let comment = find_leaf_at_offset(file.syntax(), offset) |
60 | .left_biased() | 60 | .left_biased() |
61 | .and_then(ast::Comment::cast)?; | 61 | .and_then(ast::Comment::cast)?; |
@@ -80,7 +80,7 @@ pub fn on_enter(file: &File, offset: TextUnit) -> Option<LocalEdit> { | |||
80 | }) | 80 | }) |
81 | } | 81 | } |
82 | 82 | ||
83 | fn node_indent<'a>(file: &'a File, node: SyntaxNodeRef) -> Option<&'a str> { | 83 | fn node_indent<'a>(file: &'a SourceFileNode, node: SyntaxNodeRef) -> Option<&'a str> { |
84 | 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()) { |
85 | LeafAtOffset::Between(l, r) => { | 85 | LeafAtOffset::Between(l, r) => { |
86 | assert!(r == node); | 86 | assert!(r == node); |
@@ -100,7 +100,7 @@ fn node_indent<'a>(file: &'a File, node: SyntaxNodeRef) -> Option<&'a str> { | |||
100 | Some(&text[pos..]) | 100 | Some(&text[pos..]) |
101 | } | 101 | } |
102 | 102 | ||
103 | pub fn on_eq_typed(file: &File, offset: TextUnit) -> Option<LocalEdit> { | 103 | pub fn on_eq_typed(file: &SourceFileNode, offset: TextUnit) -> Option<LocalEdit> { |
104 | let let_stmt: ast::LetStmt = find_node_at_offset(file.syntax(), offset)?; | 104 | let let_stmt: ast::LetStmt = find_node_at_offset(file.syntax(), offset)?; |
105 | if let_stmt.has_semi() { | 105 | if let_stmt.has_semi() { |
106 | return None; | 106 | return None; |
@@ -390,7 +390,7 @@ fn foo() { | |||
390 | 390 | ||
391 | fn check_join_lines_sel(before: &str, after: &str) { | 391 | fn check_join_lines_sel(before: &str, after: &str) { |
392 | let (sel, before) = extract_range(before); | 392 | let (sel, before) = extract_range(before); |
393 | let file = File::parse(&before); | 393 | let file = SourceFileNode::parse(&before); |
394 | let result = join_lines(&file, sel); | 394 | let result = join_lines(&file, sel); |
395 | let actual = result.edit.apply(&before); | 395 | let actual = result.edit.apply(&before); |
396 | assert_eq_text!(after, &actual); | 396 | assert_eq_text!(after, &actual); |
@@ -469,7 +469,7 @@ pub fn handle_find_matching_brace() { | |||
469 | fn test_on_eq_typed() { | 469 | fn test_on_eq_typed() { |
470 | fn do_check(before: &str, after: &str) { | 470 | fn do_check(before: &str, after: &str) { |
471 | let (offset, before) = extract_offset(before); | 471 | let (offset, before) = extract_offset(before); |
472 | let file = File::parse(&before); | 472 | let file = SourceFileNode::parse(&before); |
473 | let result = on_eq_typed(&file, offset).unwrap(); | 473 | let result = on_eq_typed(&file, offset).unwrap(); |
474 | let actual = result.edit.apply(&before); | 474 | let actual = result.edit.apply(&before); |
475 | assert_eq_text!(after, &actual); | 475 | assert_eq_text!(after, &actual); |
@@ -513,7 +513,7 @@ fn foo() { | |||
513 | fn test_on_enter() { | 513 | fn test_on_enter() { |
514 | fn apply_on_enter(before: &str) -> Option<String> { | 514 | fn apply_on_enter(before: &str) -> Option<String> { |
515 | let (offset, before) = extract_offset(before); | 515 | let (offset, before) = extract_offset(before); |
516 | let file = File::parse(&before); | 516 | let file = SourceFileNode::parse(&before); |
517 | let result = on_enter(&file, offset)?; | 517 | let result = on_enter(&file, offset)?; |
518 | let actual = result.edit.apply(&before); | 518 | let actual = result.edit.apply(&before); |
519 | let actual = add_cursor(&actual, result.cursor_position.unwrap()); | 519 | let actual = add_cursor(&actual, result.cursor_position.unwrap()); |