diff options
Diffstat (limited to 'crates/server')
-rw-r--r-- | crates/server/src/conv.rs | 80 | ||||
-rw-r--r-- | crates/server/src/main.rs | 3 | ||||
-rw-r--r-- | crates/server/src/main_loop/handlers.rs | 384 | ||||
-rw-r--r-- | crates/server/src/main_loop/mod.rs | 169 | ||||
-rw-r--r-- | crates/server/src/req.rs | 35 | ||||
-rw-r--r-- | crates/server/src/server_world.rs | 8 |
6 files changed, 289 insertions, 390 deletions
diff --git a/crates/server/src/conv.rs b/crates/server/src/conv.rs index fc9056914..a59308c3f 100644 --- a/crates/server/src/conv.rs +++ b/crates/server/src/conv.rs | |||
@@ -1,14 +1,16 @@ | |||
1 | use languageserver_types::{ | 1 | use languageserver_types::{ |
2 | Range, SymbolKind, Position, TextEdit, Location, Url, | 2 | Range, SymbolKind, Position, TextEdit, Location, Url, |
3 | TextDocumentIdentifier, VersionedTextDocumentIdentifier, TextDocumentItem, | 3 | TextDocumentIdentifier, VersionedTextDocumentIdentifier, TextDocumentItem, |
4 | TextDocumentPositionParams, TextDocumentEdit, | ||
4 | }; | 5 | }; |
5 | use libeditor::{LineIndex, LineCol, Edit, AtomEdit}; | 6 | use libeditor::{LineIndex, LineCol, Edit, AtomEdit}; |
6 | use libsyntax2::{SyntaxKind, TextUnit, TextRange}; | 7 | use libsyntax2::{SyntaxKind, TextUnit, TextRange}; |
7 | use libanalysis::FileId; | 8 | use libanalysis::{FileId, SourceChange, SourceFileEdit, FileSystemEdit}; |
8 | 9 | ||
9 | use { | 10 | use { |
10 | Result, | 11 | Result, |
11 | server_world::ServerWorld, | 12 | server_world::ServerWorld, |
13 | req, | ||
12 | }; | 14 | }; |
13 | 15 | ||
14 | pub trait Conv { | 16 | pub trait Conv { |
@@ -168,6 +170,82 @@ impl<'a> TryConvWith for &'a TextDocumentIdentifier { | |||
168 | } | 170 | } |
169 | } | 171 | } |
170 | 172 | ||
173 | impl<T: TryConvWith> TryConvWith for Vec<T> { | ||
174 | type Ctx = <T as TryConvWith>::Ctx; | ||
175 | type Output = Vec<<T as TryConvWith>::Output>; | ||
176 | fn try_conv_with(self, ctx: &Self::Ctx) -> Result<Self::Output> { | ||
177 | let mut res = Vec::with_capacity(self.len()); | ||
178 | for item in self { | ||
179 | res.push(item.try_conv_with(ctx)?); | ||
180 | } | ||
181 | Ok(res) | ||
182 | } | ||
183 | } | ||
184 | |||
185 | impl TryConvWith for SourceChange { | ||
186 | type Ctx = ServerWorld; | ||
187 | type Output = req::SourceChange; | ||
188 | fn try_conv_with(self, world: &ServerWorld) -> Result<req::SourceChange> { | ||
189 | let cursor_position = match self.cursor_position { | ||
190 | None => None, | ||
191 | Some(pos) => { | ||
192 | let line_index = world.analysis().file_line_index(pos.file_id); | ||
193 | Some(TextDocumentPositionParams { | ||
194 | text_document: TextDocumentIdentifier::new(pos.file_id.try_conv_with(world)?), | ||
195 | position: pos.offset.conv_with(&line_index), | ||
196 | }) | ||
197 | } | ||
198 | }; | ||
199 | let source_file_edits = self.source_file_edits.try_conv_with(world)?; | ||
200 | let file_system_edits = self.file_system_edits.try_conv_with(world)?; | ||
201 | Ok(req::SourceChange { | ||
202 | label: self.label, | ||
203 | source_file_edits, | ||
204 | file_system_edits, | ||
205 | cursor_position, | ||
206 | }) | ||
207 | } | ||
208 | } | ||
209 | |||
210 | impl TryConvWith for SourceFileEdit { | ||
211 | type Ctx = ServerWorld; | ||
212 | type Output = TextDocumentEdit; | ||
213 | fn try_conv_with(self, world: &ServerWorld) -> Result<TextDocumentEdit> { | ||
214 | let text_document = VersionedTextDocumentIdentifier { | ||
215 | uri: self.file_id.try_conv_with(world)?, | ||
216 | version: None, | ||
217 | }; | ||
218 | let line_index = world.analysis().file_line_index(self.file_id); | ||
219 | let edits = self.edits | ||
220 | .into_iter() | ||
221 | .map_conv_with(&line_index) | ||
222 | .collect(); | ||
223 | Ok(TextDocumentEdit { text_document, edits }) | ||
224 | } | ||
225 | } | ||
226 | |||
227 | impl TryConvWith for FileSystemEdit { | ||
228 | type Ctx = ServerWorld; | ||
229 | type Output = req::FileSystemEdit; | ||
230 | fn try_conv_with(self, world: &ServerWorld) -> Result<req::FileSystemEdit> { | ||
231 | let res = match self { | ||
232 | FileSystemEdit::CreateFile { anchor, path } => { | ||
233 | let uri = world.file_id_to_uri(anchor)?; | ||
234 | let path = &path.as_str()[3..]; // strip `../` b/c url is weird | ||
235 | let uri = uri.join(path)?; | ||
236 | req::FileSystemEdit::CreateFile { uri } | ||
237 | }, | ||
238 | FileSystemEdit::MoveFile { file, path } => { | ||
239 | let src = world.file_id_to_uri(file)?; | ||
240 | let path = &path.as_str()[3..]; // strip `../` b/c url is weird | ||
241 | let dst = src.join(path)?; | ||
242 | req::FileSystemEdit::MoveFile { src, dst } | ||
243 | }, | ||
244 | }; | ||
245 | Ok(res) | ||
246 | } | ||
247 | } | ||
248 | |||
171 | pub fn to_location( | 249 | pub fn to_location( |
172 | file_id: FileId, | 250 | file_id: FileId, |
173 | range: TextRange, | 251 | range: TextRange, |
diff --git a/crates/server/src/main.rs b/crates/server/src/main.rs index 1a93af65b..6af8bf81b 100644 --- a/crates/server/src/main.rs +++ b/crates/server/src/main.rs | |||
@@ -35,7 +35,7 @@ use crossbeam_channel::bounded; | |||
35 | use flexi_logger::{Logger, Duplicate}; | 35 | use flexi_logger::{Logger, Duplicate}; |
36 | 36 | ||
37 | use ::{ | 37 | use ::{ |
38 | io::{Io, RawMsg, RawResponse, RawRequest, RawNotification}, | 38 | io::{Io, RawMsg, RawResponse, RawNotification}, |
39 | }; | 39 | }; |
40 | 40 | ||
41 | pub type Result<T> = ::std::result::Result<T, ::failure::Error>; | 41 | pub type Result<T> = ::std::result::Result<T, ::failure::Error>; |
@@ -109,7 +109,6 @@ fn initialize(io: &mut Io) -> Result<()> { | |||
109 | 109 | ||
110 | enum Task { | 110 | enum Task { |
111 | Respond(RawResponse), | 111 | Respond(RawResponse), |
112 | Request(RawRequest), | ||
113 | Notify(RawNotification), | 112 | Notify(RawNotification), |
114 | Die(::failure::Error), | 113 | Die(::failure::Error), |
115 | } | 114 | } |
diff --git a/crates/server/src/main_loop/handlers.rs b/crates/server/src/main_loop/handlers.rs index 3ee0873f4..45083b084 100644 --- a/crates/server/src/main_loop/handlers.rs +++ b/crates/server/src/main_loop/handlers.rs | |||
@@ -2,16 +2,13 @@ use std::collections::HashMap; | |||
2 | 2 | ||
3 | use languageserver_types::{ | 3 | use languageserver_types::{ |
4 | Diagnostic, DiagnosticSeverity, Url, DocumentSymbol, | 4 | Diagnostic, DiagnosticSeverity, Url, DocumentSymbol, |
5 | Command, TextDocumentIdentifier, WorkspaceEdit, | 5 | Command, TextDocumentIdentifier, |
6 | SymbolInformation, Position, Location, TextEdit, | 6 | SymbolInformation, Position, Location, TextEdit, |
7 | CompletionItem, InsertTextFormat, CompletionItemKind, | 7 | CompletionItem, InsertTextFormat, CompletionItemKind, |
8 | }; | 8 | }; |
9 | use serde_json::{to_value, from_value}; | 9 | use serde_json::to_value; |
10 | use url_serde; | 10 | use libanalysis::{Query, FileId, RunnableKind}; |
11 | use libanalysis::{self, Query, FileId}; | ||
12 | use libeditor; | ||
13 | use libsyntax2::{ | 11 | use libsyntax2::{ |
14 | TextUnit, | ||
15 | text_utils::contains_offset_nonstrict, | 12 | text_utils::contains_offset_nonstrict, |
16 | }; | 13 | }; |
17 | 14 | ||
@@ -26,8 +23,8 @@ pub fn handle_syntax_tree( | |||
26 | params: req::SyntaxTreeParams, | 23 | params: req::SyntaxTreeParams, |
27 | ) -> Result<String> { | 24 | ) -> Result<String> { |
28 | let id = params.text_document.try_conv_with(&world)?; | 25 | let id = params.text_document.try_conv_with(&world)?; |
29 | let file = world.analysis().file_syntax(id)?; | 26 | let res = world.analysis().syntax_tree(id); |
30 | Ok(libeditor::syntax_tree(&file)) | 27 | Ok(res) |
31 | } | 28 | } |
32 | 29 | ||
33 | pub fn handle_extend_selection( | 30 | pub fn handle_extend_selection( |
@@ -35,11 +32,11 @@ pub fn handle_extend_selection( | |||
35 | params: req::ExtendSelectionParams, | 32 | params: req::ExtendSelectionParams, |
36 | ) -> Result<req::ExtendSelectionResult> { | 33 | ) -> Result<req::ExtendSelectionResult> { |
37 | let file_id = params.text_document.try_conv_with(&world)?; | 34 | let file_id = params.text_document.try_conv_with(&world)?; |
38 | let file = world.analysis().file_syntax(file_id)?; | 35 | let file = world.analysis().file_syntax(file_id); |
39 | let line_index = world.analysis().file_line_index(file_id)?; | 36 | let line_index = world.analysis().file_line_index(file_id); |
40 | let selections = params.selections.into_iter() | 37 | let selections = params.selections.into_iter() |
41 | .map_conv_with(&line_index) | 38 | .map_conv_with(&line_index) |
42 | .map(|r| libeditor::extend_selection(&file, r).unwrap_or(r)) | 39 | .map(|r| world.analysis().extend_selection(&file, r)) |
43 | .map_conv_with(&line_index) | 40 | .map_conv_with(&line_index) |
44 | .collect(); | 41 | .collect(); |
45 | Ok(req::ExtendSelectionResult { selections }) | 42 | Ok(req::ExtendSelectionResult { selections }) |
@@ -50,13 +47,13 @@ pub fn handle_find_matching_brace( | |||
50 | params: req::FindMatchingBraceParams, | 47 | params: req::FindMatchingBraceParams, |
51 | ) -> Result<Vec<Position>> { | 48 | ) -> Result<Vec<Position>> { |
52 | let file_id = params.text_document.try_conv_with(&world)?; | 49 | let file_id = params.text_document.try_conv_with(&world)?; |
53 | let file = world.analysis().file_syntax(file_id)?; | 50 | let file = world.analysis().file_syntax(file_id); |
54 | let line_index = world.analysis().file_line_index(file_id)?; | 51 | let line_index = world.analysis().file_line_index(file_id); |
55 | let res = params.offsets | 52 | let res = params.offsets |
56 | .into_iter() | 53 | .into_iter() |
57 | .map_conv_with(&line_index) | 54 | .map_conv_with(&line_index) |
58 | .map(|offset| { | 55 | .map(|offset| { |
59 | libeditor::matching_brace(&file, offset).unwrap_or(offset) | 56 | world.analysis().matching_brace(&file, offset).unwrap_or(offset) |
60 | }) | 57 | }) |
61 | .map_conv_with(&line_index) | 58 | .map_conv_with(&line_index) |
62 | .collect(); | 59 | .collect(); |
@@ -66,13 +63,31 @@ pub fn handle_find_matching_brace( | |||
66 | pub fn handle_join_lines( | 63 | pub fn handle_join_lines( |
67 | world: ServerWorld, | 64 | world: ServerWorld, |
68 | params: req::JoinLinesParams, | 65 | params: req::JoinLinesParams, |
69 | ) -> Result<Vec<TextEdit>> { | 66 | ) -> Result<req::SourceChange> { |
70 | let file_id = params.text_document.try_conv_with(&world)?; | 67 | let file_id = params.text_document.try_conv_with(&world)?; |
71 | let file = world.analysis().file_syntax(file_id)?; | 68 | let line_index = world.analysis().file_line_index(file_id); |
72 | let line_index = world.analysis().file_line_index(file_id)?; | ||
73 | let range = params.range.conv_with(&line_index); | 69 | let range = params.range.conv_with(&line_index); |
74 | let res = libeditor::join_lines(&file, range); | 70 | world.analysis().join_lines(file_id, range) |
75 | Ok(res.edit.conv_with(&line_index)) | 71 | .try_conv_with(&world) |
72 | } | ||
73 | |||
74 | pub fn handle_on_type_formatting( | ||
75 | world: ServerWorld, | ||
76 | params: req::DocumentOnTypeFormattingParams, | ||
77 | ) -> Result<Option<Vec<TextEdit>>> { | ||
78 | if params.ch != "=" { | ||
79 | return Ok(None); | ||
80 | } | ||
81 | |||
82 | let file_id = params.text_document.try_conv_with(&world)?; | ||
83 | let line_index = world.analysis().file_line_index(file_id); | ||
84 | let offset = params.position.conv_with(&line_index); | ||
85 | let edits = match world.analysis().on_eq_typed(file_id, offset) { | ||
86 | None => return Ok(None), | ||
87 | Some(mut action) => action.source_file_edits.pop().unwrap().edits, | ||
88 | }; | ||
89 | let edits = edits.into_iter().map_conv_with(&line_index).collect(); | ||
90 | Ok(Some(edits)) | ||
76 | } | 91 | } |
77 | 92 | ||
78 | pub fn handle_document_symbol( | 93 | pub fn handle_document_symbol( |
@@ -80,12 +95,11 @@ pub fn handle_document_symbol( | |||
80 | params: req::DocumentSymbolParams, | 95 | params: req::DocumentSymbolParams, |
81 | ) -> Result<Option<req::DocumentSymbolResponse>> { | 96 | ) -> Result<Option<req::DocumentSymbolResponse>> { |
82 | let file_id = params.text_document.try_conv_with(&world)?; | 97 | let file_id = params.text_document.try_conv_with(&world)?; |
83 | let file = world.analysis().file_syntax(file_id)?; | 98 | let line_index = world.analysis().file_line_index(file_id); |
84 | let line_index = world.analysis().file_line_index(file_id)?; | ||
85 | 99 | ||
86 | let mut parents: Vec<(DocumentSymbol, Option<usize>)> = Vec::new(); | 100 | let mut parents: Vec<(DocumentSymbol, Option<usize>)> = Vec::new(); |
87 | 101 | ||
88 | for symbol in libeditor::file_structure(&file) { | 102 | for symbol in world.analysis().file_structure(file_id) { |
89 | let doc_symbol = DocumentSymbol { | 103 | let doc_symbol = DocumentSymbol { |
90 | name: symbol.label, | 104 | name: symbol.label, |
91 | detail: Some("".to_string()), | 105 | detail: Some("".to_string()), |
@@ -114,130 +128,6 @@ pub fn handle_document_symbol( | |||
114 | Ok(Some(req::DocumentSymbolResponse::Nested(res))) | 128 | Ok(Some(req::DocumentSymbolResponse::Nested(res))) |
115 | } | 129 | } |
116 | 130 | ||
117 | pub fn handle_code_action( | ||
118 | world: ServerWorld, | ||
119 | params: req::CodeActionParams, | ||
120 | ) -> Result<Option<Vec<Command>>> { | ||
121 | let file_id = params.text_document.try_conv_with(&world)?; | ||
122 | let file = world.analysis().file_syntax(file_id)?; | ||
123 | let line_index = world.analysis().file_line_index(file_id)?; | ||
124 | let offset = params.range.conv_with(&line_index).start(); | ||
125 | let mut res = Vec::new(); | ||
126 | |||
127 | let actions = &[ | ||
128 | (ActionId::FlipComma, libeditor::flip_comma(&file, offset).is_some()), | ||
129 | (ActionId::AddDerive, libeditor::add_derive(&file, offset).is_some()), | ||
130 | (ActionId::AddImpl, libeditor::add_impl(&file, offset).is_some()), | ||
131 | ]; | ||
132 | |||
133 | for (id, edit) in actions { | ||
134 | if *edit { | ||
135 | let cmd = apply_code_action_cmd(*id, params.text_document.clone(), offset); | ||
136 | res.push(cmd); | ||
137 | } | ||
138 | } | ||
139 | |||
140 | for (diag, quick_fix) in world.analysis().diagnostics(file_id)? { | ||
141 | let quick_fix = match quick_fix { | ||
142 | Some(quick_fix) => quick_fix, | ||
143 | None => continue, | ||
144 | }; | ||
145 | if !contains_offset_nonstrict(diag.range, offset) { | ||
146 | continue; | ||
147 | } | ||
148 | let mut ops = Vec::new(); | ||
149 | for op in quick_fix.fs_ops { | ||
150 | let op = match op { | ||
151 | libanalysis::FsOp::CreateFile { anchor, path } => { | ||
152 | let uri = world.file_id_to_uri(anchor)?; | ||
153 | let path = &path.as_str()[3..]; // strip `../` b/c url is weird | ||
154 | let uri = uri.join(path)?; | ||
155 | FsOp::CreateFile { uri } | ||
156 | }, | ||
157 | libanalysis::FsOp::MoveFile { file, path } => { | ||
158 | let src = world.file_id_to_uri(file)?; | ||
159 | let path = &path.as_str()[3..]; // strip `../` b/c url is weird | ||
160 | let dst = src.join(path)?; | ||
161 | FsOp::MoveFile { src, dst } | ||
162 | }, | ||
163 | }; | ||
164 | ops.push(op) | ||
165 | } | ||
166 | let cmd = Command { | ||
167 | title: "Create module".to_string(), | ||
168 | command: "libsyntax-rust.fsEdit".to_string(), | ||
169 | arguments: Some(vec![to_value(ops).unwrap()]), | ||
170 | }; | ||
171 | res.push(cmd) | ||
172 | } | ||
173 | return Ok(Some(res)); | ||
174 | } | ||
175 | |||
176 | #[derive(Serialize)] | ||
177 | #[serde(tag = "type", rename_all = "camelCase")] | ||
178 | enum FsOp { | ||
179 | CreateFile { | ||
180 | #[serde(with = "url_serde")] | ||
181 | uri: Url | ||
182 | }, | ||
183 | MoveFile { | ||
184 | #[serde(with = "url_serde")] | ||
185 | src: Url, | ||
186 | #[serde(with = "url_serde")] | ||
187 | dst: Url, | ||
188 | } | ||
189 | } | ||
190 | |||
191 | pub fn handle_runnables( | ||
192 | world: ServerWorld, | ||
193 | params: req::RunnablesParams, | ||
194 | ) -> Result<Vec<req::Runnable>> { | ||
195 | let file_id = params.text_document.try_conv_with(&world)?; | ||
196 | let file = world.analysis().file_syntax(file_id)?; | ||
197 | let line_index = world.analysis().file_line_index(file_id)?; | ||
198 | let offset = params.position.map(|it| it.conv_with(&line_index)); | ||
199 | let mut res = Vec::new(); | ||
200 | for runnable in libeditor::runnables(&file) { | ||
201 | if let Some(offset) = offset { | ||
202 | if !contains_offset_nonstrict(runnable.range, offset) { | ||
203 | continue; | ||
204 | } | ||
205 | } | ||
206 | |||
207 | let r = req::Runnable { | ||
208 | range: runnable.range.conv_with(&line_index), | ||
209 | label: match &runnable.kind { | ||
210 | libeditor::RunnableKind::Test { name } => | ||
211 | format!("test {}", name), | ||
212 | libeditor::RunnableKind::Bin => | ||
213 | "run binary".to_string(), | ||
214 | }, | ||
215 | bin: "cargo".to_string(), | ||
216 | args: match runnable.kind { | ||
217 | libeditor::RunnableKind::Test { name } => { | ||
218 | vec![ | ||
219 | "test".to_string(), | ||
220 | "--".to_string(), | ||
221 | name, | ||
222 | "--nocapture".to_string(), | ||
223 | ] | ||
224 | } | ||
225 | libeditor::RunnableKind::Bin => vec!["run".to_string()] | ||
226 | }, | ||
227 | env: { | ||
228 | let mut m = HashMap::new(); | ||
229 | m.insert( | ||
230 | "RUST_BACKTRACE".to_string(), | ||
231 | "short".to_string(), | ||
232 | ); | ||
233 | m | ||
234 | } | ||
235 | }; | ||
236 | res.push(r); | ||
237 | } | ||
238 | return Ok(res); | ||
239 | } | ||
240 | |||
241 | pub fn handle_workspace_symbol( | 131 | pub fn handle_workspace_symbol( |
242 | world: ServerWorld, | 132 | world: ServerWorld, |
243 | params: req::WorkspaceSymbolParams, | 133 | params: req::WorkspaceSymbolParams, |
@@ -265,8 +155,8 @@ pub fn handle_workspace_symbol( | |||
265 | 155 | ||
266 | fn exec_query(world: &ServerWorld, query: Query) -> Result<Vec<SymbolInformation>> { | 156 | fn exec_query(world: &ServerWorld, query: Query) -> Result<Vec<SymbolInformation>> { |
267 | let mut res = Vec::new(); | 157 | let mut res = Vec::new(); |
268 | for (file_id, symbol) in world.analysis().world_symbols(query) { | 158 | for (file_id, symbol) in world.analysis().symbol_search(query) { |
269 | let line_index = world.analysis().file_line_index(file_id)?; | 159 | let line_index = world.analysis().file_line_index(file_id); |
270 | let info = SymbolInformation { | 160 | let info = SymbolInformation { |
271 | name: symbol.name.to_string(), | 161 | name: symbol.name.to_string(), |
272 | kind: symbol.kind.conv(), | 162 | kind: symbol.kind.conv(), |
@@ -287,11 +177,11 @@ pub fn handle_goto_definition( | |||
287 | params: req::TextDocumentPositionParams, | 177 | params: req::TextDocumentPositionParams, |
288 | ) -> Result<Option<req::GotoDefinitionResponse>> { | 178 | ) -> Result<Option<req::GotoDefinitionResponse>> { |
289 | let file_id = params.text_document.try_conv_with(&world)?; | 179 | let file_id = params.text_document.try_conv_with(&world)?; |
290 | let line_index = world.analysis().file_line_index(file_id)?; | 180 | let line_index = world.analysis().file_line_index(file_id); |
291 | let offset = params.position.conv_with(&line_index); | 181 | let offset = params.position.conv_with(&line_index); |
292 | let mut res = Vec::new(); | 182 | let mut res = Vec::new(); |
293 | for (file_id, symbol) in world.analysis().approximately_resolve_symbol(file_id, offset)? { | 183 | for (file_id, symbol) in world.analysis().approximately_resolve_symbol(file_id, offset) { |
294 | let line_index = world.analysis().file_line_index(file_id)?; | 184 | let line_index = world.analysis().file_line_index(file_id); |
295 | let location = to_location( | 185 | let location = to_location( |
296 | file_id, symbol.node_range, | 186 | file_id, symbol.node_range, |
297 | &world, &line_index, | 187 | &world, &line_index, |
@@ -308,7 +198,7 @@ pub fn handle_parent_module( | |||
308 | let file_id = params.try_conv_with(&world)?; | 198 | let file_id = params.try_conv_with(&world)?; |
309 | let mut res = Vec::new(); | 199 | let mut res = Vec::new(); |
310 | for (file_id, symbol) in world.analysis().parent_module(file_id) { | 200 | for (file_id, symbol) in world.analysis().parent_module(file_id) { |
311 | let line_index = world.analysis().file_line_index(file_id)?; | 201 | let line_index = world.analysis().file_line_index(file_id); |
312 | let location = to_location( | 202 | let location = to_location( |
313 | file_id, symbol.node_range, | 203 | file_id, symbol.node_range, |
314 | &world, &line_index | 204 | &world, &line_index |
@@ -318,15 +208,71 @@ pub fn handle_parent_module( | |||
318 | Ok(res) | 208 | Ok(res) |
319 | } | 209 | } |
320 | 210 | ||
211 | pub fn handle_runnables( | ||
212 | world: ServerWorld, | ||
213 | params: req::RunnablesParams, | ||
214 | ) -> Result<Vec<req::Runnable>> { | ||
215 | let file_id = params.text_document.try_conv_with(&world)?; | ||
216 | let line_index = world.analysis().file_line_index(file_id); | ||
217 | let offset = params.position.map(|it| it.conv_with(&line_index)); | ||
218 | let mut res = Vec::new(); | ||
219 | for runnable in world.analysis().runnables(file_id) { | ||
220 | if let Some(offset) = offset { | ||
221 | if !contains_offset_nonstrict(runnable.range, offset) { | ||
222 | continue; | ||
223 | } | ||
224 | } | ||
225 | |||
226 | let r = req::Runnable { | ||
227 | range: runnable.range.conv_with(&line_index), | ||
228 | label: match &runnable.kind { | ||
229 | RunnableKind::Test { name } => | ||
230 | format!("test {}", name), | ||
231 | RunnableKind::Bin => | ||
232 | "run binary".to_string(), | ||
233 | }, | ||
234 | bin: "cargo".to_string(), | ||
235 | args: match runnable.kind { | ||
236 | RunnableKind::Test { name } => { | ||
237 | vec![ | ||
238 | "test".to_string(), | ||
239 | "--".to_string(), | ||
240 | name, | ||
241 | "--nocapture".to_string(), | ||
242 | ] | ||
243 | } | ||
244 | RunnableKind::Bin => vec!["run".to_string()] | ||
245 | }, | ||
246 | env: { | ||
247 | let mut m = HashMap::new(); | ||
248 | m.insert( | ||
249 | "RUST_BACKTRACE".to_string(), | ||
250 | "short".to_string(), | ||
251 | ); | ||
252 | m | ||
253 | } | ||
254 | }; | ||
255 | res.push(r); | ||
256 | } | ||
257 | return Ok(res); | ||
258 | } | ||
259 | |||
260 | pub fn handle_decorations( | ||
261 | world: ServerWorld, | ||
262 | params: TextDocumentIdentifier, | ||
263 | ) -> Result<Vec<Decoration>> { | ||
264 | let file_id = params.try_conv_with(&world)?; | ||
265 | Ok(highlight(&world, file_id)) | ||
266 | } | ||
267 | |||
321 | pub fn handle_completion( | 268 | pub fn handle_completion( |
322 | world: ServerWorld, | 269 | world: ServerWorld, |
323 | params: req::CompletionParams, | 270 | params: req::CompletionParams, |
324 | ) -> Result<Option<req::CompletionResponse>> { | 271 | ) -> Result<Option<req::CompletionResponse>> { |
325 | let file_id = params.text_document.try_conv_with(&world)?; | 272 | let file_id = params.text_document.try_conv_with(&world)?; |
326 | let file = world.analysis().file_syntax(file_id)?; | 273 | let line_index = world.analysis().file_line_index(file_id); |
327 | let line_index = world.analysis().file_line_index(file_id)?; | ||
328 | let offset = params.position.conv_with(&line_index); | 274 | let offset = params.position.conv_with(&line_index); |
329 | let items = match libeditor::scope_completion(&file, offset) { | 275 | let items = match world.analysis().completions(file_id, offset) { |
330 | None => return Ok(None), | 276 | None => return Ok(None), |
331 | Some(items) => items, | 277 | Some(items) => items, |
332 | }; | 278 | }; |
@@ -348,91 +294,33 @@ pub fn handle_completion( | |||
348 | Ok(Some(req::CompletionResponse::Array(items))) | 294 | Ok(Some(req::CompletionResponse::Array(items))) |
349 | } | 295 | } |
350 | 296 | ||
351 | pub fn handle_on_type_formatting( | 297 | pub fn handle_code_action( |
352 | world: ServerWorld, | 298 | world: ServerWorld, |
353 | params: req::DocumentOnTypeFormattingParams, | 299 | params: req::CodeActionParams, |
354 | ) -> Result<Option<Vec<TextEdit>>> { | 300 | ) -> Result<Option<Vec<Command>>> { |
355 | if params.ch != "=" { | ||
356 | return Ok(None); | ||
357 | } | ||
358 | |||
359 | let file_id = params.text_document.try_conv_with(&world)?; | 301 | let file_id = params.text_document.try_conv_with(&world)?; |
360 | let line_index = world.analysis().file_line_index(file_id)?; | 302 | let line_index = world.analysis().file_line_index(file_id); |
361 | let offset = params.position.conv_with(&line_index); | 303 | let offset = params.range.conv_with(&line_index).start(); |
362 | let file = world.analysis().file_syntax(file_id)?; | ||
363 | let action = match libeditor::on_eq_typed(&file, offset) { | ||
364 | None => return Ok(None), | ||
365 | Some(action) => action, | ||
366 | }; | ||
367 | Ok(Some(action.edit.conv_with(&line_index))) | ||
368 | } | ||
369 | |||
370 | pub fn handle_execute_command( | ||
371 | world: ServerWorld, | ||
372 | mut params: req::ExecuteCommandParams, | ||
373 | ) -> Result<(req::ApplyWorkspaceEditParams, Option<Position>)> { | ||
374 | if params.command.as_str() != "apply_code_action" { | ||
375 | bail!("unknown cmd: {:?}", params.command); | ||
376 | } | ||
377 | if params.arguments.len() != 1 { | ||
378 | bail!("expected single arg, got {}", params.arguments.len()); | ||
379 | } | ||
380 | let arg = params.arguments.pop().unwrap(); | ||
381 | let arg: ActionRequest = from_value(arg)?; | ||
382 | let file_id = arg.text_document.try_conv_with(&world)?; | ||
383 | let file = world.analysis().file_syntax(file_id)?; | ||
384 | let action_result = match arg.id { | ||
385 | ActionId::FlipComma => libeditor::flip_comma(&file, arg.offset).map(|f| f()), | ||
386 | ActionId::AddDerive => libeditor::add_derive(&file, arg.offset).map(|f| f()), | ||
387 | ActionId::AddImpl => libeditor::add_impl(&file, arg.offset).map(|f| f()), | ||
388 | }.ok_or_else(|| format_err!("command not applicable"))?; | ||
389 | let line_index = world.analysis().file_line_index(file_id)?; | ||
390 | let mut changes = HashMap::new(); | ||
391 | changes.insert( | ||
392 | arg.text_document.uri, | ||
393 | action_result.edit.conv_with(&line_index), | ||
394 | ); | ||
395 | let edit = WorkspaceEdit { | ||
396 | changes: Some(changes), | ||
397 | document_changes: None, | ||
398 | }; | ||
399 | let edit = req::ApplyWorkspaceEditParams { edit }; | ||
400 | let cursor_pos = action_result.cursor_position | ||
401 | .map(|off| off.conv_with(&line_index)); | ||
402 | Ok((edit, cursor_pos)) | ||
403 | } | ||
404 | 304 | ||
405 | #[derive(Serialize, Deserialize)] | 305 | let assists = world.analysis().assists(file_id, offset).into_iter(); |
406 | struct ActionRequest { | 306 | let fixes = world.analysis().diagnostics(file_id).into_iter() |
407 | id: ActionId, | 307 | .filter_map(|d| Some((d.range, d.fix?))) |
408 | text_document: TextDocumentIdentifier, | 308 | .filter(|(range, _fix)| contains_offset_nonstrict(*range, offset)) |
409 | offset: TextUnit, | 309 | .map(|(_range, fix)| fix); |
410 | } | ||
411 | 310 | ||
412 | fn apply_code_action_cmd(id: ActionId, doc: TextDocumentIdentifier, offset: TextUnit) -> Command { | 311 | let mut res = Vec::new(); |
413 | let action_request = ActionRequest { id, text_document: doc, offset }; | 312 | for source_edit in assists.chain(fixes) { |
414 | Command { | 313 | let title = source_edit.label.clone(); |
415 | title: id.title().to_string(), | 314 | let edit = source_edit.try_conv_with(&world)?; |
416 | command: "apply_code_action".to_string(), | 315 | let cmd = Command { |
417 | arguments: Some(vec![to_value(action_request).unwrap()]), | 316 | title, |
317 | command: "libsyntax-rust.applySourceChange".to_string(), | ||
318 | arguments: Some(vec![to_value(edit).unwrap()]), | ||
319 | }; | ||
320 | res.push(cmd); | ||
418 | } | 321 | } |
419 | } | ||
420 | 322 | ||
421 | #[derive(Serialize, Deserialize, Clone, Copy)] | 323 | Ok(Some(res)) |
422 | enum ActionId { | ||
423 | FlipComma, | ||
424 | AddDerive, | ||
425 | AddImpl, | ||
426 | } | ||
427 | |||
428 | impl ActionId { | ||
429 | fn title(&self) -> &'static str { | ||
430 | match *self { | ||
431 | ActionId::FlipComma => "Flip `,`", | ||
432 | ActionId::AddDerive => "Add `#[derive]`", | ||
433 | ActionId::AddImpl => "Add impl", | ||
434 | } | ||
435 | } | ||
436 | } | 324 | } |
437 | 325 | ||
438 | pub fn publish_diagnostics( | 326 | pub fn publish_diagnostics( |
@@ -440,28 +328,20 @@ pub fn publish_diagnostics( | |||
440 | uri: Url | 328 | uri: Url |
441 | ) -> Result<req::PublishDiagnosticsParams> { | 329 | ) -> Result<req::PublishDiagnosticsParams> { |
442 | let file_id = world.uri_to_file_id(&uri)?; | 330 | let file_id = world.uri_to_file_id(&uri)?; |
443 | let line_index = world.analysis().file_line_index(file_id)?; | 331 | let line_index = world.analysis().file_line_index(file_id); |
444 | let diagnostics = world.analysis().diagnostics(file_id)? | 332 | let diagnostics = world.analysis().diagnostics(file_id) |
445 | .into_iter() | 333 | .into_iter() |
446 | .map(|(d, _quick_fix)| Diagnostic { | 334 | .map(|d| Diagnostic { |
447 | range: d.range.conv_with(&line_index), | 335 | range: d.range.conv_with(&line_index), |
448 | severity: Some(DiagnosticSeverity::Error), | 336 | severity: Some(DiagnosticSeverity::Error), |
449 | code: None, | 337 | code: None, |
450 | source: Some("libsyntax2".to_string()), | 338 | source: Some("libsyntax2".to_string()), |
451 | message: d.msg, | 339 | message: d.message, |
452 | related_information: None, | 340 | related_information: None, |
453 | }).collect(); | 341 | }).collect(); |
454 | Ok(req::PublishDiagnosticsParams { uri, diagnostics }) | 342 | Ok(req::PublishDiagnosticsParams { uri, diagnostics }) |
455 | } | 343 | } |
456 | 344 | ||
457 | pub fn handle_decorations( | ||
458 | world: ServerWorld, | ||
459 | params: TextDocumentIdentifier, | ||
460 | ) -> Result<Vec<Decoration>> { | ||
461 | let file_id = params.try_conv_with(&world)?; | ||
462 | highlight(&world, file_id) | ||
463 | } | ||
464 | |||
465 | pub fn publish_decorations( | 345 | pub fn publish_decorations( |
466 | world: ServerWorld, | 346 | world: ServerWorld, |
467 | uri: Url | 347 | uri: Url |
@@ -469,18 +349,16 @@ pub fn publish_decorations( | |||
469 | let file_id = world.uri_to_file_id(&uri)?; | 349 | let file_id = world.uri_to_file_id(&uri)?; |
470 | Ok(req::PublishDecorationsParams { | 350 | Ok(req::PublishDecorationsParams { |
471 | uri, | 351 | uri, |
472 | decorations: highlight(&world, file_id)? | 352 | decorations: highlight(&world, file_id), |
473 | }) | 353 | }) |
474 | } | 354 | } |
475 | 355 | ||
476 | fn highlight(world: &ServerWorld, file_id: FileId) -> Result<Vec<Decoration>> { | 356 | fn highlight(world: &ServerWorld, file_id: FileId) -> Vec<Decoration> { |
477 | let file = world.analysis().file_syntax(file_id)?; | 357 | let line_index = world.analysis().file_line_index(file_id); |
478 | let line_index = world.analysis().file_line_index(file_id)?; | 358 | world.analysis().highlight(file_id) |
479 | let res = libeditor::highlight(&file) | ||
480 | .into_iter() | 359 | .into_iter() |
481 | .map(|h| Decoration { | 360 | .map(|h| Decoration { |
482 | range: h.range.conv_with(&line_index), | 361 | range: h.range.conv_with(&line_index), |
483 | tag: h.tag, | 362 | tag: h.tag, |
484 | }).collect(); | 363 | }).collect() |
485 | Ok(res) | ||
486 | } | 364 | } |
diff --git a/crates/server/src/main_loop/mod.rs b/crates/server/src/main_loop/mod.rs index accb13878..0f66248a5 100644 --- a/crates/server/src/main_loop/mod.rs +++ b/crates/server/src/main_loop/mod.rs | |||
@@ -7,7 +7,6 @@ use std::{ | |||
7 | use threadpool::ThreadPool; | 7 | use threadpool::ThreadPool; |
8 | use crossbeam_channel::{Sender, Receiver}; | 8 | use crossbeam_channel::{Sender, Receiver}; |
9 | use languageserver_types::Url; | 9 | use languageserver_types::Url; |
10 | use serde_json::to_value; | ||
11 | 10 | ||
12 | use { | 11 | use { |
13 | req, dispatch, | 12 | req, dispatch, |
@@ -15,24 +14,6 @@ use { | |||
15 | io::{Io, RawMsg, RawRequest, RawNotification}, | 14 | io::{Io, RawMsg, RawRequest, RawNotification}, |
16 | vfs::FileEvent, | 15 | vfs::FileEvent, |
17 | server_world::{ServerWorldState, ServerWorld}, | 16 | server_world::{ServerWorldState, ServerWorld}, |
18 | main_loop::handlers::{ | ||
19 | handle_syntax_tree, | ||
20 | handle_extend_selection, | ||
21 | publish_diagnostics, | ||
22 | publish_decorations, | ||
23 | handle_document_symbol, | ||
24 | handle_code_action, | ||
25 | handle_execute_command, | ||
26 | handle_workspace_symbol, | ||
27 | handle_goto_definition, | ||
28 | handle_find_matching_brace, | ||
29 | handle_parent_module, | ||
30 | handle_join_lines, | ||
31 | handle_completion, | ||
32 | handle_runnables, | ||
33 | handle_decorations, | ||
34 | handle_on_type_formatting, | ||
35 | }, | ||
36 | }; | 17 | }; |
37 | 18 | ||
38 | pub(super) fn main_loop( | 19 | pub(super) fn main_loop( |
@@ -45,7 +26,6 @@ pub(super) fn main_loop( | |||
45 | info!("server initialized, serving requests"); | 26 | info!("server initialized, serving requests"); |
46 | let mut state = ServerWorldState::new(); | 27 | let mut state = ServerWorldState::new(); |
47 | 28 | ||
48 | let mut next_request_id = 0; | ||
49 | let mut pending_requests: HashSet<u64> = HashSet::new(); | 29 | let mut pending_requests: HashSet<u64> = HashSet::new(); |
50 | let mut fs_events_receiver = Some(&fs_events_receiver); | 30 | let mut fs_events_receiver = Some(&fs_events_receiver); |
51 | loop { | 31 | loop { |
@@ -78,12 +58,6 @@ pub(super) fn main_loop( | |||
78 | } | 58 | } |
79 | Event::Task(task) => { | 59 | Event::Task(task) => { |
80 | match task { | 60 | match task { |
81 | Task::Request(mut request) => { | ||
82 | request.id = next_request_id; | ||
83 | pending_requests.insert(next_request_id); | ||
84 | next_request_id += 1; | ||
85 | io.send(RawMsg::Request(request)); | ||
86 | } | ||
87 | Task::Respond(response) => | 61 | Task::Respond(response) => |
88 | io.send(RawMsg::Response(response)), | 62 | io.send(RawMsg::Response(response)), |
89 | Task::Notify(n) => | 63 | Task::Notify(n) => |
@@ -125,79 +99,26 @@ fn on_request( | |||
125 | sender: &Sender<Task>, | 99 | sender: &Sender<Task>, |
126 | req: RawRequest, | 100 | req: RawRequest, |
127 | ) -> Result<bool> { | 101 | ) -> Result<bool> { |
128 | let mut req = Some(req); | 102 | let mut pool_dispatcher = PoolDispatcher { |
129 | handle_request_on_threadpool::<req::SyntaxTree>( | 103 | req: Some(req), |
130 | &mut req, pool, world, sender, handle_syntax_tree, | 104 | pool, world, sender |
131 | )?; | 105 | }; |
132 | handle_request_on_threadpool::<req::ExtendSelection>( | 106 | pool_dispatcher |
133 | &mut req, pool, world, sender, handle_extend_selection, | 107 | .on::<req::SyntaxTree>(handlers::handle_syntax_tree)? |
134 | )?; | 108 | .on::<req::ExtendSelection>(handlers::handle_extend_selection)? |
135 | handle_request_on_threadpool::<req::FindMatchingBrace>( | 109 | .on::<req::FindMatchingBrace>(handlers::handle_find_matching_brace)? |
136 | &mut req, pool, world, sender, handle_find_matching_brace, | 110 | .on::<req::JoinLines>(handlers::handle_join_lines)? |
137 | )?; | 111 | .on::<req::OnTypeFormatting>(handlers::handle_on_type_formatting)? |
138 | handle_request_on_threadpool::<req::DocumentSymbolRequest>( | 112 | .on::<req::DocumentSymbolRequest>(handlers::handle_document_symbol)? |
139 | &mut req, pool, world, sender, handle_document_symbol, | 113 | .on::<req::WorkspaceSymbol>(handlers::handle_workspace_symbol)? |
140 | )?; | 114 | .on::<req::GotoDefinition>(handlers::handle_goto_definition)? |
141 | handle_request_on_threadpool::<req::CodeActionRequest>( | 115 | .on::<req::ParentModule>(handlers::handle_parent_module)? |
142 | &mut req, pool, world, sender, handle_code_action, | 116 | .on::<req::Runnables>(handlers::handle_runnables)? |
143 | )?; | 117 | .on::<req::DecorationsRequest>(handlers::handle_decorations)? |
144 | handle_request_on_threadpool::<req::Runnables>( | 118 | .on::<req::Completion>(handlers::handle_completion)? |
145 | &mut req, pool, world, sender, handle_runnables, | 119 | .on::<req::CodeActionRequest>(handlers::handle_code_action)?; |
146 | )?; | ||
147 | handle_request_on_threadpool::<req::WorkspaceSymbol>( | ||
148 | &mut req, pool, world, sender, handle_workspace_symbol, | ||
149 | )?; | ||
150 | handle_request_on_threadpool::<req::GotoDefinition>( | ||
151 | &mut req, pool, world, sender, handle_goto_definition, | ||
152 | )?; | ||
153 | handle_request_on_threadpool::<req::Completion>( | ||
154 | &mut req, pool, world, sender, handle_completion, | ||
155 | )?; | ||
156 | handle_request_on_threadpool::<req::ParentModule>( | ||
157 | &mut req, pool, world, sender, handle_parent_module, | ||
158 | )?; | ||
159 | handle_request_on_threadpool::<req::JoinLines>( | ||
160 | &mut req, pool, world, sender, handle_join_lines, | ||
161 | )?; | ||
162 | handle_request_on_threadpool::<req::DecorationsRequest>( | ||
163 | &mut req, pool, world, sender, handle_decorations, | ||
164 | )?; | ||
165 | handle_request_on_threadpool::<req::OnTypeFormatting>( | ||
166 | &mut req, pool, world, sender, handle_on_type_formatting, | ||
167 | )?; | ||
168 | dispatch::handle_request::<req::ExecuteCommand, _>(&mut req, |params, resp| { | ||
169 | io.send(RawMsg::Response(resp.into_response(Ok(None))?)); | ||
170 | |||
171 | let world = world.snapshot(); | ||
172 | let sender = sender.clone(); | ||
173 | pool.execute(move || { | ||
174 | let (edit, cursor) = match handle_execute_command(world, params) { | ||
175 | Ok(res) => res, | ||
176 | Err(e) => return sender.send(Task::Die(e)), | ||
177 | }; | ||
178 | match to_value(edit) { | ||
179 | Err(e) => return sender.send(Task::Die(e.into())), | ||
180 | Ok(params) => { | ||
181 | let request = RawRequest { | ||
182 | id: 0, | ||
183 | method: <req::ApplyWorkspaceEdit as req::ClientRequest>::METHOD.to_string(), | ||
184 | params, | ||
185 | }; | ||
186 | sender.send(Task::Request(request)) | ||
187 | } | ||
188 | } | ||
189 | if let Some(cursor) = cursor { | ||
190 | let request = RawRequest { | ||
191 | id: 0, | ||
192 | method: <req::MoveCursor as req::ClientRequest>::METHOD.to_string(), | ||
193 | params: to_value(cursor).unwrap(), | ||
194 | }; | ||
195 | sender.send(Task::Request(request)) | ||
196 | } | ||
197 | }); | ||
198 | Ok(()) | ||
199 | })?; | ||
200 | 120 | ||
121 | let mut req = pool_dispatcher.req; | ||
201 | let mut shutdown = false; | 122 | let mut shutdown = false; |
202 | dispatch::handle_request::<req::Shutdown, _>(&mut req, |(), resp| { | 123 | dispatch::handle_request::<req::Shutdown, _>(&mut req, |(), resp| { |
203 | let resp = resp.into_response(Ok(()))?; | 124 | let resp = resp.into_response(Ok(()))?; |
@@ -273,27 +194,33 @@ fn on_notification( | |||
273 | Ok(()) | 194 | Ok(()) |
274 | } | 195 | } |
275 | 196 | ||
276 | fn handle_request_on_threadpool<R: req::ClientRequest>( | 197 | struct PoolDispatcher<'a> { |
277 | req: &mut Option<RawRequest>, | 198 | req: Option<RawRequest>, |
278 | pool: &ThreadPool, | 199 | pool: &'a ThreadPool, |
279 | world: &ServerWorldState, | 200 | world: &'a ServerWorldState, |
280 | sender: &Sender<Task>, | 201 | sender: &'a Sender<Task>, |
281 | f: fn(ServerWorld, R::Params) -> Result<R::Result>, | 202 | } |
282 | ) -> Result<()> | 203 | |
283 | { | 204 | impl<'a> PoolDispatcher<'a> { |
284 | dispatch::handle_request::<R, _>(req, |params, resp| { | 205 | fn on<'b, R: req::ClientRequest>(&'b mut self, f: fn(ServerWorld, R::Params) -> Result<R::Result>) -> Result<&'b mut Self> { |
285 | let world = world.snapshot(); | 206 | let world = self.world; |
286 | let sender = sender.clone(); | 207 | let sender = self.sender; |
287 | pool.execute(move || { | 208 | let pool = self.pool; |
288 | let res = f(world, params); | 209 | dispatch::handle_request::<R, _>(&mut self.req, |params, resp| { |
289 | let task = match resp.into_response(res) { | 210 | let world = world.snapshot(); |
290 | Ok(resp) => Task::Respond(resp), | 211 | let sender = sender.clone(); |
291 | Err(e) => Task::Die(e), | 212 | pool.execute(move || { |
292 | }; | 213 | let res = f(world, params); |
293 | sender.send(task); | 214 | let task = match resp.into_response(res) { |
294 | }); | 215 | Ok(resp) => Task::Respond(resp), |
295 | Ok(()) | 216 | Err(e) => Task::Die(e), |
296 | }) | 217 | }; |
218 | sender.send(task); | ||
219 | }); | ||
220 | Ok(()) | ||
221 | })?; | ||
222 | Ok(self) | ||
223 | } | ||
297 | } | 224 | } |
298 | 225 | ||
299 | fn update_file_notifications_on_threadpool( | 226 | fn update_file_notifications_on_threadpool( |
@@ -303,7 +230,7 @@ fn update_file_notifications_on_threadpool( | |||
303 | uri: Url, | 230 | uri: Url, |
304 | ) { | 231 | ) { |
305 | pool.execute(move || { | 232 | pool.execute(move || { |
306 | match publish_diagnostics(world.clone(), uri.clone()) { | 233 | match handlers::publish_diagnostics(world.clone(), uri.clone()) { |
307 | Err(e) => { | 234 | Err(e) => { |
308 | error!("failed to compute diagnostics: {:?}", e) | 235 | error!("failed to compute diagnostics: {:?}", e) |
309 | } | 236 | } |
@@ -312,7 +239,7 @@ fn update_file_notifications_on_threadpool( | |||
312 | sender.send(Task::Notify(not)); | 239 | sender.send(Task::Notify(not)); |
313 | } | 240 | } |
314 | } | 241 | } |
315 | match publish_decorations(world, uri) { | 242 | match handlers::publish_decorations(world, uri) { |
316 | Err(e) => { | 243 | Err(e) => { |
317 | error!("failed to compute decorations: {:?}", e) | 244 | error!("failed to compute decorations: {:?}", e) |
318 | } | 245 | } |
diff --git a/crates/server/src/req.rs b/crates/server/src/req.rs index 881069b1f..c6d2f2efb 100644 --- a/crates/server/src/req.rs +++ b/crates/server/src/req.rs | |||
@@ -15,6 +15,7 @@ pub use languageserver_types::{ | |||
15 | TextEdit, | 15 | TextEdit, |
16 | CompletionParams, CompletionResponse, | 16 | CompletionParams, CompletionResponse, |
17 | DocumentOnTypeFormattingParams, | 17 | DocumentOnTypeFormattingParams, |
18 | TextDocumentEdit, | ||
18 | }; | 19 | }; |
19 | 20 | ||
20 | 21 | ||
@@ -115,14 +116,6 @@ pub struct Decoration { | |||
115 | pub tag: &'static str | 116 | pub tag: &'static str |
116 | } | 117 | } |
117 | 118 | ||
118 | pub enum MoveCursor {} | ||
119 | |||
120 | impl Request for MoveCursor { | ||
121 | type Params = Position; | ||
122 | type Result = (); | ||
123 | const METHOD: &'static str = "m/moveCursor"; | ||
124 | } | ||
125 | |||
126 | pub enum ParentModule {} | 119 | pub enum ParentModule {} |
127 | 120 | ||
128 | impl Request for ParentModule { | 121 | impl Request for ParentModule { |
@@ -135,7 +128,7 @@ pub enum JoinLines {} | |||
135 | 128 | ||
136 | impl Request for JoinLines { | 129 | impl Request for JoinLines { |
137 | type Params = JoinLinesParams; | 130 | type Params = JoinLinesParams; |
138 | type Result = Vec<TextEdit>; | 131 | type Result = SourceChange; |
139 | const METHOD: &'static str = "m/joinLines"; | 132 | const METHOD: &'static str = "m/joinLines"; |
140 | } | 133 | } |
141 | 134 | ||
@@ -170,3 +163,27 @@ pub struct Runnable { | |||
170 | pub args: Vec<String>, | 163 | pub args: Vec<String>, |
171 | pub env: HashMap<String, String>, | 164 | pub env: HashMap<String, String>, |
172 | } | 165 | } |
166 | |||
167 | #[derive(Serialize, Debug)] | ||
168 | #[serde(rename_all = "camelCase")] | ||
169 | pub struct SourceChange { | ||
170 | pub label: String, | ||
171 | pub source_file_edits: Vec<TextDocumentEdit>, | ||
172 | pub file_system_edits: Vec<FileSystemEdit>, | ||
173 | pub cursor_position: Option<TextDocumentPositionParams>, | ||
174 | } | ||
175 | |||
176 | #[derive(Serialize, Debug)] | ||
177 | #[serde(tag = "type", rename_all = "camelCase")] | ||
178 | pub enum FileSystemEdit { | ||
179 | CreateFile { | ||
180 | #[serde(with = "url_serde")] | ||
181 | uri: Url | ||
182 | }, | ||
183 | MoveFile { | ||
184 | #[serde(with = "url_serde")] | ||
185 | src: Url, | ||
186 | #[serde(with = "url_serde")] | ||
187 | dst: Url, | ||
188 | } | ||
189 | } | ||
diff --git a/crates/server/src/server_world.rs b/crates/server/src/server_world.rs index 1982e727f..6c85914ba 100644 --- a/crates/server/src/server_world.rs +++ b/crates/server/src/server_world.rs | |||
@@ -5,7 +5,7 @@ use std::{ | |||
5 | }; | 5 | }; |
6 | 6 | ||
7 | use languageserver_types::Url; | 7 | use languageserver_types::Url; |
8 | use libanalysis::{FileId, WorldState, World}; | 8 | use libanalysis::{FileId, WorldState, Analysis}; |
9 | 9 | ||
10 | use { | 10 | use { |
11 | Result, | 11 | Result, |
@@ -22,7 +22,7 @@ pub struct ServerWorldState { | |||
22 | 22 | ||
23 | #[derive(Clone)] | 23 | #[derive(Clone)] |
24 | pub struct ServerWorld { | 24 | pub struct ServerWorld { |
25 | pub analysis: World, | 25 | pub analysis: Analysis, |
26 | pub path_map: PathMap, | 26 | pub path_map: PathMap, |
27 | } | 27 | } |
28 | 28 | ||
@@ -91,14 +91,14 @@ impl ServerWorldState { | |||
91 | 91 | ||
92 | pub fn snapshot(&self) -> ServerWorld { | 92 | pub fn snapshot(&self) -> ServerWorld { |
93 | ServerWorld { | 93 | ServerWorld { |
94 | analysis: self.analysis.snapshot(self.path_map.clone()), | 94 | analysis: self.analysis.analysis(self.path_map.clone()), |
95 | path_map: self.path_map.clone() | 95 | path_map: self.path_map.clone() |
96 | } | 96 | } |
97 | } | 97 | } |
98 | } | 98 | } |
99 | 99 | ||
100 | impl ServerWorld { | 100 | impl ServerWorld { |
101 | pub fn analysis(&self) -> &World { | 101 | pub fn analysis(&self) -> &Analysis { |
102 | &self.analysis | 102 | &self.analysis |
103 | } | 103 | } |
104 | 104 | ||