Prep dynamic workspace loading

This commit is contained in:
Aleksey Kladov 2020-06-25 23:26:21 +02:00
parent 73d73077fe
commit 3d0f782138
2 changed files with 79 additions and 96 deletions

View file

@ -7,16 +7,16 @@ use std::{convert::TryFrom, sync::Arc};
use crossbeam_channel::{unbounded, Receiver, Sender}; use crossbeam_channel::{unbounded, Receiver, Sender};
use flycheck::{FlycheckConfig, FlycheckHandle}; use flycheck::{FlycheckConfig, FlycheckHandle};
use lsp_types::Url; use lsp_types::{request::Request, Url};
use parking_lot::RwLock; use parking_lot::RwLock;
use ra_db::{CrateId, SourceRoot, VfsPath}; use ra_db::{CrateId, SourceRoot, VfsPath};
use ra_ide::{Analysis, AnalysisChange, AnalysisHost, CrateGraph, FileId}; use ra_ide::{Analysis, AnalysisChange, AnalysisHost, CrateGraph, FileId};
use ra_project_model::{CargoWorkspace, ProcMacroClient, ProjectWorkspace, Target}; use ra_project_model::{CargoWorkspace, PackageRoot, ProcMacroClient, ProjectWorkspace, Target};
use stdx::format_to; use stdx::format_to;
use vfs::{file_set::FileSetConfig, loader::Handle, AbsPath, AbsPathBuf}; use vfs::{file_set::FileSetConfig, loader::Handle, AbsPath, AbsPathBuf};
use crate::{ use crate::{
config::{Config, FilesWatcher}, config::{Config, FilesWatcher, LinkedProject},
diagnostics::{CheckFixes, DiagnosticCollection}, diagnostics::{CheckFixes, DiagnosticCollection},
from_proto, from_proto,
line_endings::LineEndings, line_endings::LineEndings,
@ -98,10 +98,8 @@ pub(crate) struct GlobalStateSnapshot {
impl GlobalState { impl GlobalState {
pub(crate) fn new( pub(crate) fn new(
sender: Sender<lsp_server::Message>, sender: Sender<lsp_server::Message>,
workspaces: Vec<ProjectWorkspace>,
lru_capacity: Option<usize>, lru_capacity: Option<usize>,
config: Config, config: Config,
req_queue: ReqQueue,
) -> GlobalState { ) -> GlobalState {
let (task_sender, task_receiver) = unbounded::<vfs::loader::Message>(); let (task_sender, task_receiver) = unbounded::<vfs::loader::Message>();
@ -117,7 +115,7 @@ impl GlobalState {
(TaskPool::new(sender), receiver) (TaskPool::new(sender), receiver)
}; };
let mut res = GlobalState { GlobalState {
sender, sender,
config, config,
task_pool, task_pool,
@ -129,17 +127,75 @@ impl GlobalState {
mem_docs: FxHashSet::default(), mem_docs: FxHashSet::default(),
vfs: Arc::new(RwLock::new((vfs::Vfs::default(), FxHashMap::default()))), vfs: Arc::new(RwLock::new((vfs::Vfs::default(), FxHashMap::default()))),
status: Status::default(), status: Status::default(),
req_queue, req_queue: ReqQueue::default(),
latest_requests: Default::default(), latest_requests: Default::default(),
source_root_config: SourceRootConfig::default(), source_root_config: SourceRootConfig::default(),
proc_macro_client: ProcMacroClient::dummy(), proc_macro_client: ProcMacroClient::dummy(),
workspaces: Arc::new(Vec::new()), workspaces: Arc::new(Vec::new()),
}; }
res.reload(workspaces); }
res
pub(crate) fn reload(&mut self) {
let workspaces = {
if self.config.linked_projects.is_empty()
&& self.config.notifications.cargo_toml_not_found
{
self.show_message(
lsp_types::MessageType::Error,
"rust-analyzer failed to discover workspace".to_string(),
);
};
self.config
.linked_projects
.iter()
.filter_map(|project| match project {
LinkedProject::ProjectManifest(manifest) => {
ra_project_model::ProjectWorkspace::load(
manifest.clone(),
&self.config.cargo,
self.config.with_sysroot,
)
.map_err(|err| {
log::error!("failed to load workspace: {:#}", err);
self.show_message(
lsp_types::MessageType::Error,
format!("rust-analyzer failed to load workspace: {:#}", err),
);
})
.ok()
}
LinkedProject::InlineJsonProject(it) => {
Some(ra_project_model::ProjectWorkspace::Json { project: it.clone() })
}
})
.collect::<Vec<_>>()
};
if let FilesWatcher::Client = self.config.files.watcher {
let registration_options = lsp_types::DidChangeWatchedFilesRegistrationOptions {
watchers: workspaces
.iter()
.flat_map(ProjectWorkspace::to_roots)
.filter(PackageRoot::is_member)
.map(|root| format!("{}/**/*.rs", root.path().display()))
.map(|glob_pattern| lsp_types::FileSystemWatcher { glob_pattern, kind: None })
.collect(),
};
let registration = lsp_types::Registration {
id: "file-watcher".to_string(),
method: "workspace/didChangeWatchedFiles".to_string(),
register_options: Some(serde_json::to_value(registration_options).unwrap()),
};
let params = lsp_types::RegistrationParams { registrations: vec![registration] };
let request = self.req_queue.outgoing.register(
lsp_types::request::RegisterCapability::METHOD.to_string(),
params,
|_, _| (),
);
self.send(request.into());
} }
pub(crate) fn reload(&mut self, workspaces: Vec<ProjectWorkspace>) {
let mut change = AnalysisChange::new(); let mut change = AnalysisChange::new();
let project_folders = ProjectFolders::new(&workspaces); let project_folders = ProjectFolders::new(&workspaces);
@ -275,7 +331,7 @@ impl GlobalState {
self.send(response.into()); self.send(response.into());
} }
} }
pub(crate) fn show_message(&mut self, typ: lsp_types::MessageType, message: String) { pub(crate) fn show_message(&self, typ: lsp_types::MessageType, message: String) {
show_message(typ, message, &self.sender) show_message(typ, message, &self.sender)
} }
} }

View file

@ -11,17 +11,14 @@ use lsp_types::{notification::Notification as _, request::Request as _};
use ra_db::VfsPath; use ra_db::VfsPath;
use ra_ide::{Canceled, FileId}; use ra_ide::{Canceled, FileId};
use ra_prof::profile; use ra_prof::profile;
use ra_project_model::{PackageRoot, ProjectWorkspace};
use crate::{ use crate::{
config::{Config, FilesWatcher, LinkedProject}, config::Config,
dispatch::{NotificationDispatcher, RequestDispatcher}, dispatch::{NotificationDispatcher, RequestDispatcher},
from_proto, from_proto,
global_state::{file_id_to_url, GlobalState, Status}, global_state::{file_id_to_url, GlobalState, Status},
handlers, lsp_ext, handlers, lsp_ext,
lsp_utils::{ lsp_utils::{apply_document_changes, is_canceled, notification_is, notification_new},
apply_document_changes, is_canceled, notification_is, notification_new, show_message,
},
Result, Result,
}; };
@ -47,81 +44,8 @@ pub fn main_loop(config: Config, connection: Connection) -> Result<()> {
SetThreadPriority(thread, thread_priority_above_normal); SetThreadPriority(thread, thread_priority_above_normal);
} }
let global_state = { GlobalState::new(connection.sender.clone(), config.lru_capacity, config)
let workspaces = { .run(connection.receiver)
if config.linked_projects.is_empty() && config.notifications.cargo_toml_not_found {
show_message(
lsp_types::MessageType::Error,
"rust-analyzer failed to discover workspace".to_string(),
&connection.sender,
);
};
config
.linked_projects
.iter()
.filter_map(|project| match project {
LinkedProject::ProjectManifest(manifest) => {
ra_project_model::ProjectWorkspace::load(
manifest.clone(),
&config.cargo,
config.with_sysroot,
)
.map_err(|err| {
log::error!("failed to load workspace: {:#}", err);
show_message(
lsp_types::MessageType::Error,
format!("rust-analyzer failed to load workspace: {:#}", err),
&connection.sender,
);
})
.ok()
}
LinkedProject::InlineJsonProject(it) => {
Some(ra_project_model::ProjectWorkspace::Json { project: it.clone() })
}
})
.collect::<Vec<_>>()
};
let mut req_queue = ReqQueue::default();
if let FilesWatcher::Client = config.files.watcher {
let registration_options = lsp_types::DidChangeWatchedFilesRegistrationOptions {
watchers: workspaces
.iter()
.flat_map(ProjectWorkspace::to_roots)
.filter(PackageRoot::is_member)
.map(|root| format!("{}/**/*.rs", root.path().display()))
.map(|glob_pattern| lsp_types::FileSystemWatcher { glob_pattern, kind: None })
.collect(),
};
let registration = lsp_types::Registration {
id: "file-watcher".to_string(),
method: "workspace/didChangeWatchedFiles".to_string(),
register_options: Some(serde_json::to_value(registration_options).unwrap()),
};
let params = lsp_types::RegistrationParams { registrations: vec![registration] };
let request = req_queue.outgoing.register(
lsp_types::request::RegisterCapability::METHOD.to_string(),
params,
DO_NOTHING,
);
connection.sender.send(request.into()).unwrap();
}
GlobalState::new(
connection.sender.clone(),
workspaces,
config.lru_capacity,
config,
req_queue,
)
};
log::info!("server initialized, serving requests");
global_state.run(connection.receiver)?;
Ok(())
} }
enum Event { enum Event {
@ -188,23 +112,26 @@ impl GlobalState {
} }
fn run(mut self, inbox: Receiver<lsp_server::Message>) -> Result<()> { fn run(mut self, inbox: Receiver<lsp_server::Message>) -> Result<()> {
self.reload();
while let Some(event) = self.next_event(&inbox) { while let Some(event) = self.next_event(&inbox) {
if let Event::Lsp(lsp_server::Message::Notification(not)) = &event { if let Event::Lsp(lsp_server::Message::Notification(not)) = &event {
if not.method == lsp_types::notification::Exit::METHOD { if not.method == lsp_types::notification::Exit::METHOD {
return Ok(()); return Ok(());
} }
} }
self.loop_turn(event)? self.handle_event(event)?
} }
Err("client exited without proper shutdown sequence")? Err("client exited without proper shutdown sequence")?
} }
fn loop_turn(&mut self, event: Event) -> Result<()> { fn handle_event(&mut self, event: Event) -> Result<()> {
let loop_start = Instant::now(); let loop_start = Instant::now();
// NOTE: don't count blocking select! call as a loop-turn time // NOTE: don't count blocking select! call as a loop-turn time
let _p = profile("main_loop_inner/loop-turn"); let _p = profile("GlobalState::handle_event");
log::info!("loop turn = {:?}", event); log::info!("handle_event({:?})", event);
let queue_count = self.task_pool.0.len(); let queue_count = self.task_pool.0.len();
if queue_count > 0 { if queue_count > 0 {
log::info!("queued count = {}", queue_count); log::info!("queued count = {}", queue_count);