diff options
17 files changed, 773 insertions, 196 deletions
diff --git a/crates/ra_batch/src/lib.rs b/crates/ra_batch/src/lib.rs index 69d66113e..89a234f4c 100644 --- a/crates/ra_batch/src/lib.rs +++ b/crates/ra_batch/src/lib.rs | |||
@@ -75,7 +75,7 @@ impl BatchDatabase { | |||
75 | let source_root = SourceRoot { files: file_map }; | 75 | let source_root = SourceRoot { files: file_map }; |
76 | db.set_source_root(source_root_id, Arc::new(source_root)); | 76 | db.set_source_root(source_root_id, Arc::new(source_root)); |
77 | roots_loaded.insert(source_root_id); | 77 | roots_loaded.insert(source_root_id); |
78 | if roots_loaded.len() == vfs.num_roots() { | 78 | if roots_loaded.len() == vfs.n_roots() { |
79 | done = true; | 79 | done = true; |
80 | } | 80 | } |
81 | } | 81 | } |
@@ -137,14 +137,14 @@ mod tests { | |||
137 | path = path.parent().unwrap().to_owned(); | 137 | path = path.parent().unwrap().to_owned(); |
138 | } | 138 | } |
139 | let (db, roots) = BatchDatabase::load_cargo(path).unwrap(); | 139 | let (db, roots) = BatchDatabase::load_cargo(path).unwrap(); |
140 | let mut num_crates = 0; | 140 | let mut n_crates = 0; |
141 | for root in roots { | 141 | for root in roots { |
142 | for _krate in Crate::source_root_crates(&db, root) { | 142 | for _krate in Crate::source_root_crates(&db, root) { |
143 | num_crates += 1; | 143 | n_crates += 1; |
144 | } | 144 | } |
145 | } | 145 | } |
146 | 146 | ||
147 | // RA has quite a few crates, but the exact count doesn't matter | 147 | // RA has quite a few crates, but the exact count doesn't matter |
148 | assert!(num_crates > 20); | 148 | assert!(n_crates > 20); |
149 | } | 149 | } |
150 | } | 150 | } |
diff --git a/crates/ra_syntax/src/ast.rs b/crates/ra_syntax/src/ast.rs index 22105d6c9..350f01f33 100644 --- a/crates/ra_syntax/src/ast.rs +++ b/crates/ra_syntax/src/ast.rs | |||
@@ -153,6 +153,20 @@ impl FnDef { | |||
153 | } | 153 | } |
154 | 154 | ||
155 | impl Attr { | 155 | impl Attr { |
156 | pub fn is_inner(&self) -> bool { | ||
157 | let tt = match self.value() { | ||
158 | None => return false, | ||
159 | Some(tt) => tt, | ||
160 | }; | ||
161 | |||
162 | let prev = match tt.syntax().prev_sibling() { | ||
163 | None => return false, | ||
164 | Some(prev) => prev, | ||
165 | }; | ||
166 | |||
167 | prev.kind() == EXCL | ||
168 | } | ||
169 | |||
156 | pub fn as_atom(&self) -> Option<SmolStr> { | 170 | pub fn as_atom(&self) -> Option<SmolStr> { |
157 | let tt = self.value()?; | 171 | let tt = self.value()?; |
158 | let (_bra, attr, _ket) = tt.syntax().children().collect_tuple()?; | 172 | let (_bra, attr, _ket) = tt.syntax().children().collect_tuple()?; |
diff --git a/crates/ra_syntax/src/ast/generated.rs b/crates/ra_syntax/src/ast/generated.rs index dd91b5063..c7aaccc95 100644 --- a/crates/ra_syntax/src/ast/generated.rs +++ b/crates/ra_syntax/src/ast/generated.rs | |||
@@ -1946,6 +1946,7 @@ impl ToOwned for MatchArm { | |||
1946 | } | 1946 | } |
1947 | 1947 | ||
1948 | 1948 | ||
1949 | impl ast::AttrsOwner for MatchArm {} | ||
1949 | impl MatchArm { | 1950 | impl MatchArm { |
1950 | pub fn pats(&self) -> impl Iterator<Item = &Pat> { | 1951 | pub fn pats(&self) -> impl Iterator<Item = &Pat> { |
1951 | super::children(self) | 1952 | super::children(self) |
@@ -1986,6 +1987,7 @@ impl ToOwned for MatchArmList { | |||
1986 | } | 1987 | } |
1987 | 1988 | ||
1988 | 1989 | ||
1990 | impl ast::AttrsOwner for MatchArmList {} | ||
1989 | impl MatchArmList { | 1991 | impl MatchArmList { |
1990 | pub fn arms(&self) -> impl Iterator<Item = &MatchArm> { | 1992 | pub fn arms(&self) -> impl Iterator<Item = &MatchArm> { |
1991 | super::children(self) | 1993 | super::children(self) |
diff --git a/crates/ra_syntax/src/grammar.ron b/crates/ra_syntax/src/grammar.ron index 27a123681..193b563aa 100644 --- a/crates/ra_syntax/src/grammar.ron +++ b/crates/ra_syntax/src/grammar.ron | |||
@@ -413,13 +413,15 @@ Grammar( | |||
413 | ), | 413 | ), |
414 | "MatchArmList": ( | 414 | "MatchArmList": ( |
415 | collections: [ ["arms", "MatchArm"] ], | 415 | collections: [ ["arms", "MatchArm"] ], |
416 | traits: [ "AttrsOwner" ] | ||
416 | ), | 417 | ), |
417 | "MatchArm": ( | 418 | "MatchArm": ( |
418 | options: [ | 419 | options: [ |
419 | [ "guard", "MatchGuard" ], | 420 | [ "guard", "MatchGuard" ], |
420 | "Expr", | 421 | "Expr", |
421 | ], | 422 | ], |
422 | collections: [ [ "pats", "Pat" ] ] | 423 | collections: [ [ "pats", "Pat" ] ], |
424 | traits: [ "AttrsOwner" ] | ||
423 | ), | 425 | ), |
424 | "MatchGuard": (options: ["Expr"]), | 426 | "MatchGuard": (options: ["Expr"]), |
425 | "StructLit": (options: ["Path", "NamedFieldList", ["spread", "Expr"]]), | 427 | "StructLit": (options: ["Path", "NamedFieldList", ["spread", "Expr"]]), |
diff --git a/crates/ra_syntax/src/grammar/expressions/atom.rs b/crates/ra_syntax/src/grammar/expressions/atom.rs index 27ba87657..e74305b6a 100644 --- a/crates/ra_syntax/src/grammar/expressions/atom.rs +++ b/crates/ra_syntax/src/grammar/expressions/atom.rs | |||
@@ -313,11 +313,39 @@ pub(crate) fn match_arm_list(p: &mut Parser) { | |||
313 | assert!(p.at(L_CURLY)); | 313 | assert!(p.at(L_CURLY)); |
314 | let m = p.start(); | 314 | let m = p.start(); |
315 | p.eat(L_CURLY); | 315 | p.eat(L_CURLY); |
316 | |||
317 | // test match_arms_inner_attribute | ||
318 | // fn foo() { | ||
319 | // match () { | ||
320 | // #![doc("Inner attribute")] | ||
321 | // #![doc("Can be")] | ||
322 | // #![doc("Stacked")] | ||
323 | // _ => (), | ||
324 | // } | ||
325 | // } | ||
326 | attributes::inner_attributes(p); | ||
327 | |||
316 | while !p.at(EOF) && !p.at(R_CURLY) { | 328 | while !p.at(EOF) && !p.at(R_CURLY) { |
317 | if p.at(L_CURLY) { | 329 | if p.at(L_CURLY) { |
318 | error_block(p, "expected match arm"); | 330 | error_block(p, "expected match arm"); |
319 | continue; | 331 | continue; |
320 | } | 332 | } |
333 | |||
334 | // test match_arms_outer_attributes | ||
335 | // fn foo() { | ||
336 | // match () { | ||
337 | // #[cfg(feature = "some")] | ||
338 | // _ => (), | ||
339 | // #[cfg(feature = "other")] | ||
340 | // _ => (), | ||
341 | // #[cfg(feature = "many")] | ||
342 | // #[cfg(feature = "attributes")] | ||
343 | // #[cfg(feature = "before")] | ||
344 | // _ => (), | ||
345 | // } | ||
346 | // } | ||
347 | attributes::outer_attributes(p); | ||
348 | |||
321 | // test match_arms_commas | 349 | // test match_arms_commas |
322 | // fn foo() { | 350 | // fn foo() { |
323 | // match () { | 351 | // match () { |
diff --git a/crates/ra_syntax/src/syntax_node/syntax_error.rs b/crates/ra_syntax/src/syntax_node/syntax_error.rs index 412cf82cc..4ff998090 100644 --- a/crates/ra_syntax/src/syntax_node/syntax_error.rs +++ b/crates/ra_syntax/src/syntax_node/syntax_error.rs | |||
@@ -92,6 +92,7 @@ pub enum SyntaxErrorKind { | |||
92 | UnclosedString, | 92 | UnclosedString, |
93 | InvalidSuffix, | 93 | InvalidSuffix, |
94 | InvalidBlockAttr, | 94 | InvalidBlockAttr, |
95 | InvalidMatchInnerAttr, | ||
95 | } | 96 | } |
96 | 97 | ||
97 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 98 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
@@ -136,6 +137,9 @@ impl fmt::Display for SyntaxErrorKind { | |||
136 | InvalidBlockAttr => { | 137 | InvalidBlockAttr => { |
137 | write!(f, "A block in this position cannot accept inner attributes") | 138 | write!(f, "A block in this position cannot accept inner attributes") |
138 | } | 139 | } |
140 | InvalidMatchInnerAttr => { | ||
141 | write!(f, "Inner attributes are only allowed directly after the opening brace of the match expression") | ||
142 | } | ||
139 | ParseError(msg) => write!(f, "{}", msg.0), | 143 | ParseError(msg) => write!(f, "{}", msg.0), |
140 | } | 144 | } |
141 | } | 145 | } |
diff --git a/crates/ra_syntax/tests/data/parser/err/0032_match_arms_inner_attrs.rs b/crates/ra_syntax/tests/data/parser/err/0032_match_arms_inner_attrs.rs new file mode 100644 index 000000000..06aa47770 --- /dev/null +++ b/crates/ra_syntax/tests/data/parser/err/0032_match_arms_inner_attrs.rs | |||
@@ -0,0 +1,20 @@ | |||
1 | fn foo() { | ||
2 | match () { | ||
3 | _ => (), | ||
4 | #![doc("Not allowed here")] | ||
5 | _ => (), | ||
6 | } | ||
7 | |||
8 | match () { | ||
9 | _ => (), | ||
10 | _ => (), | ||
11 | #![doc("Nor here")] | ||
12 | } | ||
13 | |||
14 | match () { | ||
15 | #[cfg(test)] | ||
16 | #![doc("Nor here")] | ||
17 | _ => (), | ||
18 | _ => (), | ||
19 | } | ||
20 | } | ||
diff --git a/crates/ra_syntax/tests/data/parser/err/0032_match_arms_inner_attrs.txt b/crates/ra_syntax/tests/data/parser/err/0032_match_arms_inner_attrs.txt new file mode 100644 index 000000000..b7543abc9 --- /dev/null +++ b/crates/ra_syntax/tests/data/parser/err/0032_match_arms_inner_attrs.txt | |||
@@ -0,0 +1,201 @@ | |||
1 | SOURCE_FILE@[0; 293) | ||
2 | FN_DEF@[0; 292) | ||
3 | FN_KW@[0; 2) | ||
4 | WHITESPACE@[2; 3) | ||
5 | NAME@[3; 6) | ||
6 | IDENT@[3; 6) "foo" | ||
7 | PARAM_LIST@[6; 8) | ||
8 | L_PAREN@[6; 7) | ||
9 | R_PAREN@[7; 8) | ||
10 | WHITESPACE@[8; 9) | ||
11 | BLOCK@[9; 292) | ||
12 | L_CURLY@[9; 10) | ||
13 | WHITESPACE@[10; 15) | ||
14 | EXPR_STMT@[15; 101) | ||
15 | MATCH_EXPR@[15; 101) | ||
16 | MATCH_KW@[15; 20) | ||
17 | WHITESPACE@[20; 21) | ||
18 | TUPLE_EXPR@[21; 23) | ||
19 | L_PAREN@[21; 22) | ||
20 | R_PAREN@[22; 23) | ||
21 | WHITESPACE@[23; 24) | ||
22 | MATCH_ARM_LIST@[24; 101) | ||
23 | L_CURLY@[24; 25) | ||
24 | WHITESPACE@[25; 34) | ||
25 | MATCH_ARM@[34; 41) | ||
26 | PLACEHOLDER_PAT@[34; 35) | ||
27 | UNDERSCORE@[34; 35) | ||
28 | WHITESPACE@[35; 36) | ||
29 | FAT_ARROW@[36; 38) | ||
30 | WHITESPACE@[38; 39) | ||
31 | TUPLE_EXPR@[39; 41) | ||
32 | L_PAREN@[39; 40) | ||
33 | R_PAREN@[40; 41) | ||
34 | COMMA@[41; 42) | ||
35 | WHITESPACE@[42; 51) | ||
36 | ATTR@[51; 52) | ||
37 | POUND@[51; 52) | ||
38 | err: `expected `[`` | ||
39 | err: `expected pattern` | ||
40 | MATCH_ARM@[52; 78) | ||
41 | ERROR@[52; 53) | ||
42 | EXCL@[52; 53) | ||
43 | err: `expected FAT_ARROW` | ||
44 | ARRAY_EXPR@[53; 78) | ||
45 | L_BRACK@[53; 54) | ||
46 | CALL_EXPR@[54; 77) | ||
47 | PATH_EXPR@[54; 57) | ||
48 | PATH@[54; 57) | ||
49 | PATH_SEGMENT@[54; 57) | ||
50 | NAME_REF@[54; 57) | ||
51 | IDENT@[54; 57) "doc" | ||
52 | ARG_LIST@[57; 77) | ||
53 | L_PAREN@[57; 58) | ||
54 | LITERAL@[58; 76) | ||
55 | STRING@[58; 76) | ||
56 | R_PAREN@[76; 77) | ||
57 | R_BRACK@[77; 78) | ||
58 | err: `expected COMMA` | ||
59 | WHITESPACE@[78; 87) | ||
60 | MATCH_ARM@[87; 94) | ||
61 | PLACEHOLDER_PAT@[87; 88) | ||
62 | UNDERSCORE@[87; 88) | ||
63 | WHITESPACE@[88; 89) | ||
64 | FAT_ARROW@[89; 91) | ||
65 | WHITESPACE@[91; 92) | ||
66 | TUPLE_EXPR@[92; 94) | ||
67 | L_PAREN@[92; 93) | ||
68 | R_PAREN@[93; 94) | ||
69 | COMMA@[94; 95) | ||
70 | WHITESPACE@[95; 100) | ||
71 | R_CURLY@[100; 101) | ||
72 | WHITESPACE@[101; 107) | ||
73 | EXPR_STMT@[107; 185) | ||
74 | MATCH_EXPR@[107; 185) | ||
75 | MATCH_KW@[107; 112) | ||
76 | WHITESPACE@[112; 113) | ||
77 | TUPLE_EXPR@[113; 115) | ||
78 | L_PAREN@[113; 114) | ||
79 | R_PAREN@[114; 115) | ||
80 | WHITESPACE@[115; 116) | ||
81 | MATCH_ARM_LIST@[116; 185) | ||
82 | L_CURLY@[116; 117) | ||
83 | WHITESPACE@[117; 126) | ||
84 | MATCH_ARM@[126; 133) | ||
85 | PLACEHOLDER_PAT@[126; 127) | ||
86 | UNDERSCORE@[126; 127) | ||
87 | WHITESPACE@[127; 128) | ||
88 | FAT_ARROW@[128; 130) | ||
89 | WHITESPACE@[130; 131) | ||
90 | TUPLE_EXPR@[131; 133) | ||
91 | L_PAREN@[131; 132) | ||
92 | R_PAREN@[132; 133) | ||
93 | COMMA@[133; 134) | ||
94 | WHITESPACE@[134; 143) | ||
95 | MATCH_ARM@[143; 150) | ||
96 | PLACEHOLDER_PAT@[143; 144) | ||
97 | UNDERSCORE@[143; 144) | ||
98 | WHITESPACE@[144; 145) | ||
99 | FAT_ARROW@[145; 147) | ||
100 | WHITESPACE@[147; 148) | ||
101 | TUPLE_EXPR@[148; 150) | ||
102 | L_PAREN@[148; 149) | ||
103 | R_PAREN@[149; 150) | ||
104 | COMMA@[150; 151) | ||
105 | WHITESPACE@[151; 160) | ||
106 | ATTR@[160; 161) | ||
107 | POUND@[160; 161) | ||
108 | err: `expected `[`` | ||
109 | err: `expected pattern` | ||
110 | MATCH_ARM@[161; 179) | ||
111 | ERROR@[161; 162) | ||
112 | EXCL@[161; 162) | ||
113 | err: `expected FAT_ARROW` | ||
114 | ARRAY_EXPR@[162; 179) | ||
115 | L_BRACK@[162; 163) | ||
116 | CALL_EXPR@[163; 178) | ||
117 | PATH_EXPR@[163; 166) | ||
118 | PATH@[163; 166) | ||
119 | PATH_SEGMENT@[163; 166) | ||
120 | NAME_REF@[163; 166) | ||
121 | IDENT@[163; 166) "doc" | ||
122 | ARG_LIST@[166; 178) | ||
123 | L_PAREN@[166; 167) | ||
124 | LITERAL@[167; 177) | ||
125 | STRING@[167; 177) | ||
126 | R_PAREN@[177; 178) | ||
127 | R_BRACK@[178; 179) | ||
128 | WHITESPACE@[179; 184) | ||
129 | R_CURLY@[184; 185) | ||
130 | WHITESPACE@[185; 191) | ||
131 | MATCH_EXPR@[191; 290) | ||
132 | MATCH_KW@[191; 196) | ||
133 | WHITESPACE@[196; 197) | ||
134 | TUPLE_EXPR@[197; 199) | ||
135 | L_PAREN@[197; 198) | ||
136 | R_PAREN@[198; 199) | ||
137 | WHITESPACE@[199; 200) | ||
138 | MATCH_ARM_LIST@[200; 290) | ||
139 | L_CURLY@[200; 201) | ||
140 | WHITESPACE@[201; 210) | ||
141 | ATTR@[210; 222) | ||
142 | POUND@[210; 211) | ||
143 | TOKEN_TREE@[211; 222) | ||
144 | L_BRACK@[211; 212) | ||
145 | IDENT@[212; 215) "cfg" | ||
146 | TOKEN_TREE@[215; 221) | ||
147 | L_PAREN@[215; 216) | ||
148 | IDENT@[216; 220) "test" | ||
149 | R_PAREN@[220; 221) | ||
150 | R_BRACK@[221; 222) | ||
151 | WHITESPACE@[222; 231) | ||
152 | ATTR@[231; 232) | ||
153 | POUND@[231; 232) | ||
154 | err: `expected `[`` | ||
155 | err: `expected pattern` | ||
156 | MATCH_ARM@[232; 250) | ||
157 | ERROR@[232; 233) | ||
158 | EXCL@[232; 233) | ||
159 | err: `expected FAT_ARROW` | ||
160 | ARRAY_EXPR@[233; 250) | ||
161 | L_BRACK@[233; 234) | ||
162 | CALL_EXPR@[234; 249) | ||
163 | PATH_EXPR@[234; 237) | ||
164 | PATH@[234; 237) | ||
165 | PATH_SEGMENT@[234; 237) | ||
166 | NAME_REF@[234; 237) | ||
167 | IDENT@[234; 237) "doc" | ||
168 | ARG_LIST@[237; 249) | ||
169 | L_PAREN@[237; 238) | ||
170 | LITERAL@[238; 248) | ||
171 | STRING@[238; 248) | ||
172 | R_PAREN@[248; 249) | ||
173 | R_BRACK@[249; 250) | ||
174 | err: `expected COMMA` | ||
175 | WHITESPACE@[250; 259) | ||
176 | MATCH_ARM@[259; 266) | ||
177 | PLACEHOLDER_PAT@[259; 260) | ||
178 | UNDERSCORE@[259; 260) | ||
179 | WHITESPACE@[260; 261) | ||
180 | FAT_ARROW@[261; 263) | ||
181 | WHITESPACE@[263; 264) | ||
182 | TUPLE_EXPR@[264; 266) | ||
183 | L_PAREN@[264; 265) | ||
184 | R_PAREN@[265; 266) | ||
185 | COMMA@[266; 267) | ||
186 | WHITESPACE@[267; 276) | ||
187 | MATCH_ARM@[276; 283) | ||
188 | PLACEHOLDER_PAT@[276; 277) | ||
189 | UNDERSCORE@[276; 277) | ||
190 | WHITESPACE@[277; 278) | ||
191 | FAT_ARROW@[278; 280) | ||
192 | WHITESPACE@[280; 281) | ||
193 | TUPLE_EXPR@[281; 283) | ||
194 | L_PAREN@[281; 282) | ||
195 | R_PAREN@[282; 283) | ||
196 | COMMA@[283; 284) | ||
197 | WHITESPACE@[284; 289) | ||
198 | R_CURLY@[289; 290) | ||
199 | WHITESPACE@[290; 291) | ||
200 | R_CURLY@[291; 292) | ||
201 | WHITESPACE@[292; 293) | ||
diff --git a/crates/ra_syntax/tests/data/parser/err/0033_match_arms_outer_attrs.rs b/crates/ra_syntax/tests/data/parser/err/0033_match_arms_outer_attrs.rs new file mode 100644 index 000000000..4635222da --- /dev/null +++ b/crates/ra_syntax/tests/data/parser/err/0033_match_arms_outer_attrs.rs | |||
@@ -0,0 +1,7 @@ | |||
1 | fn foo() { | ||
2 | match () { | ||
3 | _ => (), | ||
4 | _ => (), | ||
5 | #[cfg(test)] | ||
6 | } | ||
7 | } | ||
diff --git a/crates/ra_syntax/tests/data/parser/err/0033_match_arms_outer_attrs.txt b/crates/ra_syntax/tests/data/parser/err/0033_match_arms_outer_attrs.txt new file mode 100644 index 000000000..7f8767001 --- /dev/null +++ b/crates/ra_syntax/tests/data/parser/err/0033_match_arms_outer_attrs.txt | |||
@@ -0,0 +1,64 @@ | |||
1 | SOURCE_FILE@[0; 89) | ||
2 | FN_DEF@[0; 88) | ||
3 | FN_KW@[0; 2) | ||
4 | WHITESPACE@[2; 3) | ||
5 | NAME@[3; 6) | ||
6 | IDENT@[3; 6) "foo" | ||
7 | PARAM_LIST@[6; 8) | ||
8 | L_PAREN@[6; 7) | ||
9 | R_PAREN@[7; 8) | ||
10 | WHITESPACE@[8; 9) | ||
11 | BLOCK@[9; 88) | ||
12 | L_CURLY@[9; 10) | ||
13 | WHITESPACE@[10; 15) | ||
14 | MATCH_EXPR@[15; 86) | ||
15 | MATCH_KW@[15; 20) | ||
16 | WHITESPACE@[20; 21) | ||
17 | TUPLE_EXPR@[21; 23) | ||
18 | L_PAREN@[21; 22) | ||
19 | R_PAREN@[22; 23) | ||
20 | WHITESPACE@[23; 24) | ||
21 | MATCH_ARM_LIST@[24; 86) | ||
22 | L_CURLY@[24; 25) | ||
23 | WHITESPACE@[25; 34) | ||
24 | MATCH_ARM@[34; 41) | ||
25 | PLACEHOLDER_PAT@[34; 35) | ||
26 | UNDERSCORE@[34; 35) | ||
27 | WHITESPACE@[35; 36) | ||
28 | FAT_ARROW@[36; 38) | ||
29 | WHITESPACE@[38; 39) | ||
30 | TUPLE_EXPR@[39; 41) | ||
31 | L_PAREN@[39; 40) | ||
32 | R_PAREN@[40; 41) | ||
33 | COMMA@[41; 42) | ||
34 | WHITESPACE@[42; 51) | ||
35 | MATCH_ARM@[51; 58) | ||
36 | PLACEHOLDER_PAT@[51; 52) | ||
37 | UNDERSCORE@[51; 52) | ||
38 | WHITESPACE@[52; 53) | ||
39 | FAT_ARROW@[53; 55) | ||
40 | WHITESPACE@[55; 56) | ||
41 | TUPLE_EXPR@[56; 58) | ||
42 | L_PAREN@[56; 57) | ||
43 | R_PAREN@[57; 58) | ||
44 | COMMA@[58; 59) | ||
45 | WHITESPACE@[59; 68) | ||
46 | ATTR@[68; 80) | ||
47 | POUND@[68; 69) | ||
48 | TOKEN_TREE@[69; 80) | ||
49 | L_BRACK@[69; 70) | ||
50 | IDENT@[70; 73) "cfg" | ||
51 | TOKEN_TREE@[73; 79) | ||
52 | L_PAREN@[73; 74) | ||
53 | IDENT@[74; 78) "test" | ||
54 | R_PAREN@[78; 79) | ||
55 | R_BRACK@[79; 80) | ||
56 | WHITESPACE@[80; 85) | ||
57 | err: `expected pattern` | ||
58 | err: `expected FAT_ARROW` | ||
59 | err: `expected expression` | ||
60 | MATCH_ARM@[85; 85) | ||
61 | R_CURLY@[85; 86) | ||
62 | WHITESPACE@[86; 87) | ||
63 | R_CURLY@[87; 88) | ||
64 | WHITESPACE@[88; 89) | ||
diff --git a/crates/ra_syntax/tests/data/parser/inline/ok/0120_match_arms_inner_attribute.rs b/crates/ra_syntax/tests/data/parser/inline/ok/0120_match_arms_inner_attribute.rs new file mode 100644 index 000000000..54a67c9d7 --- /dev/null +++ b/crates/ra_syntax/tests/data/parser/inline/ok/0120_match_arms_inner_attribute.rs | |||
@@ -0,0 +1,8 @@ | |||
1 | fn foo() { | ||
2 | match () { | ||
3 | #![doc("Inner attribute")] | ||
4 | #![doc("Can be")] | ||
5 | #![doc("Stacked")] | ||
6 | _ => (), | ||
7 | } | ||
8 | } | ||
diff --git a/crates/ra_syntax/tests/data/parser/inline/ok/0120_match_arms_inner_attribute.txt b/crates/ra_syntax/tests/data/parser/inline/ok/0120_match_arms_inner_attribute.txt new file mode 100644 index 000000000..b39a217bd --- /dev/null +++ b/crates/ra_syntax/tests/data/parser/inline/ok/0120_match_arms_inner_attribute.txt | |||
@@ -0,0 +1,74 @@ | |||
1 | SOURCE_FILE@[0; 139) | ||
2 | FN_DEF@[0; 138) | ||
3 | FN_KW@[0; 2) | ||
4 | WHITESPACE@[2; 3) | ||
5 | NAME@[3; 6) | ||
6 | IDENT@[3; 6) "foo" | ||
7 | PARAM_LIST@[6; 8) | ||
8 | L_PAREN@[6; 7) | ||
9 | R_PAREN@[7; 8) | ||
10 | WHITESPACE@[8; 9) | ||
11 | BLOCK@[9; 138) | ||
12 | L_CURLY@[9; 10) | ||
13 | WHITESPACE@[10; 15) | ||
14 | MATCH_EXPR@[15; 136) | ||
15 | MATCH_KW@[15; 20) | ||
16 | WHITESPACE@[20; 21) | ||
17 | TUPLE_EXPR@[21; 23) | ||
18 | L_PAREN@[21; 22) | ||
19 | R_PAREN@[22; 23) | ||
20 | WHITESPACE@[23; 24) | ||
21 | MATCH_ARM_LIST@[24; 136) | ||
22 | L_CURLY@[24; 25) | ||
23 | WHITESPACE@[25; 34) | ||
24 | ATTR@[34; 60) | ||
25 | POUND@[34; 35) | ||
26 | EXCL@[35; 36) | ||
27 | TOKEN_TREE@[36; 60) | ||
28 | L_BRACK@[36; 37) | ||
29 | IDENT@[37; 40) "doc" | ||
30 | TOKEN_TREE@[40; 59) | ||
31 | L_PAREN@[40; 41) | ||
32 | STRING@[41; 58) | ||
33 | R_PAREN@[58; 59) | ||
34 | R_BRACK@[59; 60) | ||
35 | WHITESPACE@[60; 69) | ||
36 | ATTR@[69; 86) | ||
37 | POUND@[69; 70) | ||
38 | EXCL@[70; 71) | ||
39 | TOKEN_TREE@[71; 86) | ||
40 | L_BRACK@[71; 72) | ||
41 | IDENT@[72; 75) "doc" | ||
42 | TOKEN_TREE@[75; 85) | ||
43 | L_PAREN@[75; 76) | ||
44 | STRING@[76; 84) | ||
45 | R_PAREN@[84; 85) | ||
46 | R_BRACK@[85; 86) | ||
47 | WHITESPACE@[86; 95) | ||
48 | ATTR@[95; 113) | ||
49 | POUND@[95; 96) | ||
50 | EXCL@[96; 97) | ||
51 | TOKEN_TREE@[97; 113) | ||
52 | L_BRACK@[97; 98) | ||
53 | IDENT@[98; 101) "doc" | ||
54 | TOKEN_TREE@[101; 112) | ||
55 | L_PAREN@[101; 102) | ||
56 | STRING@[102; 111) | ||
57 | R_PAREN@[111; 112) | ||
58 | R_BRACK@[112; 113) | ||
59 | WHITESPACE@[113; 122) | ||
60 | MATCH_ARM@[122; 129) | ||
61 | PLACEHOLDER_PAT@[122; 123) | ||
62 | UNDERSCORE@[122; 123) | ||
63 | WHITESPACE@[123; 124) | ||
64 | FAT_ARROW@[124; 126) | ||
65 | WHITESPACE@[126; 127) | ||
66 | TUPLE_EXPR@[127; 129) | ||
67 | L_PAREN@[127; 128) | ||
68 | R_PAREN@[128; 129) | ||
69 | COMMA@[129; 130) | ||
70 | WHITESPACE@[130; 135) | ||
71 | R_CURLY@[135; 136) | ||
72 | WHITESPACE@[136; 137) | ||
73 | R_CURLY@[137; 138) | ||
74 | WHITESPACE@[138; 139) | ||
diff --git a/crates/ra_syntax/tests/data/parser/inline/ok/0121_match_arms_outer_attributes.rs b/crates/ra_syntax/tests/data/parser/inline/ok/0121_match_arms_outer_attributes.rs new file mode 100644 index 000000000..676db42d1 --- /dev/null +++ b/crates/ra_syntax/tests/data/parser/inline/ok/0121_match_arms_outer_attributes.rs | |||
@@ -0,0 +1,12 @@ | |||
1 | fn foo() { | ||
2 | match () { | ||
3 | #[cfg(feature = "some")] | ||
4 | _ => (), | ||
5 | #[cfg(feature = "other")] | ||
6 | _ => (), | ||
7 | #[cfg(feature = "many")] | ||
8 | #[cfg(feature = "attributes")] | ||
9 | #[cfg(feature = "before")] | ||
10 | _ => (), | ||
11 | } | ||
12 | } | ||
diff --git a/crates/ra_syntax/tests/data/parser/inline/ok/0121_match_arms_outer_attributes.txt b/crates/ra_syntax/tests/data/parser/inline/ok/0121_match_arms_outer_attributes.txt new file mode 100644 index 000000000..c888fb8f0 --- /dev/null +++ b/crates/ra_syntax/tests/data/parser/inline/ok/0121_match_arms_outer_attributes.txt | |||
@@ -0,0 +1,135 @@ | |||
1 | SOURCE_FILE@[0; 259) | ||
2 | FN_DEF@[0; 258) | ||
3 | FN_KW@[0; 2) | ||
4 | WHITESPACE@[2; 3) | ||
5 | NAME@[3; 6) | ||
6 | IDENT@[3; 6) "foo" | ||
7 | PARAM_LIST@[6; 8) | ||
8 | L_PAREN@[6; 7) | ||
9 | R_PAREN@[7; 8) | ||
10 | WHITESPACE@[8; 9) | ||
11 | BLOCK@[9; 258) | ||
12 | L_CURLY@[9; 10) | ||
13 | WHITESPACE@[10; 15) | ||
14 | MATCH_EXPR@[15; 256) | ||
15 | MATCH_KW@[15; 20) | ||
16 | WHITESPACE@[20; 21) | ||
17 | TUPLE_EXPR@[21; 23) | ||
18 | L_PAREN@[21; 22) | ||
19 | R_PAREN@[22; 23) | ||
20 | WHITESPACE@[23; 24) | ||
21 | MATCH_ARM_LIST@[24; 256) | ||
22 | L_CURLY@[24; 25) | ||
23 | WHITESPACE@[25; 34) | ||
24 | ATTR@[34; 58) | ||
25 | POUND@[34; 35) | ||
26 | TOKEN_TREE@[35; 58) | ||
27 | L_BRACK@[35; 36) | ||
28 | IDENT@[36; 39) "cfg" | ||
29 | TOKEN_TREE@[39; 57) | ||
30 | L_PAREN@[39; 40) | ||
31 | IDENT@[40; 47) "feature" | ||
32 | WHITESPACE@[47; 48) | ||
33 | EQ@[48; 49) | ||
34 | WHITESPACE@[49; 50) | ||
35 | STRING@[50; 56) | ||
36 | R_PAREN@[56; 57) | ||
37 | R_BRACK@[57; 58) | ||
38 | WHITESPACE@[58; 67) | ||
39 | MATCH_ARM@[67; 74) | ||
40 | PLACEHOLDER_PAT@[67; 68) | ||
41 | UNDERSCORE@[67; 68) | ||
42 | WHITESPACE@[68; 69) | ||
43 | FAT_ARROW@[69; 71) | ||
44 | WHITESPACE@[71; 72) | ||
45 | TUPLE_EXPR@[72; 74) | ||
46 | L_PAREN@[72; 73) | ||
47 | R_PAREN@[73; 74) | ||
48 | COMMA@[74; 75) | ||
49 | WHITESPACE@[75; 84) | ||
50 | ATTR@[84; 109) | ||
51 | POUND@[84; 85) | ||
52 | TOKEN_TREE@[85; 109) | ||
53 | L_BRACK@[85; 86) | ||
54 | IDENT@[86; 89) "cfg" | ||
55 | TOKEN_TREE@[89; 108) | ||
56 | L_PAREN@[89; 90) | ||
57 | IDENT@[90; 97) "feature" | ||
58 | WHITESPACE@[97; 98) | ||
59 | EQ@[98; 99) | ||
60 | WHITESPACE@[99; 100) | ||
61 | STRING@[100; 107) | ||
62 | R_PAREN@[107; 108) | ||
63 | R_BRACK@[108; 109) | ||
64 | WHITESPACE@[109; 118) | ||
65 | MATCH_ARM@[118; 125) | ||
66 | PLACEHOLDER_PAT@[118; 119) | ||
67 | UNDERSCORE@[118; 119) | ||
68 | WHITESPACE@[119; 120) | ||
69 | FAT_ARROW@[120; 122) | ||
70 | WHITESPACE@[122; 123) | ||
71 | TUPLE_EXPR@[123; 125) | ||
72 | L_PAREN@[123; 124) | ||
73 | R_PAREN@[124; 125) | ||
74 | COMMA@[125; 126) | ||
75 | WHITESPACE@[126; 135) | ||
76 | ATTR@[135; 159) | ||
77 | POUND@[135; 136) | ||
78 | TOKEN_TREE@[136; 159) | ||
79 | L_BRACK@[136; 137) | ||
80 | IDENT@[137; 140) "cfg" | ||
81 | TOKEN_TREE@[140; 158) | ||
82 | L_PAREN@[140; 141) | ||
83 | IDENT@[141; 148) "feature" | ||
84 | WHITESPACE@[148; 149) | ||
85 | EQ@[149; 150) | ||
86 | WHITESPACE@[150; 151) | ||
87 | STRING@[151; 157) | ||
88 | R_PAREN@[157; 158) | ||
89 | R_BRACK@[158; 159) | ||
90 | WHITESPACE@[159; 168) | ||
91 | ATTR@[168; 198) | ||
92 | POUND@[168; 169) | ||
93 | TOKEN_TREE@[169; 198) | ||
94 | L_BRACK@[169; 170) | ||
95 | IDENT@[170; 173) "cfg" | ||
96 | TOKEN_TREE@[173; 197) | ||
97 | L_PAREN@[173; 174) | ||
98 | IDENT@[174; 181) "feature" | ||
99 | WHITESPACE@[181; 182) | ||
100 | EQ@[182; 183) | ||
101 | WHITESPACE@[183; 184) | ||
102 | STRING@[184; 196) | ||
103 | R_PAREN@[196; 197) | ||
104 | R_BRACK@[197; 198) | ||
105 | WHITESPACE@[198; 207) | ||
106 | ATTR@[207; 233) | ||
107 | POUND@[207; 208) | ||
108 | TOKEN_TREE@[208; 233) | ||
109 | L_BRACK@[208; 209) | ||
110 | IDENT@[209; 212) "cfg" | ||
111 | TOKEN_TREE@[212; 232) | ||
112 | L_PAREN@[212; 213) | ||
113 | IDENT@[213; 220) "feature" | ||
114 | WHITESPACE@[220; 221) | ||
115 | EQ@[221; 222) | ||
116 | WHITESPACE@[222; 223) | ||
117 | STRING@[223; 231) | ||
118 | R_PAREN@[231; 232) | ||
119 | R_BRACK@[232; 233) | ||
120 | WHITESPACE@[233; 242) | ||
121 | MATCH_ARM@[242; 249) | ||
122 | PLACEHOLDER_PAT@[242; 243) | ||
123 | UNDERSCORE@[242; 243) | ||
124 | WHITESPACE@[243; 244) | ||
125 | FAT_ARROW@[244; 246) | ||
126 | WHITESPACE@[246; 247) | ||
127 | TUPLE_EXPR@[247; 249) | ||
128 | L_PAREN@[247; 248) | ||
129 | R_PAREN@[248; 249) | ||
130 | COMMA@[249; 250) | ||
131 | WHITESPACE@[250; 255) | ||
132 | R_CURLY@[255; 256) | ||
133 | WHITESPACE@[256; 257) | ||
134 | R_CURLY@[257; 258) | ||
135 | WHITESPACE@[258; 259) | ||
diff --git a/crates/ra_vfs/src/io.rs b/crates/ra_vfs/src/io.rs index f64b4c532..0cffc03f3 100644 --- a/crates/ra_vfs/src/io.rs +++ b/crates/ra_vfs/src/io.rs | |||
@@ -9,10 +9,10 @@ use relative_path::RelativePathBuf; | |||
9 | use walkdir::WalkDir; | 9 | use walkdir::WalkDir; |
10 | use notify::{DebouncedEvent, RecommendedWatcher, RecursiveMode, Watcher as _Watcher}; | 10 | use notify::{DebouncedEvent, RecommendedWatcher, RecursiveMode, Watcher as _Watcher}; |
11 | 11 | ||
12 | use crate::{RootConfig, Roots, VfsRoot}; | 12 | use crate::{Roots, VfsRoot}; |
13 | 13 | ||
14 | pub(crate) enum Task { | 14 | pub(crate) enum Task { |
15 | AddRoot { root: VfsRoot, config: Arc<RootConfig> }, | 15 | AddRoot { root: VfsRoot }, |
16 | } | 16 | } |
17 | 17 | ||
18 | /// `TaskResult` transfers files read on the IO thread to the VFS on the main | 18 | /// `TaskResult` transfers files read on the IO thread to the VFS on the main |
@@ -98,8 +98,8 @@ pub(crate) fn start(roots: Arc<Roots>) -> Worker { | |||
98 | drop(input_receiver); | 98 | drop(input_receiver); |
99 | break | 99 | break |
100 | }, | 100 | }, |
101 | Ok(Task::AddRoot { root, config }) => { | 101 | Ok(Task::AddRoot { root }) => { |
102 | watch_root(watcher.as_mut(), &output_sender, root, Arc::clone(&config)); | 102 | watch_root(watcher.as_mut(), &output_sender, &*roots, root); |
103 | } | 103 | } |
104 | }, | 104 | }, |
105 | // Watcher send us changes. If **this** channel is | 105 | // Watcher send us changes. If **this** channel is |
@@ -123,20 +123,21 @@ pub(crate) fn start(roots: Arc<Roots>) -> Worker { | |||
123 | fn watch_root( | 123 | fn watch_root( |
124 | watcher: Option<&mut RecommendedWatcher>, | 124 | watcher: Option<&mut RecommendedWatcher>, |
125 | sender: &Sender<TaskResult>, | 125 | sender: &Sender<TaskResult>, |
126 | roots: &Roots, | ||
126 | root: VfsRoot, | 127 | root: VfsRoot, |
127 | config: Arc<RootConfig>, | ||
128 | ) { | 128 | ) { |
129 | log::debug!("loading {} ...", config.root.as_path().display()); | 129 | let root_path = roots.path(root); |
130 | let files = watch_recursive(watcher, config.root.as_path(), &*config) | 130 | log::debug!("loading {} ...", root_path.display()); |
131 | let files = watch_recursive(watcher, root_path, roots, root) | ||
131 | .into_iter() | 132 | .into_iter() |
132 | .filter_map(|path| { | 133 | .filter_map(|path| { |
133 | let abs_path = path.to_path(&config.root); | 134 | let abs_path = path.to_path(&root_path); |
134 | let text = read_to_string(&abs_path)?; | 135 | let text = read_to_string(&abs_path)?; |
135 | Some((path, text)) | 136 | Some((path, text)) |
136 | }) | 137 | }) |
137 | .collect(); | 138 | .collect(); |
138 | sender.send(TaskResult::BulkLoadRoot { root, files }).unwrap(); | 139 | sender.send(TaskResult::BulkLoadRoot { root, files }).unwrap(); |
139 | log::debug!("... loaded {}", config.root.as_path().display()); | 140 | log::debug!("... loaded {}", root_path.display()); |
140 | } | 141 | } |
141 | 142 | ||
142 | fn convert_notify_event(event: DebouncedEvent, sender: &Sender<(PathBuf, ChangeKind)>) { | 143 | fn convert_notify_event(event: DebouncedEvent, sender: &Sender<(PathBuf, ChangeKind)>) { |
@@ -181,19 +182,18 @@ fn handle_change( | |||
181 | None => return, | 182 | None => return, |
182 | Some(it) => it, | 183 | Some(it) => it, |
183 | }; | 184 | }; |
184 | let config = &roots[root]; | ||
185 | match kind { | 185 | match kind { |
186 | ChangeKind::Create => { | 186 | ChangeKind::Create => { |
187 | let mut paths = Vec::new(); | 187 | let mut paths = Vec::new(); |
188 | if path.is_dir() { | 188 | if path.is_dir() { |
189 | paths.extend(watch_recursive(watcher, &path, &config)); | 189 | paths.extend(watch_recursive(watcher, &path, roots, root)); |
190 | } else { | 190 | } else { |
191 | paths.push(rel_path); | 191 | paths.push(rel_path); |
192 | } | 192 | } |
193 | paths | 193 | paths |
194 | .into_iter() | 194 | .into_iter() |
195 | .try_for_each(|rel_path| { | 195 | .try_for_each(|rel_path| { |
196 | let abs_path = rel_path.to_path(&config.root); | 196 | let abs_path = rel_path.to_path(&roots.path(root)); |
197 | let text = read_to_string(&abs_path); | 197 | let text = read_to_string(&abs_path); |
198 | sender.send(TaskResult::SingleFile { root, path: rel_path, text }) | 198 | sender.send(TaskResult::SingleFile { root, path: rel_path, text }) |
199 | }) | 199 | }) |
@@ -209,12 +209,13 @@ fn handle_change( | |||
209 | fn watch_recursive( | 209 | fn watch_recursive( |
210 | mut watcher: Option<&mut RecommendedWatcher>, | 210 | mut watcher: Option<&mut RecommendedWatcher>, |
211 | dir: &Path, | 211 | dir: &Path, |
212 | config: &RootConfig, | 212 | roots: &Roots, |
213 | root: VfsRoot, | ||
213 | ) -> Vec<RelativePathBuf> { | 214 | ) -> Vec<RelativePathBuf> { |
214 | let mut files = Vec::new(); | 215 | let mut files = Vec::new(); |
215 | for entry in WalkDir::new(dir) | 216 | for entry in WalkDir::new(dir) |
216 | .into_iter() | 217 | .into_iter() |
217 | .filter_entry(|it| config.contains(it.path()).is_some()) | 218 | .filter_entry(|it| roots.contains(root, it.path()).is_some()) |
218 | .filter_map(|it| it.map_err(|e| log::warn!("watcher error: {}", e)).ok()) | 219 | .filter_map(|it| it.map_err(|e| log::warn!("watcher error: {}", e)).ok()) |
219 | { | 220 | { |
220 | if entry.file_type().is_dir() { | 221 | if entry.file_type().is_dir() { |
@@ -222,7 +223,7 @@ fn watch_recursive( | |||
222 | watch_one(watcher, entry.path()); | 223 | watch_one(watcher, entry.path()); |
223 | } | 224 | } |
224 | } else { | 225 | } else { |
225 | let path = config.contains(entry.path()).unwrap(); | 226 | let path = roots.contains(root, entry.path()).unwrap(); |
226 | files.push(path.to_owned()); | 227 | files.push(path.to_owned()); |
227 | } | 228 | } |
228 | } | 229 | } |
diff --git a/crates/ra_vfs/src/lib.rs b/crates/ra_vfs/src/lib.rs index cfdc1275f..7f555a3c0 100644 --- a/crates/ra_vfs/src/lib.rs +++ b/crates/ra_vfs/src/lib.rs | |||
@@ -15,10 +15,10 @@ | |||
15 | //! VFS is based on a concept of roots: a set of directories on the file system | 15 | //! VFS is based on a concept of roots: a set of directories on the file system |
16 | //! which are watched for changes. Typically, there will be a root for each | 16 | //! which are watched for changes. Typically, there will be a root for each |
17 | //! Cargo package. | 17 | //! Cargo package. |
18 | mod roots; | ||
18 | mod io; | 19 | mod io; |
19 | 20 | ||
20 | use std::{ | 21 | use std::{ |
21 | cmp::Reverse, | ||
22 | fmt, fs, mem, | 22 | fmt, fs, mem, |
23 | path::{Path, PathBuf}, | 23 | path::{Path, PathBuf}, |
24 | sync::Arc, | 24 | sync::Arc, |
@@ -26,106 +26,18 @@ use std::{ | |||
26 | 26 | ||
27 | use crossbeam_channel::Receiver; | 27 | use crossbeam_channel::Receiver; |
28 | use ra_arena::{impl_arena_id, Arena, RawId, map::ArenaMap}; | 28 | use ra_arena::{impl_arena_id, Arena, RawId, map::ArenaMap}; |
29 | use relative_path::{Component, RelativePath, RelativePathBuf}; | 29 | use relative_path::{RelativePath, RelativePathBuf}; |
30 | use rustc_hash::{FxHashMap, FxHashSet}; | 30 | use rustc_hash::{FxHashMap, FxHashSet}; |
31 | 31 | ||
32 | pub use crate::io::TaskResult as VfsTask; | 32 | use crate::{ |
33 | use io::{TaskResult, Worker}; | 33 | io::{TaskResult, Worker}, |
34 | 34 | roots::Roots, | |
35 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] | 35 | }; |
36 | pub struct VfsRoot(pub RawId); | ||
37 | impl_arena_id!(VfsRoot); | ||
38 | |||
39 | /// Describes the contents of a single source root. | ||
40 | /// | ||
41 | /// `RootConfig` can be thought of as a glob pattern like `src/**.rs` which | ||
42 | /// specifies the source root or as a function which takes a `PathBuf` and | ||
43 | /// returns `true` iff path belongs to the source root | ||
44 | pub(crate) struct RootConfig { | ||
45 | root: PathBuf, | ||
46 | // result of `root.canonicalize()` if that differs from `root`; `None` otherwise. | ||
47 | canonical_root: Option<PathBuf>, | ||
48 | excluded_dirs: Vec<PathBuf>, | ||
49 | } | ||
50 | |||
51 | pub(crate) struct Roots { | ||
52 | roots: Arena<VfsRoot, Arc<RootConfig>>, | ||
53 | } | ||
54 | |||
55 | impl std::ops::Deref for Roots { | ||
56 | type Target = Arena<VfsRoot, Arc<RootConfig>>; | ||
57 | fn deref(&self) -> &Self::Target { | ||
58 | &self.roots | ||
59 | } | ||
60 | } | ||
61 | |||
62 | impl RootConfig { | ||
63 | fn new(root: PathBuf, excluded_dirs: Vec<PathBuf>) -> RootConfig { | ||
64 | let mut canonical_root = root.canonicalize().ok(); | ||
65 | if Some(&root) == canonical_root.as_ref() { | ||
66 | canonical_root = None; | ||
67 | } | ||
68 | RootConfig { root, canonical_root, excluded_dirs } | ||
69 | } | ||
70 | /// Checks if root contains a path and returns a root-relative path. | ||
71 | pub(crate) fn contains(&self, path: &Path) -> Option<RelativePathBuf> { | ||
72 | // First, check excluded dirs | ||
73 | if self.excluded_dirs.iter().any(|it| path.starts_with(it)) { | ||
74 | return None; | ||
75 | } | ||
76 | let rel_path = path | ||
77 | .strip_prefix(&self.root) | ||
78 | .or_else(|err_payload| { | ||
79 | self.canonical_root | ||
80 | .as_ref() | ||
81 | .map_or(Err(err_payload), |canonical_root| path.strip_prefix(canonical_root)) | ||
82 | }) | ||
83 | .ok()?; | ||
84 | let rel_path = RelativePathBuf::from_path(rel_path).ok()?; | ||
85 | |||
86 | // Ignore some common directories. | ||
87 | // | ||
88 | // FIXME: don't hard-code, specify at source-root creation time using | ||
89 | // gitignore | ||
90 | for (i, c) in rel_path.components().enumerate() { | ||
91 | if let Component::Normal(c) = c { | ||
92 | if (i == 0 && c == "target") || c == ".git" || c == "node_modules" { | ||
93 | return None; | ||
94 | } | ||
95 | } | ||
96 | } | ||
97 | |||
98 | if path.is_file() && rel_path.extension() != Some("rs") { | ||
99 | return None; | ||
100 | } | ||
101 | |||
102 | Some(rel_path) | ||
103 | } | ||
104 | } | ||
105 | 36 | ||
106 | impl Roots { | 37 | pub use crate::{ |
107 | pub(crate) fn new(mut paths: Vec<PathBuf>) -> Roots { | 38 | io::TaskResult as VfsTask, |
108 | let mut roots = Arena::default(); | 39 | roots::VfsRoot, |
109 | // A hack to make nesting work. | 40 | }; |
110 | paths.sort_by_key(|it| Reverse(it.as_os_str().len())); | ||
111 | paths.dedup(); | ||
112 | for (i, path) in paths.iter().enumerate() { | ||
113 | let nested_roots = paths[..i] | ||
114 | .iter() | ||
115 | .filter(|it| it.starts_with(path)) | ||
116 | .map(|it| it.clone()) | ||
117 | .collect::<Vec<_>>(); | ||
118 | |||
119 | let config = Arc::new(RootConfig::new(path.clone(), nested_roots)); | ||
120 | |||
121 | roots.alloc(config.clone()); | ||
122 | } | ||
123 | Roots { roots } | ||
124 | } | ||
125 | pub(crate) fn find(&self, path: &Path) -> Option<(VfsRoot, RelativePathBuf)> { | ||
126 | self.roots.iter().find_map(|(root, data)| data.contains(path).map(|it| (root, it))) | ||
127 | } | ||
128 | } | ||
129 | 41 | ||
130 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] | 42 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] |
131 | pub struct VfsFile(pub RawId); | 43 | pub struct VfsFile(pub RawId); |
@@ -162,18 +74,18 @@ impl Vfs { | |||
162 | let worker = io::start(Arc::clone(&roots)); | 74 | let worker = io::start(Arc::clone(&roots)); |
163 | let mut root2files = ArenaMap::default(); | 75 | let mut root2files = ArenaMap::default(); |
164 | 76 | ||
165 | for (root, config) in roots.iter() { | 77 | for root in roots.iter() { |
166 | root2files.insert(root, Default::default()); | 78 | root2files.insert(root, Default::default()); |
167 | worker.sender().send(io::Task::AddRoot { root, config: Arc::clone(config) }).unwrap(); | 79 | worker.sender().send(io::Task::AddRoot { root }).unwrap(); |
168 | } | 80 | } |
169 | let res = | 81 | let res = |
170 | Vfs { roots, files: Arena::default(), root2files, worker, pending_changes: Vec::new() }; | 82 | Vfs { roots, files: Arena::default(), root2files, worker, pending_changes: Vec::new() }; |
171 | let vfs_roots = res.roots.iter().map(|(id, _)| id).collect(); | 83 | let vfs_roots = res.roots.iter().collect(); |
172 | (res, vfs_roots) | 84 | (res, vfs_roots) |
173 | } | 85 | } |
174 | 86 | ||
175 | pub fn root2path(&self, root: VfsRoot) -> PathBuf { | 87 | pub fn root2path(&self, root: VfsRoot) -> PathBuf { |
176 | self.roots[root].root.clone() | 88 | self.roots.path(root).to_path_buf() |
177 | } | 89 | } |
178 | 90 | ||
179 | pub fn path2file(&self, path: &Path) -> Option<VfsFile> { | 91 | pub fn path2file(&self, path: &Path) -> Option<VfsFile> { |
@@ -185,18 +97,11 @@ impl Vfs { | |||
185 | 97 | ||
186 | pub fn file2path(&self, file: VfsFile) -> PathBuf { | 98 | pub fn file2path(&self, file: VfsFile) -> PathBuf { |
187 | let rel_path = &self.files[file].path; | 99 | let rel_path = &self.files[file].path; |
188 | let root_path = &self.roots[self.files[file].root].root; | 100 | let root_path = &self.roots.path(self.files[file].root); |
189 | rel_path.to_path(root_path) | 101 | rel_path.to_path(root_path) |
190 | } | 102 | } |
191 | 103 | ||
192 | pub fn file_for_path(&self, path: &Path) -> Option<VfsFile> { | 104 | pub fn n_roots(&self) -> usize { |
193 | if let Some((_root, _path, Some(file))) = self.find_root(path) { | ||
194 | return Some(file); | ||
195 | } | ||
196 | None | ||
197 | } | ||
198 | |||
199 | pub fn num_roots(&self) -> usize { | ||
200 | self.roots.len() | 105 | self.roots.len() |
201 | } | 106 | } |
202 | 107 | ||
@@ -207,7 +112,7 @@ impl Vfs { | |||
207 | } else { | 112 | } else { |
208 | let text = fs::read_to_string(path).unwrap_or_default(); | 113 | let text = fs::read_to_string(path).unwrap_or_default(); |
209 | let text = Arc::new(text); | 114 | let text = Arc::new(text); |
210 | let file = self.add_file(root, rel_path.clone(), Arc::clone(&text), false); | 115 | let file = self.raw_add_file(root, rel_path.clone(), Arc::clone(&text), false); |
211 | let change = VfsChange::AddFile { file, text, root, path: rel_path }; | 116 | let change = VfsChange::AddFile { file, text, root, path: rel_path }; |
212 | self.pending_changes.push(change); | 117 | self.pending_changes.push(change); |
213 | Some(file) | 118 | Some(file) |
@@ -216,6 +121,39 @@ impl Vfs { | |||
216 | None | 121 | None |
217 | } | 122 | } |
218 | 123 | ||
124 | pub fn add_file_overlay(&mut self, path: &Path, text: String) -> Option<VfsFile> { | ||
125 | let (root, rel_path, file) = self.find_root(path)?; | ||
126 | if let Some(file) = file { | ||
127 | self.change_file_event(file, text, true); | ||
128 | Some(file) | ||
129 | } else { | ||
130 | self.add_file_event(root, rel_path, text, true) | ||
131 | } | ||
132 | } | ||
133 | |||
134 | pub fn change_file_overlay(&mut self, path: &Path, new_text: String) { | ||
135 | if let Some((_root, _path, file)) = self.find_root(path) { | ||
136 | let file = file.expect("can't change a file which wasn't added"); | ||
137 | self.change_file_event(file, new_text, true); | ||
138 | } | ||
139 | } | ||
140 | |||
141 | pub fn remove_file_overlay(&mut self, path: &Path) -> Option<VfsFile> { | ||
142 | let (root, rel_path, file) = self.find_root(path)?; | ||
143 | let file = file.expect("can't remove a file which wasn't added"); | ||
144 | let full_path = rel_path.to_path(&self.roots.path(root)); | ||
145 | if let Ok(text) = fs::read_to_string(&full_path) { | ||
146 | self.change_file_event(file, text, false); | ||
147 | } else { | ||
148 | self.remove_file_event(root, rel_path, file); | ||
149 | } | ||
150 | Some(file) | ||
151 | } | ||
152 | |||
153 | pub fn commit_changes(&mut self) -> Vec<VfsChange> { | ||
154 | mem::replace(&mut self.pending_changes, Vec::new()) | ||
155 | } | ||
156 | |||
219 | pub fn task_receiver(&self) -> &Receiver<io::TaskResult> { | 157 | pub fn task_receiver(&self) -> &Receiver<io::TaskResult> { |
220 | self.worker.receiver() | 158 | self.worker.receiver() |
221 | } | 159 | } |
@@ -237,7 +175,7 @@ impl Vfs { | |||
237 | continue; | 175 | continue; |
238 | } | 176 | } |
239 | let text = Arc::new(text); | 177 | let text = Arc::new(text); |
240 | let file = self.add_file(root, path.clone(), Arc::clone(&text), false); | 178 | let file = self.raw_add_file(root, path.clone(), Arc::clone(&text), false); |
241 | cur_files.push((file, path, text)); | 179 | cur_files.push((file, path, text)); |
242 | } | 180 | } |
243 | 181 | ||
@@ -245,15 +183,19 @@ impl Vfs { | |||
245 | self.pending_changes.push(change); | 183 | self.pending_changes.push(change); |
246 | } | 184 | } |
247 | TaskResult::SingleFile { root, path, text } => { | 185 | TaskResult::SingleFile { root, path, text } => { |
248 | match (self.find_file(root, &path), text) { | 186 | let existing_file = self.find_file(root, &path); |
187 | if existing_file.map(|file| self.files[file].is_overlayed) == Some(true) { | ||
188 | return; | ||
189 | } | ||
190 | match (existing_file, text) { | ||
249 | (Some(file), None) => { | 191 | (Some(file), None) => { |
250 | self.do_remove_file(root, path, file, false); | 192 | self.remove_file_event(root, path, file); |
251 | } | 193 | } |
252 | (None, Some(text)) => { | 194 | (None, Some(text)) => { |
253 | self.do_add_file(root, path, text, false); | 195 | self.add_file_event(root, path, text, false); |
254 | } | 196 | } |
255 | (Some(file), Some(text)) => { | 197 | (Some(file), Some(text)) => { |
256 | self.do_change_file(file, text, false); | 198 | self.change_file_event(file, text, false); |
257 | } | 199 | } |
258 | (None, None) => (), | 200 | (None, None) => (), |
259 | } | 201 | } |
@@ -261,7 +203,10 @@ impl Vfs { | |||
261 | } | 203 | } |
262 | } | 204 | } |
263 | 205 | ||
264 | fn do_add_file( | 206 | // *_event calls change the state of VFS and push a change onto pending |
207 | // changes array. | ||
208 | |||
209 | fn add_file_event( | ||
265 | &mut self, | 210 | &mut self, |
266 | root: VfsRoot, | 211 | root: VfsRoot, |
267 | path: RelativePathBuf, | 212 | path: RelativePathBuf, |
@@ -269,74 +214,25 @@ impl Vfs { | |||
269 | is_overlay: bool, | 214 | is_overlay: bool, |
270 | ) -> Option<VfsFile> { | 215 | ) -> Option<VfsFile> { |
271 | let text = Arc::new(text); | 216 | let text = Arc::new(text); |
272 | let file = self.add_file(root, path.clone(), text.clone(), is_overlay); | 217 | let file = self.raw_add_file(root, path.clone(), text.clone(), is_overlay); |
273 | self.pending_changes.push(VfsChange::AddFile { file, root, path, text }); | 218 | self.pending_changes.push(VfsChange::AddFile { file, root, path, text }); |
274 | Some(file) | 219 | Some(file) |
275 | } | 220 | } |
276 | 221 | ||
277 | fn do_change_file(&mut self, file: VfsFile, text: String, is_overlay: bool) { | 222 | fn change_file_event(&mut self, file: VfsFile, text: String, is_overlay: bool) { |
278 | if !is_overlay && self.files[file].is_overlayed { | ||
279 | return; | ||
280 | } | ||
281 | let text = Arc::new(text); | 223 | let text = Arc::new(text); |
282 | self.change_file(file, text.clone(), is_overlay); | 224 | self.raw_change_file(file, text.clone(), is_overlay); |
283 | self.pending_changes.push(VfsChange::ChangeFile { file, text }); | 225 | self.pending_changes.push(VfsChange::ChangeFile { file, text }); |
284 | } | 226 | } |
285 | 227 | ||
286 | fn do_remove_file( | 228 | fn remove_file_event(&mut self, root: VfsRoot, path: RelativePathBuf, file: VfsFile) { |
287 | &mut self, | 229 | self.raw_remove_file(file); |
288 | root: VfsRoot, | ||
289 | path: RelativePathBuf, | ||
290 | file: VfsFile, | ||
291 | is_overlay: bool, | ||
292 | ) { | ||
293 | if !is_overlay && self.files[file].is_overlayed { | ||
294 | return; | ||
295 | } | ||
296 | self.remove_file(file); | ||
297 | self.pending_changes.push(VfsChange::RemoveFile { root, path, file }); | 230 | self.pending_changes.push(VfsChange::RemoveFile { root, path, file }); |
298 | } | 231 | } |
299 | 232 | ||
300 | pub fn add_file_overlay(&mut self, path: &Path, text: String) -> Option<VfsFile> { | 233 | // raw_* calls change the state of VFS, but **do not** emit events. |
301 | if let Some((root, rel_path, file)) = self.find_root(path) { | ||
302 | if let Some(file) = file { | ||
303 | self.do_change_file(file, text, true); | ||
304 | Some(file) | ||
305 | } else { | ||
306 | self.do_add_file(root, rel_path, text, true) | ||
307 | } | ||
308 | } else { | ||
309 | None | ||
310 | } | ||
311 | } | ||
312 | |||
313 | pub fn change_file_overlay(&mut self, path: &Path, new_text: String) { | ||
314 | if let Some((_root, _path, file)) = self.find_root(path) { | ||
315 | let file = file.expect("can't change a file which wasn't added"); | ||
316 | self.do_change_file(file, new_text, true); | ||
317 | } | ||
318 | } | ||
319 | |||
320 | pub fn remove_file_overlay(&mut self, path: &Path) -> Option<VfsFile> { | ||
321 | if let Some((root, path, file)) = self.find_root(path) { | ||
322 | let file = file.expect("can't remove a file which wasn't added"); | ||
323 | let full_path = path.to_path(&self.roots[root].root); | ||
324 | if let Ok(text) = fs::read_to_string(&full_path) { | ||
325 | self.do_change_file(file, text, true); | ||
326 | } else { | ||
327 | self.do_remove_file(root, path, file, true); | ||
328 | } | ||
329 | Some(file) | ||
330 | } else { | ||
331 | None | ||
332 | } | ||
333 | } | ||
334 | |||
335 | pub fn commit_changes(&mut self) -> Vec<VfsChange> { | ||
336 | mem::replace(&mut self.pending_changes, Vec::new()) | ||
337 | } | ||
338 | 234 | ||
339 | fn add_file( | 235 | fn raw_add_file( |
340 | &mut self, | 236 | &mut self, |
341 | root: VfsRoot, | 237 | root: VfsRoot, |
342 | path: RelativePathBuf, | 238 | path: RelativePathBuf, |
@@ -349,13 +245,13 @@ impl Vfs { | |||
349 | file | 245 | file |
350 | } | 246 | } |
351 | 247 | ||
352 | fn change_file(&mut self, file: VfsFile, new_text: Arc<String>, is_overlayed: bool) { | 248 | fn raw_change_file(&mut self, file: VfsFile, new_text: Arc<String>, is_overlayed: bool) { |
353 | let mut file_data = &mut self.files[file]; | 249 | let mut file_data = &mut self.files[file]; |
354 | file_data.text = new_text; | 250 | file_data.text = new_text; |
355 | file_data.is_overlayed = is_overlayed; | 251 | file_data.is_overlayed = is_overlayed; |
356 | } | 252 | } |
357 | 253 | ||
358 | fn remove_file(&mut self, file: VfsFile) { | 254 | fn raw_remove_file(&mut self, file: VfsFile) { |
359 | // FIXME: use arena with removal | 255 | // FIXME: use arena with removal |
360 | self.files[file].text = Default::default(); | 256 | self.files[file].text = Default::default(); |
361 | self.files[file].path = Default::default(); | 257 | self.files[file].path = Default::default(); |
diff --git a/crates/ra_vfs/src/roots.rs b/crates/ra_vfs/src/roots.rs new file mode 100644 index 000000000..5e2776a35 --- /dev/null +++ b/crates/ra_vfs/src/roots.rs | |||
@@ -0,0 +1,109 @@ | |||
1 | use std::{ | ||
2 | iter, | ||
3 | sync::Arc, | ||
4 | path::{Path, PathBuf}, | ||
5 | }; | ||
6 | |||
7 | use relative_path::{ RelativePath, RelativePathBuf}; | ||
8 | use ra_arena::{impl_arena_id, Arena, RawId}; | ||
9 | |||
10 | /// VfsRoot identifies a watched directory on the file system. | ||
11 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] | ||
12 | pub struct VfsRoot(pub RawId); | ||
13 | impl_arena_id!(VfsRoot); | ||
14 | |||
15 | /// Describes the contents of a single source root. | ||
16 | /// | ||
17 | /// `RootConfig` can be thought of as a glob pattern like `src/**.rs` which | ||
18 | /// specifies the source root or as a function which takes a `PathBuf` and | ||
19 | /// returns `true` iff path belongs to the source root | ||
20 | struct RootData { | ||
21 | path: PathBuf, | ||
22 | // result of `root.canonicalize()` if that differs from `root`; `None` otherwise. | ||
23 | canonical_path: Option<PathBuf>, | ||
24 | excluded_dirs: Vec<RelativePathBuf>, | ||
25 | } | ||
26 | |||
27 | pub(crate) struct Roots { | ||
28 | roots: Arena<VfsRoot, Arc<RootData>>, | ||
29 | } | ||
30 | |||
31 | impl Roots { | ||
32 | pub(crate) fn new(mut paths: Vec<PathBuf>) -> Roots { | ||
33 | let mut roots = Arena::default(); | ||
34 | // A hack to make nesting work. | ||
35 | paths.sort_by_key(|it| std::cmp::Reverse(it.as_os_str().len())); | ||
36 | paths.dedup(); | ||
37 | for (i, path) in paths.iter().enumerate() { | ||
38 | let nested_roots = | ||
39 | paths[..i].iter().filter_map(|it| rel_path(path, it)).collect::<Vec<_>>(); | ||
40 | |||
41 | let config = Arc::new(RootData::new(path.clone(), nested_roots)); | ||
42 | |||
43 | roots.alloc(config.clone()); | ||
44 | } | ||
45 | Roots { roots } | ||
46 | } | ||
47 | pub(crate) fn find(&self, path: &Path) -> Option<(VfsRoot, RelativePathBuf)> { | ||
48 | self.iter().find_map(|root| { | ||
49 | let rel_path = self.contains(root, path)?; | ||
50 | Some((root, rel_path)) | ||
51 | }) | ||
52 | } | ||
53 | pub(crate) fn len(&self) -> usize { | ||
54 | self.roots.len() | ||
55 | } | ||
56 | pub(crate) fn iter<'a>(&'a self) -> impl Iterator<Item = VfsRoot> + 'a { | ||
57 | self.roots.iter().map(|(id, _)| id) | ||
58 | } | ||
59 | pub(crate) fn path(&self, root: VfsRoot) -> &Path { | ||
60 | self.roots[root].path.as_path() | ||
61 | } | ||
62 | /// Checks if root contains a path and returns a root-relative path. | ||
63 | pub(crate) fn contains(&self, root: VfsRoot, path: &Path) -> Option<RelativePathBuf> { | ||
64 | let data = &self.roots[root]; | ||
65 | iter::once(&data.path) | ||
66 | .chain(data.canonical_path.as_ref().into_iter()) | ||
67 | .find_map(|base| rel_path(base, path)) | ||
68 | .filter(|path| !data.excluded_dirs.contains(path)) | ||
69 | .filter(|path| !data.is_excluded(path)) | ||
70 | } | ||
71 | } | ||
72 | |||
73 | impl RootData { | ||
74 | fn new(path: PathBuf, excluded_dirs: Vec<RelativePathBuf>) -> RootData { | ||
75 | let mut canonical_path = path.canonicalize().ok(); | ||
76 | if Some(&path) == canonical_path.as_ref() { | ||
77 | canonical_path = None; | ||
78 | } | ||
79 | RootData { path, canonical_path, excluded_dirs } | ||
80 | } | ||
81 | |||
82 | fn is_excluded(&self, path: &RelativePath) -> bool { | ||
83 | if self.excluded_dirs.iter().any(|it| it == path) { | ||
84 | return true; | ||
85 | } | ||
86 | // Ignore some common directories. | ||
87 | // | ||
88 | // FIXME: don't hard-code, specify at source-root creation time using | ||
89 | // gitignore | ||
90 | for (i, c) in path.components().enumerate() { | ||
91 | if let relative_path::Component::Normal(c) = c { | ||
92 | if (i == 0 && c == "target") || c == ".git" || c == "node_modules" { | ||
93 | return true; | ||
94 | } | ||
95 | } | ||
96 | } | ||
97 | |||
98 | match path.extension() { | ||
99 | None | Some("rs") => false, | ||
100 | _ => true, | ||
101 | } | ||
102 | } | ||
103 | } | ||
104 | |||
105 | fn rel_path(base: &Path, path: &Path) -> Option<RelativePathBuf> { | ||
106 | let path = path.strip_prefix(base).ok()?; | ||
107 | let path = RelativePathBuf::from_path(path).unwrap(); | ||
108 | Some(path) | ||
109 | } | ||