138 lines
3.7 KiB
Rust
138 lines
3.7 KiB
Rust
#[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 url_serde;
|
|
extern crate flexi_logger;
|
|
extern crate libeditor;
|
|
extern crate libanalysis;
|
|
extern crate libsyntax2;
|
|
|
|
mod io;
|
|
mod caps;
|
|
mod req;
|
|
mod dispatch;
|
|
mod util;
|
|
mod conv;
|
|
mod main_loop;
|
|
|
|
use threadpool::ThreadPool;
|
|
use crossbeam_channel::bounded;
|
|
use flexi_logger::Logger;
|
|
use libanalysis::WorldState;
|
|
|
|
use ::{
|
|
io::{Io, RawMsg, RawResponse, RawNotification}
|
|
};
|
|
|
|
pub type Result<T> = ::std::result::Result<T, ::failure::Error>;
|
|
|
|
fn main() -> Result<()> {
|
|
Logger::with_env()
|
|
.log_to_file()
|
|
.directory("log")
|
|
.start()?;
|
|
info!("lifecycle: server started");
|
|
match ::std::panic::catch_unwind(|| main_inner()) {
|
|
Ok(res) => {
|
|
info!("lifecycle: terminating process with {:?}", 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<()> {
|
|
match io.recv()? {
|
|
RawMsg::Notification(n) =>
|
|
bail!("expected initialize request, got {:?}", n),
|
|
RawMsg::Response(res) =>
|
|
bail!("expected initialize request, got {:?}", res),
|
|
|
|
RawMsg::Request(req) => {
|
|
let mut req = Some(req);
|
|
dispatch::handle_request::<req::Initialize, _>(&mut req, |_params, resp| {
|
|
let res = req::InitializeResult { capabilities: caps::SERVER_CAPABILITIES };
|
|
let resp = resp.into_response(Ok(res))?;
|
|
io.send(RawMsg::Response(resp));
|
|
Ok(())
|
|
})?;
|
|
if let Some(req) = req {
|
|
bail!("expected initialize request, got {:?}", req)
|
|
}
|
|
match io.recv()? {
|
|
RawMsg::Notification(n) => {
|
|
if n.method != "initialized" {
|
|
bail!("expected initialized notification");
|
|
}
|
|
}
|
|
_ => bail!("expected initialized notification"),
|
|
}
|
|
}
|
|
}
|
|
initialized(io)
|
|
}
|
|
|
|
enum Task {
|
|
Respond(RawResponse),
|
|
Notify(RawNotification),
|
|
Die(::failure::Error),
|
|
}
|
|
|
|
fn initialized(io: &mut Io) -> Result<()> {
|
|
{
|
|
let mut world = WorldState::new();
|
|
let mut pool = ThreadPool::new(4);
|
|
let (sender, receiver) = bounded::<Task>(16);
|
|
info!("lifecycle: handshake finished, server ready to serve requests");
|
|
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();
|
|
info!("...background jobs have finished");
|
|
res
|
|
}?;
|
|
|
|
match io.recv()? {
|
|
RawMsg::Notification(n) => {
|
|
if n.method == "exit" {
|
|
info!("lifecycle: shutdown complete");
|
|
return Ok(());
|
|
}
|
|
bail!("unexpected notification during shutdown: {:?}", n)
|
|
}
|
|
m => {
|
|
bail!("unexpected message during shutdown: {:?}", m)
|
|
}
|
|
}
|
|
}
|