From 7c67612b8a894187fa3b64725531a5459f9211bf Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 10 Aug 2018 22:33:29 +0300 Subject: organizize --- codeless/server/Cargo.toml | 18 --- codeless/server/src/caps.rs | 36 ------ codeless/server/src/dispatch.rs | 174 ---------------------------- codeless/server/src/handlers.rs | 61 ---------- codeless/server/src/io.rs | 202 -------------------------------- codeless/server/src/main.rs | 249 ---------------------------------------- codeless/server/src/req.rs | 41 ------- 7 files changed, 781 deletions(-) delete mode 100644 codeless/server/Cargo.toml delete mode 100644 codeless/server/src/caps.rs delete mode 100644 codeless/server/src/dispatch.rs delete mode 100644 codeless/server/src/handlers.rs delete mode 100644 codeless/server/src/io.rs delete mode 100644 codeless/server/src/main.rs delete mode 100644 codeless/server/src/req.rs (limited to 'codeless/server') diff --git a/codeless/server/Cargo.toml b/codeless/server/Cargo.toml deleted file mode 100644 index f5c32b878..000000000 --- a/codeless/server/Cargo.toml +++ /dev/null @@ -1,18 +0,0 @@ -[package] -name = "m" -version = "0.1.0" -authors = ["Aleksey Kladov "] - -[dependencies] -failure = "0.1.2" -languageserver-types = "0.48.0" -serde_json = "1.0.24" -serde = "1.0.71" -serde_derive = "1.0.71" -drop_bomb = "0.1.0" -crossbeam-channel = "0.2.4" -threadpool = "1.7.1" -flexi_logger = "0.9.0" -log = "0.4.3" -libeditor = { path = "../../libeditor" } -libanalysis = { path = "../../libanalysis" } diff --git a/codeless/server/src/caps.rs b/codeless/server/src/caps.rs deleted file mode 100644 index 3d89c64a9..000000000 --- a/codeless/server/src/caps.rs +++ /dev/null @@ -1,36 +0,0 @@ -use languageserver_types::{ - ServerCapabilities, - TextDocumentSyncCapability, - TextDocumentSyncOptions, - TextDocumentSyncKind, -}; - -pub const SERVER_CAPABILITIES: ServerCapabilities = ServerCapabilities { - text_document_sync: Some(TextDocumentSyncCapability::Options( - TextDocumentSyncOptions { - open_close: Some(true), - change: Some(TextDocumentSyncKind::Full), - will_save: None, - will_save_wait_until: None, - save: None, - } - )), - hover_provider: None, - completion_provider: None, - signature_help_provider: None, - definition_provider: None, - type_definition_provider: None, - implementation_provider: None, - references_provider: None, - document_highlight_provider: None, - document_symbol_provider: None, - workspace_symbol_provider: None, - code_action_provider: None, - code_lens_provider: None, - document_formatting_provider: None, - document_range_formatting_provider: None, - document_on_type_formatting_provider: None, - rename_provider: None, - color_provider: None, - execute_command_provider: None, -}; diff --git a/codeless/server/src/dispatch.rs b/codeless/server/src/dispatch.rs deleted file mode 100644 index 2da0996e3..000000000 --- a/codeless/server/src/dispatch.rs +++ /dev/null @@ -1,174 +0,0 @@ -use std::marker::PhantomData; - -use serde::{ - ser::Serialize, - de::DeserializeOwned, -}; -use serde_json; -use drop_bomb::DropBomb; - -use ::{ - Result, - req::{Request, Notification}, - io::{Io, RawMsg, RawResponse, RawRequest, RawNotification}, -}; - -pub struct Responder { - id: u64, - bomb: DropBomb, - ph: PhantomData, -} - -impl Responder - where - R::Params: DeserializeOwned, - R::Result: Serialize, -{ - pub fn response(self, io: &mut Io, resp: Result) -> Result<()> { - match resp { - Ok(res) => self.result(io, res)?, - Err(e) => { - self.error(io)?; - return Err(e); - } - } - Ok(()) - } - - pub fn result(mut self, io: &mut Io, result: R::Result) -> Result<()> { - self.bomb.defuse(); - io.send(RawMsg::Response(RawResponse { - id: Some(self.id), - result: serde_json::to_value(result)?, - error: serde_json::Value::Null, - })); - Ok(()) - } - - pub fn error(mut self, io: &mut Io) -> Result<()> { - self.bomb.defuse(); - error(io, self.id, ErrorCode::InternalError, "internal error") - } -} - - -fn parse_request_as(raw: RawRequest) -> Result<::std::result::Result<(R::Params, Responder), RawRequest>> - where - R: Request, - R::Params: DeserializeOwned, - R::Result: Serialize, -{ - if raw.method != R::METHOD { - return Ok(Err(raw)); - } - - let params: R::Params = serde_json::from_value(raw.params)?; - let responder = Responder { - id: raw.id, - bomb: DropBomb::new("dropped request"), - ph: PhantomData, - }; - Ok(Ok((params, responder))) -} - -pub fn handle_request(req: &mut Option, f: F) -> Result<()> - where - R: Request, - R::Params: DeserializeOwned, - R::Result: Serialize, - F: FnOnce(R::Params, Responder) -> Result<()> -{ - match req.take() { - None => Ok(()), - Some(r) => match parse_request_as::(r)? { - Ok((params, responder)) => f(params, responder), - Err(r) => { - *req = Some(r); - Ok(()) - }, - } - } -} - -pub fn expect_request(io: &mut Io, raw: RawRequest) -> Result)>> - where - R: Request, - R::Params: DeserializeOwned, - R::Result: Serialize, -{ - let ret = match parse_request_as::(raw)? { - Ok(x) => Some(x), - Err(raw) => { - unknown_method(io, raw)?; - None - } - }; - Ok(ret) -} - -fn parse_notification_as(raw: RawNotification) -> Result<::std::result::Result> - where - N: Notification, - N::Params: DeserializeOwned, -{ - if raw.method != N::METHOD { - return Ok(Err(raw)); - } - let params: N::Params = serde_json::from_value(raw.params)?; - Ok(Ok(params)) -} - -pub fn handle_notification(not: &mut Option, f: F) -> Result<()> - where - N: Notification, - N::Params: DeserializeOwned, - F: FnOnce(N::Params) -> Result<()> -{ - match not.take() { - None => Ok(()), - Some(n) => match parse_notification_as::(n)? { - Ok(params) => f(params), - Err(n) => { - *not = Some(n); - Ok(()) - }, - } - } -} - - -pub fn unknown_method(io: &mut Io, raw: RawRequest) -> Result<()> { - error(io, raw.id, ErrorCode::MethodNotFound, "unknown method") -} - -fn error(io: &mut Io, id: u64, code: ErrorCode, message: &'static str) -> Result<()> { - #[derive(Serialize)] - struct Error { - code: i32, - message: &'static str, - } - io.send(RawMsg::Response(RawResponse { - id: Some(id), - result: serde_json::Value::Null, - error: serde_json::to_value(Error { - code: code as i32, - message, - })?, - })); - Ok(()) -} - - -#[allow(unused)] -enum ErrorCode { - ParseError = -32700, - InvalidRequest = -32600, - MethodNotFound = -32601, - InvalidParams = -32602, - InternalError = -32603, - ServerErrorStart = -32099, - ServerErrorEnd = -32000, - ServerNotInitialized = -32002, - UnknownErrorCode = -32001, - RequestCancelled = -32800, -} diff --git a/codeless/server/src/handlers.rs b/codeless/server/src/handlers.rs deleted file mode 100644 index 5ee87a4dd..000000000 --- a/codeless/server/src/handlers.rs +++ /dev/null @@ -1,61 +0,0 @@ -use languageserver_types::{Range, Position}; -use libanalysis::World; -use libeditor::{self, LineIndex, LineCol, TextRange, TextUnit}; -use {req, Result, FilePath}; - -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| { - let r = to_text_range(&line_index, r); - let r = libeditor::extend_selection(&file, r).unwrap_or(r); - to_vs_range(&line_index, r) - }) - .collect(); - Ok(req::ExtendSelectionResult { selections }) -} - - -fn to_text_range(line_index: &LineIndex, range: Range) -> TextRange { - TextRange::from_to( - to_text_unit(line_index, range.start), - to_text_unit(line_index, range.end), - ) -} - -fn to_text_unit(line_index: &LineIndex, position: Position) -> TextUnit { - // TODO: UTF-16 - let line_col = LineCol { - line: position.line as u32, - col: (position.character as u32).into(), - }; - line_index.offset(line_col) -} - - -fn to_vs_range(line_index: &LineIndex, range: TextRange) -> Range { - Range::new( - to_vs_position(line_index, range.start()), - to_vs_position(line_index, range.end()), - ) -} - -fn to_vs_position(line_index: &LineIndex, offset: TextUnit) -> Position { - let line_col = line_index.line_col(offset); - // TODO: UTF-16 - Position::new(line_col.line as u64, u32::from(line_col.col) as u64) -} diff --git a/codeless/server/src/io.rs b/codeless/server/src/io.rs deleted file mode 100644 index 5eafc6942..000000000 --- a/codeless/server/src/io.rs +++ /dev/null @@ -1,202 +0,0 @@ -use std::{ - thread, - io::{ - stdout, stdin, - BufRead, Write, - }, -}; -use serde_json::{Value, from_str, to_string}; -use crossbeam_channel::{Receiver, Sender, bounded}; - -use Result; - - -#[derive(Debug, Serialize, Deserialize)] -#[serde(untagged)] -pub enum RawMsg { - Request(RawRequest), - Notification(RawNotification), - Response(RawResponse), -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct RawRequest { - pub id: u64, - pub method: String, - pub params: Value, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct RawNotification { - pub method: String, - pub params: Value, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct RawResponse { - pub id: Option, - pub result: Value, - pub error: Value, -} - -struct MsgReceiver { - chan: Receiver, - thread: Option>>, -} - -impl MsgReceiver { - fn recv(&mut self) -> Result { - match self.chan.recv() { - Some(msg) => Ok(msg), - None => { - self.cleanup()?; - unreachable!() - } - } - } - - fn cleanup(&mut self) -> Result<()> { - self.thread - .take() - .ok_or_else(|| format_err!("MsgReceiver thread panicked"))? - .join() - .map_err(|_| format_err!("MsgReceiver thread panicked"))??; - bail!("client disconnected") - } - - fn stop(self) -> Result<()> { - // Can't really self.thread.join() here, b/c it might be - // blocking on read - Ok(()) - } -} - -struct MsgSender { - chan: Sender, - thread: thread::JoinHandle>, -} - -impl MsgSender { - fn send(&mut self, msg: RawMsg) { - self.chan.send(msg) - } - - fn stop(self) -> Result<()> { - drop(self.chan); - self.thread.join() - .map_err(|_| format_err!("MsgSender thread panicked"))??; - Ok(()) - } -} - -pub struct Io { - receiver: MsgReceiver, - sender: MsgSender, -} - -impl Io { - pub fn from_stdio() -> Io { - let sender = { - let (tx, rx) = bounded(16); - MsgSender { - chan: tx, - thread: thread::spawn(move || { - let stdout = stdout(); - let mut stdout = stdout.lock(); - for msg in rx { - #[derive(Serialize)] - struct JsonRpc { - jsonrpc: &'static str, - #[serde(flatten)] - msg: RawMsg, - } - let text = to_string(&JsonRpc { - jsonrpc: "2.0", - msg, - })?; - write_msg_text(&mut stdout, &text)?; - } - Ok(()) - }), - } - }; - let receiver = { - let (tx, rx) = bounded(16); - MsgReceiver { - chan: rx, - thread: Some(thread::spawn(move || { - let stdin = stdin(); - let mut stdin = stdin.lock(); - while let Some(text) = read_msg_text(&mut stdin)? { - let msg: RawMsg = from_str(&text)?; - tx.send(msg); - } - Ok(()) - })), - } - }; - Io { receiver, sender } - } - - pub fn send(&mut self, msg: RawMsg) { - self.sender.send(msg) - } - - pub fn recv(&mut self) -> Result { - self.receiver.recv() - } - - pub fn receiver(&mut self) -> &mut Receiver { - &mut self.receiver.chan - } - - pub fn cleanup_receiver(&mut self) -> Result<()> { - self.receiver.cleanup() - } - - pub fn stop(self) -> Result<()> { - self.receiver.stop()?; - self.sender.stop()?; - Ok(()) - } -} - - -fn read_msg_text(inp: &mut impl BufRead) -> Result> { - let mut size = None; - let mut buf = String::new(); - loop { - buf.clear(); - if inp.read_line(&mut buf)? == 0 { - return Ok(None); - } - if !buf.ends_with("\r\n") { - bail!("malformed header: {:?}", buf); - } - let buf = &buf[..buf.len() - 2]; - if buf.is_empty() { - break; - } - let mut parts = buf.splitn(2, ": "); - let header_name = parts.next().unwrap(); - let header_value = parts.next().ok_or_else(|| format_err!("malformed header: {:?}", buf))?; - if header_name == "Content-Length" { - size = Some(header_value.parse::()?); - } - } - let size = size.ok_or_else(|| format_err!("no Content-Length"))?; - let mut buf = buf.into_bytes(); - buf.resize(size, 0); - inp.read_exact(&mut buf)?; - let buf = String::from_utf8(buf)?; - debug!("< {}", buf); - Ok(Some(buf)) -} - -fn write_msg_text(out: &mut impl Write, msg: &str) -> Result<()> { - debug!("> {}", msg); - write!(out, "Content-Length: {}\r\n\r\n", msg.len())?; - out.write_all(msg.as_bytes())?; - out.flush()?; - Ok(()) -} diff --git a/codeless/server/src/main.rs b/codeless/server/src/main.rs deleted file mode 100644 index 116abce1c..000000000 --- a/codeless/server/src/main.rs +++ /dev/null @@ -1,249 +0,0 @@ -#[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)) - } -} diff --git a/codeless/server/src/req.rs b/codeless/server/src/req.rs deleted file mode 100644 index 4e588159b..000000000 --- a/codeless/server/src/req.rs +++ /dev/null @@ -1,41 +0,0 @@ -use languageserver_types::{TextDocumentIdentifier, Range}; - -pub use languageserver_types::{ - request::*, notification::*, - InitializeResult, -}; - -pub enum SyntaxTree {} - -impl Request for SyntaxTree { - type Params = SyntaxTreeParams; - type Result = String; - const METHOD: &'static str = "m/syntaxTree"; -} - -#[derive(Deserialize, Debug)] -#[serde(rename_all = "camelCase")] -pub struct SyntaxTreeParams { - pub text_document: TextDocumentIdentifier -} - -pub enum ExtendSelection {} - -impl Request for ExtendSelection { - type Params = ExtendSelectionParams; - type Result = ExtendSelectionResult; - const METHOD: &'static str = "m/extendSelection"; -} - -#[derive(Deserialize, Debug)] -#[serde(rename_all = "camelCase")] -pub struct ExtendSelectionParams { - pub text_document: TextDocumentIdentifier, - pub selections: Vec, -} - -#[derive(Serialize, Debug)] -#[serde(rename_all = "camelCase")] -pub struct ExtendSelectionResult { - pub selections: Vec, -} -- cgit v1.2.3