aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/assists/src/handlers/flip_comma.rs6
-rw-r--r--crates/base_db/src/fixture.rs16
-rw-r--r--crates/flycheck/Cargo.toml1
-rw-r--r--crates/flycheck/src/lib.rs23
-rw-r--r--crates/ide/src/join_lines.rs12
-rw-r--r--crates/ide/src/syntax_highlighting/injection.rs56
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/injection.html48
-rw-r--r--crates/ide/src/syntax_highlighting/tests.rs19
-rw-r--r--crates/project_model/src/cargo_workspace.rs26
-rw-r--r--crates/project_model/src/workspace.rs22
-rw-r--r--crates/rust-analyzer/src/cli/load_cargo.rs1
-rw-r--r--crates/rust-analyzer/src/main_loop.rs12
-rw-r--r--crates/rust-analyzer/src/reload.rs34
-rw-r--r--crates/stdx/src/lib.rs23
-rw-r--r--crates/test_utils/src/lib.rs1
-rw-r--r--crates/vfs/src/lib.rs51
16 files changed, 268 insertions, 83 deletions
diff --git a/crates/assists/src/handlers/flip_comma.rs b/crates/assists/src/handlers/flip_comma.rs
index a48b0e450..18cf64a34 100644
--- a/crates/assists/src/handlers/flip_comma.rs
+++ b/crates/assists/src/handlers/flip_comma.rs
@@ -49,14 +49,14 @@ mod tests {
49 fn flip_comma_works_for_function_parameters() { 49 fn flip_comma_works_for_function_parameters() {
50 check_assist( 50 check_assist(
51 flip_comma, 51 flip_comma,
52 "fn foo(x: i32,$0 y: Result<(), ()>) {}", 52 r#"fn foo(x: i32,$0 y: Result<(), ()>) {}"#,
53 "fn foo(y: Result<(), ()>, x: i32) {}", 53 r#"fn foo(y: Result<(), ()>, x: i32) {}"#,
54 ) 54 )
55 } 55 }
56 56
57 #[test] 57 #[test]
58 fn flip_comma_target() { 58 fn flip_comma_target() {
59 check_assist_target(flip_comma, "fn foo(x: i32,$0 y: Result<(), ()>) {}", ",") 59 check_assist_target(flip_comma, r#"fn foo(x: i32,$0 y: Result<(), ()>) {}"#, ",")
60 } 60 }
61 61
62 #[test] 62 #[test]
diff --git a/crates/base_db/src/fixture.rs b/crates/base_db/src/fixture.rs
index 66e6443cb..98acd61b1 100644
--- a/crates/base_db/src/fixture.rs
+++ b/crates/base_db/src/fixture.rs
@@ -61,7 +61,9 @@ use std::{str::FromStr, sync::Arc};
61 61
62use cfg::CfgOptions; 62use cfg::CfgOptions;
63use rustc_hash::FxHashMap; 63use rustc_hash::FxHashMap;
64use test_utils::{extract_range_or_offset, Fixture, RangeOrOffset, CURSOR_MARKER}; 64use test_utils::{
65 extract_range_or_offset, Fixture, RangeOrOffset, CURSOR_MARKER, ESCAPED_CURSOR_MARKER,
66};
65use vfs::{file_set::FileSet, VfsPath}; 67use vfs::{file_set::FileSet, VfsPath};
66 68
67use crate::{ 69use crate::{
@@ -142,10 +144,14 @@ impl ChangeFixture {
142 144
143 for entry in fixture { 145 for entry in fixture {
144 let text = if entry.text.contains(CURSOR_MARKER) { 146 let text = if entry.text.contains(CURSOR_MARKER) {
145 let (range_or_offset, text) = extract_range_or_offset(&entry.text); 147 if entry.text.contains(ESCAPED_CURSOR_MARKER) {
146 assert!(file_position.is_none()); 148 entry.text.replace(ESCAPED_CURSOR_MARKER, CURSOR_MARKER)
147 file_position = Some((file_id, range_or_offset)); 149 } else {
148 text.to_string() 150 let (range_or_offset, text) = extract_range_or_offset(&entry.text);
151 assert!(file_position.is_none());
152 file_position = Some((file_id, range_or_offset));
153 text.to_string()
154 }
149 } else { 155 } else {
150 entry.text.clone() 156 entry.text.clone()
151 }; 157 };
diff --git a/crates/flycheck/Cargo.toml b/crates/flycheck/Cargo.toml
index 3d9436d69..1bad64a1b 100644
--- a/crates/flycheck/Cargo.toml
+++ b/crates/flycheck/Cargo.toml
@@ -17,3 +17,4 @@ serde_json = "1.0.48"
17jod-thread = "0.1.1" 17jod-thread = "0.1.1"
18 18
19toolchain = { path = "../toolchain", version = "0.0.0" } 19toolchain = { path = "../toolchain", version = "0.0.0" }
20stdx = { path = "../stdx", version = "0.0.0" }
diff --git a/crates/flycheck/src/lib.rs b/crates/flycheck/src/lib.rs
index d982c5f29..4388e8c67 100644
--- a/crates/flycheck/src/lib.rs
+++ b/crates/flycheck/src/lib.rs
@@ -5,13 +5,13 @@
5use std::{ 5use std::{
6 fmt, 6 fmt,
7 io::{self, BufReader}, 7 io::{self, BufReader},
8 ops,
9 path::PathBuf, 8 path::PathBuf,
10 process::{self, Command, Stdio}, 9 process::{self, Command, Stdio},
11 time::Duration, 10 time::Duration,
12}; 11};
13 12
14use crossbeam_channel::{never, select, unbounded, Receiver, Sender}; 13use crossbeam_channel::{never, select, unbounded, Receiver, Sender};
14use stdx::JodChild;
15 15
16pub use cargo_metadata::diagnostic::{ 16pub use cargo_metadata::diagnostic::{
17 Applicability, Diagnostic, DiagnosticCode, DiagnosticLevel, DiagnosticSpan, 17 Applicability, Diagnostic, DiagnosticCode, DiagnosticLevel, DiagnosticSpan,
@@ -323,24 +323,3 @@ impl CargoActor {
323 Ok(read_at_least_one_message) 323 Ok(read_at_least_one_message)
324 } 324 }
325} 325}
326
327struct JodChild(process::Child);
328
329impl ops::Deref for JodChild {
330 type Target = process::Child;
331 fn deref(&self) -> &process::Child {
332 &self.0
333 }
334}
335
336impl ops::DerefMut for JodChild {
337 fn deref_mut(&mut self) -> &mut process::Child {
338 &mut self.0
339 }
340}
341
342impl Drop for JodChild {
343 fn drop(&mut self) {
344 let _ = self.0.kill();
345 }
346}
diff --git a/crates/ide/src/join_lines.rs b/crates/ide/src/join_lines.rs
index 296893d2f..05380f2a1 100644
--- a/crates/ide/src/join_lines.rs
+++ b/crates/ide/src/join_lines.rs
@@ -198,8 +198,8 @@ mod tests {
198 198
199 use super::*; 199 use super::*;
200 200
201 fn check_join_lines(before: &str, after: &str) { 201 fn check_join_lines(ra_fixture_before: &str, ra_fixture_after: &str) {
202 let (before_cursor_pos, before) = extract_offset(before); 202 let (before_cursor_pos, before) = extract_offset(ra_fixture_before);
203 let file = SourceFile::parse(&before).ok().unwrap(); 203 let file = SourceFile::parse(&before).ok().unwrap();
204 204
205 let range = TextRange::empty(before_cursor_pos); 205 let range = TextRange::empty(before_cursor_pos);
@@ -214,7 +214,7 @@ mod tests {
214 .apply_to_offset(before_cursor_pos) 214 .apply_to_offset(before_cursor_pos)
215 .expect("cursor position is affected by the edit"); 215 .expect("cursor position is affected by the edit");
216 let actual = add_cursor(&actual, actual_cursor_pos); 216 let actual = add_cursor(&actual, actual_cursor_pos);
217 assert_eq_text!(after, &actual); 217 assert_eq_text!(ra_fixture_after, &actual);
218 } 218 }
219 219
220 #[test] 220 #[test]
@@ -604,8 +604,8 @@ fn foo() {
604 ); 604 );
605 } 605 }
606 606
607 fn check_join_lines_sel(before: &str, after: &str) { 607 fn check_join_lines_sel(ra_fixture_before: &str, ra_fixture_after: &str) {
608 let (sel, before) = extract_range(before); 608 let (sel, before) = extract_range(ra_fixture_before);
609 let parse = SourceFile::parse(&before); 609 let parse = SourceFile::parse(&before);
610 let result = join_lines(&parse.tree(), sel); 610 let result = join_lines(&parse.tree(), sel);
611 let actual = { 611 let actual = {
@@ -613,7 +613,7 @@ fn foo() {
613 result.apply(&mut actual); 613 result.apply(&mut actual);
614 actual 614 actual
615 }; 615 };
616 assert_eq_text!(after, &actual); 616 assert_eq_text!(ra_fixture_after, &actual);
617 } 617 }
618 618
619 #[test] 619 #[test]
diff --git a/crates/ide/src/syntax_highlighting/injection.rs b/crates/ide/src/syntax_highlighting/injection.rs
index 6cbd683c6..d6be9708d 100644
--- a/crates/ide/src/syntax_highlighting/injection.rs
+++ b/crates/ide/src/syntax_highlighting/injection.rs
@@ -22,7 +22,8 @@ pub(super) fn highlight_injection(
22 return None; 22 return None;
23 } 23 }
24 let value = literal.value()?; 24 let value = literal.value()?;
25 let (analysis, tmp_file_id) = Analysis::from_single_file(value.into_owned()); 25 let marker_info = MarkerInfo::new(&*value);
26 let (analysis, tmp_file_id) = Analysis::from_single_file(marker_info.cleaned_text.clone());
26 27
27 if let Some(range) = literal.open_quote_text_range() { 28 if let Some(range) = literal.open_quote_text_range() {
28 acc.add(HighlightedRange { 29 acc.add(HighlightedRange {
@@ -33,9 +34,10 @@ pub(super) fn highlight_injection(
33 } 34 }
34 35
35 for mut h in analysis.highlight(tmp_file_id).unwrap() { 36 for mut h in analysis.highlight(tmp_file_id).unwrap() {
36 if let Some(r) = literal.map_range_up(h.range) { 37 let range = marker_info.map_range_up(h.range);
37 h.range = r; 38 if let Some(range) = literal.map_range_up(range) {
38 acc.add(h) 39 h.range = range;
40 acc.add(h);
39 } 41 }
40 } 42 }
41 43
@@ -50,6 +52,52 @@ pub(super) fn highlight_injection(
50 Some(()) 52 Some(())
51} 53}
52 54
55/// Data to remove `$0` from string and map ranges
56#[derive(Default, Debug)]
57struct MarkerInfo {
58 cleaned_text: String,
59 markers: Vec<TextRange>,
60}
61
62impl MarkerInfo {
63 fn new(mut text: &str) -> Self {
64 let marker = "$0";
65
66 let mut res = MarkerInfo::default();
67 let mut offset: TextSize = 0.into();
68 while !text.is_empty() {
69 let idx = text.find(marker).unwrap_or(text.len());
70 let (chunk, next) = text.split_at(idx);
71 text = next;
72 res.cleaned_text.push_str(chunk);
73 offset += TextSize::of(chunk);
74
75 if let Some(next) = text.strip_prefix(marker) {
76 text = next;
77
78 let marker_len = TextSize::of(marker);
79 res.markers.push(TextRange::at(offset, marker_len));
80 offset += marker_len;
81 }
82 }
83 res
84 }
85 fn map_range_up(&self, range: TextRange) -> TextRange {
86 TextRange::new(
87 self.map_offset_up(range.start(), true),
88 self.map_offset_up(range.end(), false),
89 )
90 }
91 fn map_offset_up(&self, mut offset: TextSize, start: bool) -> TextSize {
92 for r in &self.markers {
93 if r.start() < offset || (start && r.start() == offset) {
94 offset += r.len()
95 }
96 }
97 offset
98 }
99}
100
53/// Mapping from extracted documentation code to original code 101/// Mapping from extracted documentation code to original code
54type RangesMap = BTreeMap<TextSize, TextSize>; 102type RangesMap = BTreeMap<TextSize, TextSize>;
55 103
diff --git a/crates/ide/src/syntax_highlighting/test_data/injection.html b/crates/ide/src/syntax_highlighting/test_data/injection.html
new file mode 100644
index 000000000..a54d303b4
--- /dev/null
+++ b/crates/ide/src/syntax_highlighting/test_data/injection.html
@@ -0,0 +1,48 @@
1
2<style>
3body { margin: 0; }
4pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; }
5
6.lifetime { color: #DFAF8F; font-style: italic; }
7.label { color: #DFAF8F; font-style: italic; }
8.comment { color: #7F9F7F; }
9.documentation { color: #629755; }
10.injected { opacity: 0.65 ; }
11.struct, .enum { color: #7CB8BB; }
12.enum_variant { color: #BDE0F3; }
13.string_literal { color: #CC9393; }
14.field { color: #94BFF3; }
15.function { color: #93E0E3; }
16.function.unsafe { color: #BC8383; }
17.operator.unsafe { color: #BC8383; }
18.parameter { color: #94BFF3; }
19.text { color: #DCDCCC; }
20.type { color: #7CB8BB; }
21.builtin_type { color: #8CD0D3; }
22.type_param { color: #DFAF8F; }
23.attribute { color: #94BFF3; }
24.numeric_literal { color: #BFEBBF; }
25.bool_literal { color: #BFE6EB; }
26.macro { color: #94BFF3; }
27.module { color: #AFD8AF; }
28.value_param { color: #DCDCCC; }
29.variable { color: #DCDCCC; }
30.format_specifier { color: #CC696B; }
31.mutable { text-decoration: underline; }
32.escape_sequence { color: #94BFF3; }
33.keyword { color: #F0DFAF; font-weight: bold; }
34.keyword.unsafe { color: #BC8383; font-weight: bold; }
35.control { font-style: italic; }
36
37.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
38</style>
39<pre><code><span class="keyword">fn</span> <span class="function declaration">f</span><span class="punctuation">(</span><span class="value_param declaration">ra_fixture</span><span class="punctuation">:</span> <span class="operator">&</span><span class="builtin_type">str</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span>
40<span class="keyword">fn</span> <span class="function declaration">main</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span>
41 <span class="function">f</span><span class="punctuation">(</span><span class="string_literal">r"</span>
42<span class="keyword">fn</span> <span class="function declaration">foo</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span>
43 <span class="function">foo</span><span class="punctuation">(</span>$0<span class="punctuation">{</span>
44 <span class="numeric_literal">92</span>
45 <span class="punctuation">}</span>$0<span class="punctuation">)</span>
46<span class="punctuation">}</span><span class="string_literal">"</span><span class="punctuation">)</span><span class="punctuation">;</span>
47<span class="punctuation">}</span>
48 </code></pre> \ No newline at end of file
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs
index 30b5b648e..9e1a3974c 100644
--- a/crates/ide/src/syntax_highlighting/tests.rs
+++ b/crates/ide/src/syntax_highlighting/tests.rs
@@ -555,6 +555,25 @@ impl t for foo {
555 ) 555 )
556} 556}
557 557
558#[test]
559fn test_injection() {
560 check_highlighting(
561 r##"
562fn f(ra_fixture: &str) {}
563fn main() {
564 f(r"
565fn foo() {
566 foo(\$0{
567 92
568 }\$0)
569}");
570}
571 "##,
572 expect_file!["./test_data/injection.html"],
573 false,
574 );
575}
576
558/// Highlights the code given by the `ra_fixture` argument, renders the 577/// Highlights the code given by the `ra_fixture` argument, renders the
559/// result as HTML, and compares it with the HTML file given as `snapshot`. 578/// result as HTML, and compares it with the HTML file given as `snapshot`.
560/// Note that the `snapshot` file is overwritten by the rendered HTML. 579/// Note that the `snapshot` file is overwritten by the rendered HTML.
diff --git a/crates/project_model/src/cargo_workspace.rs b/crates/project_model/src/cargo_workspace.rs
index 0e6679542..2ee4e88b2 100644
--- a/crates/project_model/src/cargo_workspace.rs
+++ b/crates/project_model/src/cargo_workspace.rs
@@ -3,9 +3,10 @@
3use std::{ 3use std::{
4 convert::TryInto, 4 convert::TryInto,
5 ffi::OsStr, 5 ffi::OsStr,
6 io::BufReader,
6 ops, 7 ops,
7 path::{Path, PathBuf}, 8 path::{Path, PathBuf},
8 process::Command, 9 process::{Command, Stdio},
9}; 10};
10 11
11use anyhow::{Context, Result}; 12use anyhow::{Context, Result};
@@ -15,6 +16,7 @@ use cargo_metadata::{BuildScript, CargoOpt, Message, MetadataCommand, PackageId}
15use itertools::Itertools; 16use itertools::Itertools;
16use paths::{AbsPath, AbsPathBuf}; 17use paths::{AbsPath, AbsPathBuf};
17use rustc_hash::FxHashMap; 18use rustc_hash::FxHashMap;
19use stdx::JodChild;
18 20
19use crate::cfg_flag::CfgFlag; 21use crate::cfg_flag::CfgFlag;
20use crate::utf8_stdout; 22use crate::utf8_stdout;
@@ -171,6 +173,7 @@ impl CargoWorkspace {
171 pub fn from_cargo_metadata( 173 pub fn from_cargo_metadata(
172 cargo_toml: &AbsPath, 174 cargo_toml: &AbsPath,
173 config: &CargoConfig, 175 config: &CargoConfig,
176 progress: &dyn Fn(String),
174 ) -> Result<CargoWorkspace> { 177 ) -> Result<CargoWorkspace> {
175 let mut meta = MetadataCommand::new(); 178 let mut meta = MetadataCommand::new();
176 meta.cargo_path(toolchain::cargo()); 179 meta.cargo_path(toolchain::cargo());
@@ -220,6 +223,9 @@ impl CargoWorkspace {
220 meta.other_options(vec![String::from("--filter-platform"), target]); 223 meta.other_options(vec![String::from("--filter-platform"), target]);
221 } 224 }
222 225
226 // FIXME: Currently MetadataCommand is not based on parse_stream,
227 // So we just report it as a whole
228 progress("metadata".to_string());
223 let mut meta = meta.exec().with_context(|| { 229 let mut meta = meta.exec().with_context(|| {
224 let cwd: Option<AbsPathBuf> = 230 let cwd: Option<AbsPathBuf> =
225 std::env::current_dir().ok().and_then(|p| p.try_into().ok()); 231 std::env::current_dir().ok().and_then(|p| p.try_into().ok());
@@ -243,7 +249,7 @@ impl CargoWorkspace {
243 let mut envs = FxHashMap::default(); 249 let mut envs = FxHashMap::default();
244 let mut proc_macro_dylib_paths = FxHashMap::default(); 250 let mut proc_macro_dylib_paths = FxHashMap::default();
245 if config.load_out_dirs_from_check { 251 if config.load_out_dirs_from_check {
246 let resources = load_extern_resources(cargo_toml, config)?; 252 let resources = load_extern_resources(cargo_toml, config, progress)?;
247 out_dir_by_id = resources.out_dirs; 253 out_dir_by_id = resources.out_dirs;
248 cfgs = resources.cfgs; 254 cfgs = resources.cfgs;
249 envs = resources.env; 255 envs = resources.env;
@@ -368,6 +374,7 @@ pub(crate) struct ExternResources {
368pub(crate) fn load_extern_resources( 374pub(crate) fn load_extern_resources(
369 cargo_toml: &Path, 375 cargo_toml: &Path,
370 cargo_features: &CargoConfig, 376 cargo_features: &CargoConfig,
377 progress: &dyn Fn(String),
371) -> Result<ExternResources> { 378) -> Result<ExternResources> {
372 let mut cmd = Command::new(toolchain::cargo()); 379 let mut cmd = Command::new(toolchain::cargo());
373 cmd.args(&["check", "--message-format=json", "--manifest-path"]).arg(cargo_toml); 380 cmd.args(&["check", "--message-format=json", "--manifest-path"]).arg(cargo_toml);
@@ -395,11 +402,14 @@ pub(crate) fn load_extern_resources(
395 } 402 }
396 } 403 }
397 404
398 let output = cmd.output()?; 405 cmd.stdout(Stdio::piped()).stderr(Stdio::null()).stdin(Stdio::null());
399 406
400 let mut res = ExternResources::default(); 407 let mut child = cmd.spawn().map(JodChild)?;
408 let child_stdout = child.stdout.take().unwrap();
409 let stdout = BufReader::new(child_stdout);
401 410
402 for message in cargo_metadata::Message::parse_stream(output.stdout.as_slice()) { 411 let mut res = ExternResources::default();
412 for message in cargo_metadata::Message::parse_stream(stdout) {
403 if let Ok(message) = message { 413 if let Ok(message) = message {
404 match message { 414 match message {
405 Message::BuildScriptExecuted(BuildScript { 415 Message::BuildScriptExecuted(BuildScript {
@@ -432,6 +442,8 @@ pub(crate) fn load_extern_resources(
432 res.env.insert(package_id, env); 442 res.env.insert(package_id, env);
433 } 443 }
434 Message::CompilerArtifact(message) => { 444 Message::CompilerArtifact(message) => {
445 progress(format!("metadata {}", message.target.name));
446
435 if message.target.kind.contains(&"proc-macro".to_string()) { 447 if message.target.kind.contains(&"proc-macro".to_string()) {
436 let package_id = message.package_id; 448 let package_id = message.package_id;
437 // Skip rmeta file 449 // Skip rmeta file
@@ -442,7 +454,9 @@ pub(crate) fn load_extern_resources(
442 } 454 }
443 } 455 }
444 } 456 }
445 Message::CompilerMessage(_) => (), 457 Message::CompilerMessage(message) => {
458 progress(message.target.name.clone());
459 }
446 Message::Unknown => (), 460 Message::Unknown => (),
447 Message::BuildFinished(_) => {} 461 Message::BuildFinished(_) => {}
448 Message::TextLine(_) => {} 462 Message::TextLine(_) => {}
diff --git a/crates/project_model/src/workspace.rs b/crates/project_model/src/workspace.rs
index 68a235ce3..06a0be284 100644
--- a/crates/project_model/src/workspace.rs
+++ b/crates/project_model/src/workspace.rs
@@ -64,7 +64,11 @@ impl fmt::Debug for ProjectWorkspace {
64} 64}
65 65
66impl ProjectWorkspace { 66impl ProjectWorkspace {
67 pub fn load(manifest: ProjectManifest, config: &CargoConfig) -> Result<ProjectWorkspace> { 67 pub fn load(
68 manifest: ProjectManifest,
69 config: &CargoConfig,
70 progress: &dyn Fn(String),
71 ) -> Result<ProjectWorkspace> {
68 let res = match manifest { 72 let res = match manifest {
69 ProjectManifest::ProjectJson(project_json) => { 73 ProjectManifest::ProjectJson(project_json) => {
70 let file = fs::read_to_string(&project_json).with_context(|| { 74 let file = fs::read_to_string(&project_json).with_context(|| {
@@ -84,15 +88,14 @@ impl ProjectWorkspace {
84 cmd 88 cmd
85 })?; 89 })?;
86 90
87 let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml, config).with_context( 91 let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml, config, progress)
88 || { 92 .with_context(|| {
89 format!( 93 format!(
90 "Failed to read Cargo metadata from Cargo.toml file {}, {}", 94 "Failed to read Cargo metadata from Cargo.toml file {}, {}",
91 cargo_toml.display(), 95 cargo_toml.display(),
92 cargo_version 96 cargo_version
93 ) 97 )
94 }, 98 })?;
95 )?;
96 let sysroot = if config.no_sysroot { 99 let sysroot = if config.no_sysroot {
97 Sysroot::default() 100 Sysroot::default()
98 } else { 101 } else {
@@ -105,9 +108,12 @@ impl ProjectWorkspace {
105 }; 108 };
106 109
107 let rustc = if let Some(rustc_dir) = &config.rustc_source { 110 let rustc = if let Some(rustc_dir) = &config.rustc_source {
108 Some(CargoWorkspace::from_cargo_metadata(&rustc_dir, config).with_context( 111 Some(
109 || format!("Failed to read Cargo metadata for Rust sources"), 112 CargoWorkspace::from_cargo_metadata(&rustc_dir, config, progress)
110 )?) 113 .with_context(|| {
114 format!("Failed to read Cargo metadata for Rust sources")
115 })?,
116 )
111 } else { 117 } else {
112 None 118 None
113 }; 119 };
diff --git a/crates/rust-analyzer/src/cli/load_cargo.rs b/crates/rust-analyzer/src/cli/load_cargo.rs
index e5ab6c73b..31a16ca46 100644
--- a/crates/rust-analyzer/src/cli/load_cargo.rs
+++ b/crates/rust-analyzer/src/cli/load_cargo.rs
@@ -21,6 +21,7 @@ pub fn load_cargo(
21 let ws = ProjectWorkspace::load( 21 let ws = ProjectWorkspace::load(
22 root, 22 root,
23 &CargoConfig { load_out_dirs_from_check, ..Default::default() }, 23 &CargoConfig { load_out_dirs_from_check, ..Default::default() },
24 &|_| {},
24 )?; 25 )?;
25 26
26 let (sender, receiver) = unbounded(); 27 let (sender, receiver) = unbounded();
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 7ac6acf70..22ee96775 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -22,6 +22,7 @@ use crate::{
22 global_state::{file_id_to_url, url_to_file_id, GlobalState, Status}, 22 global_state::{file_id_to_url, url_to_file_id, GlobalState, Status},
23 handlers, lsp_ext, 23 handlers, lsp_ext,
24 lsp_utils::{apply_document_changes, is_canceled, notification_is, Progress}, 24 lsp_utils::{apply_document_changes, is_canceled, notification_is, Progress},
25 reload::ProjectWorkspaceProgress,
25 Result, 26 Result,
26}; 27};
27 28
@@ -63,6 +64,7 @@ pub(crate) enum Task {
63 Diagnostics(Vec<(FileId, Vec<lsp_types::Diagnostic>)>), 64 Diagnostics(Vec<(FileId, Vec<lsp_types::Diagnostic>)>),
64 Workspaces(Vec<anyhow::Result<ProjectWorkspace>>), 65 Workspaces(Vec<anyhow::Result<ProjectWorkspace>>),
65 PrimeCaches(PrimeCachesProgress), 66 PrimeCaches(PrimeCachesProgress),
67 FetchWorkspace(ProjectWorkspaceProgress),
66} 68}
67 69
68impl fmt::Debug for Event { 70impl fmt::Debug for Event {
@@ -216,6 +218,16 @@ impl GlobalState {
216 } 218 }
217 PrimeCachesProgress::Finished => prime_caches_progress.push(progress), 219 PrimeCachesProgress::Finished => prime_caches_progress.push(progress),
218 }, 220 },
221 Task::FetchWorkspace(progress) => {
222 let (state, msg) = match progress {
223 ProjectWorkspaceProgress::Begin => (Progress::Begin, None),
224 ProjectWorkspaceProgress::Report(msg) => {
225 (Progress::Report, Some(msg))
226 }
227 ProjectWorkspaceProgress::End => (Progress::End, None),
228 };
229 self.report_progress("fetching", state, msg, None);
230 }
219 } 231 }
220 // Coalesce multiple task events into one loop turn 232 // Coalesce multiple task events into one loop turn
221 task = match self.task_pool.receiver.try_recv() { 233 task = match self.task_pool.receiver.try_recv() {
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs
index 76b50931a..f4e084741 100644
--- a/crates/rust-analyzer/src/reload.rs
+++ b/crates/rust-analyzer/src/reload.rs
@@ -15,6 +15,13 @@ use crate::{
15}; 15};
16use lsp_ext::StatusParams; 16use lsp_ext::StatusParams;
17 17
18#[derive(Debug)]
19pub(crate) enum ProjectWorkspaceProgress {
20 Begin,
21 Report(String),
22 End,
23}
24
18impl GlobalState { 25impl GlobalState {
19 pub(crate) fn update_configuration(&mut self, config: Config) { 26 pub(crate) fn update_configuration(&mut self, config: Config) {
20 let _p = profile::span("GlobalState::update_configuration"); 27 let _p = profile::span("GlobalState::update_configuration");
@@ -93,23 +100,42 @@ impl GlobalState {
93 } 100 }
94 pub(crate) fn fetch_workspaces(&mut self) { 101 pub(crate) fn fetch_workspaces(&mut self) {
95 log::info!("will fetch workspaces"); 102 log::info!("will fetch workspaces");
96 self.task_pool.handle.spawn({ 103
104 self.task_pool.handle.spawn_with_sender({
97 let linked_projects = self.config.linked_projects(); 105 let linked_projects = self.config.linked_projects();
98 let cargo_config = self.config.cargo(); 106 let cargo_config = self.config.cargo();
99 move || { 107
108 move |sender| {
109 let progress = {
110 let sender = sender.clone();
111 move |msg| {
112 sender
113 .send(Task::FetchWorkspace(ProjectWorkspaceProgress::Report(msg)))
114 .unwrap()
115 }
116 };
117
118 sender.send(Task::FetchWorkspace(ProjectWorkspaceProgress::Begin)).unwrap();
119
100 let workspaces = linked_projects 120 let workspaces = linked_projects
101 .iter() 121 .iter()
102 .map(|project| match project { 122 .map(|project| match project {
103 LinkedProject::ProjectManifest(manifest) => { 123 LinkedProject::ProjectManifest(manifest) => {
104 project_model::ProjectWorkspace::load(manifest.clone(), &cargo_config) 124 project_model::ProjectWorkspace::load(
125 manifest.clone(),
126 &cargo_config,
127 &progress,
128 )
105 } 129 }
106 LinkedProject::InlineJsonProject(it) => { 130 LinkedProject::InlineJsonProject(it) => {
107 project_model::ProjectWorkspace::load_inline(it.clone()) 131 project_model::ProjectWorkspace::load_inline(it.clone())
108 } 132 }
109 }) 133 })
110 .collect::<Vec<_>>(); 134 .collect::<Vec<_>>();
135
136 sender.send(Task::FetchWorkspace(ProjectWorkspaceProgress::End)).unwrap();
111 log::info!("did fetch workspaces {:?}", workspaces); 137 log::info!("did fetch workspaces {:?}", workspaces);
112 Task::Workspaces(workspaces) 138 sender.send(Task::Workspaces(workspaces)).unwrap()
113 } 139 }
114 }); 140 });
115 } 141 }
diff --git a/crates/stdx/src/lib.rs b/crates/stdx/src/lib.rs
index 374ed5910..5332edb09 100644
--- a/crates/stdx/src/lib.rs
+++ b/crates/stdx/src/lib.rs
@@ -1,5 +1,5 @@
1//! Missing batteries for standard libraries. 1//! Missing batteries for standard libraries.
2use std::time::Instant; 2use std::{ops, process, time::Instant};
3 3
4mod macros; 4mod macros;
5pub mod panic_context; 5pub mod panic_context;
@@ -147,6 +147,27 @@ where
147 left 147 left
148} 148}
149 149
150pub struct JodChild(pub process::Child);
151
152impl ops::Deref for JodChild {
153 type Target = process::Child;
154 fn deref(&self) -> &process::Child {
155 &self.0
156 }
157}
158
159impl ops::DerefMut for JodChild {
160 fn deref_mut(&mut self) -> &mut process::Child {
161 &mut self.0
162 }
163}
164
165impl Drop for JodChild {
166 fn drop(&mut self) {
167 let _ = self.0.kill();
168 }
169}
170
150#[cfg(test)] 171#[cfg(test)]
151mod tests { 172mod tests {
152 use super::*; 173 use super::*;
diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs
index 05d6e8c9e..84c1d7ebb 100644
--- a/crates/test_utils/src/lib.rs
+++ b/crates/test_utils/src/lib.rs
@@ -26,6 +26,7 @@ pub use rustc_hash::FxHashMap;
26pub use crate::fixture::Fixture; 26pub use crate::fixture::Fixture;
27 27
28pub const CURSOR_MARKER: &str = "$0"; 28pub const CURSOR_MARKER: &str = "$0";
29pub const ESCAPED_CURSOR_MARKER: &str = "\\$0";
29 30
30/// Asserts that two strings are equal, otherwise displays a rich diff between them. 31/// Asserts that two strings are equal, otherwise displays a rich diff between them.
31/// 32///
diff --git a/crates/vfs/src/lib.rs b/crates/vfs/src/lib.rs
index 9cf2afd33..2b7b14524 100644
--- a/crates/vfs/src/lib.rs
+++ b/crates/vfs/src/lib.rs
@@ -2,43 +2,46 @@
2//! 2//!
3//! VFS stores all files read by rust-analyzer. Reading file contents from VFS 3//! VFS stores all files read by rust-analyzer. Reading file contents from VFS
4//! always returns the same contents, unless VFS was explicitly modified with 4//! always returns the same contents, unless VFS was explicitly modified with
5//! `set_file_contents`. All changes to VFS are logged, and can be retrieved via 5//! [`set_file_contents`]. All changes to VFS are logged, and can be retrieved via
6//! `take_changes` method. The pack of changes is then pushed to `salsa` and 6//! [`take_changes`] method. The pack of changes is then pushed to `salsa` and
7//! triggers incremental recomputation. 7//! triggers incremental recomputation.
8//! 8//!
9//! Files in VFS are identified with `FileId`s -- interned paths. The notion of 9//! Files in VFS are identified with [`FileId`]s -- interned paths. The notion of
10//! the path, `VfsPath` is somewhat abstract: at the moment, it is represented 10//! the path, [`VfsPath`] is somewhat abstract: at the moment, it is represented
11//! as an `std::path::PathBuf` internally, but this is an implementation detail. 11//! as an [`std::path::PathBuf`] internally, but this is an implementation detail.
12//! 12//!
13//! VFS doesn't do IO or file watching itself. For that, see the `loader` 13//! VFS doesn't do IO or file watching itself. For that, see the [`loader`]
14//! module. `loader::Handle` is an object-safe trait which abstracts both file 14//! module. [`loader::Handle`] is an object-safe trait which abstracts both file
15//! loading and file watching. `Handle` is dynamically configured with a set of 15//! loading and file watching. [`Handle`] is dynamically configured with a set of
16//! directory entries which should be scanned and watched. `Handle` then 16//! directory entries which should be scanned and watched. [`Handle`] then
17//! asynchronously pushes file changes. Directory entries are configured in 17//! asynchronously pushes file changes. Directory entries are configured in
18//! free-form via list of globs, it's up to the `Handle` to interpret the globs 18//! free-form via list of globs, it's up to the [`Handle`] to interpret the globs
19//! in any specific way. 19//! in any specific way.
20//! 20//!
21//! A simple `WalkdirLoaderHandle` is provided, which doesn't implement watching 21//! VFS stores a flat list of files. [`file_set::FileSet`] can partition this list
22//! and just scans the directory using walkdir. 22//! of files into disjoint sets of files. Traversal-like operations (including
23//! 23//! getting the neighbor file by the relative path) are handled by the [`FileSet`].
24//! VFS stores a flat list of files. `FileSet` can partition this list of files 24//! [`FileSet`]s are also pushed to salsa and cause it to re-check `mod foo;`
25//! into disjoint sets of files. Traversal-like operations (including getting
26//! the neighbor file by the relative path) are handled by the `FileSet`.
27//! `FileSet`s are also pushed to salsa and cause it to re-check `mod foo;`
28//! declarations when files are created or deleted. 25//! declarations when files are created or deleted.
29//! 26//!
30//! `file_set::FileSet` and `loader::Entry` play similar, but different roles. 27//! [`FileSet`] and [`loader::Entry`] play similar, but different roles.
31//! Both specify the "set of paths/files", one is geared towards file watching, 28//! Both specify the "set of paths/files", one is geared towards file watching,
32//! the other towards salsa changes. In particular, single `file_set::FileSet` 29//! the other towards salsa changes. In particular, single [`FileSet`]
33//! may correspond to several `loader::Entry`. For example, a crate from 30//! may correspond to several [`loader::Entry`]. For example, a crate from
34//! crates.io which uses code generation would have two `Entries` -- for sources 31//! crates.io which uses code generation would have two [`Entries`] -- for sources
35//! in `~/.cargo`, and for generated code in `./target/debug/build`. It will 32//! in `~/.cargo`, and for generated code in `./target/debug/build`. It will
36//! have a single `FileSet` which unions the two sources. 33//! have a single [`FileSet`] which unions the two sources.
37mod vfs_path; 34//!
38mod path_interner; 35//! [`set_file_contents`]: Vfs::set_file_contents
36//! [`take_changes`]: Vfs::take_changes
37//! [`FileSet`]: file_set::FileSet
38//! [`Handle`]: loader::Handle
39//! [`Entries`]: loader::Entry
39mod anchored_path; 40mod anchored_path;
40pub mod file_set; 41pub mod file_set;
41pub mod loader; 42pub mod loader;
43mod path_interner;
44mod vfs_path;
42 45
43use std::{fmt, mem}; 46use std::{fmt, mem};
44 47