diff options
Diffstat (limited to 'helix-term/src/application.rs')
-rw-r--r-- | helix-term/src/application.rs | 196 |
1 files changed, 137 insertions, 59 deletions
diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs index 3d043441..ce43808a 100644 --- a/helix-term/src/application.rs +++ b/helix-term/src/application.rs @@ -1,7 +1,7 @@ -use helix_lsp::lsp; +use helix_lsp::{lsp, LspProgressMap}; use helix_view::{document::Mode, Document, Editor, Theme, View}; -use crate::{args::Args, compositor::Compositor, ui}; +use crate::{args::Args, compositor::Compositor, config::Config, ui}; use log::{error, info}; @@ -9,6 +9,7 @@ use std::{ future::Future, io::{self, stdout, Stdout, Write}, path::PathBuf, + pin::Pin, sync::Arc, time::Duration, }; @@ -22,8 +23,7 @@ use crossterm::{ use tui::layout::Rect; -use futures_util::stream::FuturesUnordered; -use std::pin::Pin; +use futures_util::{future, stream::FuturesUnordered}; type BoxFuture<T> = Pin<Box<dyn Future<Output = T> + Send>>; pub type LspCallback = @@ -37,16 +37,20 @@ pub struct Application { editor: Editor, callbacks: LspCallbacks, + + lsp_progress: LspProgressMap, + lsp_progress_enabled: bool, } impl Application { - pub fn new(mut args: Args) -> Result<Self, Error> { + pub fn new(mut args: Args, config: Config) -> Result<Self, Error> { use helix_view::editor::Action; let mut compositor = Compositor::new()?; let size = compositor.size(); let mut editor = Editor::new(size); - compositor.push(Box::new(ui::EditorView::new())); + let mut editor_view = Box::new(ui::EditorView::new(config.keys)); + compositor.push(editor_view); if !args.files.is_empty() { let first = &args.files[0]; // we know it's not empty @@ -73,6 +77,8 @@ impl Application { editor, callbacks: FuturesUnordered::new(), + lsp_progress: LspProgressMap::new(), + lsp_progress_enabled: config.global.lsp_progress, }; Ok(app) @@ -108,8 +114,8 @@ impl Application { event = reader.next() => { self.handle_terminal_events(event) } - Some(call) = self.editor.language_servers.incoming.next() => { - self.handle_language_server_message(call).await + Some((id, call)) = self.editor.language_servers.incoming.next() => { + self.handle_language_server_message(call, id).await } Some(callback) = &mut self.callbacks.next() => { self.handle_language_server_callback(callback) @@ -152,8 +158,12 @@ impl Application { } } - pub async fn handle_language_server_message(&mut self, call: helix_lsp::Call) { - use helix_lsp::{Call, Notification}; + pub async fn handle_language_server_message( + &mut self, + call: helix_lsp::Call, + server_id: usize, + ) { + use helix_lsp::{Call, MethodCall, Notification}; match call { Call::Notification(helix_lsp::jsonrpc::Notification { method, params, .. }) => { let notification = match Notification::parse(&method, params) { @@ -241,64 +251,130 @@ impl Application { log::warn!("unhandled window/logMessage: {:?}", params); } Notification::ProgressMessage(params) => { - let token = match params.token { - lsp::NumberOrString::Number(n) => n.to_string(), + let lsp::ProgressParams { token, value } = params; + + let lsp::ProgressParamsValue::WorkDone(work) = value; + let parts = match &work { + lsp::WorkDoneProgress::Begin(lsp::WorkDoneProgressBegin { + title, + message, + percentage, + .. + }) => (Some(title), message, percentage), + lsp::WorkDoneProgress::Report(lsp::WorkDoneProgressReport { + message, + percentage, + .. + }) => (None, message, percentage), + lsp::WorkDoneProgress::End(lsp::WorkDoneProgressEnd { message }) => { + if message.is_some() { + (None, message, &None) + } else { + self.lsp_progress.end_progress(server_id, &token); + self.editor.clear_status(); + return; + } + } + }; + let token_d: &dyn std::fmt::Display = match &token { + lsp::NumberOrString::Number(n) => n, lsp::NumberOrString::String(s) => s, }; - let msg = { - let lsp::ProgressParamsValue::WorkDone(work) = params.value; - let parts = match work { - lsp::WorkDoneProgress::Begin(lsp::WorkDoneProgressBegin { - title, - message, - percentage, - .. - }) => (Some(title), message, percentage.map(|n| n.to_string())), - lsp::WorkDoneProgress::Report(lsp::WorkDoneProgressReport { - message, - percentage, - .. - }) => (None, message, percentage.map(|n| n.to_string())), - lsp::WorkDoneProgress::End(lsp::WorkDoneProgressEnd { - message, - }) => { - if let Some(message) = message { - (None, Some(message), None) - } else { - self.editor.clear_status(); - return; - } - } - }; - match parts { - (Some(title), Some(message), Some(percentage)) => { - format!("{}% {} - {}", percentage, title, message) - } - (Some(title), None, Some(percentage)) => { - format!("{}% {}", percentage, title) - } - (Some(title), Some(message), None) => { - format!("{} - {}", title, message) - } - (None, Some(message), Some(percentage)) => { - format!("{}% {}", percentage, message) - } - (Some(title), None, None) => title, - (None, Some(message), None) => message, - (None, None, Some(percentage)) => format!("{}%", percentage), - (None, None, None) => "".into(), + + let status = match parts { + (Some(title), Some(message), Some(percentage)) => { + format!("[{}] {}% {} - {}", token_d, percentage, title, message) + } + (Some(title), None, Some(percentage)) => { + format!("[{}] {}% {}", token_d, percentage, title) + } + (Some(title), Some(message), None) => { + format!("[{}] {} - {}", token_d, title, message) + } + (None, Some(message), Some(percentage)) => { + format!("[{}] {}% {}", token_d, percentage, message) + } + (Some(title), None, None) => { + format!("[{}] {}", token_d, title) } + (None, Some(message), None) => { + format!("[{}] {}", token_d, message) + } + (None, None, Some(percentage)) => { + format!("[{}] {}%", token_d, percentage) + } + (None, None, None) => format!("[{}]", token_d), }; - let status = format!("[{}] {}", token, msg); - self.editor.set_status(status); - self.render(); + + if let lsp::WorkDoneProgress::End(_) = work { + self.lsp_progress.end_progress(server_id, &token); + } else { + self.lsp_progress.update(server_id, token, work); + } + + if self.lsp_progress_enabled { + self.editor.set_status(status); + self.render(); + } } _ => unreachable!(), } } - Call::MethodCall(call) => { - error!("Method not found {}", call.method); + Call::MethodCall(helix_lsp::jsonrpc::MethodCall { + method, + params, + jsonrpc, + id, + }) => { + let call = match MethodCall::parse(&method, params) { + Some(call) => call, + None => { + error!("Method not found {}", method); + return; + } + }; + match call { + MethodCall::WorkDoneProgressCreate(params) => { + self.lsp_progress.create(server_id, params.token); + + let doc = self.editor.documents().find(|doc| { + doc.language_server() + .map(|server| server.id() == server_id) + .unwrap_or_default() + }); + match doc { + Some(doc) => { + // it's ok to unwrap, we check for the language server before + let server = doc.language_server().unwrap(); + tokio::spawn(server.reply(id, Ok(serde_json::Value::Null))); + } + None => { + if let Some(server) = + self.editor.language_servers.get_by_id(server_id) + { + log::warn!( + "missing document with language server id `{}`", + server_id + ); + tokio::spawn(server.reply( + id, + Err(helix_lsp::jsonrpc::Error { + code: helix_lsp::jsonrpc::ErrorCode::InternalError, + message: "document missing".to_string(), + data: None, + }), + )); + } else { + log::warn!( + "can't find language server with id `{}`", + server_id + ); + } + } + } + } + } // self.language_server.reply( // call.id, // // TODO: make a Into trait that can cast to Err(jsonrpc::Error) @@ -330,6 +406,8 @@ impl Application { self.event_loop().await; + self.editor.close_language_servers(None).await; + // reset cursor shape write!(stdout, "\x1B[2 q"); |