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 flycheck::{FlycheckConfig, FlycheckHandle};
use lsp_types::Url;
use lsp_types::{request::Request, Url};
use parking_lot::RwLock;
use ra_db::{CrateId, SourceRoot, VfsPath};
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 vfs::{file_set::FileSetConfig, loader::Handle, AbsPath, AbsPathBuf};
use crate::{
config::{Config, FilesWatcher},
config::{Config, FilesWatcher, LinkedProject},
diagnostics::{CheckFixes, DiagnosticCollection},
from_proto,
line_endings::LineEndings,
@ -98,10 +98,8 @@ pub(crate) struct GlobalStateSnapshot {
impl GlobalState {
pub(crate) fn new(
sender: Sender<lsp_server::Message>,
workspaces: Vec<ProjectWorkspace>,
lru_capacity: Option<usize>,
config: Config,
req_queue: ReqQueue,
) -> GlobalState {
let (task_sender, task_receiver) = unbounded::<vfs::loader::Message>();
@ -117,7 +115,7 @@ impl GlobalState {
(TaskPool::new(sender), receiver)
};
let mut res = GlobalState {
GlobalState {
sender,
config,
task_pool,
@ -129,17 +127,75 @@ impl GlobalState {
mem_docs: FxHashSet::default(),
vfs: Arc::new(RwLock::new((vfs::Vfs::default(), FxHashMap::default()))),
status: Status::default(),
req_queue,
req_queue: ReqQueue::default(),
latest_requests: Default::default(),
source_root_config: SourceRootConfig::default(),
proc_macro_client: ProcMacroClient::dummy(),
workspaces: Arc::new(Vec::new()),
};
res.reload(workspaces);
res
}
}
pub(crate) fn reload(&mut self, workspaces: Vec<ProjectWorkspace>) {
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());
}
let mut change = AnalysisChange::new();
let project_folders = ProjectFolders::new(&workspaces);
@ -275,7 +331,7 @@ impl GlobalState {
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)
}
}

View File

@ -11,17 +11,14 @@ use lsp_types::{notification::Notification as _, request::Request as _};
use ra_db::VfsPath;
use ra_ide::{Canceled, FileId};
use ra_prof::profile;
use ra_project_model::{PackageRoot, ProjectWorkspace};
use crate::{
config::{Config, FilesWatcher, LinkedProject},
config::Config,
dispatch::{NotificationDispatcher, RequestDispatcher},
from_proto,
global_state::{file_id_to_url, GlobalState, Status},
handlers, lsp_ext,
lsp_utils::{
apply_document_changes, is_canceled, notification_is, notification_new, show_message,
},
lsp_utils::{apply_document_changes, is_canceled, notification_is, notification_new},
Result,
};
@ -47,81 +44,8 @@ pub fn main_loop(config: Config, connection: Connection) -> Result<()> {
SetThreadPriority(thread, thread_priority_above_normal);
}
let global_state = {
let workspaces = {
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(())
GlobalState::new(connection.sender.clone(), config.lru_capacity, config)
.run(connection.receiver)
}
enum Event {
@ -188,23 +112,26 @@ impl GlobalState {
}
fn run(mut self, inbox: Receiver<lsp_server::Message>) -> Result<()> {
self.reload();
while let Some(event) = self.next_event(&inbox) {
if let Event::Lsp(lsp_server::Message::Notification(not)) = &event {
if not.method == lsp_types::notification::Exit::METHOD {
return Ok(());
}
}
self.loop_turn(event)?
self.handle_event(event)?
}
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();
// 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();
if queue_count > 0 {
log::info!("queued count = {}", queue_count);