aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2020-05-21 18:50:23 +0100
committerAleksey Kladov <[email protected]>2020-05-21 19:05:33 +0100
commit5b5ebec440841ee98a0aa70b71a135d94f5ca077 (patch)
tree5accb5fce10496334b49ed5a823d321572b375b4 /crates
parentba6cf638fbf3d0a025e804f2d354d91abc8afd28 (diff)
Formalize JoinLines protocol extension
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_ide/src/lib.rs9
-rw-r--r--crates/ra_text_edit/src/lib.rs33
-rw-r--r--crates/rust-analyzer/src/caps.rs9
-rw-r--r--crates/rust-analyzer/src/lsp_ext.rs6
-rw-r--r--crates/rust-analyzer/src/main_loop/handlers.rs30
-rw-r--r--crates/rust-analyzer/src/to_proto.rs7
6 files changed, 60 insertions, 34 deletions
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs
index d0aeb3ba7..97ff67ee8 100644
--- a/crates/ra_ide/src/lib.rs
+++ b/crates/ra_ide/src/lib.rs
@@ -89,6 +89,7 @@ pub use ra_ide_db::{
89 symbol_index::Query, 89 symbol_index::Query,
90 RootDatabase, 90 RootDatabase,
91}; 91};
92pub use ra_text_edit::{Indel, TextEdit};
92 93
93pub type Cancelable<T> = Result<T, Canceled>; 94pub type Cancelable<T> = Result<T, Canceled>;
94 95
@@ -285,14 +286,10 @@ impl Analysis {
285 286
286 /// Returns an edit to remove all newlines in the range, cleaning up minor 287 /// Returns an edit to remove all newlines in the range, cleaning up minor
287 /// stuff like trailing commas. 288 /// stuff like trailing commas.
288 pub fn join_lines(&self, frange: FileRange) -> Cancelable<SourceChange> { 289 pub fn join_lines(&self, frange: FileRange) -> Cancelable<TextEdit> {
289 self.with_db(|db| { 290 self.with_db(|db| {
290 let parse = db.parse(frange.file_id); 291 let parse = db.parse(frange.file_id);
291 let file_edit = SourceFileEdit { 292 join_lines::join_lines(&parse.tree(), frange.range)
292 file_id: frange.file_id,
293 edit: join_lines::join_lines(&parse.tree(), frange.range),
294 };
295 SourceChange::source_file_edit("Join lines", file_edit)
296 }) 293 })
297 } 294 }
298 295
diff --git a/crates/ra_text_edit/src/lib.rs b/crates/ra_text_edit/src/lib.rs
index 199fd1096..25554f583 100644
--- a/crates/ra_text_edit/src/lib.rs
+++ b/crates/ra_text_edit/src/lib.rs
@@ -17,7 +17,7 @@ pub struct Indel {
17 pub delete: TextRange, 17 pub delete: TextRange,
18} 18}
19 19
20#[derive(Debug, Clone)] 20#[derive(Default, Debug, Clone)]
21pub struct TextEdit { 21pub struct TextEdit {
22 indels: Vec<Indel>, 22 indels: Vec<Indel>,
23} 23}
@@ -64,14 +64,6 @@ impl TextEdit {
64 builder.finish() 64 builder.finish()
65 } 65 }
66 66
67 pub(crate) fn from_indels(mut indels: Vec<Indel>) -> TextEdit {
68 indels.sort_by_key(|a| (a.delete.start(), a.delete.end()));
69 for (a1, a2) in indels.iter().zip(indels.iter().skip(1)) {
70 assert!(a1.delete.end() <= a2.delete.start())
71 }
72 TextEdit { indels }
73 }
74
75 pub fn len(&self) -> usize { 67 pub fn len(&self) -> usize {
76 self.indels.len() 68 self.indels.len()
77 } 69 }
@@ -122,6 +114,17 @@ impl TextEdit {
122 *text = buf 114 *text = buf
123 } 115 }
124 116
117 pub fn union(&mut self, other: TextEdit) -> Result<(), TextEdit> {
118 // FIXME: can be done without allocating intermediate vector
119 let mut all = self.iter().chain(other.iter()).collect::<Vec<_>>();
120 if !check_disjoint(&mut all) {
121 return Err(other);
122 }
123 self.indels.extend(other.indels);
124 assert!(check_disjoint(&mut self.indels));
125 Ok(())
126 }
127
125 pub fn apply_to_offset(&self, offset: TextSize) -> Option<TextSize> { 128 pub fn apply_to_offset(&self, offset: TextSize) -> Option<TextSize> {
126 let mut res = offset; 129 let mut res = offset;
127 for indel in self.indels.iter() { 130 for indel in self.indels.iter() {
@@ -149,9 +152,19 @@ impl TextEditBuilder {
149 self.indels.push(Indel::insert(offset, text)) 152 self.indels.push(Indel::insert(offset, text))
150 } 153 }
151 pub fn finish(self) -> TextEdit { 154 pub fn finish(self) -> TextEdit {
152 TextEdit::from_indels(self.indels) 155 let mut indels = self.indels;
156 assert!(check_disjoint(&mut indels));
157 TextEdit { indels }
153 } 158 }
154 pub fn invalidates_offset(&self, offset: TextSize) -> bool { 159 pub fn invalidates_offset(&self, offset: TextSize) -> bool {
155 self.indels.iter().any(|indel| indel.delete.contains_inclusive(offset)) 160 self.indels.iter().any(|indel| indel.delete.contains_inclusive(offset))
156 } 161 }
157} 162}
163
164fn check_disjoint(indels: &mut [impl std::borrow::Borrow<Indel>]) -> bool {
165 indels.sort_by_key(|indel| (indel.borrow().delete.start(), indel.borrow().delete.end()));
166 indels
167 .iter()
168 .zip(indels.iter().skip(1))
169 .all(|(l, r)| l.borrow().delete.end() <= r.borrow().delete.start())
170}
diff --git a/crates/rust-analyzer/src/caps.rs b/crates/rust-analyzer/src/caps.rs
index 110c9a442..4c417c270 100644
--- a/crates/rust-analyzer/src/caps.rs
+++ b/crates/rust-analyzer/src/caps.rs
@@ -1,8 +1,6 @@
1//! Advertizes the capabilities of the LSP Server. 1//! Advertizes the capabilities of the LSP Server.
2use std::env; 2use std::env;
3 3
4use crate::semantic_tokens;
5
6use lsp_types::{ 4use lsp_types::{
7 CallHierarchyServerCapability, CodeActionOptions, CodeActionProviderCapability, 5 CallHierarchyServerCapability, CodeActionOptions, CodeActionProviderCapability,
8 CodeLensOptions, CompletionOptions, DocumentOnTypeFormattingOptions, 6 CodeLensOptions, CompletionOptions, DocumentOnTypeFormattingOptions,
@@ -12,6 +10,9 @@ use lsp_types::{
12 ServerCapabilities, SignatureHelpOptions, TextDocumentSyncCapability, TextDocumentSyncKind, 10 ServerCapabilities, SignatureHelpOptions, TextDocumentSyncCapability, TextDocumentSyncKind,
13 TextDocumentSyncOptions, TypeDefinitionProviderCapability, WorkDoneProgressOptions, 11 TextDocumentSyncOptions, TypeDefinitionProviderCapability, WorkDoneProgressOptions,
14}; 12};
13use serde_json::json;
14
15use crate::semantic_tokens;
15 16
16pub fn server_capabilities() -> ServerCapabilities { 17pub fn server_capabilities() -> ServerCapabilities {
17 ServerCapabilities { 18 ServerCapabilities {
@@ -91,6 +92,8 @@ pub fn server_capabilities() -> ServerCapabilities {
91 } 92 }
92 .into(), 93 .into(),
93 ), 94 ),
94 experimental: Default::default(), 95 experimental: Some(json!({
96 "joinLines": true,
97 })),
95 } 98 }
96} 99}
diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs
index 3c7bd609d..1bb1b02ab 100644
--- a/crates/rust-analyzer/src/lsp_ext.rs
+++ b/crates/rust-analyzer/src/lsp_ext.rs
@@ -87,15 +87,15 @@ pub enum JoinLines {}
87 87
88impl Request for JoinLines { 88impl Request for JoinLines {
89 type Params = JoinLinesParams; 89 type Params = JoinLinesParams;
90 type Result = SourceChange; 90 type Result = Vec<lsp_types::TextEdit>;
91 const METHOD: &'static str = "rust-analyzer/joinLines"; 91 const METHOD: &'static str = "experimental/joinLines";
92} 92}
93 93
94#[derive(Deserialize, Serialize, Debug)] 94#[derive(Deserialize, Serialize, Debug)]
95#[serde(rename_all = "camelCase")] 95#[serde(rename_all = "camelCase")]
96pub struct JoinLinesParams { 96pub struct JoinLinesParams {
97 pub text_document: TextDocumentIdentifier, 97 pub text_document: TextDocumentIdentifier,
98 pub range: Range, 98 pub ranges: Vec<Range>,
99} 99}
100 100
101pub enum OnEnter {} 101pub enum OnEnter {}
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs
index fcf08cd79..121964718 100644
--- a/crates/rust-analyzer/src/main_loop/handlers.rs
+++ b/crates/rust-analyzer/src/main_loop/handlers.rs
@@ -15,10 +15,11 @@ use lsp_types::{
15 DocumentSymbol, FoldingRange, FoldingRangeParams, Hover, HoverContents, Location, 15 DocumentSymbol, FoldingRange, FoldingRangeParams, Hover, HoverContents, Location,
16 MarkupContent, MarkupKind, Position, PrepareRenameResponse, Range, RenameParams, 16 MarkupContent, MarkupKind, Position, PrepareRenameResponse, Range, RenameParams,
17 SemanticTokensParams, SemanticTokensRangeParams, SemanticTokensRangeResult, 17 SemanticTokensParams, SemanticTokensRangeParams, SemanticTokensRangeResult,
18 SemanticTokensResult, SymbolInformation, TextDocumentIdentifier, TextEdit, Url, WorkspaceEdit, 18 SemanticTokensResult, SymbolInformation, TextDocumentIdentifier, Url, WorkspaceEdit,
19}; 19};
20use ra_ide::{ 20use ra_ide::{
21 Assist, FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind, SearchScope, 21 Assist, FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind, SearchScope,
22 TextEdit,
22}; 23};
23use ra_prof::profile; 24use ra_prof::profile;
24use ra_project_model::TargetKind; 25use ra_project_model::TargetKind;
@@ -149,11 +150,24 @@ pub fn handle_find_matching_brace(
149pub fn handle_join_lines( 150pub fn handle_join_lines(
150 world: WorldSnapshot, 151 world: WorldSnapshot,
151 params: lsp_ext::JoinLinesParams, 152 params: lsp_ext::JoinLinesParams,
152) -> Result<lsp_ext::SourceChange> { 153) -> Result<Vec<lsp_types::TextEdit>> {
153 let _p = profile("handle_join_lines"); 154 let _p = profile("handle_join_lines");
154 let frange = from_proto::file_range(&world, params.text_document, params.range)?; 155 let file_id = from_proto::file_id(&world, &params.text_document.uri)?;
155 let source_change = world.analysis().join_lines(frange)?; 156 let line_index = world.analysis().file_line_index(file_id)?;
156 to_proto::source_change(&world, source_change) 157 let line_endings = world.file_line_endings(file_id);
158 let mut res = TextEdit::default();
159 for range in params.ranges {
160 let range = from_proto::text_range(&line_index, range);
161 let edit = world.analysis().join_lines(FileRange { file_id, range })?;
162 match res.union(edit) {
163 Ok(()) => (),
164 Err(_edit) => {
165 // just ignore overlapping edits
166 }
167 }
168 }
169 let res = to_proto::text_edit_vec(&line_index, line_endings, res);
170 Ok(res)
157} 171}
158 172
159pub fn handle_on_enter( 173pub fn handle_on_enter(
@@ -172,7 +186,7 @@ pub fn handle_on_enter(
172pub fn handle_on_type_formatting( 186pub fn handle_on_type_formatting(
173 world: WorldSnapshot, 187 world: WorldSnapshot,
174 params: lsp_types::DocumentOnTypeFormattingParams, 188 params: lsp_types::DocumentOnTypeFormattingParams,
175) -> Result<Option<Vec<TextEdit>>> { 189) -> Result<Option<Vec<lsp_types::TextEdit>>> {
176 let _p = profile("handle_on_type_formatting"); 190 let _p = profile("handle_on_type_formatting");
177 let mut position = from_proto::file_position(&world, params.text_document_position)?; 191 let mut position = from_proto::file_position(&world, params.text_document_position)?;
178 let line_index = world.analysis().file_line_index(position.file_id)?; 192 let line_index = world.analysis().file_line_index(position.file_id)?;
@@ -618,7 +632,7 @@ pub fn handle_references(
618pub fn handle_formatting( 632pub fn handle_formatting(
619 world: WorldSnapshot, 633 world: WorldSnapshot,
620 params: DocumentFormattingParams, 634 params: DocumentFormattingParams,
621) -> Result<Option<Vec<TextEdit>>> { 635) -> Result<Option<Vec<lsp_types::TextEdit>>> {
622 let _p = profile("handle_formatting"); 636 let _p = profile("handle_formatting");
623 let file_id = from_proto::file_id(&world, &params.text_document.uri)?; 637 let file_id = from_proto::file_id(&world, &params.text_document.uri)?;
624 let file = world.analysis().file_text(file_id)?; 638 let file = world.analysis().file_text(file_id)?;
@@ -685,7 +699,7 @@ pub fn handle_formatting(
685 } 699 }
686 } 700 }
687 701
688 Ok(Some(vec![TextEdit { 702 Ok(Some(vec![lsp_types::TextEdit {
689 range: Range::new(Position::new(0, 0), end_position), 703 range: Range::new(Position::new(0, 0), end_position),
690 new_text: captured_stdout, 704 new_text: captured_stdout,
691 }])) 705 }]))
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index 617197963..f6f4bb134 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -2,12 +2,11 @@
2use ra_db::{FileId, FileRange}; 2use ra_db::{FileId, FileRange};
3use ra_ide::{ 3use ra_ide::{
4 Assist, CompletionItem, CompletionItemKind, Documentation, FileSystemEdit, Fold, FoldKind, 4 Assist, CompletionItem, CompletionItemKind, Documentation, FileSystemEdit, Fold, FoldKind,
5 FunctionSignature, Highlight, HighlightModifier, HighlightTag, HighlightedRange, InlayHint, 5 FunctionSignature, Highlight, HighlightModifier, HighlightTag, HighlightedRange, Indel,
6 InlayKind, InsertTextFormat, LineIndex, NavigationTarget, ReferenceAccess, Severity, 6 InlayHint, InlayKind, InsertTextFormat, LineIndex, NavigationTarget, ReferenceAccess, Severity,
7 SourceChange, SourceFileEdit, 7 SourceChange, SourceFileEdit, TextEdit,
8}; 8};
9use ra_syntax::{SyntaxKind, TextRange, TextSize}; 9use ra_syntax::{SyntaxKind, TextRange, TextSize};
10use ra_text_edit::{Indel, TextEdit};
11use ra_vfs::LineEndings; 10use ra_vfs::LineEndings;
12 11
13use crate::{lsp_ext, semantic_tokens, world::WorldSnapshot, Result}; 12use crate::{lsp_ext, semantic_tokens, world::WorldSnapshot, Result};