diff options
author | bors[bot] <bors[bot]@users.noreply.github.com> | 2019-01-03 13:54:43 +0000 |
---|---|---|
committer | bors[bot] <bors[bot]@users.noreply.github.com> | 2019-01-03 13:54:43 +0000 |
commit | 5443205fdd9f4886cc88ad15c3a6061ffa90ca19 (patch) | |
tree | 380084b87b5b6b9174f0a8eb6a7652942e862e23 | |
parent | aea2183799e7975d3d9000cec9bb9a3c001a3d4e (diff) | |
parent | 7d9e02e5a201fe997f98b6908daadd820d4a6593 (diff) |
Merge #415
415: use LSP file system operations r=matklad a=vemoo
implements #131
I've replaced `source_file_edits` and `file_system_edits` with `workspace_edit` because [`WorkspacEdit`](https://docs.rs/languageserver-types/0.53.1/languageserver_types/struct.WorkspaceEdit.html) can represent both.
I only use `document_changes` because `changes` cannot represent file system operations.
But if the client doesn't have the `workspace.workspaceEdit.resourceOperations` capability `WorkspaceEdit` cannot replace the current `FileSystemEdit`. Can we assume that the client will support it?
I also adapted the extension code to make use of the new response type, but only for vscode, i don't know if changes have to be made for the emacs part.
Co-authored-by: Bernardo <[email protected]>
-rw-r--r-- | crates/ra_lsp_server/src/conv.rs | 51 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/req.rs | 22 | ||||
-rw-r--r-- | crates/ra_lsp_server/tests/heavy_tests/main.rs | 23 | ||||
-rw-r--r-- | editors/code/src/commands/apply_source_change.ts | 43 |
4 files changed, 61 insertions, 78 deletions
diff --git a/crates/ra_lsp_server/src/conv.rs b/crates/ra_lsp_server/src/conv.rs index 1107ffc8b..7230fb101 100644 --- a/crates/ra_lsp_server/src/conv.rs +++ b/crates/ra_lsp_server/src/conv.rs | |||
@@ -1,11 +1,16 @@ | |||
1 | use languageserver_types::{ | 1 | use languageserver_types::{ |
2 | self, Location, Position, Range, SymbolKind, TextDocumentEdit, TextDocumentIdentifier, | 2 | self, CreateFile, DocumentChangeOperation, DocumentChanges, InsertTextFormat, Location, |
3 | TextDocumentItem, TextDocumentPositionParams, Url, VersionedTextDocumentIdentifier, InsertTextFormat, | 3 | Position, Range, RenameFile, ResourceOp, SymbolKind, TextDocumentEdit, TextDocumentIdentifier, |
4 | TextDocumentItem, TextDocumentPositionParams, Url, VersionedTextDocumentIdentifier, | ||
5 | WorkspaceEdit, | ||
4 | }; | 6 | }; |
5 | use ra_analysis::{FileId, FileSystemEdit, SourceChange, SourceFileEdit, FilePosition,FileRange, CompletionItem, CompletionItemKind, InsertText, NavigationTarget}; | 7 | use ra_analysis::{ |
6 | use ra_editor::{LineCol, LineIndex, translate_offset_with_edit}; | 8 | CompletionItem, CompletionItemKind, FileId, FilePosition, FileRange, FileSystemEdit, |
7 | use ra_text_edit::{AtomTextEdit, TextEdit}; | 9 | InsertText, NavigationTarget, SourceChange, SourceFileEdit, |
10 | }; | ||
11 | use ra_editor::{translate_offset_with_edit, LineCol, LineIndex}; | ||
8 | use ra_syntax::{SyntaxKind, TextRange, TextUnit}; | 12 | use ra_syntax::{SyntaxKind, TextRange, TextUnit}; |
13 | use ra_text_edit::{AtomTextEdit, TextEdit}; | ||
9 | 14 | ||
10 | use crate::{req, server_world::ServerWorld, Result}; | 15 | use crate::{req, server_world::ServerWorld, Result}; |
11 | 16 | ||
@@ -49,7 +54,7 @@ impl Conv for CompletionItemKind { | |||
49 | type Output = ::languageserver_types::CompletionItemKind; | 54 | type Output = ::languageserver_types::CompletionItemKind; |
50 | 55 | ||
51 | fn conv(self) -> <Self as Conv>::Output { | 56 | fn conv(self) -> <Self as Conv>::Output { |
52 | use ::languageserver_types::CompletionItemKind::*; | 57 | use languageserver_types::CompletionItemKind::*; |
53 | match self { | 58 | match self { |
54 | CompletionItemKind::Keyword => Keyword, | 59 | CompletionItemKind::Keyword => Keyword, |
55 | CompletionItemKind::Snippet => Snippet, | 60 | CompletionItemKind::Snippet => Snippet, |
@@ -266,12 +271,20 @@ impl TryConvWith for SourceChange { | |||
266 | }) | 271 | }) |
267 | } | 272 | } |
268 | }; | 273 | }; |
269 | let source_file_edits = self.source_file_edits.try_conv_with(world)?; | 274 | let mut document_changes: Vec<DocumentChangeOperation> = Vec::new(); |
270 | let file_system_edits = self.file_system_edits.try_conv_with(world)?; | 275 | for resource_op in self.file_system_edits.try_conv_with(world)? { |
276 | document_changes.push(DocumentChangeOperation::Op(resource_op)); | ||
277 | } | ||
278 | for text_document_edit in self.source_file_edits.try_conv_with(world)? { | ||
279 | document_changes.push(DocumentChangeOperation::Edit(text_document_edit)); | ||
280 | } | ||
281 | let workspace_edit = WorkspaceEdit { | ||
282 | changes: None, | ||
283 | document_changes: Some(DocumentChanges::Operations(document_changes)), | ||
284 | }; | ||
271 | Ok(req::SourceChange { | 285 | Ok(req::SourceChange { |
272 | label: self.label, | 286 | label: self.label, |
273 | source_file_edits, | 287 | workspace_edit, |
274 | file_system_edits, | ||
275 | cursor_position, | 288 | cursor_position, |
276 | }) | 289 | }) |
277 | } | 290 | } |
@@ -301,21 +314,25 @@ impl TryConvWith for SourceFileEdit { | |||
301 | 314 | ||
302 | impl TryConvWith for FileSystemEdit { | 315 | impl TryConvWith for FileSystemEdit { |
303 | type Ctx = ServerWorld; | 316 | type Ctx = ServerWorld; |
304 | type Output = req::FileSystemEdit; | 317 | type Output = ResourceOp; |
305 | fn try_conv_with(self, world: &ServerWorld) -> Result<req::FileSystemEdit> { | 318 | fn try_conv_with(self, world: &ServerWorld) -> Result<ResourceOp> { |
306 | let res = match self { | 319 | let res = match self { |
307 | FileSystemEdit::CreateFile { source_root, path } => { | 320 | FileSystemEdit::CreateFile { source_root, path } => { |
308 | let uri = world.path_to_uri(source_root, &path)?; | 321 | let uri = world.path_to_uri(source_root, &path)?.to_string(); |
309 | req::FileSystemEdit::CreateFile { uri } | 322 | ResourceOp::Create(CreateFile { uri, options: None }) |
310 | } | 323 | } |
311 | FileSystemEdit::MoveFile { | 324 | FileSystemEdit::MoveFile { |
312 | src, | 325 | src, |
313 | dst_source_root, | 326 | dst_source_root, |
314 | dst_path, | 327 | dst_path, |
315 | } => { | 328 | } => { |
316 | let src = world.file_id_to_uri(src)?; | 329 | let old_uri = world.file_id_to_uri(src)?.to_string(); |
317 | let dst = world.path_to_uri(dst_source_root, &dst_path)?; | 330 | let new_uri = world.path_to_uri(dst_source_root, &dst_path)?.to_string(); |
318 | req::FileSystemEdit::MoveFile { src, dst } | 331 | ResourceOp::Rename(RenameFile { |
332 | old_uri, | ||
333 | new_uri, | ||
334 | options: None, | ||
335 | }) | ||
319 | } | 336 | } |
320 | }; | 337 | }; |
321 | Ok(res) | 338 | Ok(res) |
diff --git a/crates/ra_lsp_server/src/req.rs b/crates/ra_lsp_server/src/req.rs index 747ab8a8c..b41e90328 100644 --- a/crates/ra_lsp_server/src/req.rs +++ b/crates/ra_lsp_server/src/req.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | use serde::{Serialize, Deserialize}; | ||
2 | use languageserver_types::{Location, Position, Range, TextDocumentIdentifier, Url}; | 1 | use languageserver_types::{Location, Position, Range, TextDocumentIdentifier, Url}; |
3 | use rustc_hash::FxHashMap; | 2 | use rustc_hash::FxHashMap; |
3 | use serde::{Deserialize, Serialize}; | ||
4 | use url_serde; | 4 | use url_serde; |
5 | 5 | ||
6 | pub use languageserver_types::{ | 6 | pub use languageserver_types::{ |
@@ -8,7 +8,7 @@ pub use languageserver_types::{ | |||
8 | CompletionResponse, DocumentOnTypeFormattingParams, DocumentSymbolParams, | 8 | CompletionResponse, DocumentOnTypeFormattingParams, DocumentSymbolParams, |
9 | DocumentSymbolResponse, ExecuteCommandParams, Hover, InitializeResult, | 9 | DocumentSymbolResponse, ExecuteCommandParams, Hover, InitializeResult, |
10 | PublishDiagnosticsParams, ReferenceParams, SignatureHelp, TextDocumentEdit, | 10 | PublishDiagnosticsParams, ReferenceParams, SignatureHelp, TextDocumentEdit, |
11 | TextDocumentPositionParams, TextEdit, WorkspaceSymbolParams, | 11 | TextDocumentPositionParams, TextEdit, WorkspaceEdit, WorkspaceSymbolParams, |
12 | }; | 12 | }; |
13 | 13 | ||
14 | pub enum SyntaxTree {} | 14 | pub enum SyntaxTree {} |
@@ -151,26 +151,10 @@ pub struct Runnable { | |||
151 | #[serde(rename_all = "camelCase")] | 151 | #[serde(rename_all = "camelCase")] |
152 | pub struct SourceChange { | 152 | pub struct SourceChange { |
153 | pub label: String, | 153 | pub label: String, |
154 | pub source_file_edits: Vec<TextDocumentEdit>, | 154 | pub workspace_edit: WorkspaceEdit, |
155 | pub file_system_edits: Vec<FileSystemEdit>, | ||
156 | pub cursor_position: Option<TextDocumentPositionParams>, | 155 | pub cursor_position: Option<TextDocumentPositionParams>, |
157 | } | 156 | } |
158 | 157 | ||
159 | #[derive(Serialize, Debug)] | ||
160 | #[serde(tag = "type", rename_all = "camelCase")] | ||
161 | pub enum FileSystemEdit { | ||
162 | CreateFile { | ||
163 | #[serde(with = "url_serde")] | ||
164 | uri: Url, | ||
165 | }, | ||
166 | MoveFile { | ||
167 | #[serde(with = "url_serde")] | ||
168 | src: Url, | ||
169 | #[serde(with = "url_serde")] | ||
170 | dst: Url, | ||
171 | }, | ||
172 | } | ||
173 | |||
174 | pub enum InternalFeedback {} | 158 | pub enum InternalFeedback {} |
175 | 159 | ||
176 | impl Notification for InternalFeedback { | 160 | impl Notification for InternalFeedback { |
diff --git a/crates/ra_lsp_server/tests/heavy_tests/main.rs b/crates/ra_lsp_server/tests/heavy_tests/main.rs index b0e1e65b6..4cae44eab 100644 --- a/crates/ra_lsp_server/tests/heavy_tests/main.rs +++ b/crates/ra_lsp_server/tests/heavy_tests/main.rs | |||
@@ -1,8 +1,12 @@ | |||
1 | mod support; | 1 | mod support; |
2 | 2 | ||
3 | use languageserver_types::{ | ||
4 | CodeActionContext, DocumentFormattingParams, FormattingOptions, Position, Range, | ||
5 | }; | ||
6 | use ra_lsp_server::req::{ | ||
7 | CodeActionParams, CodeActionRequest, Formatting, Runnables, RunnablesParams, | ||
8 | }; | ||
3 | use serde_json::json; | 9 | use serde_json::json; |
4 | use ra_lsp_server::req::{Runnables, RunnablesParams, CodeActionRequest, CodeActionParams, Formatting}; | ||
5 | use languageserver_types::{Position, Range, CodeActionContext, DocumentFormattingParams, FormattingOptions}; | ||
6 | 10 | ||
7 | use crate::support::project; | 11 | use crate::support::project; |
8 | 12 | ||
@@ -203,14 +207,15 @@ fn main() {} | |||
203 | "arguments": [ | 207 | "arguments": [ |
204 | { | 208 | { |
205 | "cursorPosition": null, | 209 | "cursorPosition": null, |
206 | "fileSystemEdits": [ | 210 | "workspaceEdit": { |
207 | { | 211 | "documentChanges": [ |
208 | "type": "createFile", | 212 | { |
213 | "kind": "create", | ||
209 | "uri": "file:///[..]/src/bar.rs" | 214 | "uri": "file:///[..]/src/bar.rs" |
210 | } | 215 | } |
211 | ], | 216 | ] |
212 | "label": "create module", | 217 | }, |
213 | "sourceFileEdits": [] | 218 | "label": "create module" |
214 | } | 219 | } |
215 | ], | 220 | ], |
216 | "command": "ra-lsp.applySourceChange", | 221 | "command": "ra-lsp.applySourceChange", |
diff --git a/editors/code/src/commands/apply_source_change.ts b/editors/code/src/commands/apply_source_change.ts index cf921e3ac..10dbf72c0 100644 --- a/editors/code/src/commands/apply_source_change.ts +++ b/editors/code/src/commands/apply_source_change.ts | |||
@@ -3,46 +3,23 @@ import * as lc from 'vscode-languageclient'; | |||
3 | 3 | ||
4 | import { Server } from '../server'; | 4 | import { Server } from '../server'; |
5 | 5 | ||
6 | interface FileSystemEdit { | ||
7 | type: string; | ||
8 | uri?: string; | ||
9 | src?: string; | ||
10 | dst?: string; | ||
11 | } | ||
12 | |||
13 | export interface SourceChange { | 6 | export interface SourceChange { |
14 | label: string; | 7 | label: string; |
15 | sourceFileEdits: lc.TextDocumentEdit[]; | 8 | workspaceEdit: lc.WorkspaceEdit; |
16 | fileSystemEdits: FileSystemEdit[]; | ||
17 | cursorPosition?: lc.TextDocumentPositionParams; | 9 | cursorPosition?: lc.TextDocumentPositionParams; |
18 | } | 10 | } |
19 | 11 | ||
20 | export async function handle(change: SourceChange) { | 12 | export async function handle(change: SourceChange) { |
21 | const wsEdit = new vscode.WorkspaceEdit(); | 13 | const wsEdit = Server.client.protocol2CodeConverter.asWorkspaceEdit(change.workspaceEdit); |
22 | for (const sourceEdit of change.sourceFileEdits) { | ||
23 | const uri = Server.client.protocol2CodeConverter.asUri( | ||
24 | sourceEdit.textDocument.uri | ||
25 | ); | ||
26 | const edits = Server.client.protocol2CodeConverter.asTextEdits( | ||
27 | sourceEdit.edits | ||
28 | ); | ||
29 | wsEdit.set(uri, edits); | ||
30 | } | ||
31 | let created; | 14 | let created; |
32 | let moved; | 15 | let moved; |
33 | for (const fsEdit of change.fileSystemEdits) { | 16 | if (change.workspaceEdit.documentChanges) { |
34 | switch (fsEdit.type) { | 17 | for (const docChange of change.workspaceEdit.documentChanges) { |
35 | case 'createFile': | 18 | if (lc.CreateFile.is(docChange)) { |
36 | const uri = vscode.Uri.parse(fsEdit.uri!); | 19 | created = docChange.uri; |
37 | wsEdit.createFile(uri); | 20 | } else if (lc.RenameFile.is(docChange)) { |
38 | created = uri; | 21 | moved = docChange.newUri; |
39 | break; | 22 | } |
40 | case 'moveFile': | ||
41 | const src = vscode.Uri.parse(fsEdit.src!); | ||
42 | const dst = vscode.Uri.parse(fsEdit.dst!); | ||
43 | wsEdit.renameFile(src, dst); | ||
44 | moved = dst; | ||
45 | break; | ||
46 | } | 23 | } |
47 | } | 24 | } |
48 | const toOpen = created || moved; | 25 | const toOpen = created || moved; |
@@ -65,6 +42,6 @@ export async function handle(change: SourceChange) { | |||
65 | if (!editor.selection.isEmpty) { | 42 | if (!editor.selection.isEmpty) { |
66 | return; | 43 | return; |
67 | } | 44 | } |
68 | editor!.selection = new vscode.Selection(position, position); | 45 | editor.selection = new vscode.Selection(position, position); |
69 | } | 46 | } |
70 | } | 47 | } |