From 196236980613249f25ccb2968a214922f7db10f1 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 12 Aug 2018 22:08:14 +0300 Subject: more modules --- crates/server/src/handlers.rs | 137 ---------------------- crates/server/src/main.rs | 184 +---------------------------- crates/server/src/main_loop/handlers.rs | 137 ++++++++++++++++++++++ crates/server/src/main_loop/mod.rs | 201 ++++++++++++++++++++++++++++++++ 4 files changed, 343 insertions(+), 316 deletions(-) delete mode 100644 crates/server/src/handlers.rs create mode 100644 crates/server/src/main_loop/handlers.rs create mode 100644 crates/server/src/main_loop/mod.rs diff --git a/crates/server/src/handlers.rs b/crates/server/src/handlers.rs deleted file mode 100644 index c6db22289..000000000 --- a/crates/server/src/handlers.rs +++ /dev/null @@ -1,137 +0,0 @@ -use languageserver_types::{ - Diagnostic, DiagnosticSeverity, Url, DocumentSymbol, - Command -}; -use libanalysis::World; -use libeditor; -use serde_json::to_value; - -use ::{ - req::{self, Decoration}, Result, - util::FilePath, - conv::{Conv, ConvWith}, -}; - -pub fn handle_syntax_tree( - world: World, - params: req::SyntaxTreeParams, -) -> Result { - let path = params.text_document.file_path()?; - let file = world.file_syntax(&path)?; - Ok(libeditor::syntax_tree(&file)) -} - -pub fn handle_extend_selection( - world: World, - params: req::ExtendSelectionParams, -) -> Result { - let path = params.text_document.file_path()?; - let file = world.file_syntax(&path)?; - let line_index = world.file_line_index(&path)?; - let selections = params.selections.into_iter() - .map(|r| r.conv_with(&line_index)) - .map(|r| libeditor::extend_selection(&file, r).unwrap_or(r)) - .map(|r| r.conv_with(&line_index)) - .collect(); - Ok(req::ExtendSelectionResult { selections }) -} - -pub fn handle_document_symbol( - world: World, - params: req::DocumentSymbolParams, -) -> Result> { - let path = params.text_document.file_path()?; - let file = world.file_syntax(&path)?; - let line_index = world.file_line_index(&path)?; - - let mut res: Vec = Vec::new(); - - for symbol in libeditor::file_symbols(&file) { - let doc_symbol = DocumentSymbol { - name: symbol.name.clone(), - detail: Some(symbol.name), - kind: symbol.kind.conv(), - deprecated: None, - range: symbol.node_range.conv_with(&line_index), - selection_range: symbol.name_range.conv_with(&line_index), - children: None, - }; - if let Some(idx) = symbol.parent { - let children = &mut res[idx].children; - if children.is_none() { - *children = Some(Vec::new()); - } - children.as_mut().unwrap().push(doc_symbol); - } else { - res.push(doc_symbol); - } - } - Ok(Some(req::DocumentSymbolResponse::Nested(res))) -} - -pub fn handle_code_action( - world: World, - params: req::CodeActionParams, -) -> Result>> { - let path = params.text_document.file_path()?; - let file = world.file_syntax(&path)?; - let line_index = world.file_line_index(&path)?; - let offset = params.range.conv_with(&line_index).start(); - let ret = if libeditor::flip_comma(&file, offset).is_some() { - Some(vec![apply_code_action_cmd(ActionId::FlipComma)]) - } else { - None - }; - Ok(ret) -} - -fn apply_code_action_cmd(id: ActionId) -> Command { - Command { - title: id.title().to_string(), - command: "apply_code_action".to_string(), - arguments: Some(vec![to_value(id).unwrap()]), - } -} - -#[derive(Serialize, Deserialize, Clone, Copy)] -enum ActionId { - FlipComma -} - -impl ActionId { - fn title(&self) -> &'static str { - match *self { - ActionId::FlipComma => "Flip `,`", - } - } -} - -pub fn publish_diagnostics(world: World, uri: Url) -> Result { - let path = uri.file_path()?; - let file = world.file_syntax(&path)?; - let line_index = world.file_line_index(&path)?; - let diagnostics = libeditor::diagnostics(&file) - .into_iter() - .map(|d| Diagnostic { - range: d.range.conv_with(&line_index), - severity: Some(DiagnosticSeverity::Error), - code: None, - source: Some("libsyntax2".to_string()), - message: d.msg, - related_information: None, - }).collect(); - Ok(req::PublishDiagnosticsParams { uri, diagnostics }) -} - -pub fn publish_decorations(world: World, uri: Url) -> Result { - let path = uri.file_path()?; - let file = world.file_syntax(&path)?; - let line_index = world.file_line_index(&path)?; - let decorations = libeditor::highlight(&file) - .into_iter() - .map(|h| Decoration { - range: h.range.conv_with(&line_index), - tag: h.tag, - }).collect(); - Ok(req::PublishDecorationsParams { uri, decorations }) -} diff --git a/crates/server/src/main.rs b/crates/server/src/main.rs index fe94e901e..fccfe9b3b 100644 --- a/crates/server/src/main.rs +++ b/crates/server/src/main.rs @@ -21,21 +21,17 @@ mod io; mod caps; mod req; mod dispatch; -mod handlers; mod util; mod conv; +mod main_loop; use threadpool::ThreadPool; -use crossbeam_channel::{bounded, Sender, Receiver}; +use crossbeam_channel::bounded; use flexi_logger::Logger; -use languageserver_types::Url; -use libanalysis::{WorldState, World}; +use libanalysis::WorldState; use ::{ - io::{Io, RawMsg, RawRequest, RawResponse, RawNotification}, - handlers::{handle_syntax_tree, handle_extend_selection, publish_diagnostics, publish_decorations, - handle_document_symbol, handle_code_action}, - util::FilePath, + io::{Io, RawMsg, RawResponse, RawNotification} }; pub type Result = ::std::result::Result; @@ -121,7 +117,7 @@ fn initialized(io: &mut Io) -> Result<()> { let mut pool = ThreadPool::new(4); let (sender, receiver) = bounded::(16); info!("lifecycle: handshake finished, server ready to serve requests"); - let res = main_loop(io, &mut world, &mut pool, sender, receiver.clone()); + let res = main_loop::main_loop(io, &mut world, &mut pool, sender, receiver.clone()); info!("waiting for background jobs to finish..."); receiver.for_each(drop); pool.join(); @@ -142,173 +138,3 @@ fn initialized(io: &mut Io) -> Result<()> { } } } - -fn main_loop( - io: &mut Io, - world: &mut WorldState, - pool: &mut ThreadPool, - sender: Sender, - receiver: Receiver, -) -> Result<()> { - info!("server initialized, serving requests"); - loop { - enum Event { - Msg(RawMsg), - Task(Task), - ReceiverDead, - } - - let event = select! { - recv(io.receiver(), msg) => match msg { - Some(msg) => Event::Msg(msg), - None => Event::ReceiverDead, - }, - recv(receiver, task) => Event::Task(task.unwrap()), - }; - - let msg = match event { - Event::ReceiverDead => { - io.cleanup_receiver()?; - unreachable!(); - } - Event::Task(task) => { - match task { - Task::Respond(response) => - io.send(RawMsg::Response(response)), - Task::Notify(n) => - io.send(RawMsg::Notification(n)), - Task::Die(error) => - return Err(error), - } - continue; - } - Event::Msg(msg) => msg, - }; - - match msg { - RawMsg::Request(req) => { - let mut req = Some(req); - handle_request_on_threadpool::( - &mut req, pool, world, &sender, handle_syntax_tree, - )?; - handle_request_on_threadpool::( - &mut req, pool, world, &sender, handle_extend_selection, - )?; - handle_request_on_threadpool::( - &mut req, pool, world, &sender, handle_document_symbol, - )?; - handle_request_on_threadpool::( - &mut req, pool, world, &sender, handle_code_action, - )?; - - let mut shutdown = false; - dispatch::handle_request::(&mut req, |(), resp| { - let resp = resp.into_response(Ok(()))?; - io.send(RawMsg::Response(resp)); - shutdown = true; - Ok(()) - })?; - if shutdown { - info!("lifecycle: initiating shutdown"); - drop(sender); - return Ok(()); - } - if let Some(req) = req { - error!("unknown method: {:?}", req); - dispatch::unknown_method(io, req)?; - } - } - RawMsg::Notification(not) => { - let mut not = Some(not); - dispatch::handle_notification::(&mut not, |params| { - let path = params.text_document.file_path()?; - world.change_overlay(path, Some(params.text_document.text)); - update_file_notifications_on_threadpool( - pool, world.snapshot(), sender.clone(), params.text_document.uri, - ); - Ok(()) - })?; - dispatch::handle_notification::(&mut not, |mut params| { - let path = params.text_document.file_path()?; - let text = params.content_changes.pop() - .ok_or_else(|| format_err!("empty changes"))? - .text; - world.change_overlay(path, Some(text)); - update_file_notifications_on_threadpool( - pool, world.snapshot(), sender.clone(), params.text_document.uri, - ); - Ok(()) - })?; - dispatch::handle_notification::(&mut not, |params| { - let path = params.text_document.file_path()?; - world.change_overlay(path, None); - let not = req::PublishDiagnosticsParams { - uri: params.text_document.uri, - diagnostics: Vec::new(), - }; - let not = dispatch::send_notification::(not); - io.send(RawMsg::Notification(not)); - Ok(()) - })?; - - if let Some(not) = not { - error!("unhandled notification: {:?}", not) - } - } - msg => { - eprintln!("msg = {:?}", msg); - } - } - } -} - -fn handle_request_on_threadpool( - req: &mut Option, - pool: &ThreadPool, - world: &WorldState, - sender: &Sender, - f: fn(World, R::Params) -> Result, -) -> Result<()> -{ - dispatch::handle_request::(req, |params, resp| { - let world = world.snapshot(); - let sender = sender.clone(); - pool.execute(move || { - let res = f(world, params); - let task = match resp.into_response(res) { - Ok(resp) => Task::Respond(resp), - Err(e) => Task::Die(e), - }; - sender.send(task); - }); - Ok(()) - }) -} - -fn update_file_notifications_on_threadpool( - pool: &ThreadPool, - world: World, - sender: Sender, - uri: Url, -) { - pool.execute(move || { - match publish_diagnostics(world.clone(), uri.clone()) { - Err(e) => { - error!("failed to compute diagnostics: {:?}", e) - } - Ok(params) => { - let not = dispatch::send_notification::(params); - sender.send(Task::Notify(not)); - } - } - match publish_decorations(world, uri) { - Err(e) => { - error!("failed to compute decorations: {:?}", e) - } - Ok(params) => { - let not = dispatch::send_notification::(params); - sender.send(Task::Notify(not)) - } - } - }); -} diff --git a/crates/server/src/main_loop/handlers.rs b/crates/server/src/main_loop/handlers.rs new file mode 100644 index 000000000..c6db22289 --- /dev/null +++ b/crates/server/src/main_loop/handlers.rs @@ -0,0 +1,137 @@ +use languageserver_types::{ + Diagnostic, DiagnosticSeverity, Url, DocumentSymbol, + Command +}; +use libanalysis::World; +use libeditor; +use serde_json::to_value; + +use ::{ + req::{self, Decoration}, Result, + util::FilePath, + conv::{Conv, ConvWith}, +}; + +pub fn handle_syntax_tree( + world: World, + params: req::SyntaxTreeParams, +) -> Result { + let path = params.text_document.file_path()?; + let file = world.file_syntax(&path)?; + Ok(libeditor::syntax_tree(&file)) +} + +pub fn handle_extend_selection( + world: World, + params: req::ExtendSelectionParams, +) -> Result { + let path = params.text_document.file_path()?; + let file = world.file_syntax(&path)?; + let line_index = world.file_line_index(&path)?; + let selections = params.selections.into_iter() + .map(|r| r.conv_with(&line_index)) + .map(|r| libeditor::extend_selection(&file, r).unwrap_or(r)) + .map(|r| r.conv_with(&line_index)) + .collect(); + Ok(req::ExtendSelectionResult { selections }) +} + +pub fn handle_document_symbol( + world: World, + params: req::DocumentSymbolParams, +) -> Result> { + let path = params.text_document.file_path()?; + let file = world.file_syntax(&path)?; + let line_index = world.file_line_index(&path)?; + + let mut res: Vec = Vec::new(); + + for symbol in libeditor::file_symbols(&file) { + let doc_symbol = DocumentSymbol { + name: symbol.name.clone(), + detail: Some(symbol.name), + kind: symbol.kind.conv(), + deprecated: None, + range: symbol.node_range.conv_with(&line_index), + selection_range: symbol.name_range.conv_with(&line_index), + children: None, + }; + if let Some(idx) = symbol.parent { + let children = &mut res[idx].children; + if children.is_none() { + *children = Some(Vec::new()); + } + children.as_mut().unwrap().push(doc_symbol); + } else { + res.push(doc_symbol); + } + } + Ok(Some(req::DocumentSymbolResponse::Nested(res))) +} + +pub fn handle_code_action( + world: World, + params: req::CodeActionParams, +) -> Result>> { + let path = params.text_document.file_path()?; + let file = world.file_syntax(&path)?; + let line_index = world.file_line_index(&path)?; + let offset = params.range.conv_with(&line_index).start(); + let ret = if libeditor::flip_comma(&file, offset).is_some() { + Some(vec![apply_code_action_cmd(ActionId::FlipComma)]) + } else { + None + }; + Ok(ret) +} + +fn apply_code_action_cmd(id: ActionId) -> Command { + Command { + title: id.title().to_string(), + command: "apply_code_action".to_string(), + arguments: Some(vec![to_value(id).unwrap()]), + } +} + +#[derive(Serialize, Deserialize, Clone, Copy)] +enum ActionId { + FlipComma +} + +impl ActionId { + fn title(&self) -> &'static str { + match *self { + ActionId::FlipComma => "Flip `,`", + } + } +} + +pub fn publish_diagnostics(world: World, uri: Url) -> Result { + let path = uri.file_path()?; + let file = world.file_syntax(&path)?; + let line_index = world.file_line_index(&path)?; + let diagnostics = libeditor::diagnostics(&file) + .into_iter() + .map(|d| Diagnostic { + range: d.range.conv_with(&line_index), + severity: Some(DiagnosticSeverity::Error), + code: None, + source: Some("libsyntax2".to_string()), + message: d.msg, + related_information: None, + }).collect(); + Ok(req::PublishDiagnosticsParams { uri, diagnostics }) +} + +pub fn publish_decorations(world: World, uri: Url) -> Result { + let path = uri.file_path()?; + let file = world.file_syntax(&path)?; + let line_index = world.file_line_index(&path)?; + let decorations = libeditor::highlight(&file) + .into_iter() + .map(|h| Decoration { + range: h.range.conv_with(&line_index), + tag: h.tag, + }).collect(); + Ok(req::PublishDecorationsParams { uri, decorations }) +} diff --git a/crates/server/src/main_loop/mod.rs b/crates/server/src/main_loop/mod.rs new file mode 100644 index 000000000..e7b24e53f --- /dev/null +++ b/crates/server/src/main_loop/mod.rs @@ -0,0 +1,201 @@ +mod handlers; + +use threadpool::ThreadPool; +use crossbeam_channel::{Sender, Receiver}; +use languageserver_types::Url; +use libanalysis::{World, WorldState}; +use { + req, dispatch, + Task, Result, + io::{Io, RawMsg, RawRequest}, + util::FilePath, + main_loop::handlers::{ + handle_syntax_tree, + handle_extend_selection, + publish_diagnostics, + publish_decorations, + handle_document_symbol, + handle_code_action, + }, +}; + +pub(super) fn main_loop( + io: &mut Io, + world: &mut WorldState, + pool: &mut ThreadPool, + sender: Sender, + receiver: Receiver, +) -> Result<()> { + info!("server initialized, serving requests"); + loop { + enum Event { + Msg(RawMsg), + Task(Task), + ReceiverDead, + } + let event = select! { + recv(io.receiver(), msg) => match msg { + Some(msg) => Event::Msg(msg), + None => Event::ReceiverDead, + }, + recv(receiver, task) => Event::Task(task.unwrap()), + }; + + match event { + Event::ReceiverDead => { + io.cleanup_receiver()?; + unreachable!(); + } + Event::Task(task) => { + match task { + Task::Respond(response) => + io.send(RawMsg::Response(response)), + Task::Notify(n) => + io.send(RawMsg::Notification(n)), + Task::Die(error) => + return Err(error), + } + continue; + } + Event::Msg(msg) => { + if !on_msg(io, world, pool, &sender, msg)? { + return Ok(()); + } + } + }; + } +} + +fn on_msg( + io: &mut Io, + world: &mut WorldState, + pool: &mut ThreadPool, + sender: &Sender, + msg: RawMsg, +) -> Result { + match msg { + RawMsg::Request(req) => { + let mut req = Some(req); + handle_request_on_threadpool::( + &mut req, pool, world, sender, handle_syntax_tree, + )?; + handle_request_on_threadpool::( + &mut req, pool, world, sender, handle_extend_selection, + )?; + handle_request_on_threadpool::( + &mut req, pool, world, sender, handle_document_symbol, + )?; + handle_request_on_threadpool::( + &mut req, pool, world, sender, handle_code_action, + )?; + + let mut shutdown = false; + dispatch::handle_request::(&mut req, |(), resp| { + let resp = resp.into_response(Ok(()))?; + io.send(RawMsg::Response(resp)); + shutdown = true; + Ok(()) + })?; + if shutdown { + info!("lifecycle: initiating shutdown"); + return Ok(false); + } + if let Some(req) = req { + error!("unknown method: {:?}", req); + dispatch::unknown_method(io, req)?; + } + } + RawMsg::Notification(not) => { + let mut not = Some(not); + dispatch::handle_notification::(&mut not, |params| { + let path = params.text_document.file_path()?; + world.change_overlay(path, Some(params.text_document.text)); + update_file_notifications_on_threadpool( + pool, world.snapshot(), sender.clone(), params.text_document.uri, + ); + Ok(()) + })?; + dispatch::handle_notification::(&mut not, |mut params| { + let path = params.text_document.file_path()?; + let text = params.content_changes.pop() + .ok_or_else(|| format_err!("empty changes"))? + .text; + world.change_overlay(path, Some(text)); + update_file_notifications_on_threadpool( + pool, world.snapshot(), sender.clone(), params.text_document.uri, + ); + Ok(()) + })?; + dispatch::handle_notification::(&mut not, |params| { + let path = params.text_document.file_path()?; + world.change_overlay(path, None); + let not = req::PublishDiagnosticsParams { + uri: params.text_document.uri, + diagnostics: Vec::new(), + }; + let not = dispatch::send_notification::(not); + io.send(RawMsg::Notification(not)); + Ok(()) + })?; + + if let Some(not) = not { + error!("unhandled notification: {:?}", not) + } + } + msg => { + eprintln!("msg = {:?}", msg); + } + }; + Ok(true) +} + +fn handle_request_on_threadpool( + req: &mut Option, + pool: &ThreadPool, + world: &WorldState, + sender: &Sender, + f: fn(World, R::Params) -> Result, +) -> Result<()> +{ + dispatch::handle_request::(req, |params, resp| { + let world = world.snapshot(); + let sender = sender.clone(); + pool.execute(move || { + let res = f(world, params); + let task = match resp.into_response(res) { + Ok(resp) => Task::Respond(resp), + Err(e) => Task::Die(e), + }; + sender.send(task); + }); + Ok(()) + }) +} + +fn update_file_notifications_on_threadpool( + pool: &ThreadPool, + world: World, + sender: Sender, + uri: Url, +) { + pool.execute(move || { + match publish_diagnostics(world.clone(), uri.clone()) { + Err(e) => { + error!("failed to compute diagnostics: {:?}", e) + } + Ok(params) => { + let not = dispatch::send_notification::(params); + sender.send(Task::Notify(not)); + } + } + match publish_decorations(world, uri) { + Err(e) => { + error!("failed to compute decorations: {:?}", e) + } + Ok(params) => { + let not = dispatch::send_notification::(params); + sender.send(Task::Notify(not)) + } + } + }); +} -- cgit v1.2.3