aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_analysis/src/lib.rs30
-rw-r--r--crates/ra_editor/src/lib.rs2
-rw-r--r--crates/ra_editor/src/typing.rs54
-rw-r--r--crates/ra_lsp_server/src/main_loop/handlers.rs56
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
15mod db;
16mod imp;
17mod completion; 15mod completion;
16mod db;
18mod goto_defenition; 17mod goto_defenition;
19mod symbol_index; 18mod imp;
20pub mod mock_analysis; 19pub mod mock_analysis;
21mod runnables; 20mod runnables;
21mod symbol_index;
22 22
23mod extend_selection; 23mod extend_selection;
24mod syntax_highlighting;
25mod hover; 24mod hover;
25mod syntax_highlighting;
26 26
27use std::{fmt, sync::Arc}; 27use std::{fmt, sync::Arc};
28 28
29use rustc_hash::FxHashMap; 29use ra_syntax::{SmolStr, SourceFileNode, SyntaxKind, TextRange, TextUnit};
30use ra_syntax::{SourceFileNode, TextRange, TextUnit, SmolStr, SyntaxKind};
31use ra_text_edit::TextEdit; 30use ra_text_edit::TextEdit;
32use rayon::prelude::*; 31use rayon::prelude::*;
33use relative_path::RelativePathBuf; 32use relative_path::RelativePathBuf;
33use rustc_hash::FxHashMap;
34use salsa::ParallelDatabase; 34use salsa::ParallelDatabase;
35 35
36use crate::symbol_index::{SymbolIndex, FileSymbol}; 36use crate::symbol_index::{FileSymbol, SymbolIndex};
37 37
38pub use crate::{ 38pub use crate::{
39 completion::{CompletionItem, CompletionItemKind, InsertText}, 39 completion::{CompletionItem, CompletionItemKind, InsertText},
40 runnables::{Runnable, RunnableKind}, 40 runnables::{Runnable, RunnableKind},
41}; 41};
42pub use ra_editor::{
43 Fold, FoldKind, HighlightedRange, LineIndex, StructureNode, Severity
44};
45pub use hir::FnSignatureInfo; 42pub use hir::FnSignatureInfo;
43pub use ra_editor::{Fold, FoldKind, HighlightedRange, LineIndex, Severity, StructureNode};
46 44
47pub use ra_db::{ 45pub 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};
22use ra_text_edit::TextEditBuilder; 22use 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 @@
1use std::mem; 1use std::mem;
2 2
3use itertools::Itertools;
3use ra_syntax::{ 4use 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};
11use ra_text_edit::text_utils::contains_offset_nonstrict; 12use ra_text_edit::text_utils::contains_offset_nonstrict;
12use itertools::Itertools;
13 13
14use crate::{find_node_at_offset, TextEditBuilder, LocalEdit}; 14use crate::{find_node_at_offset, LocalEdit, TextEditBuilder};
15 15
16pub fn join_lines(file: &SourceFileNode, range: TextRange) -> LocalEdit { 16pub 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
139pub 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
139fn remove_newline( 160fn 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)]
284mod tests { 305mod 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
3use gen_lsp_server::ErrorCode; 3use gen_lsp_server::ErrorCode;
4use languageserver_types::{ 4use 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};
12use ra_analysis::{FileId, FoldKind, Query, RunnableKind, FileRange, FilePosition, Severity}; 11use ra_analysis::{
13use ra_syntax::{TextUnit, text_utils::intersect}; 12 FileId, FilePosition, FileRange, FoldKind, Query, RunnableKind, Severity, SourceChange,
13};
14use ra_syntax::{text_utils::intersect, TextUnit};
14use ra_text_edit::text_utils::contains_offset_nonstrict; 15use ra_text_edit::text_utils::contains_offset_nonstrict;
15use rustc_hash::FxHashMap; 16use rustc_hash::FxHashMap;
16use serde_json::to_value; 17use 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
120pub fn handle_document_symbol( 132pub fn handle_document_symbol(