From 26262aaf05983c5b7f41cc438e287523268fe1eb Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 10 Aug 2018 22:23:17 +0300 Subject: extend selection via LSP --- codeless/server/src/dispatch.rs | 23 ++++++++++++++-- codeless/server/src/handlers.rs | 58 +++++++++++++++++++++++++++++++++++++---- codeless/server/src/main.rs | 50 ++++++++++++++++++++--------------- codeless/server/src/req.rs | 9 ++++++- 4 files changed, 111 insertions(+), 29 deletions(-) (limited to 'codeless/server') diff --git a/codeless/server/src/dispatch.rs b/codeless/server/src/dispatch.rs index 41437b62a..2da0996e3 100644 --- a/codeless/server/src/dispatch.rs +++ b/codeless/server/src/dispatch.rs @@ -52,7 +52,7 @@ impl Responder } -pub fn parse_request_as(raw: RawRequest) -> Result<::std::result::Result<(R::Params, Responder), RawRequest>> +fn parse_request_as(raw: RawRequest) -> Result<::std::result::Result<(R::Params, Responder), RawRequest>> where R: Request, R::Params: DeserializeOwned, @@ -71,6 +71,25 @@ pub fn parse_request_as(raw: RawRequest) -> Result<::std::result::Result<(R:: 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, @@ -87,7 +106,7 @@ pub fn expect_request(io: &mut Io, raw: RawRequest) -> Result(raw: RawNotification) -> Result<::std::result::Result> +fn parse_notification_as(raw: RawNotification) -> Result<::std::result::Result> where N: Notification, N::Params: DeserializeOwned, diff --git a/codeless/server/src/handlers.rs b/codeless/server/src/handlers.rs index 3f257941a..5ee87a4dd 100644 --- a/codeless/server/src/handlers.rs +++ b/codeless/server/src/handlers.rs @@ -1,13 +1,61 @@ +use languageserver_types::{Range, Position}; use libanalysis::World; -use libeditor; -use {req, Result}; +use libeditor::{self, LineIndex, LineCol, TextRange, TextUnit}; +use {req, Result, FilePath}; pub fn handle_syntax_tree( world: World, - params: req::SyntaxTreeParams + params: req::SyntaxTreeParams, ) -> Result { - let path = params.text_document.uri.to_file_path() - .map_err(|()| format_err!("invalid path"))?; + 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/main.rs b/codeless/server/src/main.rs index 287d650fa..116abce1c 100644 --- a/codeless/server/src/main.rs +++ b/codeless/server/src/main.rs @@ -31,7 +31,7 @@ use languageserver_types::{TextDocumentItem, VersionedTextDocumentIdentifier, Te use ::{ io::{Io, RawMsg}, - handlers::handle_syntax_tree, + handlers::{handle_syntax_tree, handle_extend_selection}, }; pub type Result = ::std::result::Result; @@ -153,34 +153,42 @@ fn main_loop( match msg { RawMsg::Request(req) => { - let req = match dispatch::parse_request_as::(req)? { - Ok((params, resp)) => { - let world = world.snapshot(); - let sender = sender.clone(); - pool.execute(move || { - let res: Result = handle_syntax_tree(world, params); - sender.send(Box::new(|io: &mut Io| resp.response(io, res))) - }); - continue; - } - Err(req) => req, - }; - - if let Some(((), resp)) = dispatch::expect_request::(io, req)? { - info!("clean shutdown started"); + 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, ())?; - return Ok(()); + Ok(()) + })?; + if let Some(req) = req { + error!("unknown method: {:?}", req); + dispatch::unknown_method(io, req)?; } } RawMsg::Notification(not) => { - use dispatch::handle_notification as h; let mut not = Some(not); - h::(&mut not, |params| { + dispatch::handle_notification::(&mut not, |params| { let path = params.text_document.file_path()?; world.change_overlay(path, Some(params.text_document.text)); Ok(()) })?; - h::(&mut not, |mut params| { + 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"))? @@ -188,7 +196,7 @@ fn main_loop( world.change_overlay(path, Some(text)); Ok(()) })?; - h::(&mut not, |params| { + dispatch::handle_notification::(&mut not, |params| { let path = params.text_document.file_path()?; world.change_overlay(path, None); Ok(()) diff --git a/codeless/server/src/req.rs b/codeless/server/src/req.rs index ee4a786c7..4e588159b 100644 --- a/codeless/server/src/req.rs +++ b/codeless/server/src/req.rs @@ -21,6 +21,12 @@ pub struct SyntaxTreeParams { 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 { @@ -28,7 +34,8 @@ pub struct ExtendSelectionParams { pub selections: Vec, } - +#[derive(Serialize, Debug)] +#[serde(rename_all = "camelCase")] pub struct ExtendSelectionResult { pub selections: Vec, } -- cgit v1.2.3