aboutsummaryrefslogtreecommitdiff
path: root/crates
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
parent25aebb5225da91d34a2cb946f93435f9f7e82a47 (diff)
Apply code actions
Diffstat (limited to 'crates')
-rw-r--r--crates/libeditor/src/edit.rs75
-rw-r--r--crates/libeditor/src/lib.rs2
-rw-r--r--crates/server/Cargo.toml3
-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
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 @@
1use {TextRange, TextUnit}; 1use {TextRange, TextUnit};
2 2
3#[derive(Debug)] 3#[derive(Debug, Clone)]
4pub struct Edit { 4pub struct Edit {
5 pub atoms: Vec<AtomEdit>, 5 atoms: Vec<AtomEdit>,
6} 6}
7 7
8#[derive(Debug)] 8#[derive(Debug, Clone)]
9pub struct AtomEdit { 9pub 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
51impl Edit { 46impl 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
61impl AtomEdit { 72impl 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"
11drop_bomb = "0.1.0" 11drop_bomb = "0.1.0"
12crossbeam-channel = "0.2.4" 12crossbeam-channel = "0.2.4"
13threadpool = "1.7.1" 13threadpool = "1.7.1"
14flexi_logger = "0.9.0" 14flexi_logger = "0.9.1"
15log = "0.4.3" 15log = "0.4.3"
16url_serde = "0.2.0" 16url_serde = "0.2.0"
17languageserver-types = "0.49.0" 17languageserver-types = "0.49.0"
18text_unit = { version = "0.1.2", features = ["serde"] }
18 19
19libsyntax2 = { path = "../libsyntax2" } 20libsyntax2 = { path = "../libsyntax2" }
20libeditor = { path = "../libeditor" } 21libeditor = { 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
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