aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_analysis/src/lib.rs6
-rw-r--r--crates/ra_editor/src/lib.rs2
-rw-r--r--crates/ra_editor/src/typing.rs101
-rw-r--r--crates/ra_lsp_server/src/conv.rs41
-rw-r--r--crates/ra_lsp_server/src/main_loop/handlers.rs14
-rw-r--r--crates/ra_lsp_server/src/main_loop/mod.rs1
-rw-r--r--crates/ra_lsp_server/src/req.rs8
7 files changed, 169 insertions, 4 deletions
diff --git a/crates/ra_analysis/src/lib.rs b/crates/ra_analysis/src/lib.rs
index b4c7db476..f6ceb7eb2 100644
--- a/crates/ra_analysis/src/lib.rs
+++ b/crates/ra_analysis/src/lib.rs
@@ -184,6 +184,12 @@ impl Analysis {
184 let file = self.imp.file_syntax(file_id); 184 let file = self.imp.file_syntax(file_id);
185 SourceChange::from_local_edit(file_id, "join lines", ra_editor::join_lines(&file, range)) 185 SourceChange::from_local_edit(file_id, "join lines", ra_editor::join_lines(&file, range))
186 } 186 }
187 pub fn on_enter(&self, file_id: FileId, offset: TextUnit) -> Option<SourceChange> {
188 let file = self.imp.file_syntax(file_id);
189 let edit = ra_editor::on_enter(&file, offset)?;
190 let res = SourceChange::from_local_edit(file_id, "on enter", edit);
191 Some(res)
192 }
187 pub fn on_eq_typed(&self, file_id: FileId, offset: TextUnit) -> Option<SourceChange> { 193 pub fn on_eq_typed(&self, file_id: FileId, offset: TextUnit) -> Option<SourceChange> {
188 let file = self.imp.file_syntax(file_id); 194 let file = self.imp.file_syntax(file_id);
189 Some(SourceChange::from_local_edit(file_id, "add semicolon", ra_editor::on_eq_typed(&file, offset)?)) 195 Some(SourceChange::from_local_edit(file_id, "add semicolon", ra_editor::on_eq_typed(&file, offset)?))
diff --git a/crates/ra_editor/src/lib.rs b/crates/ra_editor/src/lib.rs
index 2a801f7da..fe0045378 100644
--- a/crates/ra_editor/src/lib.rs
+++ b/crates/ra_editor/src/lib.rs
@@ -35,7 +35,7 @@ pub use self::{
35 flip_comma, add_derive, add_impl, 35 flip_comma, add_derive, add_impl,
36 introduce_variable, 36 introduce_variable,
37 }, 37 },
38 typing::{join_lines, on_eq_typed}, 38 typing::{join_lines, on_eq_typed, on_enter},
39 completion::{scope_completion, CompletionItem}, 39 completion::{scope_completion, CompletionItem},
40 folding_ranges::{Fold, FoldKind, folding_ranges} 40 folding_ranges::{Fold, FoldKind, folding_ranges}
41}; 41};
diff --git a/crates/ra_editor/src/typing.rs b/crates/ra_editor/src/typing.rs
index 512076941..3384389d1 100644
--- a/crates/ra_editor/src/typing.rs
+++ b/crates/ra_editor/src/typing.rs
@@ -4,7 +4,7 @@ use ra_syntax::{
4 TextUnit, TextRange, SyntaxNodeRef, File, AstNode, SyntaxKind, 4 TextUnit, TextRange, SyntaxNodeRef, File, AstNode, SyntaxKind,
5 ast, 5 ast,
6 algo::{ 6 algo::{
7 find_covering_node, 7 find_covering_node, find_leaf_at_offset, LeafAtOffset,
8 }, 8 },
9 text_utils::{intersect, contains_offset_nonstrict}, 9 text_utils::{intersect, contains_offset_nonstrict},
10 SyntaxKind::*, 10 SyntaxKind::*,
@@ -56,6 +56,58 @@ pub fn join_lines(file: &File, range: TextRange) -> LocalEdit {
56 } 56 }
57} 57}
58 58
59pub fn on_enter(file: &File, offset: TextUnit) -> Option<LocalEdit> {
60 let comment = find_leaf_at_offset(file.syntax(), offset).left_biased().filter(|it| it.kind() == COMMENT)?;
61 let prefix = comment_preffix(comment)?;
62 if offset < comment.range().start() + TextUnit::of_str(prefix) {
63 return None;
64 }
65
66 let indent = node_indent(file, comment)?;
67 let inserted = format!("\n{}{}", indent, prefix);
68 let cursor_position = offset + TextUnit::of_str(&inserted);
69 let mut edit = EditBuilder::new();
70 edit.insert(offset, inserted);
71 Some(LocalEdit {
72 edit: edit.finish(),
73 cursor_position: Some(cursor_position),
74 })
75}
76
77fn comment_preffix(comment: SyntaxNodeRef) -> Option<&'static str> {
78 let text = comment.leaf_text().unwrap();
79 let res = if text.starts_with("///") {
80 "/// "
81 } else if text.starts_with("//!") {
82 "//! "
83 } else if text.starts_with("//") {
84 "// "
85 } else {
86 return None;
87 };
88 Some(res)
89}
90
91fn node_indent<'a>(file: &'a File, node: SyntaxNodeRef) -> Option<&'a str> {
92 let ws = match find_leaf_at_offset(file.syntax(), node.range().start()) {
93 LeafAtOffset::Between(l, r) => {
94 assert!(r == node);
95 l
96 }
97 LeafAtOffset::Single(n) => {
98 assert!(n == node);
99 return Some("")
100 }
101 LeafAtOffset::None => unreachable!(),
102 };
103 if ws.kind() != WHITESPACE {
104 return None;
105 }
106 let text = ws.leaf_text().unwrap();
107 let pos = text.as_str().rfind('\n').map(|it| it + 1).unwrap_or(0);
108 Some(&text[pos..])
109}
110
59pub fn on_eq_typed(file: &File, offset: TextUnit) -> Option<LocalEdit> { 111pub fn on_eq_typed(file: &File, offset: TextUnit) -> Option<LocalEdit> {
60 let let_stmt: ast::LetStmt = find_node_at_offset(file.syntax(), offset)?; 112 let let_stmt: ast::LetStmt = find_node_at_offset(file.syntax(), offset)?;
61 if let_stmt.has_semi() { 113 if let_stmt.has_semi() {
@@ -187,7 +239,7 @@ fn compute_ws(left: SyntaxNodeRef, right: SyntaxNodeRef) -> &'static str {
187#[cfg(test)] 239#[cfg(test)]
188mod tests { 240mod tests {
189 use super::*; 241 use super::*;
190 use test_utils::{check_action, extract_range, extract_offset}; 242 use test_utils::{check_action, extract_range, extract_offset, add_cursor};
191 243
192 fn check_join_lines(before: &str, after: &str) { 244 fn check_join_lines(before: &str, after: &str) {
193 check_action(before, after, |file, offset| { 245 check_action(before, after, |file, offset| {
@@ -344,4 +396,49 @@ fn foo() {
344 // } 396 // }
345 // "); 397 // ");
346 } 398 }
399
400 #[test]
401 fn test_on_enter() {
402 fn apply_on_enter(before: &str) -> Option<String> {
403 let (offset, before) = extract_offset(before);
404 let file = File::parse(&before);
405 let result = on_enter(&file, offset)?;
406 let actual = result.edit.apply(&before);
407 let actual = add_cursor(&actual, result.cursor_position.unwrap());
408 Some(actual)
409 }
410
411 fn do_check(before: &str, after: &str) {
412 let actual = apply_on_enter(before).unwrap();
413 assert_eq_text!(after, &actual);
414 }
415
416 fn do_check_noop(text: &str) {
417 assert!(apply_on_enter(text).is_none())
418 }
419
420 do_check(r"
421/// Some docs<|>
422fn foo() {
423}
424", r"
425/// Some docs
426/// <|>
427fn foo() {
428}
429");
430 do_check(r"
431impl S {
432 /// Some<|> docs.
433 fn foo() {}
434}
435", r"
436impl S {
437 /// Some
438 /// <|> docs.
439 fn foo() {}
440}
441");
442 do_check_noop(r"<|>//! docz");
443 }
347} 444}
diff --git a/crates/ra_lsp_server/src/conv.rs b/crates/ra_lsp_server/src/conv.rs
index 759e5e914..08a656569 100644
--- a/crates/ra_lsp_server/src/conv.rs
+++ b/crates/ra_lsp_server/src/conv.rs
@@ -190,9 +190,13 @@ impl TryConvWith for SourceChange {
190 None => None, 190 None => None,
191 Some(pos) => { 191 Some(pos) => {
192 let line_index = world.analysis().file_line_index(pos.file_id); 192 let line_index = world.analysis().file_line_index(pos.file_id);
193 let edits = self.source_file_edits.iter().find(|it| it.file_id == pos.file_id)
194 .map(|it| it.edits.as_slice()).unwrap_or(&[]);
195 let line_col = translate_offset_with_edit(&*line_index, pos.offset, edits);
196 let position = Position::new(line_col.line as u64, u32::from(line_col.col) as u64);
193 Some(TextDocumentPositionParams { 197 Some(TextDocumentPositionParams {
194 text_document: TextDocumentIdentifier::new(pos.file_id.try_conv_with(world)?), 198 text_document: TextDocumentIdentifier::new(pos.file_id.try_conv_with(world)?),
195 position: pos.offset.conv_with(&line_index), 199 position,
196 }) 200 })
197 } 201 }
198 }; 202 };
@@ -207,6 +211,41 @@ impl TryConvWith for SourceChange {
207 } 211 }
208} 212}
209 213
214// HACK: we should translate offset to line/column using linde_index *with edits applied*.
215// A naive version of this function would be to apply `edits` to the original text,
216// construct a new line index and use that, but it would be slow.
217//
218// Writing fast & correct version is issue #105, let's use a quick hack in the meantime
219fn translate_offset_with_edit(
220 pre_edit_index: &LineIndex,
221 offset: TextUnit,
222 edits: &[AtomEdit],
223) -> LineCol {
224 let fallback = pre_edit_index.line_col(offset);
225 let edit = match edits.first() {
226 None => return fallback,
227 Some(edit) => edit
228 };
229 let end_offset = edit.delete.start() + TextUnit::of_str(&edit.insert);
230 if !(edit.delete.start() <= offset && offset <= end_offset) {
231 return fallback
232 }
233 let rel_offset = offset - edit.delete.start();
234 let in_edit_line_col = LineIndex::new(&edit.insert).line_col(rel_offset);
235 let edit_line_col = pre_edit_index.line_col(edit.delete.start());
236 if in_edit_line_col.line == 0 {
237 LineCol {
238 line: edit_line_col.line,
239 col: edit_line_col.col + in_edit_line_col.col,
240 }
241 } else {
242 LineCol {
243 line: edit_line_col.line + in_edit_line_col.line,
244 col: in_edit_line_col.col,
245 }
246 }
247}
248
210impl TryConvWith for SourceFileEdit { 249impl TryConvWith for SourceFileEdit {
211 type Ctx = ServerWorld; 250 type Ctx = ServerWorld;
212 type Output = TextDocumentEdit; 251 type Output = TextDocumentEdit;
diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs
index 79a54183e..725036cc7 100644
--- a/crates/ra_lsp_server/src/main_loop/handlers.rs
+++ b/crates/ra_lsp_server/src/main_loop/handlers.rs
@@ -77,6 +77,20 @@ pub fn handle_join_lines(
77 .try_conv_with(&world) 77 .try_conv_with(&world)
78} 78}
79 79
80pub fn handle_on_enter(
81 world: ServerWorld,
82 params: req::TextDocumentPositionParams,
83 _token: JobToken,
84) -> Result<Option<req::SourceChange>> {
85 let file_id = params.text_document.try_conv_with(&world)?;
86 let line_index = world.analysis().file_line_index(file_id);
87 let offset = params.position.conv_with(&line_index);
88 match world.analysis().on_enter(file_id, offset) {
89 None => Ok(None),
90 Some(edit) => Ok(Some(edit.try_conv_with(&world)?))
91 }
92}
93
80pub fn handle_on_type_formatting( 94pub fn handle_on_type_formatting(
81 world: ServerWorld, 95 world: ServerWorld,
82 params: req::DocumentOnTypeFormattingParams, 96 params: req::DocumentOnTypeFormattingParams,
diff --git a/crates/ra_lsp_server/src/main_loop/mod.rs b/crates/ra_lsp_server/src/main_loop/mod.rs
index 47a9b202e..53c6f1dff 100644
--- a/crates/ra_lsp_server/src/main_loop/mod.rs
+++ b/crates/ra_lsp_server/src/main_loop/mod.rs
@@ -244,6 +244,7 @@ fn on_request(
244 .on::<req::ExtendSelection>(handlers::handle_extend_selection)? 244 .on::<req::ExtendSelection>(handlers::handle_extend_selection)?
245 .on::<req::FindMatchingBrace>(handlers::handle_find_matching_brace)? 245 .on::<req::FindMatchingBrace>(handlers::handle_find_matching_brace)?
246 .on::<req::JoinLines>(handlers::handle_join_lines)? 246 .on::<req::JoinLines>(handlers::handle_join_lines)?
247 .on::<req::OnEnter>(handlers::handle_on_enter)?
247 .on::<req::OnTypeFormatting>(handlers::handle_on_type_formatting)? 248 .on::<req::OnTypeFormatting>(handlers::handle_on_type_formatting)?
248 .on::<req::DocumentSymbolRequest>(handlers::handle_document_symbol)? 249 .on::<req::DocumentSymbolRequest>(handlers::handle_document_symbol)?
249 .on::<req::WorkspaceSymbol>(handlers::handle_workspace_symbol)? 250 .on::<req::WorkspaceSymbol>(handlers::handle_workspace_symbol)?
diff --git a/crates/ra_lsp_server/src/req.rs b/crates/ra_lsp_server/src/req.rs
index 4af61dbbd..458c79ea9 100644
--- a/crates/ra_lsp_server/src/req.rs
+++ b/crates/ra_lsp_server/src/req.rs
@@ -119,6 +119,14 @@ pub struct JoinLinesParams {
119 pub range: Range, 119 pub range: Range,
120} 120}
121 121
122pub enum OnEnter {}
123
124impl Request for OnEnter {
125 type Params = TextDocumentPositionParams;
126 type Result = Option<SourceChange>;
127 const METHOD: &'static str = "m/onEnter";
128}
129
122pub enum Runnables {} 130pub enum Runnables {}
123 131
124impl Request for Runnables { 132impl Request for Runnables {