diff options
author | Aleksey Kladov <[email protected]> | 2018-08-13 00:38:34 +0100 |
---|---|---|
committer | Aleksey Kladov <[email protected]> | 2018-08-13 00:38:34 +0100 |
commit | be742a587704f27f4e503c50f549aa9ec1527fcc (patch) | |
tree | fb15d3dd05c64c441c9cddbbce6aee3776d6ddd1 /crates | |
parent | 25aebb5225da91d34a2cb946f93435f9f7e82a47 (diff) |
Apply code actions
Diffstat (limited to 'crates')
-rw-r--r-- | crates/libeditor/src/edit.rs | 75 | ||||
-rw-r--r-- | crates/libeditor/src/lib.rs | 2 | ||||
-rw-r--r-- | crates/server/Cargo.toml | 3 | ||||
-rw-r--r-- | crates/server/src/caps.rs | 63 | ||||
-rw-r--r-- | crates/server/src/conv.rs | 82 | ||||
-rw-r--r-- | crates/server/src/dispatch.rs | 4 | ||||
-rw-r--r-- | crates/server/src/io.rs | 7 | ||||
-rw-r--r-- | crates/server/src/main.rs | 5 | ||||
-rw-r--r-- | crates/server/src/main_loop/handlers.rs | 72 | ||||
-rw-r--r-- | crates/server/src/main_loop/mod.rs | 46 | ||||
-rw-r--r-- | crates/server/src/req.rs | 3 |
11 files changed, 245 insertions, 117 deletions
diff --git a/crates/libeditor/src/edit.rs b/crates/libeditor/src/edit.rs index 163ecf6de..15a2a904f 100644 --- a/crates/libeditor/src/edit.rs +++ b/crates/libeditor/src/edit.rs | |||
@@ -1,11 +1,11 @@ | |||
1 | use {TextRange, TextUnit}; | 1 | use {TextRange, TextUnit}; |
2 | 2 | ||
3 | #[derive(Debug)] | 3 | #[derive(Debug, Clone)] |
4 | pub struct Edit { | 4 | pub struct Edit { |
5 | pub atoms: Vec<AtomEdit>, | 5 | atoms: Vec<AtomEdit>, |
6 | } | 6 | } |
7 | 7 | ||
8 | #[derive(Debug)] | 8 | #[derive(Debug, Clone)] |
9 | pub struct AtomEdit { | 9 | pub struct AtomEdit { |
10 | pub delete: TextRange, | 10 | pub delete: TextRange, |
11 | pub insert: String, | 11 | pub insert: String, |
@@ -22,7 +22,6 @@ impl EditBuilder { | |||
22 | } | 22 | } |
23 | 23 | ||
24 | pub fn replace(&mut self, range: TextRange, replacement: String) { | 24 | pub fn replace(&mut self, range: TextRange, replacement: String) { |
25 | let range = self.translate(range); | ||
26 | self.atoms.push(AtomEdit { delete: range, insert: replacement }) | 25 | self.atoms.push(AtomEdit { delete: range, insert: replacement }) |
27 | } | 26 | } |
28 | 27 | ||
@@ -35,59 +34,47 @@ impl EditBuilder { | |||
35 | } | 34 | } |
36 | 35 | ||
37 | pub fn finish(self) -> Edit { | 36 | pub fn finish(self) -> Edit { |
38 | Edit { atoms: self.atoms } | 37 | let mut atoms = self.atoms; |
39 | } | 38 | atoms.sort_by_key(|a| a.delete.start()); |
40 | 39 | for (a1, a2) in atoms.iter().zip(atoms.iter().skip(1)) { | |
41 | fn translate(&self, range: TextRange) -> TextRange { | 40 | assert!(a1.end() <= a2.start()) |
42 | let mut range = range; | ||
43 | for atom in self.atoms.iter() { | ||
44 | range = atom.apply_to_range(range) | ||
45 | .expect("conflicting edits"); | ||
46 | } | 41 | } |
47 | range | 42 | Edit { atoms } |
48 | } | 43 | } |
49 | } | 44 | } |
50 | 45 | ||
51 | impl Edit { | 46 | impl Edit { |
47 | pub fn into_atoms(self) -> Vec<AtomEdit> { | ||
48 | self.atoms | ||
49 | } | ||
50 | |||
52 | pub fn apply(&self, text: &str) -> String { | 51 | pub fn apply(&self, text: &str) -> String { |
53 | let mut text = text.to_owned(); | 52 | let mut total_len = text.len(); |
54 | for atom in self.atoms.iter() { | 53 | for atom in self.atoms.iter() { |
55 | text = atom.apply(&text); | 54 | total_len += atom.insert.len(); |
55 | total_len -= atom.end() - atom.start(); | ||
56 | } | 56 | } |
57 | text | 57 | let mut buf = String::with_capacity(total_len); |
58 | let mut prev = 0; | ||
59 | for atom in self.atoms.iter() { | ||
60 | if atom.start() > prev { | ||
61 | buf.push_str(&text[prev..atom.start()]); | ||
62 | } | ||
63 | buf.push_str(&atom.insert); | ||
64 | prev = atom.end(); | ||
65 | } | ||
66 | buf.push_str(&text[prev..text.len()]); | ||
67 | assert_eq!(buf.len(), total_len); | ||
68 | buf | ||
58 | } | 69 | } |
59 | } | 70 | } |
60 | 71 | ||
61 | impl AtomEdit { | 72 | impl AtomEdit { |
62 | fn apply(&self, text: &str) -> String { | 73 | fn start(&self) -> usize { |
63 | let prefix = &text[ | 74 | u32::from(self.delete.start()) as usize |
64 | TextRange::from_to(0.into(), self.delete.start()) | ||
65 | ]; | ||
66 | let suffix = &text[ | ||
67 | TextRange::from_to(self.delete.end(), TextUnit::of_str(text)) | ||
68 | ]; | ||
69 | let mut res = String::with_capacity(prefix.len() + self.insert.len() + suffix.len()); | ||
70 | res.push_str(prefix); | ||
71 | res.push_str(&self.insert); | ||
72 | res.push_str(suffix); | ||
73 | res | ||
74 | } | 75 | } |
75 | 76 | ||
76 | fn apply_to_position(&self, pos: TextUnit) -> Option<TextUnit> { | 77 | fn end(&self) -> usize { |
77 | if pos <= self.delete.start() { | 78 | u32::from(self.delete.end()) as usize |
78 | return Some(pos); | ||
79 | } | ||
80 | if pos < self.delete.end() { | ||
81 | return None; | ||
82 | } | ||
83 | Some(pos - self.delete.len() + TextUnit::of_str(&self.insert)) | ||
84 | } | ||
85 | |||
86 | fn apply_to_range(&self, range: TextRange) -> Option<TextRange> { | ||
87 | Some(TextRange::from_to( | ||
88 | self.apply_to_position(range.start())?, | ||
89 | self.apply_to_position(range.end())?, | ||
90 | )) | ||
91 | } | 79 | } |
92 | } | 80 | } |
93 | |||
diff --git a/crates/libeditor/src/lib.rs b/crates/libeditor/src/lib.rs index 103f32190..1e88f1471 100644 --- a/crates/libeditor/src/lib.rs +++ b/crates/libeditor/src/lib.rs | |||
@@ -19,7 +19,7 @@ pub use self::{ | |||
19 | line_index::{LineIndex, LineCol}, | 19 | line_index::{LineIndex, LineCol}, |
20 | extend_selection::extend_selection, | 20 | extend_selection::extend_selection, |
21 | symbols::{FileSymbol, file_symbols}, | 21 | symbols::{FileSymbol, file_symbols}, |
22 | edit::{EditBuilder, Edit}, | 22 | edit::{EditBuilder, Edit, AtomEdit}, |
23 | code_actions::{flip_comma}, | 23 | code_actions::{flip_comma}, |
24 | }; | 24 | }; |
25 | 25 | ||
diff --git a/crates/server/Cargo.toml b/crates/server/Cargo.toml index 2d0db2838..8e077ecf0 100644 --- a/crates/server/Cargo.toml +++ b/crates/server/Cargo.toml | |||
@@ -11,10 +11,11 @@ serde_derive = "1.0.71" | |||
11 | drop_bomb = "0.1.0" | 11 | drop_bomb = "0.1.0" |
12 | crossbeam-channel = "0.2.4" | 12 | crossbeam-channel = "0.2.4" |
13 | threadpool = "1.7.1" | 13 | threadpool = "1.7.1" |
14 | flexi_logger = "0.9.0" | 14 | flexi_logger = "0.9.1" |
15 | log = "0.4.3" | 15 | log = "0.4.3" |
16 | url_serde = "0.2.0" | 16 | url_serde = "0.2.0" |
17 | languageserver-types = "0.49.0" | 17 | languageserver-types = "0.49.0" |
18 | text_unit = { version = "0.1.2", features = ["serde"] } | ||
18 | 19 | ||
19 | libsyntax2 = { path = "../libsyntax2" } | 20 | libsyntax2 = { path = "../libsyntax2" } |
20 | libeditor = { path = "../libeditor" } | 21 | libeditor = { path = "../libeditor" } |
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 | ||
8 | pub const SERVER_CAPABILITIES: ServerCapabilities = ServerCapabilities { | 9 | pub 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 @@ | |||
1 | use languageserver_types::{Range, SymbolKind, Position}; | 1 | use languageserver_types::{Range, SymbolKind, Position, TextEdit}; |
2 | use libeditor::{LineIndex, LineCol}; | 2 | use libeditor::{LineIndex, LineCol, Edit, AtomEdit}; |
3 | use libsyntax2::{SyntaxKind, TextUnit, TextRange}; | 3 | use libsyntax2::{SyntaxKind, TextUnit, TextRange}; |
4 | 4 | ||
5 | pub trait Conv { | 5 | pub trait Conv { |
6 | type Output; | 6 | type Output; |
7 | fn conv(&self) -> Self::Output; | 7 | fn conv(self) -> Self::Output; |
8 | } | 8 | } |
9 | 9 | ||
10 | pub trait ConvWith { | 10 | pub 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 | ||
16 | impl Conv for SyntaxKind { | 16 | impl 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 | |||
83 | impl 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 | |||
95 | impl 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 | |||
108 | pub 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 | |||
117 | impl<'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 | |||
125 | pub struct ConvWithIter<'a, I, Ctx: 'a> { | ||
126 | iter: I, | ||
127 | ctx: &'a Ctx, | ||
128 | } | ||
129 | |||
130 | impl<'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)] |
36 | pub struct RawResponse { | 36 | pub 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 | ||
28 | use threadpool::ThreadPool; | 28 | use threadpool::ThreadPool; |
29 | use crossbeam_channel::bounded; | 29 | use crossbeam_channel::bounded; |
30 | use flexi_logger::Logger; | 30 | use flexi_logger::{Logger, Duplicate}; |
31 | use libanalysis::WorldState; | 31 | use libanalysis::WorldState; |
32 | 32 | ||
33 | use ::{ | 33 | use ::{ |
@@ -38,6 +38,7 @@ pub type Result<T> = ::std::result::Result<T, ::failure::Error>; | |||
38 | 38 | ||
39 | fn main() -> Result<()> { | 39 | fn 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 @@ | |||
1 | use std::collections::HashMap; | ||
2 | |||
1 | use languageserver_types::{ | 3 | use languageserver_types::{ |
2 | Diagnostic, DiagnosticSeverity, Url, DocumentSymbol, | 4 | Diagnostic, DiagnosticSeverity, Url, DocumentSymbol, |
3 | Command | 5 | Command, TextDocumentIdentifier, WorkspaceEdit |
4 | }; | 6 | }; |
5 | use libanalysis::World; | 7 | use libanalysis::World; |
6 | use libeditor; | 8 | use libeditor; |
7 | use serde_json::to_value; | 9 | use libsyntax2::TextUnit; |
10 | use serde_json::{to_value, from_value}; | ||
8 | 11 | ||
9 | use ::{ | 12 | use ::{ |
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 | ||
15 | pub fn handle_syntax_tree( | 18 | pub 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 | ||
88 | fn apply_code_action_cmd(id: ActionId) -> Command { | 96 | pub 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)] | ||
133 | struct ActionRequest { | ||
134 | id: ActionId, | ||
135 | text_document: TextDocumentIdentifier, | ||
136 | offset: TextUnit, | ||
137 | } | ||
138 | |||
139 | fn 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; | |||
6 | use crossbeam_channel::{Sender, Receiver}; | 6 | use crossbeam_channel::{Sender, Receiver}; |
7 | use languageserver_types::Url; | 7 | use languageserver_types::Url; |
8 | use libanalysis::{World, WorldState}; | 8 | use libanalysis::{World, WorldState}; |
9 | use serde_json::to_value; | ||
9 | 10 | ||
10 | use { | 11 | use { |
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 | ||