aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2020-06-25 23:28:48 +0100
committerGitHub <[email protected]>2020-06-25 23:28:48 +0100
commit6eb0349a7b2dd71eae6f1541cdd26e1ac50363d0 (patch)
treefe3b2a9c5e1facf36807730129be1a66a827b79a
parent3615347fcebebaa58a5cbf675ae062aae149d9c3 (diff)
parente70f7dc10c622e0ffec4b264235ad203b4047171 (diff)
Merge #5068
5068: Prep dynamic reload r=matklad a=matklad bors r+ 🤖 Co-authored-by: Aleksey Kladov <[email protected]>
-rw-r--r--crates/rust-analyzer/src/cli/load_cargo.rs2
-rw-r--r--crates/rust-analyzer/src/config.rs10
-rw-r--r--crates/rust-analyzer/src/dispatch.rs2
-rw-r--r--crates/rust-analyzer/src/global_state.rs227
-rw-r--r--crates/rust-analyzer/src/lib.rs1
-rw-r--r--crates/rust-analyzer/src/main_loop.rs115
-rw-r--r--crates/rust-analyzer/src/reload.rs241
7 files changed, 309 insertions, 289 deletions
diff --git a/crates/rust-analyzer/src/cli/load_cargo.rs b/crates/rust-analyzer/src/cli/load_cargo.rs
index c5cf5ff27..b1250f2fe 100644
--- a/crates/rust-analyzer/src/cli/load_cargo.rs
+++ b/crates/rust-analyzer/src/cli/load_cargo.rs
@@ -9,7 +9,7 @@ use ra_ide::{AnalysisChange, AnalysisHost};
9use ra_project_model::{CargoConfig, ProcMacroClient, ProjectManifest, ProjectWorkspace}; 9use ra_project_model::{CargoConfig, ProcMacroClient, ProjectManifest, ProjectWorkspace};
10use vfs::{loader::Handle, AbsPath}; 10use vfs::{loader::Handle, AbsPath};
11 11
12use crate::global_state::{ProjectFolders, SourceRootConfig}; 12use crate::reload::{ProjectFolders, SourceRootConfig};
13 13
14pub fn load_cargo( 14pub fn load_cargo(
15 root: &Path, 15 root: &Path,
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 435bbbb6b..6b17ce18b 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -30,7 +30,7 @@ pub struct Config {
30 30
31 pub cargo: CargoConfig, 31 pub cargo: CargoConfig,
32 pub rustfmt: RustfmtConfig, 32 pub rustfmt: RustfmtConfig,
33 pub check: Option<FlycheckConfig>, 33 pub flycheck: Option<FlycheckConfig>,
34 34
35 pub inlay_hints: InlayHintsConfig, 35 pub inlay_hints: InlayHintsConfig,
36 pub completion: CompletionConfig, 36 pub completion: CompletionConfig,
@@ -147,7 +147,7 @@ impl Config {
147 147
148 cargo: CargoConfig::default(), 148 cargo: CargoConfig::default(),
149 rustfmt: RustfmtConfig::Rustfmt { extra_args: Vec::new() }, 149 rustfmt: RustfmtConfig::Rustfmt { extra_args: Vec::new() },
150 check: Some(FlycheckConfig::CargoCommand { 150 flycheck: Some(FlycheckConfig::CargoCommand {
151 command: "check".to_string(), 151 command: "check".to_string(),
152 all_targets: true, 152 all_targets: true,
153 all_features: false, 153 all_features: false,
@@ -227,14 +227,14 @@ impl Config {
227 227
228 if let Some(false) = get(value, "/checkOnSave/enable") { 228 if let Some(false) = get(value, "/checkOnSave/enable") {
229 // check is disabled 229 // check is disabled
230 self.check = None; 230 self.flycheck = None;
231 } else { 231 } else {
232 // check is enabled 232 // check is enabled
233 match get::<Vec<String>>(value, "/checkOnSave/overrideCommand") { 233 match get::<Vec<String>>(value, "/checkOnSave/overrideCommand") {
234 // first see if the user has completely overridden the command 234 // first see if the user has completely overridden the command
235 Some(mut args) if !args.is_empty() => { 235 Some(mut args) if !args.is_empty() => {
236 let command = args.remove(0); 236 let command = args.remove(0);
237 self.check = Some(FlycheckConfig::CustomCommand { 237 self.flycheck = Some(FlycheckConfig::CustomCommand {
238 command, 238 command,
239 args, 239 args,
240 }); 240 });
@@ -242,7 +242,7 @@ impl Config {
242 // otherwise configure command customizations 242 // otherwise configure command customizations
243 _ => { 243 _ => {
244 if let Some(FlycheckConfig::CargoCommand { command, extra_args, all_targets, all_features, features }) 244 if let Some(FlycheckConfig::CargoCommand { command, extra_args, all_targets, all_features, features })
245 = &mut self.check 245 = &mut self.flycheck
246 { 246 {
247 set(value, "/checkOnSave/extraArgs", extra_args); 247 set(value, "/checkOnSave/extraArgs", extra_args);
248 set(value, "/checkOnSave/command", command); 248 set(value, "/checkOnSave/command", command);
diff --git a/crates/rust-analyzer/src/dispatch.rs b/crates/rust-analyzer/src/dispatch.rs
index 03b373dee..891fdb96d 100644
--- a/crates/rust-analyzer/src/dispatch.rs
+++ b/crates/rust-analyzer/src/dispatch.rs
@@ -59,7 +59,7 @@ impl<'a> RequestDispatcher<'a> {
59 } 59 }
60 }; 60 };
61 61
62 self.global_state.task_pool.0.spawn({ 62 self.global_state.task_pool.handle.spawn({
63 let world = self.global_state.snapshot(); 63 let world = self.global_state.snapshot();
64 move || { 64 move || {
65 let result = f(world, params); 65 let result = f(world, params);
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs
index 149b1b5f9..17de2a075 100644
--- a/crates/rust-analyzer/src/global_state.rs
+++ b/crates/rust-analyzer/src/global_state.rs
@@ -3,24 +3,25 @@
3//! 3//!
4//! Each tick provides an immutable snapshot of the state as `WorldSnapshot`. 4//! Each tick provides an immutable snapshot of the state as `WorldSnapshot`.
5 5
6use std::{convert::TryFrom, sync::Arc}; 6use std::sync::Arc;
7 7
8use crossbeam_channel::{unbounded, Receiver, Sender}; 8use crossbeam_channel::{unbounded, Receiver, Sender};
9use flycheck::{FlycheckConfig, FlycheckHandle}; 9use flycheck::FlycheckHandle;
10use lsp_types::Url; 10use lsp_types::Url;
11use parking_lot::RwLock; 11use parking_lot::RwLock;
12use ra_db::{CrateId, SourceRoot, VfsPath}; 12use ra_db::{CrateId, VfsPath};
13use ra_ide::{Analysis, AnalysisChange, AnalysisHost, CrateGraph, FileId}; 13use ra_ide::{Analysis, AnalysisChange, AnalysisHost, FileId};
14use ra_project_model::{CargoWorkspace, ProcMacroClient, ProjectWorkspace, Target}; 14use ra_project_model::{CargoWorkspace, ProcMacroClient, ProjectWorkspace, Target};
15use stdx::format_to; 15use stdx::format_to;
16use vfs::{file_set::FileSetConfig, loader::Handle, AbsPath, AbsPathBuf}; 16use vfs::loader::Handle as _;
17 17
18use crate::{ 18use crate::{
19 config::{Config, FilesWatcher}, 19 config::Config,
20 diagnostics::{CheckFixes, DiagnosticCollection}, 20 diagnostics::{CheckFixes, DiagnosticCollection},
21 from_proto, 21 from_proto,
22 line_endings::LineEndings, 22 line_endings::LineEndings,
23 main_loop::{ReqQueue, Task}, 23 main_loop::{ReqQueue, Task},
24 reload::SourceRootConfig,
24 request_metrics::{LatestRequests, RequestMetrics}, 25 request_metrics::{LatestRequests, RequestMetrics},
25 show_message, 26 show_message,
26 thread_pool::TaskPool, 27 thread_pool::TaskPool,
@@ -29,26 +30,6 @@ use crate::{
29}; 30};
30use rustc_hash::{FxHashMap, FxHashSet}; 31use rustc_hash::{FxHashMap, FxHashSet};
31 32
32fn create_flycheck(
33 workspaces: &[ProjectWorkspace],
34 config: &FlycheckConfig,
35) -> Option<(FlycheckHandle, Receiver<flycheck::Message>)> {
36 // FIXME: Figure out the multi-workspace situation
37 workspaces.iter().find_map(move |w| match w {
38 ProjectWorkspace::Cargo { cargo, .. } => {
39 let (sender, receiver) = unbounded();
40 let sender = Box::new(move |msg| sender.send(msg).unwrap());
41 let cargo_project_root = cargo.workspace_root().to_path_buf();
42 let flycheck = FlycheckHandle::spawn(sender, config.clone(), cargo_project_root.into());
43 Some((flycheck, receiver))
44 }
45 ProjectWorkspace::Json { .. } => {
46 log::warn!("Cargo check watching only supported for cargo workspaces, disabling");
47 None
48 }
49 })
50}
51
52#[derive(Eq, PartialEq)] 33#[derive(Eq, PartialEq)]
53pub(crate) enum Status { 34pub(crate) enum Status {
54 Loading, 35 Loading,
@@ -61,28 +42,35 @@ impl Default for Status {
61 } 42 }
62} 43}
63 44
45// Enforces drop order
46pub(crate) struct Handle<H, C> {
47 pub(crate) handle: H,
48 pub(crate) receiver: C,
49}
50
64/// `GlobalState` is the primary mutable state of the language server 51/// `GlobalState` is the primary mutable state of the language server
65/// 52///
66/// The most interesting components are `vfs`, which stores a consistent 53/// The most interesting components are `vfs`, which stores a consistent
67/// snapshot of the file systems, and `analysis_host`, which stores our 54/// snapshot of the file systems, and `analysis_host`, which stores our
68/// incremental salsa database. 55/// incremental salsa database.
56///
57/// Note that this struct has more than on impl in various modules!
69pub(crate) struct GlobalState { 58pub(crate) struct GlobalState {
70 sender: Sender<lsp_server::Message>, 59 sender: Sender<lsp_server::Message>,
60 pub(crate) task_pool: Handle<TaskPool<Task>, Receiver<Task>>,
61 pub(crate) loader: Handle<Box<dyn vfs::loader::Handle>, Receiver<vfs::loader::Message>>,
62 pub(crate) flycheck: Option<Handle<FlycheckHandle, Receiver<flycheck::Message>>>,
71 pub(crate) config: Config, 63 pub(crate) config: Config,
72 pub(crate) task_pool: (TaskPool<Task>, Receiver<Task>),
73 pub(crate) analysis_host: AnalysisHost, 64 pub(crate) analysis_host: AnalysisHost,
74 pub(crate) loader: Box<dyn vfs::loader::Handle>,
75 pub(crate) task_receiver: Receiver<vfs::loader::Message>,
76 pub(crate) flycheck: Option<(FlycheckHandle, Receiver<flycheck::Message>)>,
77 pub(crate) diagnostics: DiagnosticCollection, 65 pub(crate) diagnostics: DiagnosticCollection,
78 pub(crate) mem_docs: FxHashSet<VfsPath>, 66 pub(crate) mem_docs: FxHashSet<VfsPath>,
79 pub(crate) vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>, 67 pub(crate) vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>,
80 pub(crate) status: Status, 68 pub(crate) status: Status,
81 pub(crate) req_queue: ReqQueue, 69 pub(crate) req_queue: ReqQueue,
70 pub(crate) source_root_config: SourceRootConfig,
71 pub(crate) proc_macro_client: ProcMacroClient,
72 pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>,
82 latest_requests: Arc<RwLock<LatestRequests>>, 73 latest_requests: Arc<RwLock<LatestRequests>>,
83 source_root_config: SourceRootConfig,
84 _proc_macro_client: ProcMacroClient,
85 workspaces: Arc<Vec<ProjectWorkspace>>,
86} 74}
87 75
88/// An immutable snapshot of the world's state at a point in time. 76/// An immutable snapshot of the world's state at a point in time.
@@ -98,102 +86,40 @@ pub(crate) struct GlobalStateSnapshot {
98impl GlobalState { 86impl GlobalState {
99 pub(crate) fn new( 87 pub(crate) fn new(
100 sender: Sender<lsp_server::Message>, 88 sender: Sender<lsp_server::Message>,
101 workspaces: Vec<ProjectWorkspace>,
102 lru_capacity: Option<usize>, 89 lru_capacity: Option<usize>,
103 config: Config, 90 config: Config,
104 req_queue: ReqQueue,
105 ) -> GlobalState { 91 ) -> GlobalState {
106 let mut change = AnalysisChange::new(); 92 let loader = {
107 93 let (sender, receiver) = unbounded::<vfs::loader::Message>();
108 let project_folders = ProjectFolders::new(&workspaces); 94 let handle =
109 95 vfs_notify::NotifyHandle::spawn(Box::new(move |msg| sender.send(msg).unwrap()));
110 let (task_sender, task_receiver) = unbounded::<vfs::loader::Message>(); 96 let handle = Box::new(handle) as Box<dyn vfs::loader::Handle>;
111 let mut vfs = vfs::Vfs::default(); 97 Handle { handle, receiver }
112
113 let proc_macro_client = match &config.proc_macro_srv {
114 None => ProcMacroClient::dummy(),
115 Some((path, args)) => match ProcMacroClient::extern_process(path.into(), args) {
116 Ok(it) => it,
117 Err(err) => {
118 log::error!(
119 "Failed to run ra_proc_macro_srv from path {}, error: {:?}",
120 path.display(),
121 err
122 );
123 ProcMacroClient::dummy()
124 }
125 },
126 }; 98 };
127 99
128 let mut loader = {
129 let loader = vfs_notify::NotifyHandle::spawn(Box::new(move |msg| {
130 task_sender.send(msg).unwrap()
131 }));
132 Box::new(loader)
133 };
134 let watch = match config.files.watcher {
135 FilesWatcher::Client => vec![],
136 FilesWatcher::Notify => project_folders.watch,
137 };
138 loader.set_config(vfs::loader::Config { load: project_folders.load, watch });
139
140 // Create crate graph from all the workspaces
141 let mut crate_graph = CrateGraph::default();
142 let mut load = |path: &AbsPath| {
143 let contents = loader.load_sync(path);
144 let path = vfs::VfsPath::from(path.to_path_buf());
145 vfs.set_file_contents(path.clone(), contents);
146 vfs.file_id(&path)
147 };
148 for ws in workspaces.iter() {
149 crate_graph.extend(ws.to_crate_graph(
150 config.cargo.target.as_deref(),
151 &proc_macro_client,
152 &mut load,
153 ));
154 }
155 change.set_crate_graph(crate_graph);
156
157 let flycheck = config.check.as_ref().and_then(|c| create_flycheck(&workspaces, c));
158
159 let mut analysis_host = AnalysisHost::new(lru_capacity);
160 analysis_host.apply_change(change);
161
162 let task_pool = { 100 let task_pool = {
163 let (sender, receiver) = unbounded(); 101 let (sender, receiver) = unbounded();
164 (TaskPool::new(sender), receiver) 102 let handle = TaskPool::new(sender);
103 Handle { handle, receiver }
165 }; 104 };
166 105
167 let mut res = GlobalState { 106 GlobalState {
168 sender, 107 sender,
169 config,
170 task_pool, 108 task_pool,
171 analysis_host,
172 loader, 109 loader,
173 task_receiver, 110 config,
174 flycheck, 111 analysis_host: AnalysisHost::new(lru_capacity),
112 flycheck: None,
175 diagnostics: Default::default(), 113 diagnostics: Default::default(),
176 mem_docs: FxHashSet::default(), 114 mem_docs: FxHashSet::default(),
177 vfs: Arc::new(RwLock::new((vfs, FxHashMap::default()))), 115 vfs: Arc::new(RwLock::new((vfs::Vfs::default(), FxHashMap::default()))),
178 status: Status::default(), 116 status: Status::default(),
179 req_queue, 117 req_queue: ReqQueue::default(),
118 source_root_config: SourceRootConfig::default(),
119 proc_macro_client: ProcMacroClient::dummy(),
120 workspaces: Arc::new(Vec::new()),
180 latest_requests: Default::default(), 121 latest_requests: Default::default(),
181 source_root_config: project_folders.source_root_config,
182 _proc_macro_client: proc_macro_client,
183 workspaces: Arc::new(workspaces),
184 };
185 res.process_changes();
186 res
187 }
188
189 pub(crate) fn update_configuration(&mut self, config: Config) {
190 self.analysis_host.update_lru_capacity(config.lru_capacity);
191 if config.check != self.config.check {
192 self.flycheck =
193 config.check.as_ref().and_then(|it| create_flycheck(&self.workspaces, it));
194 } 122 }
195
196 self.config = config;
197 } 123 }
198 124
199 pub(crate) fn process_changes(&mut self) -> bool { 125 pub(crate) fn process_changes(&mut self) -> bool {
@@ -266,7 +192,7 @@ impl GlobalState {
266 self.send(response.into()); 192 self.send(response.into());
267 } 193 }
268 } 194 }
269 pub(crate) fn show_message(&mut self, typ: lsp_types::MessageType, message: String) { 195 pub(crate) fn show_message(&self, typ: lsp_types::MessageType, message: String) {
270 show_message(typ, message, &self.sender) 196 show_message(typ, message, &self.sender)
271 } 197 }
272} 198}
@@ -343,78 +269,3 @@ pub(crate) fn file_id_to_url(vfs: &vfs::Vfs, id: FileId) -> Url {
343 let path = path.as_path().unwrap(); 269 let path = path.as_path().unwrap();
344 url_from_abs_path(&path) 270 url_from_abs_path(&path)
345} 271}
346
347#[derive(Default)]
348pub(crate) struct ProjectFolders {
349 pub(crate) load: Vec<vfs::loader::Entry>,
350 pub(crate) watch: Vec<usize>,
351 pub(crate) source_root_config: SourceRootConfig,
352}
353
354impl ProjectFolders {
355 pub(crate) fn new(workspaces: &[ProjectWorkspace]) -> ProjectFolders {
356 let mut res = ProjectFolders::default();
357 let mut fsc = FileSetConfig::builder();
358 let mut local_filesets = vec![];
359
360 for root in workspaces.iter().flat_map(|it| it.to_roots()) {
361 let path = root.path().to_owned();
362
363 let mut file_set_roots: Vec<VfsPath> = vec![];
364
365 let entry = if root.is_member() {
366 vfs::loader::Entry::local_cargo_package(path.to_path_buf())
367 } else {
368 vfs::loader::Entry::cargo_package_dependency(path.to_path_buf())
369 };
370 res.load.push(entry);
371 if root.is_member() {
372 res.watch.push(res.load.len() - 1);
373 }
374
375 if let Some(out_dir) = root.out_dir() {
376 let out_dir = AbsPathBuf::try_from(out_dir.to_path_buf()).unwrap();
377 res.load.push(vfs::loader::Entry::rs_files_recursively(out_dir.clone()));
378 if root.is_member() {
379 res.watch.push(res.load.len() - 1);
380 }
381 file_set_roots.push(out_dir.into());
382 }
383 file_set_roots.push(path.to_path_buf().into());
384
385 if root.is_member() {
386 local_filesets.push(fsc.len());
387 }
388 fsc.add_file_set(file_set_roots)
389 }
390
391 let fsc = fsc.build();
392 res.source_root_config = SourceRootConfig { fsc, local_filesets };
393
394 res
395 }
396}
397
398#[derive(Default, Debug)]
399pub(crate) struct SourceRootConfig {
400 pub(crate) fsc: FileSetConfig,
401 pub(crate) local_filesets: Vec<usize>,
402}
403
404impl SourceRootConfig {
405 pub(crate) fn partition(&self, vfs: &vfs::Vfs) -> Vec<SourceRoot> {
406 self.fsc
407 .partition(vfs)
408 .into_iter()
409 .enumerate()
410 .map(|(idx, file_set)| {
411 let is_local = self.local_filesets.contains(&idx);
412 if is_local {
413 SourceRoot::new_local(file_set)
414 } else {
415 SourceRoot::new_library(file_set)
416 }
417 })
418 .collect()
419 }
420}
diff --git a/crates/rust-analyzer/src/lib.rs b/crates/rust-analyzer/src/lib.rs
index d503fe96e..a24dfe58c 100644
--- a/crates/rust-analyzer/src/lib.rs
+++ b/crates/rust-analyzer/src/lib.rs
@@ -18,6 +18,7 @@ macro_rules! eprintln {
18} 18}
19 19
20mod global_state; 20mod global_state;
21mod reload;
21mod main_loop; 22mod main_loop;
22mod dispatch; 23mod dispatch;
23mod handlers; 24mod handlers;
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 1bd9d6389..d4879283d 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -11,17 +11,14 @@ use lsp_types::{notification::Notification as _, request::Request as _};
11use ra_db::VfsPath; 11use ra_db::VfsPath;
12use ra_ide::{Canceled, FileId}; 12use ra_ide::{Canceled, FileId};
13use ra_prof::profile; 13use ra_prof::profile;
14use ra_project_model::{PackageRoot, ProjectWorkspace};
15 14
16use crate::{ 15use crate::{
17 config::{Config, FilesWatcher, LinkedProject}, 16 config::Config,
18 dispatch::{NotificationDispatcher, RequestDispatcher}, 17 dispatch::{NotificationDispatcher, RequestDispatcher},
19 from_proto, 18 from_proto,
20 global_state::{file_id_to_url, GlobalState, Status}, 19 global_state::{file_id_to_url, GlobalState, Status},
21 handlers, lsp_ext, 20 handlers, lsp_ext,
22 lsp_utils::{ 21 lsp_utils::{apply_document_changes, is_canceled, notification_is, notification_new},
23 apply_document_changes, is_canceled, notification_is, notification_new, show_message,
24 },
25 Result, 22 Result,
26}; 23};
27 24
@@ -47,81 +44,8 @@ pub fn main_loop(config: Config, connection: Connection) -> Result<()> {
47 SetThreadPriority(thread, thread_priority_above_normal); 44 SetThreadPriority(thread, thread_priority_above_normal);
48 } 45 }
49 46
50 let global_state = { 47 GlobalState::new(connection.sender.clone(), config.lru_capacity, config)
51 let workspaces = { 48 .run(connection.receiver)
52 if config.linked_projects.is_empty() && config.notifications.cargo_toml_not_found {
53 show_message(
54 lsp_types::MessageType::Error,
55 "rust-analyzer failed to discover workspace".to_string(),
56 &connection.sender,
57 );
58 };
59
60 config
61 .linked_projects
62 .iter()
63 .filter_map(|project| match project {
64 LinkedProject::ProjectManifest(manifest) => {
65 ra_project_model::ProjectWorkspace::load(
66 manifest.clone(),
67 &config.cargo,
68 config.with_sysroot,
69 )
70 .map_err(|err| {
71 log::error!("failed to load workspace: {:#}", err);
72 show_message(
73 lsp_types::MessageType::Error,
74 format!("rust-analyzer failed to load workspace: {:#}", err),
75 &connection.sender,
76 );
77 })
78 .ok()
79 }
80 LinkedProject::InlineJsonProject(it) => {
81 Some(ra_project_model::ProjectWorkspace::Json { project: it.clone() })
82 }
83 })
84 .collect::<Vec<_>>()
85 };
86
87 let mut req_queue = ReqQueue::default();
88
89 if let FilesWatcher::Client = config.files.watcher {
90 let registration_options = lsp_types::DidChangeWatchedFilesRegistrationOptions {
91 watchers: workspaces
92 .iter()
93 .flat_map(ProjectWorkspace::to_roots)
94 .filter(PackageRoot::is_member)
95 .map(|root| format!("{}/**/*.rs", root.path().display()))
96 .map(|glob_pattern| lsp_types::FileSystemWatcher { glob_pattern, kind: None })
97 .collect(),
98 };
99 let registration = lsp_types::Registration {
100 id: "file-watcher".to_string(),
101 method: "workspace/didChangeWatchedFiles".to_string(),
102 register_options: Some(serde_json::to_value(registration_options).unwrap()),
103 };
104 let params = lsp_types::RegistrationParams { registrations: vec![registration] };
105 let request = req_queue.outgoing.register(
106 lsp_types::request::RegisterCapability::METHOD.to_string(),
107 params,
108 DO_NOTHING,
109 );
110 connection.sender.send(request.into()).unwrap();
111 }
112
113 GlobalState::new(
114 connection.sender.clone(),
115 workspaces,
116 config.lru_capacity,
117 config,
118 req_queue,
119 )
120 };
121
122 log::info!("server initialized, serving requests");
123 global_state.run(connection.receiver)?;
124 Ok(())
125} 49}
126 50
127enum Event { 51enum Event {
@@ -176,36 +100,39 @@ impl GlobalState {
176 recv(inbox) -> msg => 100 recv(inbox) -> msg =>
177 msg.ok().map(Event::Lsp), 101 msg.ok().map(Event::Lsp),
178 102
179 recv(self.task_pool.1) -> task => 103 recv(self.task_pool.receiver) -> task =>
180 Some(Event::Task(task.unwrap())), 104 Some(Event::Task(task.unwrap())),
181 105
182 recv(self.task_receiver) -> task => 106 recv(self.loader.receiver) -> task =>
183 Some(Event::Vfs(task.unwrap())), 107 Some(Event::Vfs(task.unwrap())),
184 108
185 recv(self.flycheck.as_ref().map_or(&never(), |it| &it.1)) -> task => 109 recv(self.flycheck.as_ref().map_or(&never(), |it| &it.receiver)) -> task =>
186 Some(Event::Flycheck(task.unwrap())), 110 Some(Event::Flycheck(task.unwrap())),
187 } 111 }
188 } 112 }
189 113
190 fn run(mut self, inbox: Receiver<lsp_server::Message>) -> Result<()> { 114 fn run(mut self, inbox: Receiver<lsp_server::Message>) -> Result<()> {
115 self.reload();
116
191 while let Some(event) = self.next_event(&inbox) { 117 while let Some(event) = self.next_event(&inbox) {
192 if let Event::Lsp(lsp_server::Message::Notification(not)) = &event { 118 if let Event::Lsp(lsp_server::Message::Notification(not)) = &event {
193 if not.method == lsp_types::notification::Exit::METHOD { 119 if not.method == lsp_types::notification::Exit::METHOD {
194 return Ok(()); 120 return Ok(());
195 } 121 }
196 } 122 }
197 self.loop_turn(event)? 123 self.handle_event(event)?
198 } 124 }
125
199 Err("client exited without proper shutdown sequence")? 126 Err("client exited without proper shutdown sequence")?
200 } 127 }
201 128
202 fn loop_turn(&mut self, event: Event) -> Result<()> { 129 fn handle_event(&mut self, event: Event) -> Result<()> {
203 let loop_start = Instant::now(); 130 let loop_start = Instant::now();
204 // NOTE: don't count blocking select! call as a loop-turn time 131 // NOTE: don't count blocking select! call as a loop-turn time
205 let _p = profile("main_loop_inner/loop-turn"); 132 let _p = profile("GlobalState::handle_event");
206 133
207 log::info!("loop turn = {:?}", event); 134 log::info!("handle_event({:?})", event);
208 let queue_count = self.task_pool.0.len(); 135 let queue_count = self.task_pool.handle.len();
209 if queue_count > 0 { 136 if queue_count > 0 {
210 log::info!("queued count = {}", queue_count); 137 log::info!("queued count = {}", queue_count);
211 } 138 }
@@ -306,7 +233,7 @@ impl GlobalState {
306 let state_changed = self.process_changes(); 233 let state_changed = self.process_changes();
307 if became_ready { 234 if became_ready {
308 if let Some(flycheck) = &self.flycheck { 235 if let Some(flycheck) = &self.flycheck {
309 flycheck.0.update(); 236 flycheck.handle.update();
310 } 237 }
311 } 238 }
312 239
@@ -443,7 +370,7 @@ impl GlobalState {
443 log::error!("orphan DidCloseTextDocument: {}", path) 370 log::error!("orphan DidCloseTextDocument: {}", path)
444 } 371 }
445 if let Some(path) = path.as_path() { 372 if let Some(path) = path.as_path() {
446 this.loader.invalidate(path.to_path_buf()); 373 this.loader.handle.invalidate(path.to_path_buf());
447 } 374 }
448 } 375 }
449 let params = lsp_types::PublishDiagnosticsParams { 376 let params = lsp_types::PublishDiagnosticsParams {
@@ -457,7 +384,7 @@ impl GlobalState {
457 })? 384 })?
458 .on::<lsp_types::notification::DidSaveTextDocument>(|this, _params| { 385 .on::<lsp_types::notification::DidSaveTextDocument>(|this, _params| {
459 if let Some(flycheck) = &this.flycheck { 386 if let Some(flycheck) = &this.flycheck {
460 flycheck.0.update(); 387 flycheck.handle.update();
461 } 388 }
462 Ok(()) 389 Ok(())
463 })? 390 })?
@@ -500,7 +427,7 @@ impl GlobalState {
500 .on::<lsp_types::notification::DidChangeWatchedFiles>(|this, params| { 427 .on::<lsp_types::notification::DidChangeWatchedFiles>(|this, params| {
501 for change in params.changes { 428 for change in params.changes {
502 if let Ok(path) = from_proto::abs_path(&change.uri) { 429 if let Ok(path) = from_proto::abs_path(&change.uri) {
503 this.loader.invalidate(path); 430 this.loader.handle.invalidate(path);
504 } 431 }
505 } 432 }
506 Ok(()) 433 Ok(())
@@ -513,7 +440,7 @@ impl GlobalState {
513 if self.config.publish_diagnostics { 440 if self.config.publish_diagnostics {
514 let snapshot = self.snapshot(); 441 let snapshot = self.snapshot();
515 let subscriptions = subscriptions.clone(); 442 let subscriptions = subscriptions.clone();
516 self.task_pool.0.spawn(move || { 443 self.task_pool.handle.spawn(move || {
517 let diagnostics = subscriptions 444 let diagnostics = subscriptions
518 .into_iter() 445 .into_iter()
519 .filter_map(|file_id| { 446 .filter_map(|file_id| {
@@ -531,7 +458,7 @@ impl GlobalState {
531 Task::Diagnostics(diagnostics) 458 Task::Diagnostics(diagnostics)
532 }) 459 })
533 } 460 }
534 self.task_pool.0.spawn({ 461 self.task_pool.handle.spawn({
535 let subs = subscriptions; 462 let subs = subscriptions;
536 let snap = self.snapshot(); 463 let snap = self.snapshot();
537 move || { 464 move || {
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs
new file mode 100644
index 000000000..a22d3e262
--- /dev/null
+++ b/crates/rust-analyzer/src/reload.rs
@@ -0,0 +1,241 @@
1//! Project loading & configuration updates
2use std::sync::Arc;
3
4use crossbeam_channel::unbounded;
5use flycheck::FlycheckHandle;
6use lsp_types::request::Request;
7use ra_db::{CrateGraph, SourceRoot, VfsPath};
8use ra_ide::AnalysisChange;
9use ra_project_model::{PackageRoot, ProcMacroClient, ProjectWorkspace};
10use vfs::{file_set::FileSetConfig, AbsPath};
11
12use crate::{
13 config::{Config, FilesWatcher, LinkedProject},
14 global_state::{GlobalState, Handle},
15};
16
17impl GlobalState {
18 pub(crate) fn update_configuration(&mut self, new_config: Config) {
19 self.analysis_host.update_lru_capacity(new_config.lru_capacity);
20 if new_config.flycheck != self.config.flycheck {
21 self.reload_flycheck();
22 }
23 self.config = new_config;
24 }
25 pub(crate) fn reload(&mut self) {
26 let workspaces = {
27 if self.config.linked_projects.is_empty()
28 && self.config.notifications.cargo_toml_not_found
29 {
30 self.show_message(
31 lsp_types::MessageType::Error,
32 "rust-analyzer failed to discover workspace".to_string(),
33 );
34 };
35
36 self.config
37 .linked_projects
38 .iter()
39 .filter_map(|project| match project {
40 LinkedProject::ProjectManifest(manifest) => {
41 ra_project_model::ProjectWorkspace::load(
42 manifest.clone(),
43 &self.config.cargo,
44 self.config.with_sysroot,
45 )
46 .map_err(|err| {
47 log::error!("failed to load workspace: {:#}", err);
48 self.show_message(
49 lsp_types::MessageType::Error,
50 format!("rust-analyzer failed to load workspace: {:#}", err),
51 );
52 })
53 .ok()
54 }
55 LinkedProject::InlineJsonProject(it) => {
56 Some(ra_project_model::ProjectWorkspace::Json { project: it.clone() })
57 }
58 })
59 .collect::<Vec<_>>()
60 };
61
62 if let FilesWatcher::Client = self.config.files.watcher {
63 let registration_options = lsp_types::DidChangeWatchedFilesRegistrationOptions {
64 watchers: workspaces
65 .iter()
66 .flat_map(ProjectWorkspace::to_roots)
67 .filter(PackageRoot::is_member)
68 .map(|root| format!("{}/**/*.rs", root.path().display()))
69 .map(|glob_pattern| lsp_types::FileSystemWatcher { glob_pattern, kind: None })
70 .collect(),
71 };
72 let registration = lsp_types::Registration {
73 id: "file-watcher".to_string(),
74 method: "workspace/didChangeWatchedFiles".to_string(),
75 register_options: Some(serde_json::to_value(registration_options).unwrap()),
76 };
77 let params = lsp_types::RegistrationParams { registrations: vec![registration] };
78 let request = self.req_queue.outgoing.register(
79 lsp_types::request::RegisterCapability::METHOD.to_string(),
80 params,
81 |_, _| (),
82 );
83 self.send(request.into());
84 }
85
86 let mut change = AnalysisChange::new();
87
88 let project_folders = ProjectFolders::new(&workspaces);
89
90 self.proc_macro_client = match &self.config.proc_macro_srv {
91 None => ProcMacroClient::dummy(),
92 Some((path, args)) => match ProcMacroClient::extern_process(path.into(), args) {
93 Ok(it) => it,
94 Err(err) => {
95 log::error!(
96 "Failed to run ra_proc_macro_srv from path {}, error: {:?}",
97 path.display(),
98 err
99 );
100 ProcMacroClient::dummy()
101 }
102 },
103 };
104 let watch = match self.config.files.watcher {
105 FilesWatcher::Client => vec![],
106 FilesWatcher::Notify => project_folders.watch,
107 };
108 self.loader.handle.set_config(vfs::loader::Config { load: project_folders.load, watch });
109
110 // Create crate graph from all the workspaces
111 let crate_graph = {
112 let mut crate_graph = CrateGraph::default();
113 let vfs = &mut self.vfs.write().0;
114 let loader = &mut self.loader;
115 let mut load = |path: &AbsPath| {
116 let contents = loader.handle.load_sync(path);
117 let path = vfs::VfsPath::from(path.to_path_buf());
118 vfs.set_file_contents(path.clone(), contents);
119 vfs.file_id(&path)
120 };
121 for ws in workspaces.iter() {
122 crate_graph.extend(ws.to_crate_graph(
123 self.config.cargo.target.as_deref(),
124 &self.proc_macro_client,
125 &mut load,
126 ));
127 }
128
129 crate_graph
130 };
131 change.set_crate_graph(crate_graph);
132
133 self.source_root_config = project_folders.source_root_config;
134 self.workspaces = Arc::new(workspaces);
135
136 self.analysis_host.apply_change(change);
137 self.process_changes();
138 self.reload_flycheck();
139 }
140
141 fn reload_flycheck(&mut self) {
142 let config = match self.config.flycheck.clone() {
143 Some(it) => it,
144 None => {
145 self.flycheck = None;
146 return;
147 }
148 };
149
150 // FIXME: Figure out the multi-workspace situation
151 self.flycheck = self.workspaces.iter().find_map(move |w| match w {
152 ProjectWorkspace::Cargo { cargo, .. } => {
153 let (sender, receiver) = unbounded();
154 let sender = Box::new(move |msg| sender.send(msg).unwrap());
155 let cargo_project_root = cargo.workspace_root().to_path_buf();
156 let handle =
157 FlycheckHandle::spawn(sender, config.clone(), cargo_project_root.into());
158 Some(Handle { handle, receiver })
159 }
160 ProjectWorkspace::Json { .. } => {
161 log::warn!("Cargo check watching only supported for cargo workspaces, disabling");
162 None
163 }
164 })
165 }
166}
167
168#[derive(Default)]
169pub(crate) struct ProjectFolders {
170 pub(crate) load: Vec<vfs::loader::Entry>,
171 pub(crate) watch: Vec<usize>,
172 pub(crate) source_root_config: SourceRootConfig,
173}
174
175impl ProjectFolders {
176 pub(crate) fn new(workspaces: &[ProjectWorkspace]) -> ProjectFolders {
177 let mut res = ProjectFolders::default();
178 let mut fsc = FileSetConfig::builder();
179 let mut local_filesets = vec![];
180
181 for root in workspaces.iter().flat_map(|it| it.to_roots()) {
182 let path = root.path().to_owned();
183
184 let mut file_set_roots: Vec<VfsPath> = vec![];
185
186 let entry = if root.is_member() {
187 vfs::loader::Entry::local_cargo_package(path.to_path_buf())
188 } else {
189 vfs::loader::Entry::cargo_package_dependency(path.to_path_buf())
190 };
191 res.load.push(entry);
192 if root.is_member() {
193 res.watch.push(res.load.len() - 1);
194 }
195
196 if let Some(out_dir) = root.out_dir() {
197 let out_dir = out_dir.to_path_buf();
198 res.load.push(vfs::loader::Entry::rs_files_recursively(out_dir.clone()));
199 if root.is_member() {
200 res.watch.push(res.load.len() - 1);
201 }
202 file_set_roots.push(out_dir.into());
203 }
204 file_set_roots.push(path.to_path_buf().into());
205
206 if root.is_member() {
207 local_filesets.push(fsc.len());
208 }
209 fsc.add_file_set(file_set_roots)
210 }
211
212 let fsc = fsc.build();
213 res.source_root_config = SourceRootConfig { fsc, local_filesets };
214
215 res
216 }
217}
218
219#[derive(Default, Debug)]
220pub(crate) struct SourceRootConfig {
221 pub(crate) fsc: FileSetConfig,
222 pub(crate) local_filesets: Vec<usize>,
223}
224
225impl SourceRootConfig {
226 pub(crate) fn partition(&self, vfs: &vfs::Vfs) -> Vec<SourceRoot> {
227 self.fsc
228 .partition(vfs)
229 .into_iter()
230 .enumerate()
231 .map(|(idx, file_set)| {
232 let is_local = self.local_filesets.contains(&idx);
233 if is_local {
234 SourceRoot::new_local(file_set)
235 } else {
236 SourceRoot::new_library(file_set)
237 }
238 })
239 .collect()
240 }
241}