aboutsummaryrefslogtreecommitdiff
path: root/crates/server/src
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2018-08-13 00:38:34 +0100
committerAleksey Kladov <[email protected]>2018-08-13 00:38:34 +0100
commitbe742a587704f27f4e503c50f549aa9ec1527fcc (patch)
treefb15d3dd05c64c441c9cddbbce6aee3776d6ddd1 /crates/server/src
parent25aebb5225da91d34a2cb946f93435f9f7e82a47 (diff)
Apply code actions
Diffstat (limited to 'crates/server/src')
-rw-r--r--crates/server/src/caps.rs63
-rw-r--r--crates/server/src/conv.rs82
-rw-r--r--crates/server/src/dispatch.rs4
-rw-r--r--crates/server/src/io.rs7
-rw-r--r--crates/server/src/main.rs5
-rw-r--r--crates/server/src/main_loop/handlers.rs72
-rw-r--r--crates/server/src/main_loop/mod.rs46
-rw-r--r--crates/server/src/req.rs3
8 files changed, 211 insertions, 71 deletions
diff --git a/crates/server/src/caps.rs b/crates/server/src/caps.rs
index b502b0865..d06a43a82 100644
--- a/crates/server/src/caps.rs
+++ b/crates/server/src/caps.rs
@@ -3,34 +3,39 @@ use languageserver_types::{
3 TextDocumentSyncCapability, 3 TextDocumentSyncCapability,
4 TextDocumentSyncOptions, 4 TextDocumentSyncOptions,
5 TextDocumentSyncKind, 5 TextDocumentSyncKind,
6 ExecuteCommandOptions,
6}; 7};
7 8
8pub const SERVER_CAPABILITIES: ServerCapabilities = ServerCapabilities { 9pub fn server_capabilities() -> ServerCapabilities {
9 text_document_sync: Some(TextDocumentSyncCapability::Options( 10 ServerCapabilities {
10 TextDocumentSyncOptions { 11 text_document_sync: Some(TextDocumentSyncCapability::Options(
11 open_close: Some(true), 12 TextDocumentSyncOptions {
12 change: Some(TextDocumentSyncKind::Full), 13 open_close: Some(true),
13 will_save: None, 14 change: Some(TextDocumentSyncKind::Full),
14 will_save_wait_until: None, 15 will_save: None,
15 save: None, 16 will_save_wait_until: None,
16 } 17 save: None,
17 )), 18 }
18 hover_provider: None, 19 )),
19 completion_provider: None, 20 hover_provider: None,
20 signature_help_provider: None, 21 completion_provider: None,
21 definition_provider: None, 22 signature_help_provider: None,
22 type_definition_provider: None, 23 definition_provider: None,
23 implementation_provider: None, 24 type_definition_provider: None,
24 references_provider: None, 25 implementation_provider: None,
25 document_highlight_provider: None, 26 references_provider: None,
26 document_symbol_provider: Some(true), 27 document_highlight_provider: None,
27 workspace_symbol_provider: None, 28 document_symbol_provider: Some(true),
28 code_action_provider: Some(true), 29 workspace_symbol_provider: None,
29 code_lens_provider: None, 30 code_action_provider: Some(true),
30 document_formatting_provider: None, 31 code_lens_provider: None,
31 document_range_formatting_provider: None, 32 document_formatting_provider: None,
32 document_on_type_formatting_provider: None, 33 document_range_formatting_provider: None,
33 rename_provider: None, 34 document_on_type_formatting_provider: None,
34 color_provider: None, 35 rename_provider: None,
35 execute_command_provider: None, 36 color_provider: None,
36}; 37 execute_command_provider: Some(ExecuteCommandOptions {
38 commands: vec!["apply_code_action".to_string()],
39 }),
40 }
41}
diff --git a/crates/server/src/conv.rs b/crates/server/src/conv.rs
index c7bea15df..0ed989b32 100644
--- a/crates/server/src/conv.rs
+++ b/crates/server/src/conv.rs
@@ -1,23 +1,23 @@
1use languageserver_types::{Range, SymbolKind, Position}; 1use languageserver_types::{Range, SymbolKind, Position, TextEdit};
2use libeditor::{LineIndex, LineCol}; 2use libeditor::{LineIndex, LineCol, Edit, AtomEdit};
3use libsyntax2::{SyntaxKind, TextUnit, TextRange}; 3use libsyntax2::{SyntaxKind, TextUnit, TextRange};
4 4
5pub trait Conv { 5pub trait Conv {
6 type Output; 6 type Output;
7 fn conv(&self) -> Self::Output; 7 fn conv(self) -> Self::Output;
8} 8}
9 9
10pub trait ConvWith { 10pub trait ConvWith {
11 type Ctx; 11 type Ctx;
12 type Output; 12 type Output;
13 fn conv_with(&self, ctx: &Self::Ctx) -> Self::Output; 13 fn conv_with(self, ctx: &Self::Ctx) -> Self::Output;
14} 14}
15 15
16impl Conv for SyntaxKind { 16impl Conv for SyntaxKind {
17 type Output = SymbolKind; 17 type Output = SymbolKind;
18 18
19 fn conv(&self) -> <Self as Conv>::Output { 19 fn conv(self) -> <Self as Conv>::Output {
20 match *self { 20 match self {
21 SyntaxKind::FUNCTION => SymbolKind::Function, 21 SyntaxKind::FUNCTION => SymbolKind::Function,
22 SyntaxKind::STRUCT => SymbolKind::Struct, 22 SyntaxKind::STRUCT => SymbolKind::Struct,
23 SyntaxKind::ENUM => SymbolKind::Enum, 23 SyntaxKind::ENUM => SymbolKind::Enum,
@@ -35,7 +35,7 @@ impl ConvWith for Position {
35 type Ctx = LineIndex; 35 type Ctx = LineIndex;
36 type Output = TextUnit; 36 type Output = TextUnit;
37 37
38 fn conv_with(&self, line_index: &LineIndex) -> TextUnit { 38 fn conv_with(self, line_index: &LineIndex) -> TextUnit {
39 // TODO: UTF-16 39 // TODO: UTF-16
40 let line_col = LineCol { 40 let line_col = LineCol {
41 line: self.line as u32, 41 line: self.line as u32,
@@ -49,8 +49,8 @@ impl ConvWith for TextUnit {
49 type Ctx = LineIndex; 49 type Ctx = LineIndex;
50 type Output = Position; 50 type Output = Position;
51 51
52 fn conv_with(&self, line_index: &LineIndex) -> Position { 52 fn conv_with(self, line_index: &LineIndex) -> Position {
53 let line_col = line_index.line_col(*self); 53 let line_col = line_index.line_col(self);
54 // TODO: UTF-16 54 // TODO: UTF-16
55 Position::new(line_col.line as u64, u32::from(line_col.col) as u64) 55 Position::new(line_col.line as u64, u32::from(line_col.col) as u64)
56 } 56 }
@@ -60,7 +60,7 @@ impl ConvWith for TextRange {
60 type Ctx = LineIndex; 60 type Ctx = LineIndex;
61 type Output = Range; 61 type Output = Range;
62 62
63 fn conv_with(&self, line_index: &LineIndex) -> Range { 63 fn conv_with(self, line_index: &LineIndex) -> Range {
64 Range::new( 64 Range::new(
65 self.start().conv_with(line_index), 65 self.start().conv_with(line_index),
66 self.end().conv_with(line_index), 66 self.end().conv_with(line_index),
@@ -72,10 +72,70 @@ impl ConvWith for Range {
72 type Ctx = LineIndex; 72 type Ctx = LineIndex;
73 type Output = TextRange; 73 type Output = TextRange;
74 74
75 fn conv_with(&self, line_index: &LineIndex) -> TextRange { 75 fn conv_with(self, line_index: &LineIndex) -> TextRange {
76 TextRange::from_to( 76 TextRange::from_to(
77 self.start.conv_with(line_index), 77 self.start.conv_with(line_index),
78 self.end.conv_with(line_index), 78 self.end.conv_with(line_index),
79 ) 79 )
80 } 80 }
81} 81}
82
83impl ConvWith for Edit {
84 type Ctx = LineIndex;
85 type Output = Vec<TextEdit>;
86
87 fn conv_with(self, line_index: &LineIndex) -> Vec<TextEdit> {
88 self.into_atoms()
89 .into_iter()
90 .map_conv_with(line_index)
91 .collect()
92 }
93}
94
95impl ConvWith for AtomEdit {
96 type Ctx = LineIndex;
97 type Output = TextEdit;
98
99 fn conv_with(self, line_index: &LineIndex) -> TextEdit {
100 TextEdit {
101 range: self.delete.conv_with(line_index),
102 new_text: self.insert,
103 }
104 }
105}
106
107
108pub trait MapConvWith<'a>: Sized {
109 type Ctx;
110 type Output;
111
112 fn map_conv_with(self, ctx: &'a Self::Ctx) -> ConvWithIter<'a, Self, Self::Ctx> {
113 ConvWithIter { iter: self, ctx }
114 }
115}
116
117impl<'a, I> MapConvWith<'a> for I
118 where I: Iterator,
119 I::Item: ConvWith
120{
121 type Ctx = <I::Item as ConvWith>::Ctx;
122 type Output = <I::Item as ConvWith>::Output;
123}
124
125pub struct ConvWithIter<'a, I, Ctx: 'a> {
126 iter: I,
127 ctx: &'a Ctx,
128}
129
130impl<'a, I, Ctx> Iterator for ConvWithIter<'a, I, Ctx>
131 where
132 I: Iterator,
133 I::Item: ConvWith<Ctx=Ctx>,
134{
135 type Item = <I::Item as ConvWith>::Output;
136
137 fn next(&mut self) -> Option<Self::Item> {
138 self.iter.next().map(|item| item.conv_with(self.ctx))
139 }
140}
141
diff --git a/crates/server/src/dispatch.rs b/crates/server/src/dispatch.rs
index bfdbd30ca..3a3ee74bb 100644
--- a/crates/server/src/dispatch.rs
+++ b/crates/server/src/dispatch.rs
@@ -25,7 +25,7 @@ impl<R: ClientRequest> Responder<R> {
25 let res = match result { 25 let res = match result {
26 Ok(result) => { 26 Ok(result) => {
27 RawResponse { 27 RawResponse {
28 id: Some(self.id), 28 id: self.id,
29 result: serde_json::to_value(result)?, 29 result: serde_json::to_value(result)?,
30 error: serde_json::Value::Null, 30 error: serde_json::Value::Null,
31 } 31 }
@@ -125,7 +125,7 @@ fn error_response(id: u64, code: ErrorCode, message: &'static str) -> Result<Raw
125 message: &'static str, 125 message: &'static str,
126 } 126 }
127 let resp = RawResponse { 127 let resp = RawResponse {
128 id: Some(id), 128 id,
129 result: serde_json::Value::Null, 129 result: serde_json::Value::Null,
130 error: serde_json::to_value(Error { 130 error: serde_json::to_value(Error {
131 code: code as i32, 131 code: code as i32,
diff --git a/crates/server/src/io.rs b/crates/server/src/io.rs
index 5eafc6942..f247327ab 100644
--- a/crates/server/src/io.rs
+++ b/crates/server/src/io.rs
@@ -34,8 +34,13 @@ pub struct RawNotification {
34 34
35#[derive(Debug, Serialize, Deserialize)] 35#[derive(Debug, Serialize, Deserialize)]
36pub struct RawResponse { 36pub struct RawResponse {
37 pub id: Option<u64>, 37 // JSON RPC allows this to be null if it was impossible
38 // to decode the request's id. Ignore this special case
39 // and just die horribly.
40 pub id: u64,
41 #[serde(default)]
38 pub result: Value, 42 pub result: Value,
43 #[serde(default)]
39 pub error: Value, 44 pub error: Value,
40} 45}
41 46
diff --git a/crates/server/src/main.rs b/crates/server/src/main.rs
index 7416de08a..c2952465e 100644
--- a/crates/server/src/main.rs
+++ b/crates/server/src/main.rs
@@ -27,7 +27,7 @@ mod main_loop;
27 27
28use threadpool::ThreadPool; 28use threadpool::ThreadPool;
29use crossbeam_channel::bounded; 29use crossbeam_channel::bounded;
30use flexi_logger::Logger; 30use flexi_logger::{Logger, Duplicate};
31use libanalysis::WorldState; 31use libanalysis::WorldState;
32 32
33use ::{ 33use ::{
@@ -38,6 +38,7 @@ pub type Result<T> = ::std::result::Result<T, ::failure::Error>;
38 38
39fn main() -> Result<()> { 39fn main() -> Result<()> {
40 Logger::with_env() 40 Logger::with_env()
41 .duplicate_to_stderr(Duplicate::All)
41 .log_to_file() 42 .log_to_file()
42 .directory("log") 43 .directory("log")
43 .start()?; 44 .start()?;
@@ -81,7 +82,7 @@ fn initialize(io: &mut Io) -> Result<()> {
81 RawMsg::Request(req) => { 82 RawMsg::Request(req) => {
82 let mut req = Some(req); 83 let mut req = Some(req);
83 dispatch::handle_request::<req::Initialize, _>(&mut req, |_params, resp| { 84 dispatch::handle_request::<req::Initialize, _>(&mut req, |_params, resp| {
84 let res = req::InitializeResult { capabilities: caps::SERVER_CAPABILITIES }; 85 let res = req::InitializeResult { capabilities: caps::server_capabilities() };
85 let resp = resp.into_response(Ok(res))?; 86 let resp = resp.into_response(Ok(res))?;
86 io.send(RawMsg::Response(resp)); 87 io.send(RawMsg::Response(resp));
87 Ok(()) 88 Ok(())
diff --git a/crates/server/src/main_loop/handlers.rs b/crates/server/src/main_loop/handlers.rs
index c6db22289..d4ae2a368 100644
--- a/crates/server/src/main_loop/handlers.rs
+++ b/crates/server/src/main_loop/handlers.rs
@@ -1,15 +1,18 @@
1use std::collections::HashMap;
2
1use languageserver_types::{ 3use languageserver_types::{
2 Diagnostic, DiagnosticSeverity, Url, DocumentSymbol, 4 Diagnostic, DiagnosticSeverity, Url, DocumentSymbol,
3 Command 5 Command, TextDocumentIdentifier, WorkspaceEdit
4}; 6};
5use libanalysis::World; 7use libanalysis::World;
6use libeditor; 8use libeditor;
7use serde_json::to_value; 9use libsyntax2::TextUnit;
10use serde_json::{to_value, from_value};
8 11
9use ::{ 12use ::{
10 req::{self, Decoration}, Result, 13 req::{self, Decoration}, Result,
11 util::FilePath, 14 util::FilePath,
12 conv::{Conv, ConvWith}, 15 conv::{Conv, ConvWith, MapConvWith},
13}; 16};
14 17
15pub fn handle_syntax_tree( 18pub fn handle_syntax_tree(
@@ -29,9 +32,9 @@ pub fn handle_extend_selection(
29 let file = world.file_syntax(&path)?; 32 let file = world.file_syntax(&path)?;
30 let line_index = world.file_line_index(&path)?; 33 let line_index = world.file_line_index(&path)?;
31 let selections = params.selections.into_iter() 34 let selections = params.selections.into_iter()
32 .map(|r| r.conv_with(&line_index)) 35 .map_conv_with(&line_index)
33 .map(|r| libeditor::extend_selection(&file, r).unwrap_or(r)) 36 .map(|r| libeditor::extend_selection(&file, r).unwrap_or(r))
34 .map(|r| r.conv_with(&line_index)) 37 .map_conv_with(&line_index)
35 .collect(); 38 .collect();
36 Ok(req::ExtendSelectionResult { selections }) 39 Ok(req::ExtendSelectionResult { selections })
37} 40}
@@ -78,18 +81,71 @@ pub fn handle_code_action(
78 let line_index = world.file_line_index(&path)?; 81 let line_index = world.file_line_index(&path)?;
79 let offset = params.range.conv_with(&line_index).start(); 82 let offset = params.range.conv_with(&line_index).start();
80 let ret = if libeditor::flip_comma(&file, offset).is_some() { 83 let ret = if libeditor::flip_comma(&file, offset).is_some() {
81 Some(vec![apply_code_action_cmd(ActionId::FlipComma)]) 84 let cmd = apply_code_action_cmd(
85 ActionId::FlipComma,
86 params.text_document,
87 offset,
88 );
89 Some(vec![cmd])
82 } else { 90 } else {
83 None 91 None
84 }; 92 };
85 Ok(ret) 93 Ok(ret)
86} 94}
87 95
88fn apply_code_action_cmd(id: ActionId) -> Command { 96pub fn handle_execute_command(
97 world: World,
98 mut params: req::ExecuteCommandParams,
99) -> Result<req::ApplyWorkspaceEditParams> {
100 if params.command.as_str() != "apply_code_action" {
101 bail!("unknown cmd: {:?}", params.command);
102 }
103 if params.arguments.len() != 1 {
104 bail!("expected single arg, got {}", params.arguments.len());
105 }
106 let arg = params.arguments.pop().unwrap();
107 let arg: ActionRequest = from_value(arg)?;
108 match arg.id {
109 ActionId::FlipComma => {
110 let path = arg.text_document.file_path()?;
111 let file = world.file_syntax(&path)?;
112 let line_index = world.file_line_index(&path)?;
113 let edit = match libeditor::flip_comma(&file, arg.offset) {
114 Some(edit) => edit(),
115 None => bail!("command not applicable"),
116 };
117 let mut changes = HashMap::new();
118 changes.insert(
119 arg.text_document.uri,
120 edit.conv_with(&line_index),
121 );
122 let edit = WorkspaceEdit {
123 changes: Some(changes),
124 document_changes: None,
125 };
126
127 Ok(req::ApplyWorkspaceEditParams { edit })
128 }
129 }
130}
131
132#[derive(Serialize, Deserialize)]
133struct ActionRequest {
134 id: ActionId,
135 text_document: TextDocumentIdentifier,
136 offset: TextUnit,
137}
138
139fn apply_code_action_cmd(id: ActionId, doc: TextDocumentIdentifier, offset: TextUnit) -> Command {
140 let action_request = ActionRequest {
141 id,
142 text_document: doc,
143 offset,
144 };
89 Command { 145 Command {
90 title: id.title().to_string(), 146 title: id.title().to_string(),
91 command: "apply_code_action".to_string(), 147 command: "apply_code_action".to_string(),
92 arguments: Some(vec![to_value(id).unwrap()]), 148 arguments: Some(vec![to_value(action_request).unwrap()]),
93 } 149 }
94} 150}
95 151
diff --git a/crates/server/src/main_loop/mod.rs b/crates/server/src/main_loop/mod.rs
index 3d367f5f6..5b7093ad7 100644
--- a/crates/server/src/main_loop/mod.rs
+++ b/crates/server/src/main_loop/mod.rs
@@ -6,6 +6,7 @@ use threadpool::ThreadPool;
6use crossbeam_channel::{Sender, Receiver}; 6use crossbeam_channel::{Sender, Receiver};
7use languageserver_types::Url; 7use languageserver_types::Url;
8use libanalysis::{World, WorldState}; 8use libanalysis::{World, WorldState};
9use serde_json::to_value;
9 10
10use { 11use {
11 req, dispatch, 12 req, dispatch,
@@ -19,6 +20,7 @@ use {
19 publish_decorations, 20 publish_decorations,
20 handle_document_symbol, 21 handle_document_symbol,
21 handle_code_action, 22 handle_code_action,
23 handle_execute_command,
22 }, 24 },
23}; 25};
24 26
@@ -79,7 +81,9 @@ pub(super) fn main_loop(
79 on_notification(io, world, pool, &sender, not)? 81 on_notification(io, world, pool, &sender, not)?
80 } 82 }
81 RawMsg::Response(resp) => { 83 RawMsg::Response(resp) => {
82 error!("unexpected response: {:?}", resp) 84 if !pending_requests.remove(&resp.id) {
85 error!("unexpected response: {:?}", resp)
86 }
83 } 87 }
84 } 88 }
85 } 89 }
@@ -107,22 +111,30 @@ fn on_request(
107 handle_request_on_threadpool::<req::CodeActionRequest>( 111 handle_request_on_threadpool::<req::CodeActionRequest>(
108 &mut req, pool, world, sender, handle_code_action, 112 &mut req, pool, world, sender, handle_code_action,
109 )?; 113 )?;
110// dispatch::handle_request::<req::ExecuteCommand, _>(&mut req, |(), resp| { 114 dispatch::handle_request::<req::ExecuteCommand, _>(&mut req, |params, resp| {
111// let world = world.snapshot(); 115 io.send(RawMsg::Response(resp.into_response(Ok(None))?));
112// let sender = sender.clone(); 116
113// pool.execute(move || { 117 let world = world.snapshot();
114// let task = match handle_execute_command(world, params) { 118 let sender = sender.clone();
115// Ok(req) => Task::Request(req), 119 pool.execute(move || {
116// Err(e) => Task::Die(e), 120 let task = match handle_execute_command(world, params) {
117// }; 121 Ok(req) => match to_value(req) {
118// sender.send(task) 122 Err(e) => Task::Die(e.into()),
119// }); 123 Ok(params) => {
120// 124 let request = RawRequest {
121// let resp = resp.into_response(Ok(()))?; 125 id: 0,
122// io.send(RawMsg::Response(resp)); 126 method: <req::ApplyWorkspaceEdit as req::ClientRequest>::METHOD.to_string(),
123// shutdown = true; 127 params,
124// Ok(()) 128 };
125// })?; 129 Task::Request(request)
130 }
131 },
132 Err(e) => Task::Die(e),
133 };
134 sender.send(task)
135 });
136 Ok(())
137 })?;
126 138
127 let mut shutdown = false; 139 let mut shutdown = false;
128 dispatch::handle_request::<req::Shutdown, _>(&mut req, |(), resp| { 140 dispatch::handle_request::<req::Shutdown, _>(&mut req, |(), resp| {
diff --git a/crates/server/src/req.rs b/crates/server/src/req.rs
index e3b1cdf50..a22ba4bc3 100644
--- a/crates/server/src/req.rs
+++ b/crates/server/src/req.rs
@@ -6,7 +6,8 @@ pub use languageserver_types::{
6 request::*, notification::*, 6 request::*, notification::*,
7 InitializeResult, PublishDiagnosticsParams, 7 InitializeResult, PublishDiagnosticsParams,
8 DocumentSymbolParams, DocumentSymbolResponse, 8 DocumentSymbolParams, DocumentSymbolResponse,
9 CodeActionParams, 9 CodeActionParams, ApplyWorkspaceEditParams,
10 ExecuteCommandParams,
10}; 11};
11 12
12 13