diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs | 2 | ||||
-rw-r--r-- | crates/ra_assists/src/handlers/inline_local_variable.rs | 2 | ||||
-rw-r--r-- | crates/ra_ide/src/call_hierarchy.rs | 3 | ||||
-rw-r--r-- | crates/ra_ide/src/display/structure.rs | 428 | ||||
-rw-r--r-- | crates/ra_ide/src/expand_macro.rs | 241 | ||||
-rw-r--r-- | crates/ra_ide/src/folding_ranges.rs | 161 | ||||
-rw-r--r-- | crates/ra_ide/src/lib.rs | 6 | ||||
-rw-r--r-- | crates/ra_ide/src/references.rs | 9 | ||||
-rw-r--r-- | crates/ra_ide/src/references/rename.rs | 44 | ||||
-rw-r--r-- | crates/ra_ide_db/src/search.rs | 12 | ||||
-rw-r--r-- | crates/ra_project_model/src/project_json.rs | 6 | ||||
-rw-r--r-- | crates/rust-analyzer/src/config.rs | 2 | ||||
-rw-r--r-- | crates/rust-analyzer/src/global_state.rs | 2 | ||||
-rw-r--r-- | crates/rust-analyzer/src/main_loop.rs | 12 | ||||
-rw-r--r-- | crates/rust-analyzer/src/reload.rs | 5 | ||||
-rw-r--r-- | crates/rust-analyzer/src/to_proto.rs | 31 | ||||
-rw-r--r-- | crates/test_utils/src/lib.rs | 27 |
17 files changed, 499 insertions, 494 deletions
diff --git a/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs index bdf9d7ae2..ca19cf198 100644 --- a/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs | |||
@@ -53,7 +53,7 @@ pub(crate) fn extract_struct_from_enum_variant( | |||
53 | target, | 53 | target, |
54 | |builder| { | 54 | |builder| { |
55 | let definition = Definition::ModuleDef(ModuleDef::EnumVariant(variant_hir)); | 55 | let definition = Definition::ModuleDef(ModuleDef::EnumVariant(variant_hir)); |
56 | let res = definition.find_usages(&ctx.db(), None); | 56 | let res = definition.find_usages(&ctx.sema, None); |
57 | let start_offset = variant.parent_enum().syntax().text_range().start(); | 57 | let start_offset = variant.parent_enum().syntax().text_range().start(); |
58 | let mut visited_modules_set = FxHashSet::default(); | 58 | let mut visited_modules_set = FxHashSet::default(); |
59 | visited_modules_set.insert(current_module); | 59 | visited_modules_set.insert(current_module); |
diff --git a/crates/ra_assists/src/handlers/inline_local_variable.rs b/crates/ra_assists/src/handlers/inline_local_variable.rs index f4fb0056b..259839535 100644 --- a/crates/ra_assists/src/handlers/inline_local_variable.rs +++ b/crates/ra_assists/src/handlers/inline_local_variable.rs | |||
@@ -44,7 +44,7 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O | |||
44 | 44 | ||
45 | let def = ctx.sema.to_def(&bind_pat)?; | 45 | let def = ctx.sema.to_def(&bind_pat)?; |
46 | let def = Definition::Local(def); | 46 | let def = Definition::Local(def); |
47 | let refs = def.find_usages(ctx.db(), None); | 47 | let refs = def.find_usages(&ctx.sema, None); |
48 | if refs.is_empty() { | 48 | if refs.is_empty() { |
49 | mark::hit!(test_not_applicable_if_variable_unused); | 49 | mark::hit!(test_not_applicable_if_variable_unused); |
50 | return None; | 50 | return None; |
diff --git a/crates/ra_ide/src/call_hierarchy.rs b/crates/ra_ide/src/call_hierarchy.rs index bd0e48834..884353808 100644 --- a/crates/ra_ide/src/call_hierarchy.rs +++ b/crates/ra_ide/src/call_hierarchy.rs | |||
@@ -39,10 +39,11 @@ pub(crate) fn call_hierarchy( | |||
39 | 39 | ||
40 | pub(crate) fn incoming_calls(db: &RootDatabase, position: FilePosition) -> Option<Vec<CallItem>> { | 40 | pub(crate) fn incoming_calls(db: &RootDatabase, position: FilePosition) -> Option<Vec<CallItem>> { |
41 | let sema = Semantics::new(db); | 41 | let sema = Semantics::new(db); |
42 | |||
42 | // 1. Find all refs | 43 | // 1. Find all refs |
43 | // 2. Loop through refs and determine unique fndef. This will become our `from: CallHierarchyItem,` in the reply. | 44 | // 2. Loop through refs and determine unique fndef. This will become our `from: CallHierarchyItem,` in the reply. |
44 | // 3. Add ranges relative to the start of the fndef. | 45 | // 3. Add ranges relative to the start of the fndef. |
45 | let refs = references::find_all_refs(db, position, None)?; | 46 | let refs = references::find_all_refs(&sema, position, None)?; |
46 | 47 | ||
47 | let mut calls = CallLocations::default(); | 48 | let mut calls = CallLocations::default(); |
48 | 49 | ||
diff --git a/crates/ra_ide/src/display/structure.rs b/crates/ra_ide/src/display/structure.rs index aad5a8e4d..c22a5d17b 100644 --- a/crates/ra_ide/src/display/structure.rs +++ b/crates/ra_ide/src/display/structure.rs | |||
@@ -173,12 +173,19 @@ fn structure_node(node: &SyntaxNode) -> Option<StructureNode> { | |||
173 | 173 | ||
174 | #[cfg(test)] | 174 | #[cfg(test)] |
175 | mod tests { | 175 | mod tests { |
176 | use expect::{expect, Expect}; | ||
177 | |||
176 | use super::*; | 178 | use super::*; |
177 | use insta::assert_debug_snapshot; | 179 | |
180 | fn check(ra_fixture: &str, expect: Expect) { | ||
181 | let file = SourceFile::parse(ra_fixture).ok().unwrap(); | ||
182 | let structure = file_structure(&file); | ||
183 | expect.assert_debug_eq(&structure) | ||
184 | } | ||
178 | 185 | ||
179 | #[test] | 186 | #[test] |
180 | fn test_file_structure() { | 187 | fn test_file_structure() { |
181 | let file = SourceFile::parse( | 188 | check( |
182 | r#" | 189 | r#" |
183 | struct Foo { | 190 | struct Foo { |
184 | x: i32 | 191 | x: i32 |
@@ -223,216 +230,211 @@ fn obsolete() {} | |||
223 | #[deprecated(note = "for awhile")] | 230 | #[deprecated(note = "for awhile")] |
224 | fn very_obsolete() {} | 231 | fn very_obsolete() {} |
225 | "#, | 232 | "#, |
226 | ) | 233 | expect![[r#" |
227 | .ok() | 234 | [ |
228 | .unwrap(); | 235 | StructureNode { |
229 | let structure = file_structure(&file); | 236 | parent: None, |
230 | assert_debug_snapshot!(structure, | 237 | label: "Foo", |
231 | @r###" | 238 | navigation_range: 8..11, |
232 | [ | 239 | node_range: 1..26, |
233 | StructureNode { | 240 | kind: STRUCT_DEF, |
234 | parent: None, | 241 | detail: None, |
235 | label: "Foo", | 242 | deprecated: false, |
236 | navigation_range: 8..11, | 243 | }, |
237 | node_range: 1..26, | 244 | StructureNode { |
238 | kind: STRUCT_DEF, | 245 | parent: Some( |
239 | detail: None, | 246 | 0, |
240 | deprecated: false, | 247 | ), |
241 | }, | 248 | label: "x", |
242 | StructureNode { | 249 | navigation_range: 18..19, |
243 | parent: Some( | 250 | node_range: 18..24, |
244 | 0, | 251 | kind: RECORD_FIELD_DEF, |
245 | ), | 252 | detail: Some( |
246 | label: "x", | 253 | "i32", |
247 | navigation_range: 18..19, | 254 | ), |
248 | node_range: 18..24, | 255 | deprecated: false, |
249 | kind: RECORD_FIELD_DEF, | 256 | }, |
250 | detail: Some( | 257 | StructureNode { |
251 | "i32", | 258 | parent: None, |
252 | ), | 259 | label: "m", |
253 | deprecated: false, | 260 | navigation_range: 32..33, |
254 | }, | 261 | node_range: 28..158, |
255 | StructureNode { | 262 | kind: MODULE, |
256 | parent: None, | 263 | detail: None, |
257 | label: "m", | 264 | deprecated: false, |
258 | navigation_range: 32..33, | 265 | }, |
259 | node_range: 28..158, | 266 | StructureNode { |
260 | kind: MODULE, | 267 | parent: Some( |
261 | detail: None, | 268 | 2, |
262 | deprecated: false, | 269 | ), |
263 | }, | 270 | label: "bar1", |
264 | StructureNode { | 271 | navigation_range: 43..47, |
265 | parent: Some( | 272 | node_range: 40..52, |
266 | 2, | 273 | kind: FN_DEF, |
267 | ), | 274 | detail: Some( |
268 | label: "bar1", | 275 | "fn()", |
269 | navigation_range: 43..47, | 276 | ), |
270 | node_range: 40..52, | 277 | deprecated: false, |
271 | kind: FN_DEF, | 278 | }, |
272 | detail: Some( | 279 | StructureNode { |
273 | "fn()", | 280 | parent: Some( |
274 | ), | 281 | 2, |
275 | deprecated: false, | 282 | ), |
276 | }, | 283 | label: "bar2", |
277 | StructureNode { | 284 | navigation_range: 60..64, |
278 | parent: Some( | 285 | node_range: 57..81, |
279 | 2, | 286 | kind: FN_DEF, |
280 | ), | 287 | detail: Some( |
281 | label: "bar2", | 288 | "fn<T>(t: T) -> T", |
282 | navigation_range: 60..64, | 289 | ), |
283 | node_range: 57..81, | 290 | deprecated: false, |
284 | kind: FN_DEF, | 291 | }, |
285 | detail: Some( | 292 | StructureNode { |
286 | "fn<T>(t: T) -> T", | 293 | parent: Some( |
287 | ), | 294 | 2, |
288 | deprecated: false, | 295 | ), |
289 | }, | 296 | label: "bar3", |
290 | StructureNode { | 297 | navigation_range: 89..93, |
291 | parent: Some( | 298 | node_range: 86..156, |
292 | 2, | 299 | kind: FN_DEF, |
293 | ), | 300 | detail: Some( |
294 | label: "bar3", | 301 | "fn<A, B>(a: A, b: B) -> Vec< u32 >", |
295 | navigation_range: 89..93, | 302 | ), |
296 | node_range: 86..156, | 303 | deprecated: false, |
297 | kind: FN_DEF, | 304 | }, |
298 | detail: Some( | 305 | StructureNode { |
299 | "fn<A, B>(a: A, b: B) -> Vec< u32 >", | 306 | parent: None, |
300 | ), | 307 | label: "E", |
301 | deprecated: false, | 308 | navigation_range: 165..166, |
302 | }, | 309 | node_range: 160..180, |
303 | StructureNode { | 310 | kind: ENUM_DEF, |
304 | parent: None, | 311 | detail: None, |
305 | label: "E", | 312 | deprecated: false, |
306 | navigation_range: 165..166, | 313 | }, |
307 | node_range: 160..180, | 314 | StructureNode { |
308 | kind: ENUM_DEF, | 315 | parent: Some( |
309 | detail: None, | 316 | 6, |
310 | deprecated: false, | 317 | ), |
311 | }, | 318 | label: "X", |
312 | StructureNode { | 319 | navigation_range: 169..170, |
313 | parent: Some( | 320 | node_range: 169..170, |
314 | 6, | 321 | kind: ENUM_VARIANT, |
315 | ), | 322 | detail: None, |
316 | label: "X", | 323 | deprecated: false, |
317 | navigation_range: 169..170, | 324 | }, |
318 | node_range: 169..170, | 325 | StructureNode { |
319 | kind: ENUM_VARIANT, | 326 | parent: Some( |
320 | detail: None, | 327 | 6, |
321 | deprecated: false, | 328 | ), |
322 | }, | 329 | label: "Y", |
323 | StructureNode { | 330 | navigation_range: 172..173, |
324 | parent: Some( | 331 | node_range: 172..178, |
325 | 6, | 332 | kind: ENUM_VARIANT, |
326 | ), | 333 | detail: None, |
327 | label: "Y", | 334 | deprecated: false, |
328 | navigation_range: 172..173, | 335 | }, |
329 | node_range: 172..178, | 336 | StructureNode { |
330 | kind: ENUM_VARIANT, | 337 | parent: None, |
331 | detail: None, | 338 | label: "T", |
332 | deprecated: false, | 339 | navigation_range: 186..187, |
333 | }, | 340 | node_range: 181..193, |
334 | StructureNode { | 341 | kind: TYPE_ALIAS_DEF, |
335 | parent: None, | 342 | detail: Some( |
336 | label: "T", | 343 | "()", |
337 | navigation_range: 186..187, | 344 | ), |
338 | node_range: 181..193, | 345 | deprecated: false, |
339 | kind: TYPE_ALIAS_DEF, | 346 | }, |
340 | detail: Some( | 347 | StructureNode { |
341 | "()", | 348 | parent: None, |
342 | ), | 349 | label: "S", |
343 | deprecated: false, | 350 | navigation_range: 201..202, |
344 | }, | 351 | node_range: 194..213, |
345 | StructureNode { | 352 | kind: STATIC_DEF, |
346 | parent: None, | 353 | detail: Some( |
347 | label: "S", | 354 | "i32", |
348 | navigation_range: 201..202, | 355 | ), |
349 | node_range: 194..213, | 356 | deprecated: false, |
350 | kind: STATIC_DEF, | 357 | }, |
351 | detail: Some( | 358 | StructureNode { |
352 | "i32", | 359 | parent: None, |
353 | ), | 360 | label: "C", |
354 | deprecated: false, | 361 | navigation_range: 220..221, |
355 | }, | 362 | node_range: 214..232, |
356 | StructureNode { | 363 | kind: CONST_DEF, |
357 | parent: None, | 364 | detail: Some( |
358 | label: "C", | 365 | "i32", |
359 | navigation_range: 220..221, | 366 | ), |
360 | node_range: 214..232, | 367 | deprecated: false, |
361 | kind: CONST_DEF, | 368 | }, |
362 | detail: Some( | 369 | StructureNode { |
363 | "i32", | 370 | parent: None, |
364 | ), | 371 | label: "impl E", |
365 | deprecated: false, | 372 | navigation_range: 239..240, |
366 | }, | 373 | node_range: 234..243, |
367 | StructureNode { | 374 | kind: IMPL_DEF, |
368 | parent: None, | 375 | detail: None, |
369 | label: "impl E", | 376 | deprecated: false, |
370 | navigation_range: 239..240, | 377 | }, |
371 | node_range: 234..243, | 378 | StructureNode { |
372 | kind: IMPL_DEF, | 379 | parent: None, |
373 | detail: None, | 380 | label: "impl fmt::Debug for E", |
374 | deprecated: false, | 381 | navigation_range: 265..266, |
375 | }, | 382 | node_range: 245..269, |
376 | StructureNode { | 383 | kind: IMPL_DEF, |
377 | parent: None, | 384 | detail: None, |
378 | label: "impl fmt::Debug for E", | 385 | deprecated: false, |
379 | navigation_range: 265..266, | 386 | }, |
380 | node_range: 245..269, | 387 | StructureNode { |
381 | kind: IMPL_DEF, | 388 | parent: None, |
382 | detail: None, | 389 | label: "mc", |
383 | deprecated: false, | 390 | navigation_range: 284..286, |
384 | }, | 391 | node_range: 271..303, |
385 | StructureNode { | 392 | kind: MACRO_CALL, |
386 | parent: None, | 393 | detail: None, |
387 | label: "mc", | 394 | deprecated: false, |
388 | navigation_range: 284..286, | 395 | }, |
389 | node_range: 271..303, | 396 | StructureNode { |
390 | kind: MACRO_CALL, | 397 | parent: None, |
391 | detail: None, | 398 | label: "mcexp", |
392 | deprecated: false, | 399 | navigation_range: 334..339, |
393 | }, | 400 | node_range: 305..356, |
394 | StructureNode { | 401 | kind: MACRO_CALL, |
395 | parent: None, | 402 | detail: None, |
396 | label: "mcexp", | 403 | deprecated: false, |
397 | navigation_range: 334..339, | 404 | }, |
398 | node_range: 305..356, | 405 | StructureNode { |
399 | kind: MACRO_CALL, | 406 | parent: None, |
400 | detail: None, | 407 | label: "mcexp", |
401 | deprecated: false, | 408 | navigation_range: 387..392, |
402 | }, | 409 | node_range: 358..409, |
403 | StructureNode { | 410 | kind: MACRO_CALL, |
404 | parent: None, | 411 | detail: None, |
405 | label: "mcexp", | 412 | deprecated: false, |
406 | navigation_range: 387..392, | 413 | }, |
407 | node_range: 358..409, | 414 | StructureNode { |
408 | kind: MACRO_CALL, | 415 | parent: None, |
409 | detail: None, | 416 | label: "obsolete", |
410 | deprecated: false, | 417 | navigation_range: 428..436, |
411 | }, | 418 | node_range: 411..441, |
412 | StructureNode { | 419 | kind: FN_DEF, |
413 | parent: None, | 420 | detail: Some( |
414 | label: "obsolete", | 421 | "fn()", |
415 | navigation_range: 428..436, | 422 | ), |
416 | node_range: 411..441, | 423 | deprecated: true, |
417 | kind: FN_DEF, | 424 | }, |
418 | detail: Some( | 425 | StructureNode { |
419 | "fn()", | 426 | parent: None, |
420 | ), | 427 | label: "very_obsolete", |
421 | deprecated: true, | 428 | navigation_range: 481..494, |
422 | }, | 429 | node_range: 443..499, |
423 | StructureNode { | 430 | kind: FN_DEF, |
424 | parent: None, | 431 | detail: Some( |
425 | label: "very_obsolete", | 432 | "fn()", |
426 | navigation_range: 481..494, | 433 | ), |
427 | node_range: 443..499, | 434 | deprecated: true, |
428 | kind: FN_DEF, | 435 | }, |
429 | detail: Some( | 436 | ] |
430 | "fn()", | 437 | "#]], |
431 | ), | 438 | ); |
432 | deprecated: true, | ||
433 | }, | ||
434 | ] | ||
435 | "### | ||
436 | ); | ||
437 | } | 439 | } |
438 | } | 440 | } |
diff --git a/crates/ra_ide/src/expand_macro.rs b/crates/ra_ide/src/expand_macro.rs index 54a47aac0..043515f54 100644 --- a/crates/ra_ide/src/expand_macro.rs +++ b/crates/ra_ide/src/expand_macro.rs | |||
@@ -2,7 +2,9 @@ use hir::Semantics; | |||
2 | use ra_ide_db::RootDatabase; | 2 | use ra_ide_db::RootDatabase; |
3 | use ra_syntax::{ | 3 | use ra_syntax::{ |
4 | algo::{find_node_at_offset, SyntaxRewriter}, | 4 | algo::{find_node_at_offset, SyntaxRewriter}, |
5 | ast, AstNode, NodeOrToken, SyntaxKind, SyntaxNode, WalkEvent, T, | 5 | ast, AstNode, NodeOrToken, SyntaxKind, |
6 | SyntaxKind::*, | ||
7 | SyntaxNode, WalkEvent, T, | ||
6 | }; | 8 | }; |
7 | 9 | ||
8 | use crate::FilePosition; | 10 | use crate::FilePosition; |
@@ -65,8 +67,6 @@ fn expand_macro_recur( | |||
65 | // FIXME: It would also be cool to share logic here and in the mbe tests, | 67 | // FIXME: It would also be cool to share logic here and in the mbe tests, |
66 | // which are pretty unreadable at the moment. | 68 | // which are pretty unreadable at the moment. |
67 | fn insert_whitespaces(syn: SyntaxNode) -> String { | 69 | fn insert_whitespaces(syn: SyntaxNode) -> String { |
68 | use SyntaxKind::*; | ||
69 | |||
70 | let mut res = String::new(); | 70 | let mut res = String::new(); |
71 | let mut token_iter = syn | 71 | let mut token_iter = syn |
72 | .preorder_with_tokens() | 72 | .preorder_with_tokens() |
@@ -120,175 +120,164 @@ fn insert_whitespaces(syn: SyntaxNode) -> String { | |||
120 | 120 | ||
121 | #[cfg(test)] | 121 | #[cfg(test)] |
122 | mod tests { | 122 | mod tests { |
123 | use insta::assert_snapshot; | 123 | use expect::{expect, Expect}; |
124 | 124 | ||
125 | use crate::mock_analysis::analysis_and_position; | 125 | use crate::mock_analysis::analysis_and_position; |
126 | 126 | ||
127 | use super::*; | 127 | fn check(ra_fixture: &str, expect: Expect) { |
128 | 128 | let (analysis, pos) = analysis_and_position(ra_fixture); | |
129 | fn check_expand_macro(fixture: &str) -> ExpandedMacro { | 129 | let expansion = analysis.expand_macro(pos).unwrap().unwrap(); |
130 | let (analysis, pos) = analysis_and_position(fixture); | 130 | let actual = format!("{}\n{}", expansion.name, expansion.expansion); |
131 | analysis.expand_macro(pos).unwrap().unwrap() | 131 | expect.assert_eq(&actual); |
132 | } | 132 | } |
133 | 133 | ||
134 | #[test] | 134 | #[test] |
135 | fn macro_expand_recursive_expansion() { | 135 | fn macro_expand_recursive_expansion() { |
136 | let res = check_expand_macro( | 136 | check( |
137 | r#" | 137 | r#" |
138 | //- /lib.rs | 138 | macro_rules! bar { |
139 | macro_rules! bar { | 139 | () => { fn b() {} } |
140 | () => { fn b() {} } | 140 | } |
141 | } | 141 | macro_rules! foo { |
142 | macro_rules! foo { | 142 | () => { bar!(); } |
143 | () => { bar!(); } | 143 | } |
144 | } | 144 | macro_rules! baz { |
145 | macro_rules! baz { | 145 | () => { foo!(); } |
146 | () => { foo!(); } | 146 | } |
147 | } | 147 | f<|>oo!(); |
148 | f<|>oo!(); | 148 | "#, |
149 | "#, | 149 | expect![[r#" |
150 | foo | ||
151 | fn b(){} | ||
152 | "#]], | ||
150 | ); | 153 | ); |
151 | |||
152 | assert_eq!(res.name, "foo"); | ||
153 | assert_snapshot!(res.expansion, @r###" | ||
154 | fn b(){} | ||
155 | "###); | ||
156 | } | 154 | } |
157 | 155 | ||
158 | #[test] | 156 | #[test] |
159 | fn macro_expand_multiple_lines() { | 157 | fn macro_expand_multiple_lines() { |
160 | let res = check_expand_macro( | 158 | check( |
161 | r#" | 159 | r#" |
162 | //- /lib.rs | 160 | macro_rules! foo { |
163 | macro_rules! foo { | 161 | () => { |
164 | () => { | 162 | fn some_thing() -> u32 { |
165 | fn some_thing() -> u32 { | 163 | let a = 0; |
166 | let a = 0; | 164 | a + 10 |
167 | a + 10 | ||
168 | } | ||
169 | } | ||
170 | } | 165 | } |
171 | f<|>oo!(); | 166 | } |
167 | } | ||
168 | f<|>oo!(); | ||
172 | "#, | 169 | "#, |
170 | expect![[r#" | ||
171 | foo | ||
172 | fn some_thing() -> u32 { | ||
173 | let a = 0; | ||
174 | a+10 | ||
175 | }"#]], | ||
173 | ); | 176 | ); |
174 | |||
175 | assert_eq!(res.name, "foo"); | ||
176 | assert_snapshot!(res.expansion, @r###" | ||
177 | fn some_thing() -> u32 { | ||
178 | let a = 0; | ||
179 | a+10 | ||
180 | } | ||
181 | "###); | ||
182 | } | 177 | } |
183 | 178 | ||
184 | #[test] | 179 | #[test] |
185 | fn macro_expand_match_ast() { | 180 | fn macro_expand_match_ast() { |
186 | let res = check_expand_macro( | 181 | check( |
187 | r#" | 182 | r#" |
188 | //- /lib.rs | 183 | macro_rules! match_ast { |
189 | macro_rules! match_ast { | 184 | (match $node:ident { $($tt:tt)* }) => { match_ast!(match ($node) { $($tt)* }) }; |
190 | (match $node:ident { $($tt:tt)* }) => { match_ast!(match ($node) { $($tt)* }) }; | 185 | (match ($node:expr) { |
186 | $( ast::$ast:ident($it:ident) => $res:block, )* | ||
187 | _ => $catch_all:expr $(,)? | ||
188 | }) => {{ | ||
189 | $( if let Some($it) = ast::$ast::cast($node.clone()) $res else )* | ||
190 | { $catch_all } | ||
191 | }}; | ||
192 | } | ||
191 | 193 | ||
192 | (match ($node:expr) { | 194 | fn main() { |
193 | $( ast::$ast:ident($it:ident) => $res:block, )* | 195 | mat<|>ch_ast! { |
194 | _ => $catch_all:expr $(,)? | 196 | match container { |
195 | }) => {{ | 197 | ast::TraitDef(it) => {}, |
196 | $( if let Some($it) = ast::$ast::cast($node.clone()) $res else )* | 198 | ast::ImplDef(it) => {}, |
197 | { $catch_all } | 199 | _ => { continue }, |
198 | }}; | ||
199 | } | 200 | } |
200 | |||
201 | fn main() { | ||
202 | mat<|>ch_ast! { | ||
203 | match container { | ||
204 | ast::TraitDef(it) => {}, | ||
205 | ast::ImplDef(it) => {}, | ||
206 | _ => { continue }, | ||
207 | } | ||
208 | } | ||
209 | } | ||
210 | "#, | ||
211 | ); | ||
212 | |||
213 | assert_eq!(res.name, "match_ast"); | ||
214 | assert_snapshot!(res.expansion, @r###" | ||
215 | { | ||
216 | if let Some(it) = ast::TraitDef::cast(container.clone()){} | ||
217 | else if let Some(it) = ast::ImplDef::cast(container.clone()){} | ||
218 | else { | ||
219 | { | ||
220 | continue | ||
221 | } | 201 | } |
222 | } | ||
223 | } | 202 | } |
224 | "###); | 203 | "#, |
204 | expect![[r#" | ||
205 | match_ast | ||
206 | { | ||
207 | if let Some(it) = ast::TraitDef::cast(container.clone()){} | ||
208 | else if let Some(it) = ast::ImplDef::cast(container.clone()){} | ||
209 | else { | ||
210 | { | ||
211 | continue | ||
212 | } | ||
213 | } | ||
214 | }"#]], | ||
215 | ); | ||
225 | } | 216 | } |
226 | 217 | ||
227 | #[test] | 218 | #[test] |
228 | fn macro_expand_match_ast_inside_let_statement() { | 219 | fn macro_expand_match_ast_inside_let_statement() { |
229 | let res = check_expand_macro( | 220 | check( |
230 | r#" | 221 | r#" |
231 | //- /lib.rs | 222 | macro_rules! match_ast { |
232 | macro_rules! match_ast { | 223 | (match $node:ident { $($tt:tt)* }) => { match_ast!(match ($node) { $($tt)* }) }; |
233 | (match $node:ident { $($tt:tt)* }) => { match_ast!(match ($node) { $($tt)* }) }; | 224 | (match ($node:expr) {}) => {{}}; |
234 | (match ($node:expr) {}) => {{}}; | 225 | } |
235 | } | ||
236 | 226 | ||
237 | fn main() { | 227 | fn main() { |
238 | let p = f(|it| { | 228 | let p = f(|it| { |
239 | let res = mat<|>ch_ast! { match c {}}; | 229 | let res = mat<|>ch_ast! { match c {}}; |
240 | Some(res) | 230 | Some(res) |
241 | })?; | 231 | })?; |
242 | } | 232 | } |
243 | "#, | 233 | "#, |
234 | expect![[r#" | ||
235 | match_ast | ||
236 | {} | ||
237 | "#]], | ||
244 | ); | 238 | ); |
245 | |||
246 | assert_eq!(res.name, "match_ast"); | ||
247 | assert_snapshot!(res.expansion, @r###"{}"###); | ||
248 | } | 239 | } |
249 | 240 | ||
250 | #[test] | 241 | #[test] |
251 | fn macro_expand_inner_macro_fail_to_expand() { | 242 | fn macro_expand_inner_macro_fail_to_expand() { |
252 | let res = check_expand_macro( | 243 | check( |
253 | r#" | 244 | r#" |
254 | //- /lib.rs | 245 | macro_rules! bar { |
255 | macro_rules! bar { | 246 | (BAD) => {}; |
256 | (BAD) => {}; | 247 | } |
257 | } | 248 | macro_rules! foo { |
258 | macro_rules! foo { | 249 | () => {bar!()}; |
259 | () => {bar!()}; | 250 | } |
260 | } | ||
261 | 251 | ||
262 | fn main() { | 252 | fn main() { |
263 | let res = fo<|>o!(); | 253 | let res = fo<|>o!(); |
264 | } | 254 | } |
265 | "#, | 255 | "#, |
256 | expect![[r#" | ||
257 | foo | ||
258 | "#]], | ||
266 | ); | 259 | ); |
267 | |||
268 | assert_eq!(res.name, "foo"); | ||
269 | assert_snapshot!(res.expansion, @r###""###); | ||
270 | } | 260 | } |
271 | 261 | ||
272 | #[test] | 262 | #[test] |
273 | fn macro_expand_with_dollar_crate() { | 263 | fn macro_expand_with_dollar_crate() { |
274 | let res = check_expand_macro( | 264 | check( |
275 | r#" | 265 | r#" |
276 | //- /lib.rs | 266 | #[macro_export] |
277 | #[macro_export] | 267 | macro_rules! bar { |
278 | macro_rules! bar { | 268 | () => {0}; |
279 | () => {0}; | 269 | } |
280 | } | 270 | macro_rules! foo { |
281 | macro_rules! foo { | 271 | () => {$crate::bar!()}; |
282 | () => {$crate::bar!()}; | 272 | } |
283 | } | ||
284 | 273 | ||
285 | fn main() { | 274 | fn main() { |
286 | let res = fo<|>o!(); | 275 | let res = fo<|>o!(); |
287 | } | 276 | } |
288 | "#, | 277 | "#, |
278 | expect![[r#" | ||
279 | foo | ||
280 | 0 "#]], | ||
289 | ); | 281 | ); |
290 | |||
291 | assert_eq!(res.name, "foo"); | ||
292 | assert_snapshot!(res.expansion, @r###"0"###); | ||
293 | } | 282 | } |
294 | } | 283 | } |
diff --git a/crates/ra_ide/src/folding_ranges.rs b/crates/ra_ide/src/folding_ranges.rs index 8657377de..5cec689f8 100644 --- a/crates/ra_ide/src/folding_ranges.rs +++ b/crates/ra_ide/src/folding_ranges.rs | |||
@@ -15,6 +15,7 @@ pub enum FoldKind { | |||
15 | Imports, | 15 | Imports, |
16 | Mods, | 16 | Mods, |
17 | Block, | 17 | Block, |
18 | ArgList, | ||
18 | } | 19 | } |
19 | 20 | ||
20 | #[derive(Debug)] | 21 | #[derive(Debug)] |
@@ -83,6 +84,7 @@ fn fold_kind(kind: SyntaxKind) -> Option<FoldKind> { | |||
83 | match kind { | 84 | match kind { |
84 | COMMENT => Some(FoldKind::Comment), | 85 | COMMENT => Some(FoldKind::Comment), |
85 | USE_ITEM => Some(FoldKind::Imports), | 86 | USE_ITEM => Some(FoldKind::Imports), |
87 | ARG_LIST => Some(FoldKind::ArgList), | ||
86 | RECORD_FIELD_DEF_LIST | 88 | RECORD_FIELD_DEF_LIST |
87 | | RECORD_FIELD_PAT_LIST | 89 | | RECORD_FIELD_PAT_LIST |
88 | | ITEM_LIST | 90 | | ITEM_LIST |
@@ -196,89 +198,85 @@ fn contiguous_range_for_comment( | |||
196 | 198 | ||
197 | #[cfg(test)] | 199 | #[cfg(test)] |
198 | mod tests { | 200 | mod tests { |
201 | use test_utils::extract_tags; | ||
202 | |||
199 | use super::*; | 203 | use super::*; |
200 | use test_utils::extract_ranges; | ||
201 | 204 | ||
202 | fn do_check(text: &str, fold_kinds: &[FoldKind]) { | 205 | fn check(ra_fixture: &str) { |
203 | let (ranges, text) = extract_ranges(text, "fold"); | 206 | let (ranges, text) = extract_tags(ra_fixture, "fold"); |
207 | |||
204 | let parse = SourceFile::parse(&text); | 208 | let parse = SourceFile::parse(&text); |
205 | let folds = folding_ranges(&parse.tree()); | 209 | let folds = folding_ranges(&parse.tree()); |
206 | |||
207 | assert_eq!( | 210 | assert_eq!( |
208 | folds.len(), | 211 | folds.len(), |
209 | ranges.len(), | 212 | ranges.len(), |
210 | "The amount of folds is different than the expected amount" | 213 | "The amount of folds is different than the expected amount" |
211 | ); | 214 | ); |
212 | assert_eq!( | 215 | |
213 | folds.len(), | 216 | for (fold, (range, attr)) in folds.iter().zip(ranges.into_iter()) { |
214 | fold_kinds.len(), | ||
215 | "The amount of fold kinds is different than the expected amount" | ||
216 | ); | ||
217 | for ((fold, range), fold_kind) in | ||
218 | folds.iter().zip(ranges.into_iter()).zip(fold_kinds.iter()) | ||
219 | { | ||
220 | assert_eq!(fold.range.start(), range.start()); | 217 | assert_eq!(fold.range.start(), range.start()); |
221 | assert_eq!(fold.range.end(), range.end()); | 218 | assert_eq!(fold.range.end(), range.end()); |
222 | assert_eq!(&fold.kind, fold_kind); | 219 | |
220 | let kind = match fold.kind { | ||
221 | FoldKind::Comment => "comment", | ||
222 | FoldKind::Imports => "imports", | ||
223 | FoldKind::Mods => "mods", | ||
224 | FoldKind::Block => "block", | ||
225 | FoldKind::ArgList => "arglist", | ||
226 | }; | ||
227 | assert_eq!(kind, &attr.unwrap()); | ||
223 | } | 228 | } |
224 | } | 229 | } |
225 | 230 | ||
226 | #[test] | 231 | #[test] |
227 | fn test_fold_comments() { | 232 | fn test_fold_comments() { |
228 | let text = r#" | 233 | check( |
229 | <fold>// Hello | 234 | r#" |
235 | <fold comment>// Hello | ||
230 | // this is a multiline | 236 | // this is a multiline |
231 | // comment | 237 | // comment |
232 | //</fold> | 238 | //</fold> |
233 | 239 | ||
234 | // But this is not | 240 | // But this is not |
235 | 241 | ||
236 | fn main() <fold>{ | 242 | fn main() <fold block>{ |
237 | <fold>// We should | 243 | <fold comment>// We should |
238 | // also | 244 | // also |
239 | // fold | 245 | // fold |
240 | // this one.</fold> | 246 | // this one.</fold> |
241 | <fold>//! But this one is different | 247 | <fold comment>//! But this one is different |
242 | //! because it has another flavor</fold> | 248 | //! because it has another flavor</fold> |
243 | <fold>/* As does this | 249 | <fold comment>/* As does this |
244 | multiline comment */</fold> | 250 | multiline comment */</fold> |
245 | }</fold>"#; | 251 | }</fold>"#, |
246 | 252 | ); | |
247 | let fold_kinds = &[ | ||
248 | FoldKind::Comment, | ||
249 | FoldKind::Block, | ||
250 | FoldKind::Comment, | ||
251 | FoldKind::Comment, | ||
252 | FoldKind::Comment, | ||
253 | ]; | ||
254 | do_check(text, fold_kinds); | ||
255 | } | 253 | } |
256 | 254 | ||
257 | #[test] | 255 | #[test] |
258 | fn test_fold_imports() { | 256 | fn test_fold_imports() { |
259 | let text = r#" | 257 | check( |
260 | <fold>use std::<fold>{ | 258 | r#" |
259 | <fold imports>use std::<fold block>{ | ||
261 | str, | 260 | str, |
262 | vec, | 261 | vec, |
263 | io as iop | 262 | io as iop |
264 | }</fold>;</fold> | 263 | }</fold>;</fold> |
265 | 264 | ||
266 | fn main() <fold>{ | 265 | fn main() <fold block>{ |
267 | }</fold>"#; | 266 | }</fold>"#, |
268 | 267 | ); | |
269 | let folds = &[FoldKind::Imports, FoldKind::Block, FoldKind::Block]; | ||
270 | do_check(text, folds); | ||
271 | } | 268 | } |
272 | 269 | ||
273 | #[test] | 270 | #[test] |
274 | fn test_fold_mods() { | 271 | fn test_fold_mods() { |
275 | let text = r#" | 272 | check( |
273 | r#" | ||
276 | 274 | ||
277 | pub mod foo; | 275 | pub mod foo; |
278 | <fold>mod after_pub; | 276 | <fold mods>mod after_pub; |
279 | mod after_pub_next;</fold> | 277 | mod after_pub_next;</fold> |
280 | 278 | ||
281 | <fold>mod before_pub; | 279 | <fold mods>mod before_pub; |
282 | mod before_pub_next;</fold> | 280 | mod before_pub_next;</fold> |
283 | pub mod bar; | 281 | pub mod bar; |
284 | 282 | ||
@@ -286,90 +284,93 @@ mod not_folding_single; | |||
286 | pub mod foobar; | 284 | pub mod foobar; |
287 | pub not_folding_single_next; | 285 | pub not_folding_single_next; |
288 | 286 | ||
289 | <fold>#[cfg(test)] | 287 | <fold mods>#[cfg(test)] |
290 | mod with_attribute; | 288 | mod with_attribute; |
291 | mod with_attribute_next;</fold> | 289 | mod with_attribute_next;</fold> |
292 | 290 | ||
293 | fn main() <fold>{ | 291 | fn main() <fold block>{ |
294 | }</fold>"#; | 292 | }</fold>"#, |
295 | 293 | ); | |
296 | let folds = &[FoldKind::Mods, FoldKind::Mods, FoldKind::Mods, FoldKind::Block]; | ||
297 | do_check(text, folds); | ||
298 | } | 294 | } |
299 | 295 | ||
300 | #[test] | 296 | #[test] |
301 | fn test_fold_import_groups() { | 297 | fn test_fold_import_groups() { |
302 | let text = r#" | 298 | check( |
303 | <fold>use std::str; | 299 | r#" |
300 | <fold imports>use std::str; | ||
304 | use std::vec; | 301 | use std::vec; |
305 | use std::io as iop;</fold> | 302 | use std::io as iop;</fold> |
306 | 303 | ||
307 | <fold>use std::mem; | 304 | <fold imports>use std::mem; |
308 | use std::f64;</fold> | 305 | use std::f64;</fold> |
309 | 306 | ||
310 | use std::collections::HashMap; | 307 | use std::collections::HashMap; |
311 | // Some random comment | 308 | // Some random comment |
312 | use std::collections::VecDeque; | 309 | use std::collections::VecDeque; |
313 | 310 | ||
314 | fn main() <fold>{ | 311 | fn main() <fold block>{ |
315 | }</fold>"#; | 312 | }</fold>"#, |
316 | 313 | ); | |
317 | let folds = &[FoldKind::Imports, FoldKind::Imports, FoldKind::Block]; | ||
318 | do_check(text, folds); | ||
319 | } | 314 | } |
320 | 315 | ||
321 | #[test] | 316 | #[test] |
322 | fn test_fold_import_and_groups() { | 317 | fn test_fold_import_and_groups() { |
323 | let text = r#" | 318 | check( |
324 | <fold>use std::str; | 319 | r#" |
320 | <fold imports>use std::str; | ||
325 | use std::vec; | 321 | use std::vec; |
326 | use std::io as iop;</fold> | 322 | use std::io as iop;</fold> |
327 | 323 | ||
328 | <fold>use std::mem; | 324 | <fold imports>use std::mem; |
329 | use std::f64;</fold> | 325 | use std::f64;</fold> |
330 | 326 | ||
331 | <fold>use std::collections::<fold>{ | 327 | <fold imports>use std::collections::<fold block>{ |
332 | HashMap, | 328 | HashMap, |
333 | VecDeque, | 329 | VecDeque, |
334 | }</fold>;</fold> | 330 | }</fold>;</fold> |
335 | // Some random comment | 331 | // Some random comment |
336 | 332 | ||
337 | fn main() <fold>{ | 333 | fn main() <fold block>{ |
338 | }</fold>"#; | 334 | }</fold>"#, |
339 | 335 | ); | |
340 | let folds = &[ | ||
341 | FoldKind::Imports, | ||
342 | FoldKind::Imports, | ||
343 | FoldKind::Imports, | ||
344 | FoldKind::Block, | ||
345 | FoldKind::Block, | ||
346 | ]; | ||
347 | do_check(text, folds); | ||
348 | } | 336 | } |
349 | 337 | ||
350 | #[test] | 338 | #[test] |
351 | fn test_folds_macros() { | 339 | fn test_folds_macros() { |
352 | let text = r#" | 340 | check( |
353 | macro_rules! foo <fold>{ | 341 | r#" |
342 | macro_rules! foo <fold block>{ | ||
354 | ($($tt:tt)*) => { $($tt)* } | 343 | ($($tt:tt)*) => { $($tt)* } |
355 | }</fold> | 344 | }</fold> |
356 | "#; | 345 | "#, |
357 | 346 | ); | |
358 | let folds = &[FoldKind::Block]; | ||
359 | do_check(text, folds); | ||
360 | } | 347 | } |
361 | 348 | ||
362 | #[test] | 349 | #[test] |
363 | fn test_fold_match_arms() { | 350 | fn test_fold_match_arms() { |
364 | let text = r#" | 351 | check( |
365 | fn main() <fold>{ | 352 | r#" |
366 | match 0 <fold>{ | 353 | fn main() <fold block>{ |
354 | match 0 <fold block>{ | ||
367 | 0 => 0, | 355 | 0 => 0, |
368 | _ => 1, | 356 | _ => 1, |
369 | }</fold> | 357 | }</fold> |
370 | }</fold>"#; | 358 | }</fold>"#, |
359 | ); | ||
360 | } | ||
371 | 361 | ||
372 | let folds = &[FoldKind::Block, FoldKind::Block]; | 362 | #[test] |
373 | do_check(text, folds); | 363 | fn fold_big_calls() { |
364 | check( | ||
365 | r#" | ||
366 | fn main() <fold block>{ | ||
367 | frobnicate<fold arglist>( | ||
368 | 1, | ||
369 | 2, | ||
370 | 3, | ||
371 | )</fold> | ||
372 | }</fold> | ||
373 | "#, | ||
374 | ) | ||
374 | } | 375 | } |
375 | } | 376 | } |
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs index ecac5134e..8660278f1 100644 --- a/crates/ra_ide/src/lib.rs +++ b/crates/ra_ide/src/lib.rs | |||
@@ -75,7 +75,7 @@ pub use crate::{ | |||
75 | }, | 75 | }, |
76 | }; | 76 | }; |
77 | 77 | ||
78 | pub use hir::Documentation; | 78 | pub use hir::{Documentation, Semantics}; |
79 | pub use ra_assists::{Assist, AssistConfig, AssistId, ResolvedAssist}; | 79 | pub use ra_assists::{Assist, AssistConfig, AssistId, ResolvedAssist}; |
80 | pub use ra_db::{ | 80 | pub use ra_db::{ |
81 | Canceled, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange, SourceRoot, | 81 | Canceled, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange, SourceRoot, |
@@ -385,7 +385,9 @@ impl Analysis { | |||
385 | position: FilePosition, | 385 | position: FilePosition, |
386 | search_scope: Option<SearchScope>, | 386 | search_scope: Option<SearchScope>, |
387 | ) -> Cancelable<Option<ReferenceSearchResult>> { | 387 | ) -> Cancelable<Option<ReferenceSearchResult>> { |
388 | self.with_db(|db| references::find_all_refs(db, position, search_scope).map(|it| it.info)) | 388 | self.with_db(|db| { |
389 | references::find_all_refs(&Semantics::new(db), position, search_scope).map(|it| it.info) | ||
390 | }) | ||
389 | } | 391 | } |
390 | 392 | ||
391 | /// Returns a short text describing element at position. | 393 | /// Returns a short text describing element at position. |
diff --git a/crates/ra_ide/src/references.rs b/crates/ra_ide/src/references.rs index 3433fdae3..c2b0d5efe 100644 --- a/crates/ra_ide/src/references.rs +++ b/crates/ra_ide/src/references.rs | |||
@@ -86,12 +86,11 @@ impl IntoIterator for ReferenceSearchResult { | |||
86 | } | 86 | } |
87 | 87 | ||
88 | pub(crate) fn find_all_refs( | 88 | pub(crate) fn find_all_refs( |
89 | db: &RootDatabase, | 89 | sema: &Semantics<RootDatabase>, |
90 | position: FilePosition, | 90 | position: FilePosition, |
91 | search_scope: Option<SearchScope>, | 91 | search_scope: Option<SearchScope>, |
92 | ) -> Option<RangeInfo<ReferenceSearchResult>> { | 92 | ) -> Option<RangeInfo<ReferenceSearchResult>> { |
93 | let _p = profile("find_all_refs"); | 93 | let _p = profile("find_all_refs"); |
94 | let sema = Semantics::new(db); | ||
95 | let syntax = sema.parse(position.file_id).syntax().clone(); | 94 | let syntax = sema.parse(position.file_id).syntax().clone(); |
96 | 95 | ||
97 | let (opt_name, search_kind) = if let Some(name) = | 96 | let (opt_name, search_kind) = if let Some(name) = |
@@ -108,15 +107,15 @@ pub(crate) fn find_all_refs( | |||
108 | let RangeInfo { range, info: def } = find_name(&sema, &syntax, position, opt_name)?; | 107 | let RangeInfo { range, info: def } = find_name(&sema, &syntax, position, opt_name)?; |
109 | 108 | ||
110 | let references = def | 109 | let references = def |
111 | .find_usages(db, search_scope) | 110 | .find_usages(sema, search_scope) |
112 | .into_iter() | 111 | .into_iter() |
113 | .filter(|r| search_kind == ReferenceKind::Other || search_kind == r.kind) | 112 | .filter(|r| search_kind == ReferenceKind::Other || search_kind == r.kind) |
114 | .collect(); | 113 | .collect(); |
115 | 114 | ||
116 | let decl_range = def.try_to_nav(db)?.range(); | 115 | let decl_range = def.try_to_nav(sema.db)?.range(); |
117 | 116 | ||
118 | let declaration = Declaration { | 117 | let declaration = Declaration { |
119 | nav: def.try_to_nav(db)?, | 118 | nav: def.try_to_nav(sema.db)?, |
120 | kind: ReferenceKind::Other, | 119 | kind: ReferenceKind::Other, |
121 | access: decl_access(&def, &syntax, decl_range), | 120 | access: decl_access(&def, &syntax, decl_range), |
122 | }; | 121 | }; |
diff --git a/crates/ra_ide/src/references/rename.rs b/crates/ra_ide/src/references/rename.rs index 7ebc0adcf..b6a2266b4 100644 --- a/crates/ra_ide/src/references/rename.rs +++ b/crates/ra_ide/src/references/rename.rs | |||
@@ -24,23 +24,24 @@ pub(crate) fn rename( | |||
24 | position: FilePosition, | 24 | position: FilePosition, |
25 | new_name: &str, | 25 | new_name: &str, |
26 | ) -> Option<RangeInfo<SourceChange>> { | 26 | ) -> Option<RangeInfo<SourceChange>> { |
27 | let sema = Semantics::new(db); | ||
28 | |||
27 | match lex_single_valid_syntax_kind(new_name)? { | 29 | match lex_single_valid_syntax_kind(new_name)? { |
28 | SyntaxKind::IDENT | SyntaxKind::UNDERSCORE => (), | 30 | SyntaxKind::IDENT | SyntaxKind::UNDERSCORE => (), |
29 | SyntaxKind::SELF_KW => return rename_to_self(db, position), | 31 | SyntaxKind::SELF_KW => return rename_to_self(&sema, position), |
30 | _ => return None, | 32 | _ => return None, |
31 | } | 33 | } |
32 | 34 | ||
33 | let sema = Semantics::new(db); | ||
34 | let source_file = sema.parse(position.file_id); | 35 | let source_file = sema.parse(position.file_id); |
35 | let syntax = source_file.syntax(); | 36 | let syntax = source_file.syntax(); |
36 | if let Some(module) = find_module_at_offset(&sema, position, syntax) { | 37 | if let Some(module) = find_module_at_offset(&sema, position, syntax) { |
37 | rename_mod(db, position, module, new_name) | 38 | rename_mod(&sema, position, module, new_name) |
38 | } else if let Some(self_token) = | 39 | } else if let Some(self_token) = |
39 | syntax.token_at_offset(position.offset).find(|t| t.kind() == SyntaxKind::SELF_KW) | 40 | syntax.token_at_offset(position.offset).find(|t| t.kind() == SyntaxKind::SELF_KW) |
40 | { | 41 | { |
41 | rename_self_to_param(db, position, self_token, new_name) | 42 | rename_self_to_param(&sema, position, self_token, new_name) |
42 | } else { | 43 | } else { |
43 | rename_reference(sema.db, position, new_name) | 44 | rename_reference(&sema, position, new_name) |
44 | } | 45 | } |
45 | } | 46 | } |
46 | 47 | ||
@@ -97,7 +98,7 @@ fn source_edit_from_reference(reference: Reference, new_name: &str) -> SourceFil | |||
97 | } | 98 | } |
98 | 99 | ||
99 | fn rename_mod( | 100 | fn rename_mod( |
100 | db: &RootDatabase, | 101 | sema: &Semantics<RootDatabase>, |
101 | position: FilePosition, | 102 | position: FilePosition, |
102 | module: Module, | 103 | module: Module, |
103 | new_name: &str, | 104 | new_name: &str, |
@@ -105,12 +106,12 @@ fn rename_mod( | |||
105 | let mut source_file_edits = Vec::new(); | 106 | let mut source_file_edits = Vec::new(); |
106 | let mut file_system_edits = Vec::new(); | 107 | let mut file_system_edits = Vec::new(); |
107 | 108 | ||
108 | let src = module.definition_source(db); | 109 | let src = module.definition_source(sema.db); |
109 | let file_id = src.file_id.original_file(db); | 110 | let file_id = src.file_id.original_file(sema.db); |
110 | match src.value { | 111 | match src.value { |
111 | ModuleSource::SourceFile(..) => { | 112 | ModuleSource::SourceFile(..) => { |
112 | // mod is defined in path/to/dir/mod.rs | 113 | // mod is defined in path/to/dir/mod.rs |
113 | let dst = if module.is_mod_rs(db) { | 114 | let dst = if module.is_mod_rs(sema.db) { |
114 | format!("../{}/mod.rs", new_name) | 115 | format!("../{}/mod.rs", new_name) |
115 | } else { | 116 | } else { |
116 | format!("{}.rs", new_name) | 117 | format!("{}.rs", new_name) |
@@ -122,17 +123,17 @@ fn rename_mod( | |||
122 | ModuleSource::Module(..) => {} | 123 | ModuleSource::Module(..) => {} |
123 | } | 124 | } |
124 | 125 | ||
125 | if let Some(src) = module.declaration_source(db) { | 126 | if let Some(src) = module.declaration_source(sema.db) { |
126 | let file_id = src.file_id.original_file(db); | 127 | let file_id = src.file_id.original_file(sema.db); |
127 | let name = src.value.name()?; | 128 | let name = src.value.name()?; |
128 | let edit = SourceFileEdit { | 129 | let edit = SourceFileEdit { |
129 | file_id: file_id, | 130 | file_id, |
130 | edit: TextEdit::replace(name.syntax().text_range(), new_name.into()), | 131 | edit: TextEdit::replace(name.syntax().text_range(), new_name.into()), |
131 | }; | 132 | }; |
132 | source_file_edits.push(edit); | 133 | source_file_edits.push(edit); |
133 | } | 134 | } |
134 | 135 | ||
135 | let RangeInfo { range, info: refs } = find_all_refs(db, position, None)?; | 136 | let RangeInfo { range, info: refs } = find_all_refs(sema, position, None)?; |
136 | let ref_edits = refs | 137 | let ref_edits = refs |
137 | .references | 138 | .references |
138 | .into_iter() | 139 | .into_iter() |
@@ -142,8 +143,10 @@ fn rename_mod( | |||
142 | Some(RangeInfo::new(range, SourceChange::from_edits(source_file_edits, file_system_edits))) | 143 | Some(RangeInfo::new(range, SourceChange::from_edits(source_file_edits, file_system_edits))) |
143 | } | 144 | } |
144 | 145 | ||
145 | fn rename_to_self(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo<SourceChange>> { | 146 | fn rename_to_self( |
146 | let sema = Semantics::new(db); | 147 | sema: &Semantics<RootDatabase>, |
148 | position: FilePosition, | ||
149 | ) -> Option<RangeInfo<SourceChange>> { | ||
147 | let source_file = sema.parse(position.file_id); | 150 | let source_file = sema.parse(position.file_id); |
148 | let syn = source_file.syntax(); | 151 | let syn = source_file.syntax(); |
149 | 152 | ||
@@ -158,7 +161,7 @@ fn rename_to_self(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo | |||
158 | _ => return None, // not renaming other types | 161 | _ => return None, // not renaming other types |
159 | }; | 162 | }; |
160 | 163 | ||
161 | let RangeInfo { range, info: refs } = find_all_refs(db, position, None)?; | 164 | let RangeInfo { range, info: refs } = find_all_refs(sema, position, None)?; |
162 | 165 | ||
163 | let param_range = first_param.syntax().text_range(); | 166 | let param_range = first_param.syntax().text_range(); |
164 | let (param_ref, usages): (Vec<Reference>, Vec<Reference>) = refs | 167 | let (param_ref, usages): (Vec<Reference>, Vec<Reference>) = refs |
@@ -210,16 +213,15 @@ fn text_edit_from_self_param( | |||
210 | } | 213 | } |
211 | 214 | ||
212 | fn rename_self_to_param( | 215 | fn rename_self_to_param( |
213 | db: &RootDatabase, | 216 | sema: &Semantics<RootDatabase>, |
214 | position: FilePosition, | 217 | position: FilePosition, |
215 | self_token: SyntaxToken, | 218 | self_token: SyntaxToken, |
216 | new_name: &str, | 219 | new_name: &str, |
217 | ) -> Option<RangeInfo<SourceChange>> { | 220 | ) -> Option<RangeInfo<SourceChange>> { |
218 | let sema = Semantics::new(db); | ||
219 | let source_file = sema.parse(position.file_id); | 221 | let source_file = sema.parse(position.file_id); |
220 | let syn = source_file.syntax(); | 222 | let syn = source_file.syntax(); |
221 | 223 | ||
222 | let text = db.file_text(position.file_id); | 224 | let text = sema.db.file_text(position.file_id); |
223 | let fn_def = find_node_at_offset::<ast::FnDef>(syn, position.offset)?; | 225 | let fn_def = find_node_at_offset::<ast::FnDef>(syn, position.offset)?; |
224 | let search_range = fn_def.syntax().text_range(); | 226 | let search_range = fn_def.syntax().text_range(); |
225 | 227 | ||
@@ -249,11 +251,11 @@ fn rename_self_to_param( | |||
249 | } | 251 | } |
250 | 252 | ||
251 | fn rename_reference( | 253 | fn rename_reference( |
252 | db: &RootDatabase, | 254 | sema: &Semantics<RootDatabase>, |
253 | position: FilePosition, | 255 | position: FilePosition, |
254 | new_name: &str, | 256 | new_name: &str, |
255 | ) -> Option<RangeInfo<SourceChange>> { | 257 | ) -> Option<RangeInfo<SourceChange>> { |
256 | let RangeInfo { range, info: refs } = find_all_refs(db, position, None)?; | 258 | let RangeInfo { range, info: refs } = find_all_refs(sema, position, None)?; |
257 | 259 | ||
258 | let edit = refs | 260 | let edit = refs |
259 | .into_iter() | 261 | .into_iter() |
diff --git a/crates/ra_ide_db/src/search.rs b/crates/ra_ide_db/src/search.rs index 44d5c35e6..81553150b 100644 --- a/crates/ra_ide_db/src/search.rs +++ b/crates/ra_ide_db/src/search.rs | |||
@@ -180,20 +180,20 @@ impl Definition { | |||
180 | 180 | ||
181 | pub fn find_usages( | 181 | pub fn find_usages( |
182 | &self, | 182 | &self, |
183 | db: &RootDatabase, | 183 | sema: &Semantics<RootDatabase>, |
184 | search_scope: Option<SearchScope>, | 184 | search_scope: Option<SearchScope>, |
185 | ) -> Vec<Reference> { | 185 | ) -> Vec<Reference> { |
186 | let _p = profile("Definition::find_usages"); | 186 | let _p = profile("Definition::find_usages"); |
187 | 187 | ||
188 | let search_scope = { | 188 | let search_scope = { |
189 | let base = self.search_scope(db); | 189 | let base = self.search_scope(sema.db); |
190 | match search_scope { | 190 | match search_scope { |
191 | None => base, | 191 | None => base, |
192 | Some(scope) => base.intersection(&scope), | 192 | Some(scope) => base.intersection(&scope), |
193 | } | 193 | } |
194 | }; | 194 | }; |
195 | 195 | ||
196 | let name = match self.name(db) { | 196 | let name = match self.name(sema.db) { |
197 | None => return Vec::new(), | 197 | None => return Vec::new(), |
198 | Some(it) => it.to_string(), | 198 | Some(it) => it.to_string(), |
199 | }; | 199 | }; |
@@ -202,11 +202,10 @@ impl Definition { | |||
202 | let mut refs = vec![]; | 202 | let mut refs = vec![]; |
203 | 203 | ||
204 | for (file_id, search_range) in search_scope { | 204 | for (file_id, search_range) in search_scope { |
205 | let text = db.file_text(file_id); | 205 | let text = sema.db.file_text(file_id); |
206 | let search_range = | 206 | let search_range = |
207 | search_range.unwrap_or(TextRange::up_to(TextSize::of(text.as_str()))); | 207 | search_range.unwrap_or(TextRange::up_to(TextSize::of(text.as_str()))); |
208 | 208 | ||
209 | let sema = Semantics::new(db); | ||
210 | let tree = Lazy::new(|| sema.parse(file_id).syntax().clone()); | 209 | let tree = Lazy::new(|| sema.parse(file_id).syntax().clone()); |
211 | 210 | ||
212 | for (idx, _) in text.match_indices(pat) { | 211 | for (idx, _) in text.match_indices(pat) { |
@@ -222,9 +221,6 @@ impl Definition { | |||
222 | continue; | 221 | continue; |
223 | }; | 222 | }; |
224 | 223 | ||
225 | // FIXME: reuse sb | ||
226 | // See https://github.com/rust-lang/rust/pull/68198#issuecomment-574269098 | ||
227 | |||
228 | match classify_name_ref(&sema, &name_ref) { | 224 | match classify_name_ref(&sema, &name_ref) { |
229 | Some(NameRefClass::Definition(def)) if &def == self => { | 225 | Some(NameRefClass::Definition(def)) if &def == self => { |
230 | let kind = if is_record_lit_name_ref(&name_ref) | 226 | let kind = if is_record_lit_name_ref(&name_ref) |
diff --git a/crates/ra_project_model/src/project_json.rs b/crates/ra_project_model/src/project_json.rs index 9fe1e2dcb..b0fe09333 100644 --- a/crates/ra_project_model/src/project_json.rs +++ b/crates/ra_project_model/src/project_json.rs | |||
@@ -10,7 +10,7 @@ use serde::{de, Deserialize}; | |||
10 | use stdx::split_delim; | 10 | use stdx::split_delim; |
11 | 11 | ||
12 | /// Roots and crates that compose this Rust project. | 12 | /// Roots and crates that compose this Rust project. |
13 | #[derive(Clone, Debug)] | 13 | #[derive(Clone, Debug, Eq, PartialEq)] |
14 | pub struct ProjectJson { | 14 | pub struct ProjectJson { |
15 | pub(crate) roots: Vec<Root>, | 15 | pub(crate) roots: Vec<Root>, |
16 | pub(crate) crates: Vec<Crate>, | 16 | pub(crate) crates: Vec<Crate>, |
@@ -18,14 +18,14 @@ pub struct ProjectJson { | |||
18 | 18 | ||
19 | /// A root points to the directory which contains Rust crates. rust-analyzer watches all files in | 19 | /// A root points to the directory which contains Rust crates. rust-analyzer watches all files in |
20 | /// all roots. Roots might be nested. | 20 | /// all roots. Roots might be nested. |
21 | #[derive(Clone, Debug)] | 21 | #[derive(Clone, Debug, Eq, PartialEq)] |
22 | pub struct Root { | 22 | pub struct Root { |
23 | pub(crate) path: AbsPathBuf, | 23 | pub(crate) path: AbsPathBuf, |
24 | } | 24 | } |
25 | 25 | ||
26 | /// A crate points to the root module of a crate and lists the dependencies of the crate. This is | 26 | /// A crate points to the root module of a crate and lists the dependencies of the crate. This is |
27 | /// useful in creating the crate graph. | 27 | /// useful in creating the crate graph. |
28 | #[derive(Clone, Debug)] | 28 | #[derive(Clone, Debug, Eq, PartialEq)] |
29 | pub struct Crate { | 29 | pub struct Crate { |
30 | pub(crate) root_module: AbsPathBuf, | 30 | pub(crate) root_module: AbsPathBuf, |
31 | pub(crate) edition: Edition, | 31 | pub(crate) edition: Edition, |
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 6b17ce18b..6c311648a 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs | |||
@@ -44,7 +44,7 @@ pub struct Config { | |||
44 | pub root_path: AbsPathBuf, | 44 | pub root_path: AbsPathBuf, |
45 | } | 45 | } |
46 | 46 | ||
47 | #[derive(Debug, Clone)] | 47 | #[derive(Debug, Clone, Eq, PartialEq)] |
48 | pub enum LinkedProject { | 48 | pub enum LinkedProject { |
49 | ProjectManifest(ProjectManifest), | 49 | ProjectManifest(ProjectManifest), |
50 | InlineJsonProject(ProjectJson), | 50 | InlineJsonProject(ProjectJson), |
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs index b8aa1e5b5..b7b4edf66 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/crates/rust-analyzer/src/global_state.rs | |||
@@ -27,7 +27,7 @@ use crate::{ | |||
27 | Result, | 27 | Result, |
28 | }; | 28 | }; |
29 | 29 | ||
30 | #[derive(Eq, PartialEq)] | 30 | #[derive(Eq, PartialEq, Copy, Clone)] |
31 | pub(crate) enum Status { | 31 | pub(crate) enum Status { |
32 | Loading, | 32 | Loading, |
33 | Ready, | 33 | Ready, |
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index e3d49d24d..e03038b25 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs | |||
@@ -136,7 +136,7 @@ impl GlobalState { | |||
136 | log::info!("queued count = {}", queue_count); | 136 | log::info!("queued count = {}", queue_count); |
137 | } | 137 | } |
138 | 138 | ||
139 | let mut became_ready = false; | 139 | let prev_status = self.status; |
140 | match event { | 140 | match event { |
141 | Event::Lsp(msg) => match msg { | 141 | Event::Lsp(msg) => match msg { |
142 | lsp_server::Message::Request(req) => self.on_request(loop_start, req)?, | 142 | lsp_server::Message::Request(req) => self.on_request(loop_start, req)?, |
@@ -168,15 +168,17 @@ impl GlobalState { | |||
168 | } | 168 | } |
169 | } | 169 | } |
170 | vfs::loader::Message::Progress { n_total, n_done } => { | 170 | vfs::loader::Message::Progress { n_total, n_done } => { |
171 | if n_total > 0 { | 171 | if n_total == 0 { |
172 | self.status = Status::Ready; | ||
173 | } else { | ||
172 | let state = if n_done == 0 { | 174 | let state = if n_done == 0 { |
175 | self.status = Status::Loading; | ||
173 | Progress::Begin | 176 | Progress::Begin |
174 | } else if n_done < n_total { | 177 | } else if n_done < n_total { |
175 | Progress::Report | 178 | Progress::Report |
176 | } else { | 179 | } else { |
177 | assert_eq!(n_done, n_total); | 180 | assert_eq!(n_done, n_total); |
178 | self.status = Status::Ready; | 181 | self.status = Status::Ready; |
179 | became_ready = true; | ||
180 | Progress::End | 182 | Progress::End |
181 | }; | 183 | }; |
182 | self.report_progress( | 184 | self.report_progress( |
@@ -233,13 +235,13 @@ impl GlobalState { | |||
233 | } | 235 | } |
234 | 236 | ||
235 | let state_changed = self.process_changes(); | 237 | let state_changed = self.process_changes(); |
236 | if became_ready { | 238 | if prev_status == Status::Loading && self.status == Status::Ready { |
237 | if let Some(flycheck) = &self.flycheck { | 239 | if let Some(flycheck) = &self.flycheck { |
238 | flycheck.handle.update(); | 240 | flycheck.handle.update(); |
239 | } | 241 | } |
240 | } | 242 | } |
241 | 243 | ||
242 | if self.status == Status::Ready && (state_changed || became_ready) { | 244 | if self.status == Status::Ready && (state_changed || prev_status == Status::Loading) { |
243 | let subscriptions = self | 245 | let subscriptions = self |
244 | .mem_docs | 246 | .mem_docs |
245 | .iter() | 247 | .iter() |
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index ec71f8b29..0c1fd1b8b 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs | |||
@@ -19,11 +19,14 @@ impl GlobalState { | |||
19 | if self.config.lru_capacity != old_config.lru_capacity { | 19 | if self.config.lru_capacity != old_config.lru_capacity { |
20 | self.analysis_host.update_lru_capacity(old_config.lru_capacity); | 20 | self.analysis_host.update_lru_capacity(old_config.lru_capacity); |
21 | } | 21 | } |
22 | if self.config.flycheck != old_config.flycheck { | 22 | if self.config.linked_projects != old_config.linked_projects { |
23 | self.reload() | ||
24 | } else if self.config.flycheck != old_config.flycheck { | ||
23 | self.reload_flycheck(); | 25 | self.reload_flycheck(); |
24 | } | 26 | } |
25 | } | 27 | } |
26 | pub(crate) fn reload(&mut self) { | 28 | pub(crate) fn reload(&mut self) { |
29 | log::info!("reloading projects: {:?}", self.config.linked_projects); | ||
27 | let workspaces = { | 30 | let workspaces = { |
28 | if self.config.linked_projects.is_empty() | 31 | if self.config.linked_projects.is_empty() |
29 | && self.config.notifications.cargo_toml_not_found | 32 | && self.config.notifications.cargo_toml_not_found |
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index a0a58f689..95dd8e408 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs | |||
@@ -352,7 +352,7 @@ pub(crate) fn folding_range( | |||
352 | let kind = match fold.kind { | 352 | let kind = match fold.kind { |
353 | FoldKind::Comment => Some(lsp_types::FoldingRangeKind::Comment), | 353 | FoldKind::Comment => Some(lsp_types::FoldingRangeKind::Comment), |
354 | FoldKind::Imports => Some(lsp_types::FoldingRangeKind::Imports), | 354 | FoldKind::Imports => Some(lsp_types::FoldingRangeKind::Imports), |
355 | FoldKind::Mods | FoldKind::Block => None, | 355 | FoldKind::Mods | FoldKind::Block | FoldKind::ArgList => None, |
356 | }; | 356 | }; |
357 | 357 | ||
358 | let range = range(line_index, fold.range); | 358 | let range = range(line_index, fold.range); |
@@ -685,32 +685,27 @@ pub(crate) fn runnable( | |||
685 | 685 | ||
686 | #[cfg(test)] | 686 | #[cfg(test)] |
687 | mod tests { | 687 | mod tests { |
688 | use test_utils::extract_ranges; | 688 | use ra_ide::Analysis; |
689 | 689 | ||
690 | use super::*; | 690 | use super::*; |
691 | 691 | ||
692 | #[test] | 692 | #[test] |
693 | fn conv_fold_line_folding_only_fixup() { | 693 | fn conv_fold_line_folding_only_fixup() { |
694 | let text = r#"<fold>mod a; | 694 | let text = r#"mod a; |
695 | mod b; | 695 | mod b; |
696 | mod c;</fold> | 696 | mod c; |
697 | 697 | ||
698 | fn main() <fold>{ | 698 | fn main() { |
699 | if cond <fold>{ | 699 | if cond { |
700 | a::do_a(); | 700 | a::do_a(); |
701 | }</fold> else <fold>{ | 701 | } else { |
702 | b::do_b(); | 702 | b::do_b(); |
703 | }</fold> | 703 | } |
704 | }</fold>"#; | 704 | }"#; |
705 | 705 | ||
706 | let (ranges, text) = extract_ranges(text, "fold"); | 706 | let (analysis, file_id) = Analysis::from_single_file(text.to_string()); |
707 | assert_eq!(ranges.len(), 4); | 707 | let folds = analysis.folding_ranges(file_id).unwrap(); |
708 | let folds = vec![ | 708 | assert_eq!(folds.len(), 4); |
709 | Fold { range: ranges[0], kind: FoldKind::Mods }, | ||
710 | Fold { range: ranges[1], kind: FoldKind::Block }, | ||
711 | Fold { range: ranges[2], kind: FoldKind::Block }, | ||
712 | Fold { range: ranges[3], kind: FoldKind::Block }, | ||
713 | ]; | ||
714 | 709 | ||
715 | let line_index = LineIndex::new(&text); | 710 | let line_index = LineIndex::new(&text); |
716 | let converted: Vec<lsp_types::FoldingRange> = | 711 | let converted: Vec<lsp_types::FoldingRange> = |
diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs index fba5f4281..e4aa894ac 100644 --- a/crates/test_utils/src/lib.rs +++ b/crates/test_utils/src/lib.rs | |||
@@ -118,8 +118,8 @@ pub fn extract_range_or_offset(text: &str) -> (RangeOrOffset, String) { | |||
118 | } | 118 | } |
119 | 119 | ||
120 | /// Extracts ranges, marked with `<tag> </tag>` pairs from the `text` | 120 | /// Extracts ranges, marked with `<tag> </tag>` pairs from the `text` |
121 | pub fn extract_ranges(mut text: &str, tag: &str) -> (Vec<TextRange>, String) { | 121 | pub fn extract_tags(mut text: &str, tag: &str) -> (Vec<(TextRange, Option<String>)>, String) { |
122 | let open = format!("<{}>", tag); | 122 | let open = format!("<{}", tag); |
123 | let close = format!("</{}>", tag); | 123 | let close = format!("</{}>", tag); |
124 | let mut ranges = Vec::new(); | 124 | let mut ranges = Vec::new(); |
125 | let mut res = String::new(); | 125 | let mut res = String::new(); |
@@ -134,22 +134,35 @@ pub fn extract_ranges(mut text: &str, tag: &str) -> (Vec<TextRange>, String) { | |||
134 | res.push_str(&text[..i]); | 134 | res.push_str(&text[..i]); |
135 | text = &text[i..]; | 135 | text = &text[i..]; |
136 | if text.starts_with(&open) { | 136 | if text.starts_with(&open) { |
137 | text = &text[open.len()..]; | 137 | let close_open = text.find('>').unwrap(); |
138 | let attr = text[open.len()..close_open].trim(); | ||
139 | let attr = if attr.is_empty() { None } else { Some(attr.to_string()) }; | ||
140 | text = &text[close_open + '>'.len_utf8()..]; | ||
138 | let from = TextSize::of(&res); | 141 | let from = TextSize::of(&res); |
139 | stack.push(from); | 142 | stack.push((from, attr)); |
140 | } else if text.starts_with(&close) { | 143 | } else if text.starts_with(&close) { |
141 | text = &text[close.len()..]; | 144 | text = &text[close.len()..]; |
142 | let from = stack.pop().unwrap_or_else(|| panic!("unmatched </{}>", tag)); | 145 | let (from, attr) = |
146 | stack.pop().unwrap_or_else(|| panic!("unmatched </{}>", tag)); | ||
143 | let to = TextSize::of(&res); | 147 | let to = TextSize::of(&res); |
144 | ranges.push(TextRange::new(from, to)); | 148 | ranges.push((TextRange::new(from, to), attr)); |
149 | } else { | ||
150 | res.push('<'); | ||
151 | text = &text['<'.len_utf8()..]; | ||
145 | } | 152 | } |
146 | } | 153 | } |
147 | } | 154 | } |
148 | } | 155 | } |
149 | assert!(stack.is_empty(), "unmatched <{}>", tag); | 156 | assert!(stack.is_empty(), "unmatched <{}>", tag); |
150 | ranges.sort_by_key(|r| (r.start(), r.end())); | 157 | ranges.sort_by_key(|r| (r.0.start(), r.0.end())); |
151 | (ranges, res) | 158 | (ranges, res) |
152 | } | 159 | } |
160 | #[test] | ||
161 | fn test_extract_tags() { | ||
162 | let (tags, text) = extract_tags(r#"<tag fn>fn <tag>main</tag>() {}</tag>"#, "tag"); | ||
163 | let actual = tags.into_iter().map(|(range, attr)| (&text[range], attr)).collect::<Vec<_>>(); | ||
164 | assert_eq!(actual, vec![("fn main() {}", Some("fn".into())), ("main", None),]); | ||
165 | } | ||
153 | 166 | ||
154 | /// Inserts `<|>` marker into the `text` at `offset`. | 167 | /// Inserts `<|>` marker into the `text` at `offset`. |
155 | pub fn add_cursor(text: &str, offset: TextSize) -> String { | 168 | pub fn add_cursor(text: &str, offset: TextSize) -> String { |