-mMake it harder to accidently early-exit the loop

This commit is contained in:
Aleksey Kladov
2020-06-25 19:01:16 +02:00
parent b5aa92bab9
commit dba11cb060

View File

@@ -126,6 +126,45 @@ pub fn main_loop(config: Config, connection: Connection) -> Result<()> {
Ok(())
}
enum Event {
Lsp(lsp_server::Message),
Task(Task),
Vfs(vfs::loader::Message),
Flycheck(flycheck::Message),
}
impl fmt::Debug for Event {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let debug_verbose_not = |not: &Notification, f: &mut fmt::Formatter| {
f.debug_struct("Notification").field("method", &not.method).finish()
};
match self {
Event::Lsp(lsp_server::Message::Notification(not)) => {
if notification_is::<lsp_types::notification::DidOpenTextDocument>(not)
|| notification_is::<lsp_types::notification::DidChangeTextDocument>(not)
{
return debug_verbose_not(not, f);
}
}
Event::Task(Task::Respond(resp)) => {
return f
.debug_struct("Response")
.field("id", &resp.id)
.field("error", &resp.error)
.finish();
}
_ => (),
}
match self {
Event::Lsp(it) => fmt::Debug::fmt(it, f),
Event::Task(it) => fmt::Debug::fmt(it, f),
Event::Vfs(it) => fmt::Debug::fmt(it, f),
Event::Flycheck(it) => fmt::Debug::fmt(it, f),
}
}
}
impl GlobalState {
fn next_event(&self, inbox: &Receiver<lsp_server::Message>) -> Option<Event> {
select! {
@@ -145,6 +184,17 @@ impl GlobalState {
fn run(mut self, inbox: Receiver<lsp_server::Message>) -> Result<()> {
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)?
}
Err("client exited without proper shutdown sequence")?
}
fn loop_turn(&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");
@@ -160,14 +210,11 @@ impl GlobalState {
Event::Lsp(msg) => match msg {
lsp_server::Message::Request(req) => self.on_request(loop_start, req)?,
lsp_server::Message::Notification(not) => {
if not.method == lsp_types::notification::Exit::METHOD {
return Ok(());
}
self.on_notification(not)?;
}
lsp_server::Message::Response(resp) => {
let handler = self.req_queue.outgoing.complete(resp.id.clone());
handler(&mut self, resp)
handler(self, resp)
}
},
Event::Task(task) => {
@@ -196,7 +243,7 @@ impl GlobalState {
Progress::End
};
report_progress(
&mut self,
self,
"roots scanned",
state,
Some(format!("{}/{}", n_done, n_total)),
@@ -204,7 +251,53 @@ impl GlobalState {
)
}
},
Event::Flycheck(task) => on_check_task(task, &mut self)?,
Event::Flycheck(task) => match task {
flycheck::Message::ClearDiagnostics => {
on_diagnostic_task(DiagnosticTask::ClearCheck, self)
}
flycheck::Message::AddDiagnostic { workspace_root, diagnostic } => {
let diagnostics = crate::diagnostics::to_proto::map_rust_diagnostic_to_lsp(
&self.config.diagnostics,
&diagnostic,
&workspace_root,
);
for diag in diagnostics {
let path = from_proto::vfs_path(&diag.location.uri)?;
let file_id = match self.vfs.read().0.file_id(&path) {
Some(file) => FileId(file.0),
None => {
log::error!(
"File with cargo diagnostic not found in VFS: {}",
path
);
return Ok(());
}
};
on_diagnostic_task(
DiagnosticTask::AddCheck(
file_id,
diag.diagnostic,
diag.fixes.into_iter().map(|it| it.into()).collect(),
),
self,
)
}
}
flycheck::Message::Progress(status) => {
let (state, message) = match status {
flycheck::Progress::Being => (Progress::Begin, None),
flycheck::Progress::DidCheckCrate(target) => {
(Progress::Report, Some(target))
}
flycheck::Progress::End => (Progress::End, None),
};
report_progress(self, "cargo check", state, message, None);
}
},
}
let state_changed = self.process_changes();
@@ -234,8 +327,7 @@ impl GlobalState {
)
}
}
}
Err("client exited without proper shutdown sequence")?
Ok(())
}
fn on_request(&mut self, request_received: Instant, req: Request) -> Result<()> {
@@ -461,96 +553,10 @@ pub(crate) enum Task {
Unit,
}
enum Event {
Lsp(lsp_server::Message),
Task(Task),
Vfs(vfs::loader::Message),
Flycheck(flycheck::Message),
}
impl fmt::Debug for Event {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let debug_verbose_not = |not: &Notification, f: &mut fmt::Formatter| {
f.debug_struct("Notification").field("method", &not.method).finish()
};
match self {
Event::Lsp(lsp_server::Message::Notification(not)) => {
if notification_is::<lsp_types::notification::DidOpenTextDocument>(not)
|| notification_is::<lsp_types::notification::DidChangeTextDocument>(not)
{
return debug_verbose_not(not, f);
}
}
Event::Task(Task::Respond(resp)) => {
return f
.debug_struct("Response")
.field("id", &resp.id)
.field("error", &resp.error)
.finish();
}
_ => (),
}
match self {
Event::Lsp(it) => fmt::Debug::fmt(it, f),
Event::Task(it) => fmt::Debug::fmt(it, f),
Event::Vfs(it) => fmt::Debug::fmt(it, f),
Event::Flycheck(it) => fmt::Debug::fmt(it, f),
}
}
}
pub(crate) type ReqHandler = fn(&mut GlobalState, Response);
pub(crate) type ReqQueue = lsp_server::ReqQueue<(&'static str, Instant), ReqHandler>;
const DO_NOTHING: ReqHandler = |_, _| ();
fn on_check_task(task: flycheck::Message, global_state: &mut GlobalState) -> Result<()> {
match task {
flycheck::Message::ClearDiagnostics => {
on_diagnostic_task(DiagnosticTask::ClearCheck, global_state)
}
flycheck::Message::AddDiagnostic { workspace_root, diagnostic } => {
let diagnostics = crate::diagnostics::to_proto::map_rust_diagnostic_to_lsp(
&global_state.config.diagnostics,
&diagnostic,
&workspace_root,
);
for diag in diagnostics {
let path = from_proto::vfs_path(&diag.location.uri)?;
let file_id = match global_state.vfs.read().0.file_id(&path) {
Some(file) => FileId(file.0),
None => {
log::error!("File with cargo diagnostic not found in VFS: {}", path);
return Ok(());
}
};
on_diagnostic_task(
DiagnosticTask::AddCheck(
file_id,
diag.diagnostic,
diag.fixes.into_iter().map(|it| it.into()).collect(),
),
global_state,
)
}
}
flycheck::Message::Progress(status) => {
let (state, message) = match status {
flycheck::Progress::Being => (Progress::Begin, None),
flycheck::Progress::DidCheckCrate(target) => (Progress::Report, Some(target)),
flycheck::Progress::End => (Progress::End, None),
};
report_progress(global_state, "cargo check", state, message, None);
}
};
Ok(())
}
fn on_diagnostic_task(task: DiagnosticTask, global_state: &mut GlobalState) {
let subscriptions = global_state.diagnostics.handle_task(task);