diff options
author | Simon Vandel Sillesen <[email protected]> | 2019-01-05 23:58:03 +0000 |
---|---|---|
committer | Simon Vandel Sillesen <[email protected]> | 2019-01-05 23:58:03 +0000 |
commit | f99398d9d5e47e28f3749c7903df67b9030ac6e0 (patch) | |
tree | 64168a90441da3d19ad725b1b80513deef5864f4 | |
parent | 3e42a158787955ff9f2e81be43479dbe8f2b1bb6 (diff) |
indent on typing dot. fixes #439
-rw-r--r-- | crates/ra_analysis/src/lib.rs | 30 | ||||
-rw-r--r-- | crates/ra_editor/src/lib.rs | 2 | ||||
-rw-r--r-- | crates/ra_editor/src/typing.rs | 54 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/main_loop/handlers.rs | 56 |
4 files changed, 103 insertions, 39 deletions
diff --git a/crates/ra_analysis/src/lib.rs b/crates/ra_analysis/src/lib.rs index 390c31c3f..feed44b2d 100644 --- a/crates/ra_analysis/src/lib.rs +++ b/crates/ra_analysis/src/lib.rs | |||
@@ -12,41 +12,39 @@ macro_rules! ctry { | |||
12 | }; | 12 | }; |
13 | } | 13 | } |
14 | 14 | ||
15 | mod db; | ||
16 | mod imp; | ||
17 | mod completion; | 15 | mod completion; |
16 | mod db; | ||
18 | mod goto_defenition; | 17 | mod goto_defenition; |
19 | mod symbol_index; | 18 | mod imp; |
20 | pub mod mock_analysis; | 19 | pub mod mock_analysis; |
21 | mod runnables; | 20 | mod runnables; |
21 | mod symbol_index; | ||
22 | 22 | ||
23 | mod extend_selection; | 23 | mod extend_selection; |
24 | mod syntax_highlighting; | ||
25 | mod hover; | 24 | mod hover; |
25 | mod syntax_highlighting; | ||
26 | 26 | ||
27 | use std::{fmt, sync::Arc}; | 27 | use std::{fmt, sync::Arc}; |
28 | 28 | ||
29 | use rustc_hash::FxHashMap; | 29 | use ra_syntax::{SmolStr, SourceFileNode, SyntaxKind, TextRange, TextUnit}; |
30 | use ra_syntax::{SourceFileNode, TextRange, TextUnit, SmolStr, SyntaxKind}; | ||
31 | use ra_text_edit::TextEdit; | 30 | use ra_text_edit::TextEdit; |
32 | use rayon::prelude::*; | 31 | use rayon::prelude::*; |
33 | use relative_path::RelativePathBuf; | 32 | use relative_path::RelativePathBuf; |
33 | use rustc_hash::FxHashMap; | ||
34 | use salsa::ParallelDatabase; | 34 | use salsa::ParallelDatabase; |
35 | 35 | ||
36 | use crate::symbol_index::{SymbolIndex, FileSymbol}; | 36 | use crate::symbol_index::{FileSymbol, SymbolIndex}; |
37 | 37 | ||
38 | pub use crate::{ | 38 | pub use crate::{ |
39 | completion::{CompletionItem, CompletionItemKind, InsertText}, | 39 | completion::{CompletionItem, CompletionItemKind, InsertText}, |
40 | runnables::{Runnable, RunnableKind}, | 40 | runnables::{Runnable, RunnableKind}, |
41 | }; | 41 | }; |
42 | pub use ra_editor::{ | ||
43 | Fold, FoldKind, HighlightedRange, LineIndex, StructureNode, Severity | ||
44 | }; | ||
45 | pub use hir::FnSignatureInfo; | 42 | pub use hir::FnSignatureInfo; |
43 | pub use ra_editor::{Fold, FoldKind, HighlightedRange, LineIndex, Severity, StructureNode}; | ||
46 | 44 | ||
47 | pub use ra_db::{ | 45 | pub use ra_db::{ |
48 | Canceled, Cancelable, FilePosition, FileRange, LocalSyntaxPtr, | 46 | Cancelable, Canceled, CrateGraph, CrateId, FileId, FilePosition, FileRange, FilesDatabase, |
49 | CrateGraph, CrateId, SourceRootId, FileId, SyntaxDatabase, FilesDatabase | 47 | LocalSyntaxPtr, SourceRootId, SyntaxDatabase, |
50 | }; | 48 | }; |
51 | 49 | ||
52 | #[derive(Default)] | 50 | #[derive(Default)] |
@@ -346,7 +344,7 @@ impl Analysis { | |||
346 | let edit = ra_editor::on_enter(&file, position.offset)?; | 344 | let edit = ra_editor::on_enter(&file, position.offset)?; |
347 | Some(SourceChange::from_local_edit(position.file_id, edit)) | 345 | Some(SourceChange::from_local_edit(position.file_id, edit)) |
348 | } | 346 | } |
349 | /// Returns an edit which should be applied after `=` was typed. Primaraly, | 347 | /// Returns an edit which should be applied after `=` was typed. Primarily, |
350 | /// this works when adding `let =`. | 348 | /// this works when adding `let =`. |
351 | // FIXME: use a snippet completion instead of this hack here. | 349 | // FIXME: use a snippet completion instead of this hack here. |
352 | pub fn on_eq_typed(&self, position: FilePosition) -> Option<SourceChange> { | 350 | pub fn on_eq_typed(&self, position: FilePosition) -> Option<SourceChange> { |
@@ -354,6 +352,12 @@ impl Analysis { | |||
354 | let edit = ra_editor::on_eq_typed(&file, position.offset)?; | 352 | let edit = ra_editor::on_eq_typed(&file, position.offset)?; |
355 | Some(SourceChange::from_local_edit(position.file_id, edit)) | 353 | Some(SourceChange::from_local_edit(position.file_id, edit)) |
356 | } | 354 | } |
355 | /// Returns an edit which should be applied when a dot ('.') is typed on a blank line, indenting the line appropriately. | ||
356 | pub fn on_dot_typed(&self, position: FilePosition) -> Option<SourceChange> { | ||
357 | let file = self.db.source_file(position.file_id); | ||
358 | let edit = ra_editor::on_dot_typed(&file, position.offset)?; | ||
359 | Some(SourceChange::from_local_edit(position.file_id, edit)) | ||
360 | } | ||
357 | /// Returns a tree representation of symbols in the file. Useful to draw a | 361 | /// Returns a tree representation of symbols in the file. Useful to draw a |
358 | /// file outline. | 362 | /// file outline. |
359 | pub fn file_structure(&self, file_id: FileId) -> Vec<StructureNode> { | 363 | pub fn file_structure(&self, file_id: FileId) -> Vec<StructureNode> { |
diff --git a/crates/ra_editor/src/lib.rs b/crates/ra_editor/src/lib.rs index ac283e2e0..a3c85ed5d 100644 --- a/crates/ra_editor/src/lib.rs +++ b/crates/ra_editor/src/lib.rs | |||
@@ -16,7 +16,7 @@ pub use self::{ | |||
16 | line_index::{LineCol, LineIndex}, | 16 | line_index::{LineCol, LineIndex}, |
17 | line_index_utils::translate_offset_with_edit, | 17 | line_index_utils::translate_offset_with_edit, |
18 | structure::{file_structure, StructureNode}, | 18 | structure::{file_structure, StructureNode}, |
19 | typing::{join_lines, on_enter, on_eq_typed}, | 19 | typing::{join_lines, on_enter, on_dot_typed, on_eq_typed}, |
20 | diagnostics::diagnostics | 20 | diagnostics::diagnostics |
21 | }; | 21 | }; |
22 | use ra_text_edit::TextEditBuilder; | 22 | use ra_text_edit::TextEditBuilder; |
diff --git a/crates/ra_editor/src/typing.rs b/crates/ra_editor/src/typing.rs index 1b568e96c..aaea858ea 100644 --- a/crates/ra_editor/src/typing.rs +++ b/crates/ra_editor/src/typing.rs | |||
@@ -1,5 +1,6 @@ | |||
1 | use std::mem; | 1 | use std::mem; |
2 | 2 | ||
3 | use itertools::Itertools; | ||
3 | use ra_syntax::{ | 4 | use ra_syntax::{ |
4 | algo::{find_covering_node, find_leaf_at_offset, LeafAtOffset}, | 5 | algo::{find_covering_node, find_leaf_at_offset, LeafAtOffset}, |
5 | ast, | 6 | ast, |
@@ -9,9 +10,8 @@ use ra_syntax::{ | |||
9 | SyntaxNodeRef, TextRange, TextUnit, | 10 | SyntaxNodeRef, TextRange, TextUnit, |
10 | }; | 11 | }; |
11 | use ra_text_edit::text_utils::contains_offset_nonstrict; | 12 | use ra_text_edit::text_utils::contains_offset_nonstrict; |
12 | use itertools::Itertools; | ||
13 | 13 | ||
14 | use crate::{find_node_at_offset, TextEditBuilder, LocalEdit}; | 14 | use crate::{find_node_at_offset, LocalEdit, TextEditBuilder}; |
15 | 15 | ||
16 | pub fn join_lines(file: &SourceFileNode, range: TextRange) -> LocalEdit { | 16 | pub fn join_lines(file: &SourceFileNode, range: TextRange) -> LocalEdit { |
17 | let range = if range.is_empty() { | 17 | let range = if range.is_empty() { |
@@ -136,6 +136,27 @@ pub fn on_eq_typed(file: &SourceFileNode, offset: TextUnit) -> Option<LocalEdit> | |||
136 | }) | 136 | }) |
137 | } | 137 | } |
138 | 138 | ||
139 | pub fn on_dot_typed(file: &SourceFileNode, offset: TextUnit) -> Option<LocalEdit> { | ||
140 | let before_dot_offset = offset - TextUnit::of_char('.'); | ||
141 | |||
142 | let whitespace = find_leaf_at_offset(file.syntax(), before_dot_offset) | ||
143 | .left_biased() | ||
144 | .and_then(ast::Whitespace::cast)?; | ||
145 | |||
146 | // whitespace found just left of the dot | ||
147 | // TODO: indent is always 4 spaces now. A better heuristic could look on the previous line(s) | ||
148 | let indent = " ".to_string(); | ||
149 | |||
150 | let cursor_position = offset + TextUnit::of_str(&indent);; | ||
151 | let mut edit = TextEditBuilder::default(); | ||
152 | edit.insert(before_dot_offset, indent); | ||
153 | Some(LocalEdit { | ||
154 | label: "indent dot".to_string(), | ||
155 | edit: edit.finish(), | ||
156 | cursor_position: Some(cursor_position), | ||
157 | }) | ||
158 | } | ||
159 | |||
139 | fn remove_newline( | 160 | fn remove_newline( |
140 | edit: &mut TextEditBuilder, | 161 | edit: &mut TextEditBuilder, |
141 | node: SyntaxNodeRef, | 162 | node: SyntaxNodeRef, |
@@ -283,7 +304,9 @@ fn compute_ws(left: SyntaxNodeRef, right: SyntaxNodeRef) -> &'static str { | |||
283 | #[cfg(test)] | 304 | #[cfg(test)] |
284 | mod tests { | 305 | mod tests { |
285 | use super::*; | 306 | use super::*; |
286 | use crate::test_utils::{add_cursor, check_action, extract_offset, extract_range, assert_eq_text}; | 307 | use crate::test_utils::{ |
308 | add_cursor, assert_eq_text, check_action, extract_offset, extract_range, | ||
309 | }; | ||
287 | 310 | ||
288 | fn check_join_lines(before: &str, after: &str) { | 311 | fn check_join_lines(before: &str, after: &str) { |
289 | check_action(before, after, |file, offset| { | 312 | check_action(before, after, |file, offset| { |
@@ -615,6 +638,31 @@ fn foo() { | |||
615 | } | 638 | } |
616 | 639 | ||
617 | #[test] | 640 | #[test] |
641 | fn test_on_dot_typed() { | ||
642 | fn do_check(before: &str, after: &str) { | ||
643 | let (offset, before) = extract_offset(before); | ||
644 | let file = SourceFileNode::parse(&before); | ||
645 | let result = on_dot_typed(&file, offset).unwrap(); | ||
646 | let actual = result.edit.apply(&before); | ||
647 | assert_eq_text!(after, &actual); | ||
648 | } | ||
649 | do_check( | ||
650 | r" | ||
651 | pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> { | ||
652 | self.child_impl(db, name) | ||
653 | .<|> | ||
654 | } | ||
655 | ", | ||
656 | r" | ||
657 | pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> { | ||
658 | self.child_impl(db, name) | ||
659 | . | ||
660 | } | ||
661 | ", | ||
662 | ); | ||
663 | } | ||
664 | |||
665 | #[test] | ||
618 | fn test_on_enter() { | 666 | fn test_on_enter() { |
619 | fn apply_on_enter(before: &str) -> Option<String> { | 667 | fn apply_on_enter(before: &str) -> Option<String> { |
620 | let (offset, before) = extract_offset(before); | 668 | let (offset, before) = extract_offset(before); |
diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs index 1baed73ad..2ec9073e4 100644 --- a/crates/ra_lsp_server/src/main_loop/handlers.rs +++ b/crates/ra_lsp_server/src/main_loop/handlers.rs | |||
@@ -2,15 +2,16 @@ use std::collections::HashMap; | |||
2 | 2 | ||
3 | use gen_lsp_server::ErrorCode; | 3 | use gen_lsp_server::ErrorCode; |
4 | use languageserver_types::{ | 4 | use languageserver_types::{ |
5 | CodeActionResponse, Command, Diagnostic, | 5 | CodeActionResponse, Command, Diagnostic, DiagnosticSeverity, DocumentFormattingParams, |
6 | DiagnosticSeverity, DocumentSymbol, Documentation, FoldingRange, FoldingRangeKind, | 6 | DocumentHighlight, DocumentSymbol, Documentation, FoldingRange, FoldingRangeKind, |
7 | FoldingRangeParams, Location, MarkupContent, MarkupKind, MarkedString, Position, | 7 | FoldingRangeParams, Hover, HoverContents, Location, MarkedString, MarkupContent, MarkupKind, |
8 | PrepareRenameResponse, RenameParams, SymbolInformation, TextDocumentIdentifier, TextEdit, | 8 | ParameterInformation, ParameterLabel, Position, PrepareRenameResponse, Range, RenameParams, |
9 | Range, WorkspaceEdit, ParameterInformation, ParameterLabel, SignatureInformation, Hover, | 9 | SignatureInformation, SymbolInformation, TextDocumentIdentifier, TextEdit, WorkspaceEdit, |
10 | HoverContents, DocumentFormattingParams, DocumentHighlight, | ||
11 | }; | 10 | }; |
12 | use ra_analysis::{FileId, FoldKind, Query, RunnableKind, FileRange, FilePosition, Severity}; | 11 | use ra_analysis::{ |
13 | use ra_syntax::{TextUnit, text_utils::intersect}; | 12 | FileId, FilePosition, FileRange, FoldKind, Query, RunnableKind, Severity, SourceChange, |
13 | }; | ||
14 | use ra_syntax::{text_utils::intersect, TextUnit}; | ||
14 | use ra_text_edit::text_utils::contains_offset_nonstrict; | 15 | use ra_text_edit::text_utils::contains_offset_nonstrict; |
15 | use rustc_hash::FxHashMap; | 16 | use rustc_hash::FxHashMap; |
16 | use serde_json::to_value; | 17 | use serde_json::to_value; |
@@ -92,7 +93,7 @@ pub fn handle_on_type_formatting( | |||
92 | world: ServerWorld, | 93 | world: ServerWorld, |
93 | params: req::DocumentOnTypeFormattingParams, | 94 | params: req::DocumentOnTypeFormattingParams, |
94 | ) -> Result<Option<Vec<TextEdit>>> { | 95 | ) -> Result<Option<Vec<TextEdit>>> { |
95 | if params.ch != "=" { | 96 | if params.ch != "=" || params.ch != "." { |
96 | return Ok(None); | 97 | return Ok(None); |
97 | } | 98 | } |
98 | 99 | ||
@@ -102,19 +103,30 @@ pub fn handle_on_type_formatting( | |||
102 | file_id, | 103 | file_id, |
103 | offset: params.position.conv_with(&line_index), | 104 | offset: params.position.conv_with(&line_index), |
104 | }; | 105 | }; |
105 | let edits = match world.analysis().on_eq_typed(position) { | 106 | |
106 | None => return Ok(None), | 107 | let analysis: Vec<Box<Fn(FilePosition) -> Option<SourceChange>>> = vec![ |
107 | Some(mut action) => action | 108 | Box::new(|pos| world.analysis().on_eq_typed(pos)), |
108 | .source_file_edits | 109 | Box::new(|pos| world.analysis().on_dot_typed(pos)), |
109 | .pop() | 110 | ]; |
110 | .unwrap() | 111 | |
111 | .edit | 112 | // try all analysis until one succeeds |
112 | .as_atoms() | 113 | for ana in analysis { |
113 | .iter() | 114 | if let Some(mut action) = ana(position) { |
114 | .map_conv_with(&line_index) | 115 | return Ok(Some( |
115 | .collect(), | 116 | action |
116 | }; | 117 | .source_file_edits |
117 | Ok(Some(edits)) | 118 | .pop() |
119 | .unwrap() | ||
120 | .edit | ||
121 | .as_atoms() | ||
122 | .iter() | ||
123 | .map_conv_with(&line_index) | ||
124 | .collect(), | ||
125 | )); | ||
126 | } | ||
127 | } | ||
128 | |||
129 | return Ok(None); | ||
118 | } | 130 | } |
119 | 131 | ||
120 | pub fn handle_document_symbol( | 132 | pub fn handle_document_symbol( |