mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-03 15:15:24 +00:00
Prep dynamic workspace loading
This commit is contained in:
parent
73d73077fe
commit
3d0f782138
2 changed files with 79 additions and 96 deletions
|
@ -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, 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 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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue