diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/assists/src/handlers/extract_struct_from_enum_variant.rs | 3 | ||||
-rw-r--r-- | crates/assists/src/handlers/inline_local_variable.rs | 31 | ||||
-rw-r--r-- | crates/project_model/src/lib.rs | 245 | ||||
-rw-r--r-- | crates/project_model/src/project_json.rs | 19 | ||||
-rw-r--r-- | crates/project_model/src/sysroot.rs | 20 | ||||
-rw-r--r-- | crates/rust-analyzer/src/bin/args.rs | 492 | ||||
-rw-r--r-- | crates/rust-analyzer/src/bin/logger.rs | 73 | ||||
-rw-r--r-- | crates/rust-analyzer/src/bin/main.rs | 29 | ||||
-rw-r--r-- | crates/rust-analyzer/src/cli/diagnostics.rs | 7 | ||||
-rw-r--r-- | crates/rust-analyzer/tests/rust-analyzer/main.rs | 74 | ||||
-rw-r--r-- | crates/rust-analyzer/tests/rust-analyzer/support.rs | 5 | ||||
-rw-r--r-- | crates/text_edit/src/lib.rs | 12 |
12 files changed, 483 insertions, 527 deletions
diff --git a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs index c1124b9e2..8ac20210a 100644 --- a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs | |||
@@ -1,6 +1,7 @@ | |||
1 | use base_db::FileId; | 1 | use base_db::FileId; |
2 | use hir::{EnumVariant, Module, ModuleDef, Name}; | 2 | use hir::{EnumVariant, Module, ModuleDef, Name}; |
3 | use ide_db::{defs::Definition, search::Reference, RootDatabase}; | 3 | use ide_db::{defs::Definition, search::Reference, RootDatabase}; |
4 | use itertools::Itertools; | ||
4 | use rustc_hash::FxHashSet; | 5 | use rustc_hash::FxHashSet; |
5 | use syntax::{ | 6 | use syntax::{ |
6 | algo::find_node_at_offset, | 7 | algo::find_node_at_offset, |
@@ -203,13 +204,11 @@ fn list_with_visibility(list: &str) -> String { | |||
203 | mod_part.insert_str(index, "pub "); | 204 | mod_part.insert_str(index, "pub "); |
204 | mod_part | 205 | mod_part |
205 | }) | 206 | }) |
206 | .collect::<Vec<String>>() | ||
207 | .join(", ") | 207 | .join(", ") |
208 | } | 208 | } |
209 | 209 | ||
210 | #[cfg(test)] | 210 | #[cfg(test)] |
211 | mod tests { | 211 | mod tests { |
212 | |||
213 | use crate::{ | 212 | use crate::{ |
214 | tests::{check_assist, check_assist_not_applicable}, | 213 | tests::{check_assist, check_assist_not_applicable}, |
215 | utils::FamousDefs, | 214 | utils::FamousDefs, |
diff --git a/crates/assists/src/handlers/inline_local_variable.rs b/crates/assists/src/handlers/inline_local_variable.rs index 164bbce86..587eb5feb 100644 --- a/crates/assists/src/handlers/inline_local_variable.rs +++ b/crates/assists/src/handlers/inline_local_variable.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | use ide_db::defs::Definition; | 1 | use ide_db::{defs::Definition, search::ReferenceKind}; |
2 | use syntax::{ | 2 | use syntax::{ |
3 | ast::{self, AstNode, AstToken}, | 3 | ast::{self, AstNode, AstToken}, |
4 | TextRange, | 4 | TextRange, |
@@ -119,7 +119,13 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O | |||
119 | for (desc, should_wrap) in refs.iter().zip(wrap_in_parens) { | 119 | for (desc, should_wrap) in refs.iter().zip(wrap_in_parens) { |
120 | let replacement = | 120 | let replacement = |
121 | if should_wrap { init_in_paren.clone() } else { init_str.clone() }; | 121 | if should_wrap { init_in_paren.clone() } else { init_str.clone() }; |
122 | builder.replace(desc.file_range.range, replacement) | 122 | match desc.kind { |
123 | ReferenceKind::FieldShorthandForLocal => { | ||
124 | mark::hit!(inline_field_shorthand); | ||
125 | builder.insert(desc.file_range.range.end(), format!(": {}", replacement)) | ||
126 | } | ||
127 | _ => builder.replace(desc.file_range.range, replacement), | ||
128 | } | ||
123 | } | 129 | } |
124 | }, | 130 | }, |
125 | ) | 131 | ) |
@@ -667,6 +673,27 @@ fn foo() { | |||
667 | } | 673 | } |
668 | 674 | ||
669 | #[test] | 675 | #[test] |
676 | fn inline_field_shorthand() { | ||
677 | mark::check!(inline_field_shorthand); | ||
678 | check_assist( | ||
679 | inline_local_variable, | ||
680 | r" | ||
681 | struct S { foo: i32} | ||
682 | fn main() { | ||
683 | let <|>foo = 92; | ||
684 | S { foo } | ||
685 | } | ||
686 | ", | ||
687 | r" | ||
688 | struct S { foo: i32} | ||
689 | fn main() { | ||
690 | S { foo: 92 } | ||
691 | } | ||
692 | ", | ||
693 | ); | ||
694 | } | ||
695 | |||
696 | #[test] | ||
670 | fn test_not_applicable_if_variable_unused() { | 697 | fn test_not_applicable_if_variable_unused() { |
671 | mark::check!(test_not_applicable_if_variable_unused); | 698 | mark::check!(test_not_applicable_if_variable_unused); |
672 | check_assist_not_applicable( | 699 | check_assist_not_applicable( |
diff --git a/crates/project_model/src/lib.rs b/crates/project_model/src/lib.rs index 2d65fc076..2d91939ce 100644 --- a/crates/project_model/src/lib.rs +++ b/crates/project_model/src/lib.rs | |||
@@ -39,11 +39,18 @@ pub enum ProjectWorkspace { | |||
39 | impl fmt::Debug for ProjectWorkspace { | 39 | impl fmt::Debug for ProjectWorkspace { |
40 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | 40 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
41 | match self { | 41 | match self { |
42 | ProjectWorkspace::Cargo { cargo, .. } => { | 42 | ProjectWorkspace::Cargo { cargo, sysroot } => f |
43 | f.debug_struct("Cargo").field("n_packages", &cargo.packages().len()).finish() | 43 | .debug_struct("Cargo") |
44 | } | 44 | .field("n_packages", &cargo.packages().len()) |
45 | .field("n_sysroot_crates", &sysroot.crates().len()) | ||
46 | .finish(), | ||
45 | ProjectWorkspace::Json { project } => { | 47 | ProjectWorkspace::Json { project } => { |
46 | f.debug_struct("Json").field("n_crates", &project.crates.len()).finish() | 48 | let mut debug_struct = f.debug_struct("Json"); |
49 | debug_struct.field("n_crates", &project.n_crates()); | ||
50 | if let Some(sysroot) = &project.sysroot { | ||
51 | debug_struct.field("n_sysroot_crates", &sysroot.crates().len()); | ||
52 | } | ||
53 | debug_struct.finish() | ||
47 | } | 54 | } |
48 | } | 55 | } |
49 | } | 56 | } |
@@ -202,15 +209,21 @@ impl ProjectWorkspace { | |||
202 | pub fn to_roots(&self) -> Vec<PackageRoot> { | 209 | pub fn to_roots(&self) -> Vec<PackageRoot> { |
203 | match self { | 210 | match self { |
204 | ProjectWorkspace::Json { project } => project | 211 | ProjectWorkspace::Json { project } => project |
205 | .crates | 212 | .crates() |
206 | .iter() | 213 | .map(|(_, krate)| PackageRoot { |
207 | .map(|krate| PackageRoot { | ||
208 | is_member: krate.is_workspace_member, | 214 | is_member: krate.is_workspace_member, |
209 | include: krate.include.clone(), | 215 | include: krate.include.clone(), |
210 | exclude: krate.exclude.clone(), | 216 | exclude: krate.exclude.clone(), |
211 | }) | 217 | }) |
212 | .collect::<FxHashSet<_>>() | 218 | .collect::<FxHashSet<_>>() |
213 | .into_iter() | 219 | .into_iter() |
220 | .chain(project.sysroot.as_ref().into_iter().flat_map(|sysroot| { | ||
221 | sysroot.crates().map(move |krate| PackageRoot { | ||
222 | is_member: false, | ||
223 | include: vec![sysroot[krate].root_dir().to_path_buf()], | ||
224 | exclude: Vec::new(), | ||
225 | }) | ||
226 | })) | ||
214 | .collect::<Vec<_>>(), | 227 | .collect::<Vec<_>>(), |
215 | ProjectWorkspace::Cargo { cargo, sysroot } => cargo | 228 | ProjectWorkspace::Cargo { cargo, sysroot } => cargo |
216 | .packages() | 229 | .packages() |
@@ -243,9 +256,8 @@ impl ProjectWorkspace { | |||
243 | pub fn proc_macro_dylib_paths(&self) -> Vec<AbsPathBuf> { | 256 | pub fn proc_macro_dylib_paths(&self) -> Vec<AbsPathBuf> { |
244 | match self { | 257 | match self { |
245 | ProjectWorkspace::Json { project } => project | 258 | ProjectWorkspace::Json { project } => project |
246 | .crates | 259 | .crates() |
247 | .iter() | 260 | .filter_map(|(_, krate)| krate.proc_macro_dylib_path.as_ref()) |
248 | .filter_map(|krate| krate.proc_macro_dylib_path.as_ref()) | ||
249 | .cloned() | 261 | .cloned() |
250 | .collect(), | 262 | .collect(), |
251 | ProjectWorkspace::Cargo { cargo, sysroot: _sysroot } => cargo | 263 | ProjectWorkspace::Cargo { cargo, sysroot: _sysroot } => cargo |
@@ -258,7 +270,7 @@ impl ProjectWorkspace { | |||
258 | 270 | ||
259 | pub fn n_packages(&self) -> usize { | 271 | pub fn n_packages(&self) -> usize { |
260 | match self { | 272 | match self { |
261 | ProjectWorkspace::Json { project, .. } => project.crates.len(), | 273 | ProjectWorkspace::Json { project, .. } => project.n_crates(), |
262 | ProjectWorkspace::Cargo { cargo, sysroot } => { | 274 | ProjectWorkspace::Cargo { cargo, sysroot } => { |
263 | cargo.packages().len() + sysroot.crates().len() | 275 | cargo.packages().len() + sysroot.crates().len() |
264 | } | 276 | } |
@@ -274,12 +286,15 @@ impl ProjectWorkspace { | |||
274 | let mut crate_graph = CrateGraph::default(); | 286 | let mut crate_graph = CrateGraph::default(); |
275 | match self { | 287 | match self { |
276 | ProjectWorkspace::Json { project } => { | 288 | ProjectWorkspace::Json { project } => { |
289 | let sysroot_dps = project | ||
290 | .sysroot | ||
291 | .as_ref() | ||
292 | .map(|sysroot| sysroot_to_crate_graph(&mut crate_graph, sysroot, target, load)); | ||
293 | |||
277 | let mut cfg_cache: FxHashMap<Option<&str>, Vec<CfgFlag>> = FxHashMap::default(); | 294 | let mut cfg_cache: FxHashMap<Option<&str>, Vec<CfgFlag>> = FxHashMap::default(); |
278 | let crates: FxHashMap<_, _> = project | 295 | let crates: FxHashMap<_, _> = project |
279 | .crates | 296 | .crates() |
280 | .iter() | 297 | .filter_map(|(crate_id, krate)| { |
281 | .enumerate() | ||
282 | .filter_map(|(seq_index, krate)| { | ||
283 | let file_path = &krate.root_module; | 298 | let file_path = &krate.root_module; |
284 | let file_id = load(&file_path)?; | 299 | let file_id = load(&file_path)?; |
285 | 300 | ||
@@ -297,9 +312,8 @@ impl ProjectWorkspace { | |||
297 | let mut cfg_options = CfgOptions::default(); | 312 | let mut cfg_options = CfgOptions::default(); |
298 | cfg_options.extend(target_cfgs.iter().chain(krate.cfg.iter()).cloned()); | 313 | cfg_options.extend(target_cfgs.iter().chain(krate.cfg.iter()).cloned()); |
299 | 314 | ||
300 | // FIXME: No crate name in json definition such that we cannot add OUT_DIR to env | ||
301 | Some(( | 315 | Some(( |
302 | CrateId(seq_index as u32), | 316 | crate_id, |
303 | crate_graph.add_crate_root( | 317 | crate_graph.add_crate_root( |
304 | file_id, | 318 | file_id, |
305 | krate.edition, | 319 | krate.edition, |
@@ -313,67 +327,33 @@ impl ProjectWorkspace { | |||
313 | }) | 327 | }) |
314 | .collect(); | 328 | .collect(); |
315 | 329 | ||
316 | for (id, krate) in project.crates.iter().enumerate() { | 330 | for (from, krate) in project.crates() { |
317 | for dep in &krate.deps { | 331 | if let Some(&from) = crates.get(&from) { |
318 | let from_crate_id = CrateId(id as u32); | 332 | if let Some((public_deps, _proc_macro)) = &sysroot_dps { |
319 | let to_crate_id = dep.crate_id; | 333 | for (name, to) in public_deps.iter() { |
320 | if let (Some(&from), Some(&to)) = | 334 | if let Err(_) = crate_graph.add_dep(from, name.clone(), *to) { |
321 | (crates.get(&from_crate_id), crates.get(&to_crate_id)) | 335 | log::error!("cyclic dependency on {} for {:?}", name, from) |
322 | { | 336 | } |
323 | if crate_graph.add_dep(from, dep.name.clone(), to).is_err() { | ||
324 | log::error!( | ||
325 | "cyclic dependency {:?} -> {:?}", | ||
326 | from_crate_id, | ||
327 | to_crate_id | ||
328 | ); | ||
329 | } | 337 | } |
330 | } | 338 | } |
331 | } | ||
332 | } | ||
333 | } | ||
334 | ProjectWorkspace::Cargo { cargo, sysroot } => { | ||
335 | let mut cfg_options = CfgOptions::default(); | ||
336 | cfg_options.extend(get_rustc_cfg_options(target)); | ||
337 | |||
338 | let sysroot_crates: FxHashMap<_, _> = sysroot | ||
339 | .crates() | ||
340 | .filter_map(|krate| { | ||
341 | let file_id = load(&sysroot[krate].root)?; | ||
342 | |||
343 | let env = Env::default(); | ||
344 | let proc_macro = vec![]; | ||
345 | let name = sysroot[krate].name.clone(); | ||
346 | let crate_id = crate_graph.add_crate_root( | ||
347 | file_id, | ||
348 | Edition::Edition2018, | ||
349 | Some(name), | ||
350 | cfg_options.clone(), | ||
351 | env, | ||
352 | proc_macro, | ||
353 | ); | ||
354 | Some((krate, crate_id)) | ||
355 | }) | ||
356 | .collect(); | ||
357 | 339 | ||
358 | for from in sysroot.crates() { | 340 | for dep in &krate.deps { |
359 | for &to in sysroot[from].deps.iter() { | 341 | let to_crate_id = dep.crate_id; |
360 | let name = &sysroot[to].name; | 342 | if let Some(&to) = crates.get(&to_crate_id) { |
361 | if let (Some(&from), Some(&to)) = | 343 | if let Err(_) = crate_graph.add_dep(from, dep.name.clone(), to) { |
362 | (sysroot_crates.get(&from), sysroot_crates.get(&to)) | 344 | log::error!("cyclic dependency {:?} -> {:?}", from, to); |
363 | { | 345 | } |
364 | if crate_graph.add_dep(from, CrateName::new(name).unwrap(), to).is_err() | ||
365 | { | ||
366 | log::error!("cyclic dependency between sysroot crates") | ||
367 | } | 346 | } |
368 | } | 347 | } |
369 | } | 348 | } |
370 | } | 349 | } |
350 | } | ||
351 | ProjectWorkspace::Cargo { cargo, sysroot } => { | ||
352 | let (public_deps, libproc_macro) = | ||
353 | sysroot_to_crate_graph(&mut crate_graph, sysroot, target, load); | ||
371 | 354 | ||
372 | let libcore = sysroot.core().and_then(|it| sysroot_crates.get(&it).copied()); | 355 | let mut cfg_options = CfgOptions::default(); |
373 | let liballoc = sysroot.alloc().and_then(|it| sysroot_crates.get(&it).copied()); | 356 | cfg_options.extend(get_rustc_cfg_options(target)); |
374 | let libstd = sysroot.std().and_then(|it| sysroot_crates.get(&it).copied()); | ||
375 | let libproc_macro = | ||
376 | sysroot.proc_macro().and_then(|it| sysroot_crates.get(&it).copied()); | ||
377 | 357 | ||
378 | let mut pkg_to_lib_crate = FxHashMap::default(); | 358 | let mut pkg_to_lib_crate = FxHashMap::default(); |
379 | let mut pkg_crates = FxHashMap::default(); | 359 | let mut pkg_crates = FxHashMap::default(); |
@@ -424,14 +404,11 @@ impl ProjectWorkspace { | |||
424 | } | 404 | } |
425 | if cargo[tgt].is_proc_macro { | 405 | if cargo[tgt].is_proc_macro { |
426 | if let Some(proc_macro) = libproc_macro { | 406 | if let Some(proc_macro) = libproc_macro { |
427 | if crate_graph | 407 | if let Err(_) = crate_graph.add_dep( |
428 | .add_dep( | 408 | crate_id, |
429 | crate_id, | 409 | CrateName::new("proc_macro").unwrap(), |
430 | CrateName::new("proc_macro").unwrap(), | 410 | proc_macro, |
431 | proc_macro, | 411 | ) { |
432 | ) | ||
433 | .is_err() | ||
434 | { | ||
435 | log::error!( | 412 | log::error!( |
436 | "cyclic dependency on proc_macro for {}", | 413 | "cyclic dependency on proc_macro for {}", |
437 | &cargo[pkg].name | 414 | &cargo[pkg].name |
@@ -447,50 +424,24 @@ impl ProjectWorkspace { | |||
447 | // Set deps to the core, std and to the lib target of the current package | 424 | // Set deps to the core, std and to the lib target of the current package |
448 | for &from in pkg_crates.get(&pkg).into_iter().flatten() { | 425 | for &from in pkg_crates.get(&pkg).into_iter().flatten() { |
449 | if let Some((to, name)) = lib_tgt.clone() { | 426 | if let Some((to, name)) = lib_tgt.clone() { |
450 | if to != from | 427 | // For root projects with dashes in their name, |
451 | && crate_graph | 428 | // cargo metadata does not do any normalization, |
452 | .add_dep( | 429 | // so we do it ourselves currently |
453 | from, | 430 | let name = CrateName::normalize_dashes(&name); |
454 | // For root projects with dashes in their name, | 431 | if to != from && crate_graph.add_dep(from, name, to).is_err() { |
455 | // cargo metadata does not do any normalization, | 432 | log::error!( |
456 | // so we do it ourselves currently | 433 | "cyclic dependency between targets of {}", |
457 | CrateName::normalize_dashes(&name), | 434 | &cargo[pkg].name |
458 | to, | 435 | ) |
459 | ) | ||
460 | .is_err() | ||
461 | { | ||
462 | { | ||
463 | log::error!( | ||
464 | "cyclic dependency between targets of {}", | ||
465 | &cargo[pkg].name | ||
466 | ) | ||
467 | } | ||
468 | } | ||
469 | } | ||
470 | // core is added as a dependency before std in order to | ||
471 | // mimic rustcs dependency order | ||
472 | if let Some(core) = libcore { | ||
473 | if crate_graph | ||
474 | .add_dep(from, CrateName::new("core").unwrap(), core) | ||
475 | .is_err() | ||
476 | { | ||
477 | log::error!("cyclic dependency on core for {}", &cargo[pkg].name) | ||
478 | } | ||
479 | } | ||
480 | if let Some(alloc) = liballoc { | ||
481 | if crate_graph | ||
482 | .add_dep(from, CrateName::new("alloc").unwrap(), alloc) | ||
483 | .is_err() | ||
484 | { | ||
485 | log::error!("cyclic dependency on alloc for {}", &cargo[pkg].name) | ||
486 | } | 436 | } |
487 | } | 437 | } |
488 | if let Some(std) = libstd { | 438 | for (name, krate) in public_deps.iter() { |
489 | if crate_graph | 439 | if let Err(_) = crate_graph.add_dep(from, name.clone(), *krate) { |
490 | .add_dep(from, CrateName::new("std").unwrap(), std) | 440 | log::error!( |
491 | .is_err() | 441 | "cyclic dependency on {} for {}", |
492 | { | 442 | name, |
493 | log::error!("cyclic dependency on std for {}", &cargo[pkg].name) | 443 | &cargo[pkg].name |
444 | ) | ||
494 | } | 445 | } |
495 | } | 446 | } |
496 | } | 447 | } |
@@ -500,12 +451,10 @@ impl ProjectWorkspace { | |||
500 | // target of downstream. | 451 | // target of downstream. |
501 | for pkg in cargo.packages() { | 452 | for pkg in cargo.packages() { |
502 | for dep in cargo[pkg].dependencies.iter() { | 453 | for dep in cargo[pkg].dependencies.iter() { |
454 | let name = CrateName::new(&dep.name).unwrap(); | ||
503 | if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) { | 455 | if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) { |
504 | for &from in pkg_crates.get(&pkg).into_iter().flatten() { | 456 | for &from in pkg_crates.get(&pkg).into_iter().flatten() { |
505 | if crate_graph | 457 | if let Err(_) = crate_graph.add_dep(from, name.clone(), to) { |
506 | .add_dep(from, CrateName::new(&dep.name).unwrap(), to) | ||
507 | .is_err() | ||
508 | { | ||
509 | log::error!( | 458 | log::error!( |
510 | "cyclic dependency {} -> {}", | 459 | "cyclic dependency {} -> {}", |
511 | &cargo[pkg].name, | 460 | &cargo[pkg].name, |
@@ -563,3 +512,51 @@ fn utf8_stdout(mut cmd: Command) -> Result<String> { | |||
563 | let stdout = String::from_utf8(output.stdout)?; | 512 | let stdout = String::from_utf8(output.stdout)?; |
564 | Ok(stdout.trim().to_string()) | 513 | Ok(stdout.trim().to_string()) |
565 | } | 514 | } |
515 | |||
516 | fn sysroot_to_crate_graph( | ||
517 | crate_graph: &mut CrateGraph, | ||
518 | sysroot: &Sysroot, | ||
519 | target: Option<&str>, | ||
520 | load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, | ||
521 | ) -> (Vec<(CrateName, CrateId)>, Option<CrateId>) { | ||
522 | let mut cfg_options = CfgOptions::default(); | ||
523 | cfg_options.extend(get_rustc_cfg_options(target)); | ||
524 | let sysroot_crates: FxHashMap<_, _> = sysroot | ||
525 | .crates() | ||
526 | .filter_map(|krate| { | ||
527 | let file_id = load(&sysroot[krate].root)?; | ||
528 | |||
529 | let env = Env::default(); | ||
530 | let proc_macro = vec![]; | ||
531 | let name = sysroot[krate].name.clone(); | ||
532 | let crate_id = crate_graph.add_crate_root( | ||
533 | file_id, | ||
534 | Edition::Edition2018, | ||
535 | Some(name), | ||
536 | cfg_options.clone(), | ||
537 | env, | ||
538 | proc_macro, | ||
539 | ); | ||
540 | Some((krate, crate_id)) | ||
541 | }) | ||
542 | .collect(); | ||
543 | |||
544 | for from in sysroot.crates() { | ||
545 | for &to in sysroot[from].deps.iter() { | ||
546 | let name = CrateName::new(&sysroot[to].name).unwrap(); | ||
547 | if let (Some(&from), Some(&to)) = (sysroot_crates.get(&from), sysroot_crates.get(&to)) { | ||
548 | if let Err(_) = crate_graph.add_dep(from, name, to) { | ||
549 | log::error!("cyclic dependency between sysroot crates") | ||
550 | } | ||
551 | } | ||
552 | } | ||
553 | } | ||
554 | |||
555 | let public_deps = sysroot | ||
556 | .public_deps() | ||
557 | .map(|(name, idx)| (CrateName::new(name).unwrap(), sysroot_crates[&idx])) | ||
558 | .collect::<Vec<_>>(); | ||
559 | |||
560 | let libproc_macro = sysroot.proc_macro().and_then(|it| sysroot_crates.get(&it).copied()); | ||
561 | (public_deps, libproc_macro) | ||
562 | } | ||
diff --git a/crates/project_model/src/project_json.rs b/crates/project_model/src/project_json.rs index 060ea5b7d..5a0fe749a 100644 --- a/crates/project_model/src/project_json.rs +++ b/crates/project_model/src/project_json.rs | |||
@@ -7,12 +7,13 @@ use paths::{AbsPath, AbsPathBuf}; | |||
7 | use rustc_hash::FxHashMap; | 7 | use rustc_hash::FxHashMap; |
8 | use serde::{de, Deserialize}; | 8 | use serde::{de, Deserialize}; |
9 | 9 | ||
10 | use crate::cfg_flag::CfgFlag; | 10 | use crate::{cfg_flag::CfgFlag, Sysroot}; |
11 | 11 | ||
12 | /// Roots and crates that compose this Rust project. | 12 | /// Roots and crates that compose this Rust project. |
13 | #[derive(Clone, Debug, Eq, PartialEq)] | 13 | #[derive(Clone, Debug, Eq, PartialEq)] |
14 | pub struct ProjectJson { | 14 | pub struct ProjectJson { |
15 | pub(crate) crates: Vec<Crate>, | 15 | pub(crate) sysroot: Option<Sysroot>, |
16 | crates: Vec<Crate>, | ||
16 | } | 17 | } |
17 | 18 | ||
18 | /// A crate points to the root module of a crate and lists the dependencies of the crate. This is | 19 | /// A crate points to the root module of a crate and lists the dependencies of the crate. This is |
@@ -34,6 +35,7 @@ pub struct Crate { | |||
34 | impl ProjectJson { | 35 | impl ProjectJson { |
35 | pub fn new(base: &AbsPath, data: ProjectJsonData) -> ProjectJson { | 36 | pub fn new(base: &AbsPath, data: ProjectJsonData) -> ProjectJson { |
36 | ProjectJson { | 37 | ProjectJson { |
38 | sysroot: data.sysroot_src.map(|it| base.join(it)).map(|it| Sysroot::load(&it)), | ||
37 | crates: data | 39 | crates: data |
38 | .crates | 40 | .crates |
39 | .into_iter() | 41 | .into_iter() |
@@ -43,11 +45,13 @@ impl ProjectJson { | |||
43 | && !crate_data.root_module.starts_with("..") | 45 | && !crate_data.root_module.starts_with("..") |
44 | || crate_data.root_module.starts_with(base) | 46 | || crate_data.root_module.starts_with(base) |
45 | }); | 47 | }); |
46 | let root_module = base.join(crate_data.root_module); | 48 | let root_module = base.join(crate_data.root_module).normalize(); |
47 | let (include, exclude) = match crate_data.source { | 49 | let (include, exclude) = match crate_data.source { |
48 | Some(src) => { | 50 | Some(src) => { |
49 | let absolutize = |dirs: Vec<PathBuf>| { | 51 | let absolutize = |dirs: Vec<PathBuf>| { |
50 | dirs.into_iter().map(|it| base.join(it)).collect::<Vec<_>>() | 52 | dirs.into_iter() |
53 | .map(|it| base.join(it).normalize()) | ||
54 | .collect::<Vec<_>>() | ||
51 | }; | 55 | }; |
52 | (absolutize(src.include_dirs), absolutize(src.exclude_dirs)) | 56 | (absolutize(src.include_dirs), absolutize(src.exclude_dirs)) |
53 | } | 57 | } |
@@ -79,10 +83,17 @@ impl ProjectJson { | |||
79 | .collect::<Vec<_>>(), | 83 | .collect::<Vec<_>>(), |
80 | } | 84 | } |
81 | } | 85 | } |
86 | pub fn n_crates(&self) -> usize { | ||
87 | self.crates.len() | ||
88 | } | ||
89 | pub fn crates(&self) -> impl Iterator<Item = (CrateId, &Crate)> + '_ { | ||
90 | self.crates.iter().enumerate().map(|(idx, krate)| (CrateId(idx as u32), krate)) | ||
91 | } | ||
82 | } | 92 | } |
83 | 93 | ||
84 | #[derive(Deserialize)] | 94 | #[derive(Deserialize)] |
85 | pub struct ProjectJsonData { | 95 | pub struct ProjectJsonData { |
96 | sysroot_src: Option<PathBuf>, | ||
86 | crates: Vec<CrateData>, | 97 | crates: Vec<CrateData>, |
87 | } | 98 | } |
88 | 99 | ||
diff --git a/crates/project_model/src/sysroot.rs b/crates/project_model/src/sysroot.rs index 687e15337..74c0eda9a 100644 --- a/crates/project_model/src/sysroot.rs +++ b/crates/project_model/src/sysroot.rs | |||
@@ -34,16 +34,10 @@ impl ops::Index<SysrootCrate> for Sysroot { | |||
34 | } | 34 | } |
35 | 35 | ||
36 | impl Sysroot { | 36 | impl Sysroot { |
37 | pub fn core(&self) -> Option<SysrootCrate> { | 37 | pub fn public_deps(&self) -> impl Iterator<Item = (&'static str, SysrootCrate)> + '_ { |
38 | self.by_name("core") | 38 | // core is added as a dependency before std in order to |
39 | } | 39 | // mimic rustcs dependency order |
40 | 40 | vec!["core", "alloc", "std"].into_iter().filter_map(move |it| Some((it, self.by_name(it)?))) | |
41 | pub fn alloc(&self) -> Option<SysrootCrate> { | ||
42 | self.by_name("alloc") | ||
43 | } | ||
44 | |||
45 | pub fn std(&self) -> Option<SysrootCrate> { | ||
46 | self.by_name("std") | ||
47 | } | 41 | } |
48 | 42 | ||
49 | pub fn proc_macro(&self) -> Option<SysrootCrate> { | 43 | pub fn proc_macro(&self) -> Option<SysrootCrate> { |
@@ -81,7 +75,7 @@ impl Sysroot { | |||
81 | } | 75 | } |
82 | } | 76 | } |
83 | 77 | ||
84 | if let Some(std) = sysroot.std() { | 78 | if let Some(std) = sysroot.by_name("std") { |
85 | for dep in STD_DEPS.trim().lines() { | 79 | for dep in STD_DEPS.trim().lines() { |
86 | if let Some(dep) = sysroot.by_name(dep) { | 80 | if let Some(dep) = sysroot.by_name(dep) { |
87 | sysroot.crates[std].deps.push(dep) | 81 | sysroot.crates[std].deps.push(dep) |
@@ -89,8 +83,8 @@ impl Sysroot { | |||
89 | } | 83 | } |
90 | } | 84 | } |
91 | 85 | ||
92 | if let Some(alloc) = sysroot.alloc() { | 86 | if let Some(alloc) = sysroot.by_name("alloc") { |
93 | if let Some(core) = sysroot.core() { | 87 | if let Some(core) = sysroot.by_name("core") { |
94 | sysroot.crates[alloc].deps.push(core); | 88 | sysroot.crates[alloc].deps.push(core); |
95 | } | 89 | } |
96 | } | 90 | } |
diff --git a/crates/rust-analyzer/src/bin/args.rs b/crates/rust-analyzer/src/bin/args.rs index 0bc92431a..8ddf1e031 100644 --- a/crates/rust-analyzer/src/bin/args.rs +++ b/crates/rust-analyzer/src/bin/args.rs | |||
@@ -3,9 +3,9 @@ | |||
3 | //! If run started args, we run the LSP server loop. With a subcommand, we do a | 3 | //! If run started args, we run the LSP server loop. With a subcommand, we do a |
4 | //! one-time batch processing. | 4 | //! one-time batch processing. |
5 | 5 | ||
6 | use std::{env, fmt::Write, path::PathBuf}; | 6 | use std::{env, path::PathBuf}; |
7 | 7 | ||
8 | use anyhow::{bail, Result}; | 8 | use anyhow::{bail, format_err, Result}; |
9 | use pico_args::Arguments; | 9 | use pico_args::Arguments; |
10 | use rust_analyzer::cli::{AnalysisStatsCmd, BenchCmd, BenchWhat, Position, Verbosity}; | 10 | use rust_analyzer::cli::{AnalysisStatsCmd, BenchCmd, BenchWhat, Position, Verbosity}; |
11 | use ssr::{SsrPattern, SsrRule}; | 11 | use ssr::{SsrPattern, SsrRule}; |
@@ -13,47 +13,107 @@ use vfs::AbsPathBuf; | |||
13 | 13 | ||
14 | pub(crate) struct Args { | 14 | pub(crate) struct Args { |
15 | pub(crate) verbosity: Verbosity, | 15 | pub(crate) verbosity: Verbosity, |
16 | pub(crate) log_file: Option<PathBuf>, | ||
16 | pub(crate) command: Command, | 17 | pub(crate) command: Command, |
17 | } | 18 | } |
18 | 19 | ||
19 | pub(crate) enum Command { | 20 | pub(crate) enum Command { |
20 | Parse { | 21 | Parse { no_dump: bool }, |
21 | no_dump: bool, | ||
22 | }, | ||
23 | Symbols, | 22 | Symbols, |
24 | Highlight { | 23 | Highlight { rainbow: bool }, |
25 | rainbow: bool, | ||
26 | }, | ||
27 | AnalysisStats(AnalysisStatsCmd), | 24 | AnalysisStats(AnalysisStatsCmd), |
28 | Bench(BenchCmd), | 25 | Bench(BenchCmd), |
29 | Diagnostics { | 26 | Diagnostics { path: PathBuf, load_output_dirs: bool, with_proc_macro: bool }, |
30 | path: PathBuf, | 27 | Ssr { rules: Vec<SsrRule> }, |
31 | load_output_dirs: bool, | 28 | StructuredSearch { debug_snippet: Option<String>, patterns: Vec<SsrPattern> }, |
32 | with_proc_macro: bool, | ||
33 | /// Include files which are not modules. In rust-analyzer | ||
34 | /// this would include the parser test files. | ||
35 | all: bool, | ||
36 | }, | ||
37 | Ssr { | ||
38 | rules: Vec<SsrRule>, | ||
39 | }, | ||
40 | StructuredSearch { | ||
41 | debug_snippet: Option<String>, | ||
42 | patterns: Vec<SsrPattern>, | ||
43 | }, | ||
44 | ProcMacro, | 29 | ProcMacro, |
45 | RunServer, | 30 | RunServer, |
46 | Version, | 31 | Version, |
47 | Help, | 32 | Help, |
48 | } | 33 | } |
49 | 34 | ||
35 | const HELP: &str = "\ | ||
36 | rust-analyzer | ||
37 | |||
38 | USAGE: | ||
39 | rust-analyzer [FLAGS] [COMMAND] [COMMAND_OPTIONS] | ||
40 | |||
41 | FLAGS: | ||
42 | --version Print version | ||
43 | -h, --help Print this help | ||
44 | |||
45 | -v, --verbose | ||
46 | -vv, --spammy | ||
47 | -q, --quiet Set verbosity | ||
48 | |||
49 | --log-file <PATH> Log to the specified filed instead of stderr | ||
50 | |||
51 | ENVIRONMENTAL VARIABLES: | ||
52 | RA_LOG Set log filter in env_logger format | ||
53 | RA_PROFILE Enable hierarchical profiler | ||
54 | |||
55 | COMMANDS: | ||
56 | |||
57 | not specified Launch LSP server | ||
58 | |||
59 | parse < main.rs Parse tree | ||
60 | --no-dump Suppress printing | ||
61 | |||
62 | symbols < main.rs Parse input an print the list of symbols | ||
63 | |||
64 | highlight < main.rs Highlight input as html | ||
65 | --rainbow Enable rainbow highlighting of identifiers | ||
66 | |||
67 | analysis-stats <PATH> Batch typecheck project and print summary statistics | ||
68 | <PATH> Directory with Cargo.toml | ||
69 | --randomize Randomize order in which crates, modules, and items are processed | ||
70 | --parallel Run type inference in parallel | ||
71 | --memory-usage Collect memory usage statistics | ||
72 | -o, --only <PATH> Only analyze items matching this path | ||
73 | --with-deps Also analyze all dependencies | ||
74 | --load-output-dirs | ||
75 | Load OUT_DIR values by running `cargo check` before analysis | ||
76 | --with-proc-macro Use proc-macro-srv for proc-macro expanding | ||
77 | |||
78 | analysis-bench <PATH> Benchmark specific analysis operation | ||
79 | <PATH> Directory with Cargo.toml | ||
80 | --highlight <PATH> | ||
81 | Compute syntax highlighting for this file | ||
82 | --complete <PATH:LINE:COLUMN> | ||
83 | Compute completions at this location | ||
84 | --goto-def <PATH:LINE:COLUMN> | ||
85 | Compute goto definition at this location | ||
86 | --memory-usage Collect memory usage statistics | ||
87 | --load-output-dirs | ||
88 | Load OUT_DIR values by running `cargo check` before analysis | ||
89 | --with-proc-macro Use proc-macro-srv for proc-macro expanding | ||
90 | |||
91 | diagnostics <PATH> | ||
92 | <PATH> Directory with Cargo.toml | ||
93 | --load-output-dirs | ||
94 | Load OUT_DIR values by running `cargo check` before analysis | ||
95 | --with-proc-macro Use proc-macro-srv for proc-macro expanding | ||
96 | |||
97 | ssr [RULE...] | ||
98 | <RULE> A structured search replace rule (`$a.foo($b) ==> bar($a, $b)`) | ||
99 | |||
100 | search [PATTERN..] | ||
101 | <PATTERN> A structured search replace pattern (`$a.foo($b)`) | ||
102 | --debug <snippet> Prints debug information for any nodes with source exactly | ||
103 | equal to <snippet> | ||
104 | "; | ||
105 | |||
50 | impl Args { | 106 | impl Args { |
51 | pub(crate) fn parse() -> Result<Args> { | 107 | pub(crate) fn parse() -> Result<Args> { |
52 | let mut matches = Arguments::from_env(); | 108 | let mut matches = Arguments::from_env(); |
53 | 109 | ||
54 | if matches.contains("--version") { | 110 | if matches.contains("--version") { |
55 | matches.finish().or_else(handle_extra_flags)?; | 111 | matches.finish()?; |
56 | return Ok(Args { verbosity: Verbosity::Normal, command: Command::Version }); | 112 | return Ok(Args { |
113 | verbosity: Verbosity::Normal, | ||
114 | log_file: None, | ||
115 | command: Command::Version, | ||
116 | }); | ||
57 | } | 117 | } |
58 | 118 | ||
59 | let verbosity = match ( | 119 | let verbosity = match ( |
@@ -68,320 +128,96 @@ impl Args { | |||
68 | (false, true, false) => Verbosity::Verbose, | 128 | (false, true, false) => Verbosity::Verbose, |
69 | (false, true, true) => bail!("Invalid flags: -q conflicts with -v"), | 129 | (false, true, true) => bail!("Invalid flags: -q conflicts with -v"), |
70 | }; | 130 | }; |
131 | let log_file = matches.opt_value_from_str("--log-file")?; | ||
132 | |||
133 | if matches.contains(["-h", "--help"]) { | ||
134 | eprintln!("{}", HELP); | ||
135 | return Ok(Args { verbosity, log_file: None, command: Command::Help }); | ||
136 | } | ||
71 | 137 | ||
72 | let help = Ok(Args { verbosity, command: Command::Help }); | ||
73 | let subcommand = match matches.subcommand()? { | 138 | let subcommand = match matches.subcommand()? { |
74 | Some(it) => it, | 139 | Some(it) => it, |
75 | None => { | 140 | None => { |
76 | if matches.contains(["-h", "--help"]) { | 141 | matches.finish()?; |
77 | print_subcommands(); | 142 | return Ok(Args { verbosity, log_file, command: Command::RunServer }); |
78 | return help; | ||
79 | } | ||
80 | matches.finish().or_else(handle_extra_flags)?; | ||
81 | return Ok(Args { verbosity, command: Command::RunServer }); | ||
82 | } | 143 | } |
83 | }; | 144 | }; |
84 | let command = match subcommand.as_str() { | 145 | let command = match subcommand.as_str() { |
85 | "parse" => { | 146 | "parse" => Command::Parse { no_dump: matches.contains("--no-dump") }, |
86 | if matches.contains(["-h", "--help"]) { | 147 | "symbols" => Command::Symbols, |
87 | eprintln!( | 148 | "highlight" => Command::Highlight { rainbow: matches.contains("--rainbow") }, |
88 | "\ | 149 | "analysis-stats" => Command::AnalysisStats(AnalysisStatsCmd { |
89 | rust-analyzer parse | 150 | randomize: matches.contains("--randomize"), |
90 | 151 | parallel: matches.contains("--parallel"), | |
91 | USAGE: | 152 | memory_usage: matches.contains("--memory-usage"), |
92 | rust-analyzer parse [FLAGS] | 153 | only: matches.opt_value_from_str(["-o", "--only"])?, |
93 | 154 | with_deps: matches.contains("--with-deps"), | |
94 | FLAGS: | 155 | load_output_dirs: matches.contains("--load-output-dirs"), |
95 | -h, --help Prints help information | 156 | with_proc_macro: matches.contains("--with-proc-macro"), |
96 | --no-dump" | 157 | path: matches |
97 | ); | 158 | .free_from_str()? |
98 | return help; | 159 | .ok_or_else(|| format_err!("expected positional argument"))?, |
99 | } | 160 | }), |
100 | 161 | "analysis-bench" => Command::Bench(BenchCmd { | |
101 | let no_dump = matches.contains("--no-dump"); | 162 | what: { |
102 | matches.finish().or_else(handle_extra_flags)?; | 163 | let highlight_path: Option<String> = |
103 | Command::Parse { no_dump } | 164 | matches.opt_value_from_str("--highlight")?; |
104 | } | 165 | let complete_path: Option<Position> = |
105 | "symbols" => { | 166 | matches.opt_value_from_str("--complete")?; |
106 | if matches.contains(["-h", "--help"]) { | 167 | let goto_def_path: Option<Position> = |
107 | eprintln!( | 168 | matches.opt_value_from_str("--goto-def")?; |
108 | "\ | 169 | match (highlight_path, complete_path, goto_def_path) { |
109 | rust-analyzer symbols | 170 | (Some(path), None, None) => { |
110 | 171 | let path = env::current_dir().unwrap().join(path); | |
111 | USAGE: | 172 | BenchWhat::Highlight { path: AbsPathBuf::assert(path) } |
112 | rust-analyzer highlight [FLAGS] | 173 | } |
113 | 174 | (None, Some(position), None) => BenchWhat::Complete(position), | |
114 | FLAGS: | 175 | (None, None, Some(position)) => BenchWhat::GotoDef(position), |
115 | -h, --help Prints help inforamtion" | 176 | _ => panic!( |
116 | ); | 177 | "exactly one of `--highlight`, `--complete` or `--goto-def` must be set" |
117 | return help; | 178 | ), |
118 | } | 179 | } |
119 | 180 | }, | |
120 | matches.finish().or_else(handle_extra_flags)?; | 181 | memory_usage: matches.contains("--memory-usage"), |
121 | 182 | load_output_dirs: matches.contains("--load-output-dirs"), | |
122 | Command::Symbols | 183 | with_proc_macro: matches.contains("--with-proc-macro"), |
123 | } | 184 | path: matches |
124 | "highlight" => { | 185 | .free_from_str()? |
125 | if matches.contains(["-h", "--help"]) { | 186 | .ok_or_else(|| format_err!("expected positional argument"))?, |
126 | eprintln!( | 187 | }), |
127 | "\ | 188 | "diagnostics" => Command::Diagnostics { |
128 | rust-analyzer highlight | 189 | load_output_dirs: matches.contains("--load-output-dirs"), |
129 | 190 | with_proc_macro: matches.contains("--with-proc-macro"), | |
130 | USAGE: | 191 | path: matches |
131 | rust-analyzer highlight [FLAGS] | 192 | .free_from_str()? |
132 | 193 | .ok_or_else(|| format_err!("expected positional argument"))?, | |
133 | FLAGS: | 194 | }, |
134 | -h, --help Prints help information | 195 | "proc-macro" => Command::ProcMacro, |
135 | -r, --rainbow" | 196 | "ssr" => Command::Ssr { |
136 | ); | 197 | rules: { |
137 | return help; | 198 | let mut acc = Vec::new(); |
138 | } | 199 | while let Some(rule) = matches.free_from_str()? { |
139 | 200 | acc.push(rule); | |
140 | let rainbow = matches.contains(["-r", "--rainbow"]); | ||
141 | matches.finish().or_else(handle_extra_flags)?; | ||
142 | Command::Highlight { rainbow } | ||
143 | } | ||
144 | "analysis-stats" => { | ||
145 | if matches.contains(["-h", "--help"]) { | ||
146 | eprintln!( | ||
147 | "\ | ||
148 | rust-analyzer analysis-stats | ||
149 | |||
150 | USAGE: | ||
151 | rust-analyzer analysis-stats [FLAGS] [OPTIONS] [PATH] | ||
152 | |||
153 | FLAGS: | ||
154 | -o, --only Only analyze items matching this path | ||
155 | -h, --help Prints help information | ||
156 | --memory-usage Collect memory usage statistics | ||
157 | --randomize Randomize order in which crates, modules, and items are processed | ||
158 | --parallel Run type inference in parallel | ||
159 | --load-output-dirs Load OUT_DIR values by running `cargo check` before analysis | ||
160 | --with-proc-macro Use ra-proc-macro-srv for proc-macro expanding | ||
161 | --with-deps Also analyze all dependencies | ||
162 | -v, --verbose | ||
163 | -q, --quiet | ||
164 | |||
165 | OPTIONS: | ||
166 | -o <ONLY> | ||
167 | |||
168 | ARGS: | ||
169 | <PATH>" | ||
170 | ); | ||
171 | return help; | ||
172 | } | ||
173 | |||
174 | let randomize = matches.contains("--randomize"); | ||
175 | let parallel = matches.contains("--parallel"); | ||
176 | let memory_usage = matches.contains("--memory-usage"); | ||
177 | let only: Option<String> = matches.opt_value_from_str(["-o", "--only"])?; | ||
178 | let with_deps: bool = matches.contains("--with-deps"); | ||
179 | let load_output_dirs = matches.contains("--load-output-dirs"); | ||
180 | let with_proc_macro = matches.contains("--with-proc-macro"); | ||
181 | let path = { | ||
182 | let mut trailing = matches.free()?; | ||
183 | if trailing.len() != 1 { | ||
184 | bail!("Invalid flags"); | ||
185 | } | ||
186 | trailing.pop().unwrap().into() | ||
187 | }; | ||
188 | |||
189 | Command::AnalysisStats(AnalysisStatsCmd { | ||
190 | randomize, | ||
191 | parallel, | ||
192 | memory_usage, | ||
193 | only, | ||
194 | with_deps, | ||
195 | path, | ||
196 | load_output_dirs, | ||
197 | with_proc_macro, | ||
198 | }) | ||
199 | } | ||
200 | "analysis-bench" => { | ||
201 | if matches.contains(["-h", "--help"]) { | ||
202 | eprintln!( | ||
203 | "\ | ||
204 | rust-analyzer analysis-bench | ||
205 | |||
206 | USAGE: | ||
207 | rust-analyzer analysis-bench [FLAGS] [OPTIONS] | ||
208 | |||
209 | FLAGS: | ||
210 | -h, --help Prints help information | ||
211 | --memory-usage Collect memory usage statistics | ||
212 | --load-output-dirs Load OUT_DIR values by running `cargo check` before analysis | ||
213 | --with-proc-macro Use ra-proc-macro-srv for proc-macro expanding | ||
214 | -v, --verbose | ||
215 | |||
216 | OPTIONS: | ||
217 | --project <PATH> Path to directory with Cargo.toml | ||
218 | --complete <PATH:LINE:COLUMN> Compute completions at this location | ||
219 | --goto-def <PATH:LINE:COLUMN> Compute goto definition at this location | ||
220 | --highlight <PATH> Hightlight this file | ||
221 | |||
222 | ARGS: | ||
223 | <PATH> Project to analyse" | ||
224 | ); | ||
225 | return help; | ||
226 | } | ||
227 | |||
228 | let path: PathBuf = matches.opt_value_from_str("--project")?.unwrap_or_default(); | ||
229 | let highlight_path: Option<String> = matches.opt_value_from_str("--highlight")?; | ||
230 | let complete_path: Option<Position> = matches.opt_value_from_str("--complete")?; | ||
231 | let goto_def_path: Option<Position> = matches.opt_value_from_str("--goto-def")?; | ||
232 | let what = match (highlight_path, complete_path, goto_def_path) { | ||
233 | (Some(path), None, None) => { | ||
234 | let path = env::current_dir().unwrap().join(path); | ||
235 | BenchWhat::Highlight { path: AbsPathBuf::assert(path) } | ||
236 | } | 201 | } |
237 | (None, Some(position), None) => BenchWhat::Complete(position), | 202 | acc |
238 | (None, None, Some(position)) => BenchWhat::GotoDef(position), | 203 | }, |
239 | _ => panic!( | 204 | }, |
240 | "exactly one of `--highlight`, `--complete` or `--goto-def` must be set" | 205 | "search" => Command::StructuredSearch { |
241 | ), | 206 | debug_snippet: matches.opt_value_from_str("--debug")?, |
242 | }; | 207 | patterns: { |
243 | let memory_usage = matches.contains("--memory-usage"); | 208 | let mut acc = Vec::new(); |
244 | let load_output_dirs = matches.contains("--load-output-dirs"); | 209 | while let Some(rule) = matches.free_from_str()? { |
245 | let with_proc_macro = matches.contains("--with-proc-macro"); | 210 | acc.push(rule); |
246 | Command::Bench(BenchCmd { | ||
247 | memory_usage, | ||
248 | path, | ||
249 | what, | ||
250 | load_output_dirs, | ||
251 | with_proc_macro, | ||
252 | }) | ||
253 | } | ||
254 | "diagnostics" => { | ||
255 | if matches.contains(["-h", "--help"]) { | ||
256 | eprintln!( | ||
257 | "\ | ||
258 | rust-analyzer diagnostics | ||
259 | |||
260 | USAGE: | ||
261 | rust-analyzer diagnostics [FLAGS] [PATH] | ||
262 | |||
263 | FLAGS: | ||
264 | -h, --help Prints help information | ||
265 | --load-output-dirs Load OUT_DIR values by running `cargo check` before analysis | ||
266 | --all Include all files rather than only modules | ||
267 | |||
268 | ARGS: | ||
269 | <PATH>" | ||
270 | ); | ||
271 | return help; | ||
272 | } | ||
273 | |||
274 | let load_output_dirs = matches.contains("--load-output-dirs"); | ||
275 | let with_proc_macro = matches.contains("--with-proc-macro"); | ||
276 | let all = matches.contains("--all"); | ||
277 | let path = { | ||
278 | let mut trailing = matches.free()?; | ||
279 | if trailing.len() != 1 { | ||
280 | bail!("Invalid flags"); | ||
281 | } | 211 | } |
282 | trailing.pop().unwrap().into() | 212 | acc |
283 | }; | 213 | }, |
284 | 214 | }, | |
285 | Command::Diagnostics { path, load_output_dirs, with_proc_macro, all } | ||
286 | } | ||
287 | "proc-macro" => Command::ProcMacro, | ||
288 | "ssr" => { | ||
289 | if matches.contains(["-h", "--help"]) { | ||
290 | eprintln!( | ||
291 | "\ | ||
292 | rust-analyzer ssr | ||
293 | |||
294 | USAGE: | ||
295 | rust-analyzer ssr [FLAGS] [RULE...] | ||
296 | |||
297 | EXAMPLE: | ||
298 | rust-analyzer ssr '$a.foo($b) ==> bar($a, $b)' | ||
299 | |||
300 | FLAGS: | ||
301 | --debug <snippet> Prints debug information for any nodes with source exactly equal to <snippet> | ||
302 | -h, --help Prints help information | ||
303 | |||
304 | ARGS: | ||
305 | <RULE> A structured search replace rule" | ||
306 | ); | ||
307 | return help; | ||
308 | } | ||
309 | let mut rules = Vec::new(); | ||
310 | while let Some(rule) = matches.free_from_str()? { | ||
311 | rules.push(rule); | ||
312 | } | ||
313 | Command::Ssr { rules } | ||
314 | } | ||
315 | "search" => { | ||
316 | if matches.contains(["-h", "--help"]) { | ||
317 | eprintln!( | ||
318 | "\ | ||
319 | rust-analyzer search | ||
320 | |||
321 | USAGE: | ||
322 | rust-analyzer search [FLAGS] [PATTERN...] | ||
323 | |||
324 | EXAMPLE: | ||
325 | rust-analyzer search '$a.foo($b)' | ||
326 | |||
327 | FLAGS: | ||
328 | --debug <snippet> Prints debug information for any nodes with source exactly equal to <snippet> | ||
329 | -h, --help Prints help information | ||
330 | |||
331 | ARGS: | ||
332 | <PATTERN> A structured search pattern" | ||
333 | ); | ||
334 | return help; | ||
335 | } | ||
336 | let debug_snippet = matches.opt_value_from_str("--debug")?; | ||
337 | let mut patterns = Vec::new(); | ||
338 | while let Some(rule) = matches.free_from_str()? { | ||
339 | patterns.push(rule); | ||
340 | } | ||
341 | Command::StructuredSearch { patterns, debug_snippet } | ||
342 | } | ||
343 | _ => { | 215 | _ => { |
344 | print_subcommands(); | 216 | eprintln!("{}", HELP); |
345 | return help; | 217 | return Ok(Args { verbosity, log_file: None, command: Command::Help }); |
346 | } | 218 | } |
347 | }; | 219 | }; |
348 | Ok(Args { verbosity, command }) | 220 | matches.finish()?; |
349 | } | 221 | Ok(Args { verbosity, log_file, command }) |
350 | } | ||
351 | |||
352 | fn print_subcommands() { | ||
353 | eprintln!( | ||
354 | "\ | ||
355 | rust-analyzer | ||
356 | |||
357 | USAGE: | ||
358 | rust-analyzer <SUBCOMMAND> | ||
359 | |||
360 | FLAGS: | ||
361 | -h, --help Prints help information | ||
362 | |||
363 | SUBCOMMANDS: | ||
364 | analysis-bench | ||
365 | analysis-stats | ||
366 | highlight | ||
367 | diagnostics | ||
368 | proc-macro | ||
369 | parse | ||
370 | search | ||
371 | ssr | ||
372 | symbols" | ||
373 | ) | ||
374 | } | ||
375 | |||
376 | fn handle_extra_flags(e: pico_args::Error) -> Result<()> { | ||
377 | if let pico_args::Error::UnusedArgsLeft(flags) = e { | ||
378 | let mut invalid_flags = String::new(); | ||
379 | for flag in flags { | ||
380 | write!(&mut invalid_flags, "{}, ", flag)?; | ||
381 | } | ||
382 | let (invalid_flags, _) = invalid_flags.split_at(invalid_flags.len() - 2); | ||
383 | bail!("Invalid flags: {}", invalid_flags); | ||
384 | } else { | ||
385 | bail!(e); | ||
386 | } | 222 | } |
387 | } | 223 | } |
diff --git a/crates/rust-analyzer/src/bin/logger.rs b/crates/rust-analyzer/src/bin/logger.rs new file mode 100644 index 000000000..3bcb1ae37 --- /dev/null +++ b/crates/rust-analyzer/src/bin/logger.rs | |||
@@ -0,0 +1,73 @@ | |||
1 | //! Simple logger that logs either to stderr or to a file, using `env_logger` | ||
2 | //! filter syntax. Amusingly, there's no crates.io crate that can do this and | ||
3 | //! only this. | ||
4 | |||
5 | use std::{ | ||
6 | fs::File, | ||
7 | io::{BufWriter, Write}, | ||
8 | }; | ||
9 | |||
10 | use env_logger::filter::{Builder, Filter}; | ||
11 | use log::{Log, Metadata, Record}; | ||
12 | use parking_lot::Mutex; | ||
13 | |||
14 | pub(crate) struct Logger { | ||
15 | filter: Filter, | ||
16 | file: Option<Mutex<BufWriter<File>>>, | ||
17 | } | ||
18 | |||
19 | impl Logger { | ||
20 | pub(crate) fn new(log_file: Option<File>, filter: Option<&str>) -> Logger { | ||
21 | let filter = { | ||
22 | let mut builder = Builder::new(); | ||
23 | if let Some(filter) = filter { | ||
24 | builder.parse(filter); | ||
25 | } | ||
26 | builder.build() | ||
27 | }; | ||
28 | |||
29 | let file = log_file.map(|it| Mutex::new(BufWriter::new(it))); | ||
30 | |||
31 | Logger { filter, file } | ||
32 | } | ||
33 | |||
34 | pub(crate) fn install(self) { | ||
35 | let max_level = self.filter.filter(); | ||
36 | let _ = log::set_boxed_logger(Box::new(self)).map(|()| log::set_max_level(max_level)); | ||
37 | } | ||
38 | } | ||
39 | |||
40 | impl Log for Logger { | ||
41 | fn enabled(&self, metadata: &Metadata) -> bool { | ||
42 | self.filter.enabled(metadata) | ||
43 | } | ||
44 | |||
45 | fn log(&self, record: &Record) { | ||
46 | if !self.filter.matches(record) { | ||
47 | return; | ||
48 | } | ||
49 | match &self.file { | ||
50 | Some(w) => { | ||
51 | let _ = writeln!( | ||
52 | w.lock(), | ||
53 | "[{} {}] {}", | ||
54 | record.level(), | ||
55 | record.module_path().unwrap_or_default(), | ||
56 | record.args(), | ||
57 | ); | ||
58 | } | ||
59 | None => eprintln!( | ||
60 | "[{} {}] {}", | ||
61 | record.level(), | ||
62 | record.module_path().unwrap_or_default(), | ||
63 | record.args(), | ||
64 | ), | ||
65 | } | ||
66 | } | ||
67 | |||
68 | fn flush(&self) { | ||
69 | if let Some(w) = &self.file { | ||
70 | let _ = w.lock().flush(); | ||
71 | } | ||
72 | } | ||
73 | } | ||
diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs index 0e03a0ca8..ba4402ade 100644 --- a/crates/rust-analyzer/src/bin/main.rs +++ b/crates/rust-analyzer/src/bin/main.rs | |||
@@ -2,8 +2,9 @@ | |||
2 | //! | 2 | //! |
3 | //! Based on cli flags, either spawns an LSP server, or runs a batch analysis | 3 | //! Based on cli flags, either spawns an LSP server, or runs a batch analysis |
4 | mod args; | 4 | mod args; |
5 | mod logger; | ||
5 | 6 | ||
6 | use std::{convert::TryFrom, process}; | 7 | use std::{convert::TryFrom, env, fs, path::PathBuf, process}; |
7 | 8 | ||
8 | use lsp_server::Connection; | 9 | use lsp_server::Connection; |
9 | use project_model::ProjectManifest; | 10 | use project_model::ProjectManifest; |
@@ -26,8 +27,8 @@ fn main() { | |||
26 | } | 27 | } |
27 | 28 | ||
28 | fn try_main() -> Result<()> { | 29 | fn try_main() -> Result<()> { |
29 | setup_logging()?; | ||
30 | let args = args::Args::parse()?; | 30 | let args = args::Args::parse()?; |
31 | setup_logging(args.log_file)?; | ||
31 | match args.command { | 32 | match args.command { |
32 | args::Command::RunServer => run_server()?, | 33 | args::Command::RunServer => run_server()?, |
33 | args::Command::ProcMacro => proc_macro_srv::cli::run()?, | 34 | args::Command::ProcMacro => proc_macro_srv::cli::run()?, |
@@ -37,8 +38,8 @@ fn try_main() -> Result<()> { | |||
37 | args::Command::Highlight { rainbow } => cli::highlight(rainbow)?, | 38 | args::Command::Highlight { rainbow } => cli::highlight(rainbow)?, |
38 | args::Command::AnalysisStats(cmd) => cmd.run(args.verbosity)?, | 39 | args::Command::AnalysisStats(cmd) => cmd.run(args.verbosity)?, |
39 | args::Command::Bench(cmd) => cmd.run(args.verbosity)?, | 40 | args::Command::Bench(cmd) => cmd.run(args.verbosity)?, |
40 | args::Command::Diagnostics { path, load_output_dirs, with_proc_macro, all } => { | 41 | args::Command::Diagnostics { path, load_output_dirs, with_proc_macro } => { |
41 | cli::diagnostics(path.as_ref(), load_output_dirs, with_proc_macro, all)? | 42 | cli::diagnostics(path.as_ref(), load_output_dirs, with_proc_macro)? |
42 | } | 43 | } |
43 | args::Command::Ssr { rules } => { | 44 | args::Command::Ssr { rules } => { |
44 | cli::apply_ssr_rules(rules)?; | 45 | cli::apply_ssr_rules(rules)?; |
@@ -52,9 +53,21 @@ fn try_main() -> Result<()> { | |||
52 | Ok(()) | 53 | Ok(()) |
53 | } | 54 | } |
54 | 55 | ||
55 | fn setup_logging() -> Result<()> { | 56 | fn setup_logging(log_file: Option<PathBuf>) -> Result<()> { |
56 | std::env::set_var("RUST_BACKTRACE", "short"); | 57 | env::set_var("RUST_BACKTRACE", "short"); |
57 | env_logger::try_init_from_env("RA_LOG")?; | 58 | |
59 | let log_file = match log_file { | ||
60 | Some(path) => { | ||
61 | if let Some(parent) = path.parent() { | ||
62 | let _ = fs::create_dir_all(parent); | ||
63 | } | ||
64 | Some(fs::File::create(path)?) | ||
65 | } | ||
66 | None => None, | ||
67 | }; | ||
68 | let filter = env::var("RA_LOG").ok(); | ||
69 | logger::Logger::new(log_file, filter.as_deref()).install(); | ||
70 | |||
58 | profile::init(); | 71 | profile::init(); |
59 | Ok(()) | 72 | Ok(()) |
60 | } | 73 | } |
@@ -95,7 +108,7 @@ fn run_server() -> Result<()> { | |||
95 | { | 108 | { |
96 | Some(it) => it, | 109 | Some(it) => it, |
97 | None => { | 110 | None => { |
98 | let cwd = std::env::current_dir()?; | 111 | let cwd = env::current_dir()?; |
99 | AbsPathBuf::assert(cwd) | 112 | AbsPathBuf::assert(cwd) |
100 | } | 113 | } |
101 | }; | 114 | }; |
diff --git a/crates/rust-analyzer/src/cli/diagnostics.rs b/crates/rust-analyzer/src/cli/diagnostics.rs index c424aa6e2..f3b6c900e 100644 --- a/crates/rust-analyzer/src/cli/diagnostics.rs +++ b/crates/rust-analyzer/src/cli/diagnostics.rs | |||
@@ -12,12 +12,7 @@ use ide::{DiagnosticsConfig, Severity}; | |||
12 | 12 | ||
13 | use crate::cli::{load_cargo::load_cargo, Result}; | 13 | use crate::cli::{load_cargo::load_cargo, Result}; |
14 | 14 | ||
15 | pub fn diagnostics( | 15 | pub fn diagnostics(path: &Path, load_output_dirs: bool, with_proc_macro: bool) -> Result<()> { |
16 | path: &Path, | ||
17 | load_output_dirs: bool, | ||
18 | with_proc_macro: bool, | ||
19 | _all: bool, | ||
20 | ) -> Result<()> { | ||
21 | let (host, _vfs) = load_cargo(path, load_output_dirs, with_proc_macro)?; | 16 | let (host, _vfs) = load_cargo(path, load_output_dirs, with_proc_macro)?; |
22 | let db = host.raw_database(); | 17 | let db = host.raw_database(); |
23 | let analysis = host.analysis(); | 18 | let analysis = host.analysis(); |
diff --git a/crates/rust-analyzer/tests/rust-analyzer/main.rs b/crates/rust-analyzer/tests/rust-analyzer/main.rs index e97784c47..0880d0425 100644 --- a/crates/rust-analyzer/tests/rust-analyzer/main.rs +++ b/crates/rust-analyzer/tests/rust-analyzer/main.rs | |||
@@ -39,7 +39,6 @@ fn completes_items_from_standard_library() { | |||
39 | return; | 39 | return; |
40 | } | 40 | } |
41 | 41 | ||
42 | let project_start = Instant::now(); | ||
43 | let server = Project::with_fixture( | 42 | let server = Project::with_fixture( |
44 | r#" | 43 | r#" |
45 | //- /Cargo.toml | 44 | //- /Cargo.toml |
@@ -52,10 +51,9 @@ use std::collections::Spam; | |||
52 | "#, | 51 | "#, |
53 | ) | 52 | ) |
54 | .with_sysroot(true) | 53 | .with_sysroot(true) |
55 | .server(); | 54 | .server() |
56 | server.wait_until_workspace_is_loaded(); | 55 | .wait_until_workspace_is_loaded(); |
57 | eprintln!("loading took {:?}", project_start.elapsed()); | 56 | |
58 | let completion_start = Instant::now(); | ||
59 | let res = server.send_request::<Completion>(CompletionParams { | 57 | let res = server.send_request::<Completion>(CompletionParams { |
60 | text_document_position: TextDocumentPositionParams::new( | 58 | text_document_position: TextDocumentPositionParams::new( |
61 | server.doc_id("src/lib.rs"), | 59 | server.doc_id("src/lib.rs"), |
@@ -66,7 +64,6 @@ use std::collections::Spam; | |||
66 | work_done_progress_params: WorkDoneProgressParams::default(), | 64 | work_done_progress_params: WorkDoneProgressParams::default(), |
67 | }); | 65 | }); |
68 | assert!(res.to_string().contains("HashMap")); | 66 | assert!(res.to_string().contains("HashMap")); |
69 | eprintln!("completion took {:?}", completion_start.elapsed()); | ||
70 | } | 67 | } |
71 | 68 | ||
72 | #[test] | 69 | #[test] |
@@ -75,7 +72,8 @@ fn test_runnables_project() { | |||
75 | return; | 72 | return; |
76 | } | 73 | } |
77 | 74 | ||
78 | let code = r#" | 75 | let server = Project::with_fixture( |
76 | r#" | ||
79 | //- /foo/Cargo.toml | 77 | //- /foo/Cargo.toml |
80 | [package] | 78 | [package] |
81 | name = "foo" | 79 | name = "foo" |
@@ -95,11 +93,13 @@ version = "0.0.0" | |||
95 | 93 | ||
96 | //- /bar/src/main.rs | 94 | //- /bar/src/main.rs |
97 | fn main() {} | 95 | fn main() {} |
98 | "#; | 96 | "#, |
99 | 97 | ) | |
100 | let server = Project::with_fixture(code).root("foo").root("bar").server(); | 98 | .root("foo") |
99 | .root("bar") | ||
100 | .server() | ||
101 | .wait_until_workspace_is_loaded(); | ||
101 | 102 | ||
102 | server.wait_until_workspace_is_loaded(); | ||
103 | server.request::<Runnables>( | 103 | server.request::<Runnables>( |
104 | RunnablesParams { text_document: server.doc_id("foo/tests/spam.rs"), position: None }, | 104 | RunnablesParams { text_document: server.doc_id("foo/tests/spam.rs"), position: None }, |
105 | json!([ | 105 | json!([ |
@@ -166,8 +166,8 @@ fn main() { | |||
166 | 166 | ||
167 | pub use std::collections::HashMap; | 167 | pub use std::collections::HashMap; |
168 | "#, | 168 | "#, |
169 | ); | 169 | ) |
170 | server.wait_until_workspace_is_loaded(); | 170 | .wait_until_workspace_is_loaded(); |
171 | 171 | ||
172 | server.request::<Formatting>( | 172 | server.request::<Formatting>( |
173 | DocumentFormattingParams { | 173 | DocumentFormattingParams { |
@@ -224,8 +224,8 @@ fn main() { | |||
224 | 224 | ||
225 | pub use std::collections::HashMap; | 225 | pub use std::collections::HashMap; |
226 | "#, | 226 | "#, |
227 | ); | 227 | ) |
228 | server.wait_until_workspace_is_loaded(); | 228 | .wait_until_workspace_is_loaded(); |
229 | 229 | ||
230 | server.request::<Formatting>( | 230 | server.request::<Formatting>( |
231 | DocumentFormattingParams { | 231 | DocumentFormattingParams { |
@@ -277,14 +277,14 @@ mod bar; | |||
277 | 277 | ||
278 | fn main() {} | 278 | fn main() {} |
279 | "#, | 279 | "#, |
280 | ); | 280 | ) |
281 | server.wait_until_workspace_is_loaded(); | 281 | .wait_until_workspace_is_loaded(); |
282 | let empty_context = || CodeActionContext { diagnostics: Vec::new(), only: None }; | 282 | |
283 | server.request::<CodeActionRequest>( | 283 | server.request::<CodeActionRequest>( |
284 | CodeActionParams { | 284 | CodeActionParams { |
285 | text_document: server.doc_id("src/lib.rs"), | 285 | text_document: server.doc_id("src/lib.rs"), |
286 | range: Range::new(Position::new(0, 4), Position::new(0, 7)), | 286 | range: Range::new(Position::new(0, 4), Position::new(0, 7)), |
287 | context: empty_context(), | 287 | context: CodeActionContext::default(), |
288 | partial_result_params: PartialResultParams::default(), | 288 | partial_result_params: PartialResultParams::default(), |
289 | work_done_progress_params: WorkDoneProgressParams::default(), | 289 | work_done_progress_params: WorkDoneProgressParams::default(), |
290 | }, | 290 | }, |
@@ -307,7 +307,7 @@ fn main() {} | |||
307 | CodeActionParams { | 307 | CodeActionParams { |
308 | text_document: server.doc_id("src/lib.rs"), | 308 | text_document: server.doc_id("src/lib.rs"), |
309 | range: Range::new(Position::new(2, 4), Position::new(2, 7)), | 309 | range: Range::new(Position::new(2, 4), Position::new(2, 7)), |
310 | context: empty_context(), | 310 | context: CodeActionContext::default(), |
311 | partial_result_params: PartialResultParams::default(), | 311 | partial_result_params: PartialResultParams::default(), |
312 | work_done_progress_params: WorkDoneProgressParams::default(), | 312 | work_done_progress_params: WorkDoneProgressParams::default(), |
313 | }, | 313 | }, |
@@ -348,15 +348,14 @@ fn main() {{}} | |||
348 | PROJECT = project.to_string(), | 348 | PROJECT = project.to_string(), |
349 | ); | 349 | ); |
350 | 350 | ||
351 | let server = Project::with_fixture(&code).tmp_dir(tmp_dir).server(); | 351 | let server = |
352 | Project::with_fixture(&code).tmp_dir(tmp_dir).server().wait_until_workspace_is_loaded(); | ||
352 | 353 | ||
353 | server.wait_until_workspace_is_loaded(); | ||
354 | let empty_context = || CodeActionContext { diagnostics: Vec::new(), only: None }; | ||
355 | server.request::<CodeActionRequest>( | 354 | server.request::<CodeActionRequest>( |
356 | CodeActionParams { | 355 | CodeActionParams { |
357 | text_document: server.doc_id("src/lib.rs"), | 356 | text_document: server.doc_id("src/lib.rs"), |
358 | range: Range::new(Position::new(0, 4), Position::new(0, 7)), | 357 | range: Range::new(Position::new(0, 4), Position::new(0, 7)), |
359 | context: empty_context(), | 358 | context: CodeActionContext::default(), |
360 | partial_result_params: PartialResultParams::default(), | 359 | partial_result_params: PartialResultParams::default(), |
361 | work_done_progress_params: WorkDoneProgressParams::default(), | 360 | work_done_progress_params: WorkDoneProgressParams::default(), |
362 | }, | 361 | }, |
@@ -379,7 +378,7 @@ fn main() {{}} | |||
379 | CodeActionParams { | 378 | CodeActionParams { |
380 | text_document: server.doc_id("src/lib.rs"), | 379 | text_document: server.doc_id("src/lib.rs"), |
381 | range: Range::new(Position::new(2, 4), Position::new(2, 7)), | 380 | range: Range::new(Position::new(2, 4), Position::new(2, 7)), |
382 | context: empty_context(), | 381 | context: CodeActionContext::default(), |
383 | partial_result_params: PartialResultParams::default(), | 382 | partial_result_params: PartialResultParams::default(), |
384 | work_done_progress_params: WorkDoneProgressParams::default(), | 383 | work_done_progress_params: WorkDoneProgressParams::default(), |
385 | }, | 384 | }, |
@@ -412,9 +411,9 @@ fn main() {{}} | |||
412 | librs, libs | 411 | librs, libs |
413 | )) | 412 | )) |
414 | .with_sysroot(true) | 413 | .with_sysroot(true) |
415 | .server(); | 414 | .server() |
415 | .wait_until_workspace_is_loaded(); | ||
416 | 416 | ||
417 | server.wait_until_workspace_is_loaded(); | ||
418 | for i in 0..10 { | 417 | for i in 0..10 { |
419 | server.notification::<DidOpenTextDocument>(DidOpenTextDocumentParams { | 418 | server.notification::<DidOpenTextDocument>(DidOpenTextDocumentParams { |
420 | text_document: TextDocumentItem { | 419 | text_document: TextDocumentItem { |
@@ -425,7 +424,7 @@ fn main() {{}} | |||
425 | }, | 424 | }, |
426 | }); | 425 | }); |
427 | } | 426 | } |
428 | let start = std::time::Instant::now(); | 427 | let start = Instant::now(); |
429 | server.request::<OnEnter>( | 428 | server.request::<OnEnter>( |
430 | TextDocumentPositionParams { | 429 | TextDocumentPositionParams { |
431 | text_document: server.doc_id("src/m0.rs"), | 430 | text_document: server.doc_id("src/m0.rs"), |
@@ -461,8 +460,8 @@ version = \"0.0.0\" | |||
461 | /// Some Docs\r\nfn main() {} | 460 | /// Some Docs\r\nfn main() {} |
462 | ", | 461 | ", |
463 | ) | 462 | ) |
464 | .server(); | 463 | .server() |
465 | server.wait_until_workspace_is_loaded(); | 464 | .wait_until_workspace_is_loaded(); |
466 | 465 | ||
467 | server.request::<OnEnter>( | 466 | server.request::<OnEnter>( |
468 | TextDocumentPositionParams { | 467 | TextDocumentPositionParams { |
@@ -536,8 +535,9 @@ fn main() { | |||
536 | .with_config(|config| { | 535 | .with_config(|config| { |
537 | config.cargo.load_out_dirs_from_check = true; | 536 | config.cargo.load_out_dirs_from_check = true; |
538 | }) | 537 | }) |
539 | .server(); | 538 | .server() |
540 | server.wait_until_workspace_is_loaded(); | 539 | .wait_until_workspace_is_loaded(); |
540 | |||
541 | let res = server.send_request::<HoverRequest>(HoverParams { | 541 | let res = server.send_request::<HoverRequest>(HoverParams { |
542 | text_document_position_params: TextDocumentPositionParams::new( | 542 | text_document_position_params: TextDocumentPositionParams::new( |
543 | server.doc_id("src/main.rs"), | 543 | server.doc_id("src/main.rs"), |
@@ -546,6 +546,7 @@ fn main() { | |||
546 | work_done_progress_params: Default::default(), | 546 | work_done_progress_params: Default::default(), |
547 | }); | 547 | }); |
548 | assert!(res.to_string().contains("&str")); | 548 | assert!(res.to_string().contains("&str")); |
549 | |||
549 | let res = server.send_request::<HoverRequest>(HoverParams { | 550 | let res = server.send_request::<HoverRequest>(HoverParams { |
550 | text_document_position_params: TextDocumentPositionParams::new( | 551 | text_document_position_params: TextDocumentPositionParams::new( |
551 | server.doc_id("src/main.rs"), | 552 | server.doc_id("src/main.rs"), |
@@ -554,6 +555,7 @@ fn main() { | |||
554 | work_done_progress_params: Default::default(), | 555 | work_done_progress_params: Default::default(), |
555 | }); | 556 | }); |
556 | assert!(res.to_string().contains("&str")); | 557 | assert!(res.to_string().contains("&str")); |
558 | |||
557 | server.request::<GotoTypeDefinition>( | 559 | server.request::<GotoTypeDefinition>( |
558 | GotoDefinitionParams { | 560 | GotoDefinitionParams { |
559 | text_document_position_params: TextDocumentPositionParams::new( | 561 | text_document_position_params: TextDocumentPositionParams::new( |
@@ -579,6 +581,7 @@ fn main() { | |||
579 | "targetUri": "file:///[..]src/main.rs" | 581 | "targetUri": "file:///[..]src/main.rs" |
580 | }]), | 582 | }]), |
581 | ); | 583 | ); |
584 | |||
582 | server.request::<GotoTypeDefinition>( | 585 | server.request::<GotoTypeDefinition>( |
583 | GotoDefinitionParams { | 586 | GotoDefinitionParams { |
584 | text_document_position_params: TextDocumentPositionParams::new( | 587 | text_document_position_params: TextDocumentPositionParams::new( |
@@ -611,6 +614,7 @@ fn resolve_proc_macro() { | |||
611 | if skip_slow_tests() { | 614 | if skip_slow_tests() { |
612 | return; | 615 | return; |
613 | } | 616 | } |
617 | |||
614 | let server = Project::with_fixture( | 618 | let server = Project::with_fixture( |
615 | r###" | 619 | r###" |
616 | //- /foo/Cargo.toml | 620 | //- /foo/Cargo.toml |
@@ -679,8 +683,9 @@ pub fn foo(_input: TokenStream) -> TokenStream { | |||
679 | }) | 683 | }) |
680 | .root("foo") | 684 | .root("foo") |
681 | .root("bar") | 685 | .root("bar") |
682 | .server(); | 686 | .server() |
683 | server.wait_until_workspace_is_loaded(); | 687 | .wait_until_workspace_is_loaded(); |
688 | |||
684 | let res = server.send_request::<HoverRequest>(HoverParams { | 689 | let res = server.send_request::<HoverRequest>(HoverParams { |
685 | text_document_position_params: TextDocumentPositionParams::new( | 690 | text_document_position_params: TextDocumentPositionParams::new( |
686 | server.doc_id("foo/src/main.rs"), | 691 | server.doc_id("foo/src/main.rs"), |
@@ -688,7 +693,6 @@ pub fn foo(_input: TokenStream) -> TokenStream { | |||
688 | ), | 693 | ), |
689 | work_done_progress_params: Default::default(), | 694 | work_done_progress_params: Default::default(), |
690 | }); | 695 | }); |
691 | |||
692 | let value = res.get("contents").unwrap().get("value").unwrap().to_string(); | 696 | let value = res.get("contents").unwrap().get("value").unwrap().to_string(); |
693 | assert_eq!(value, r#""\n```rust\nfoo::Bar\n```\n\n```rust\nfn bar()\n```""#) | 697 | assert_eq!(value, r#""\n```rust\nfoo::Bar\n```\n\n```rust\nfn bar()\n```""#) |
694 | } | 698 | } |
diff --git a/crates/rust-analyzer/tests/rust-analyzer/support.rs b/crates/rust-analyzer/tests/rust-analyzer/support.rs index 5bafeba79..784cbda79 100644 --- a/crates/rust-analyzer/tests/rust-analyzer/support.rs +++ b/crates/rust-analyzer/tests/rust-analyzer/support.rs | |||
@@ -202,7 +202,7 @@ impl Server { | |||
202 | } | 202 | } |
203 | panic!("no response"); | 203 | panic!("no response"); |
204 | } | 204 | } |
205 | pub fn wait_until_workspace_is_loaded(&self) { | 205 | pub fn wait_until_workspace_is_loaded(self) -> Server { |
206 | self.wait_for_message_cond(1, &|msg: &Message| match msg { | 206 | self.wait_for_message_cond(1, &|msg: &Message| match msg { |
207 | Message::Notification(n) if n.method == "$/progress" => { | 207 | Message::Notification(n) if n.method == "$/progress" => { |
208 | match n.clone().extract::<ProgressParams>("$/progress").unwrap() { | 208 | match n.clone().extract::<ProgressParams>("$/progress").unwrap() { |
@@ -214,7 +214,8 @@ impl Server { | |||
214 | } | 214 | } |
215 | } | 215 | } |
216 | _ => false, | 216 | _ => false, |
217 | }) | 217 | }); |
218 | self | ||
218 | } | 219 | } |
219 | fn wait_for_message_cond(&self, n: usize, cond: &dyn Fn(&Message) -> bool) { | 220 | fn wait_for_message_cond(&self, n: usize, cond: &dyn Fn(&Message) -> bool) { |
220 | let mut total = 0; | 221 | let mut total = 0; |
diff --git a/crates/text_edit/src/lib.rs b/crates/text_edit/src/lib.rs index ab8cd7fd1..e417e8ea6 100644 --- a/crates/text_edit/src/lib.rs +++ b/crates/text_edit/src/lib.rs | |||
@@ -159,13 +159,13 @@ impl<'a> IntoIterator for &'a TextEdit { | |||
159 | 159 | ||
160 | impl TextEditBuilder { | 160 | impl TextEditBuilder { |
161 | pub fn replace(&mut self, range: TextRange, replace_with: String) { | 161 | pub fn replace(&mut self, range: TextRange, replace_with: String) { |
162 | self.indels.push(Indel::replace(range, replace_with)) | 162 | self.indel(Indel::replace(range, replace_with)) |
163 | } | 163 | } |
164 | pub fn delete(&mut self, range: TextRange) { | 164 | pub fn delete(&mut self, range: TextRange) { |
165 | self.indels.push(Indel::delete(range)) | 165 | self.indel(Indel::delete(range)) |
166 | } | 166 | } |
167 | pub fn insert(&mut self, offset: TextSize, text: String) { | 167 | pub fn insert(&mut self, offset: TextSize, text: String) { |
168 | self.indels.push(Indel::insert(offset, text)) | 168 | self.indel(Indel::insert(offset, text)) |
169 | } | 169 | } |
170 | pub fn finish(self) -> TextEdit { | 170 | pub fn finish(self) -> TextEdit { |
171 | let mut indels = self.indels; | 171 | let mut indels = self.indels; |
@@ -175,6 +175,12 @@ impl TextEditBuilder { | |||
175 | pub fn invalidates_offset(&self, offset: TextSize) -> bool { | 175 | pub fn invalidates_offset(&self, offset: TextSize) -> bool { |
176 | self.indels.iter().any(|indel| indel.delete.contains_inclusive(offset)) | 176 | self.indels.iter().any(|indel| indel.delete.contains_inclusive(offset)) |
177 | } | 177 | } |
178 | fn indel(&mut self, indel: Indel) { | ||
179 | self.indels.push(indel); | ||
180 | if self.indels.len() <= 16 { | ||
181 | check_disjoint(&mut self.indels); | ||
182 | } | ||
183 | } | ||
178 | } | 184 | } |
179 | 185 | ||
180 | fn check_disjoint(indels: &mut [impl std::borrow::Borrow<Indel>]) -> bool { | 186 | fn check_disjoint(indels: &mut [impl std::borrow::Borrow<Indel>]) -> bool { |