From 7c67612b8a894187fa3b64725531a5459f9211bf Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 10 Aug 2018 22:33:29 +0300 Subject: organizize --- crates/server/src/main.rs | 249 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 249 insertions(+) create mode 100644 crates/server/src/main.rs (limited to 'crates/server/src/main.rs') diff --git a/crates/server/src/main.rs b/crates/server/src/main.rs new file mode 100644 index 000000000..116abce1c --- /dev/null +++ b/crates/server/src/main.rs @@ -0,0 +1,249 @@ +#[macro_use] +extern crate failure; +#[macro_use] +extern crate serde_derive; +extern crate serde; +extern crate serde_json; +extern crate languageserver_types; +extern crate drop_bomb; +#[macro_use] +extern crate crossbeam_channel; +extern crate threadpool; +#[macro_use] +extern crate log; +extern crate flexi_logger; +extern crate libeditor; +extern crate libanalysis; + +mod io; +mod caps; +mod req; +mod dispatch; +mod handlers; + +use std::path::PathBuf; + +use threadpool::ThreadPool; +use crossbeam_channel::{bounded, Sender, Receiver}; +use flexi_logger::Logger; +use libanalysis::WorldState; +use languageserver_types::{TextDocumentItem, VersionedTextDocumentIdentifier, TextDocumentIdentifier}; + +use ::{ + io::{Io, RawMsg}, + handlers::{handle_syntax_tree, handle_extend_selection}, +}; + +pub type Result = ::std::result::Result; + +fn main() -> Result<()> { + Logger::with_env_or_str("m=trace, libanalysis=trace") + .log_to_file() + .directory("log") + .start()?; + info!("starting server"); + match ::std::panic::catch_unwind(|| main_inner()) { + Ok(res) => { + info!("shutting down: {:?}", res); + res + } + Err(_) => { + error!("server panicked"); + bail!("server panicked") + } + } +} + +fn main_inner() -> Result<()> { + let mut io = Io::from_stdio(); + let res = initialize(&mut io); + info!("shutting down IO..."); + let io_res = io.stop(); + info!("... IO is down"); + match (res, io_res) { + (Ok(()), Ok(())) => Ok(()), + (res, Ok(())) => res, + (Ok(()), io_res) => io_res, + (res, Err(io_err)) => { + error!("shutdown error: {:?}", io_err); + res + } + } +} + +fn initialize(io: &mut Io) -> Result<()> { + loop { + match io.recv()? { + RawMsg::Request(req) => { + if let Some((_params, resp)) = dispatch::expect_request::(io, req)? { + resp.result(io, req::InitializeResult { + capabilities: caps::SERVER_CAPABILITIES + })?; + match io.recv()? { + RawMsg::Notification(n) => { + if n.method != "initialized" { + bail!("expected initialized notification"); + } + } + _ => { + bail!("expected initialized notification"); + } + } + return initialized(io); + } + } + RawMsg::Notification(n) => { + bail!("expected initialize request, got {:?}", n) + } + RawMsg::Response(res) => { + bail!("expected initialize request, got {:?}", res) + } + } + } +} + +type Thunk = Box FnBox<&'a mut Io, Result<()>>>; + +fn initialized(io: &mut Io) -> Result<()> { + let mut world = WorldState::new(); + let mut pool = ThreadPool::new(4); + let (sender, receiver) = bounded::(16); + let res = main_loop(io, &mut world, &mut pool, sender, receiver.clone()); + info!("waiting for background jobs to finish..."); + receiver.for_each(drop); + pool.join(); + info!("...background jobs have finished"); + res +} + +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), + Thunk(Thunk), + ReceiverDead, + } + + let event = select! { + recv(io.receiver(), msg) => match msg { + Some(msg) => Event::Msg(msg), + None => Event::ReceiverDead, + }, + recv(receiver, thunk) => Event::Thunk(thunk.unwrap()), + }; + + let msg = match event { + Event::ReceiverDead => { + io.cleanup_receiver()?; + unreachable!(); + } + Event::Thunk(thunk) => { + thunk.call_box(io)?; + continue; + } + Event::Msg(msg) => msg, + }; + + match msg { + RawMsg::Request(req) => { + let mut req = Some(req); + dispatch::handle_request::(&mut req, |params, resp| { + let world = world.snapshot(); + let sender = sender.clone(); + pool.execute(move || { + let res = handle_syntax_tree(world, params); + sender.send(Box::new(|io: &mut Io| resp.response(io, res))) + }); + Ok(()) + })?; + dispatch::handle_request::(&mut req, |params, resp| { + let world = world.snapshot(); + let sender = sender.clone(); + pool.execute(move || { + let res = handle_extend_selection(world, params); + sender.send(Box::new(|io: &mut Io| resp.response(io, res))) + }); + Ok(()) + })?; + dispatch::handle_request::(&mut req, |(), resp| { + resp.result(io, ())?; + 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)); + 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)); + Ok(()) + })?; + dispatch::handle_notification::(&mut not, |params| { + let path = params.text_document.file_path()?; + world.change_overlay(path, None); + Ok(()) + })?; + + if let Some(not) = not { + error!("unhandled notification: {:?}", not) + } + } + msg => { + eprintln!("msg = {:?}", msg); + } + } + } +} + +trait FnBox: Send { + fn call_box(self: Box, a: A) -> R; +} + +impl R + Send> FnBox for F { + fn call_box(self: Box, a: A) -> R { + (*self)(a) + } +} + +trait FilePath { + fn file_path(&self) -> Result; +} + +impl FilePath for TextDocumentItem { + fn file_path(&self) -> Result { + self.uri.to_file_path() + .map_err(|()| format_err!("invalid uri: {}", self.uri)) + } +} + +impl FilePath for VersionedTextDocumentIdentifier { + fn file_path(&self) -> Result { + self.uri.to_file_path() + .map_err(|()| format_err!("invalid uri: {}", self.uri)) + } +} + +impl FilePath for TextDocumentIdentifier { + fn file_path(&self) -> Result { + self.uri.to_file_path() + .map_err(|()| format_err!("invalid uri: {}", self.uri)) + } +} -- cgit v1.2.3