aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_ide_api/src/lib.rs29
-rw-r--r--crates/ra_ide_api/src/typing.rs81
-rw-r--r--crates/ra_ide_api_light/src/lib.rs15
3 files changed, 55 insertions, 70 deletions
diff --git a/crates/ra_ide_api/src/lib.rs b/crates/ra_ide_api/src/lib.rs
index c2ef61ae2..0e7b47e3c 100644
--- a/crates/ra_ide_api/src/lib.rs
+++ b/crates/ra_ide_api/src/lib.rs
@@ -72,7 +72,7 @@ pub use crate::{
72 folding_ranges::{Fold, FoldKind}, 72 folding_ranges::{Fold, FoldKind},
73}; 73};
74pub use ra_ide_api_light::{ 74pub use ra_ide_api_light::{
75 HighlightedRange, Severity, StructureNode, LocalEdit, 75 HighlightedRange, Severity, StructureNode,
76}; 76};
77pub use ra_db::{ 77pub use ra_db::{
78 Canceled, CrateGraph, CrateId, FileId, FilePosition, FileRange, SourceRootId, 78 Canceled, CrateGraph, CrateId, FileId, FilePosition, FileRange, SourceRootId,
@@ -295,9 +295,7 @@ impl Analysis {
295 /// Returns an edit which should be applied when opening a new line, fixing 295 /// Returns an edit which should be applied when opening a new line, fixing
296 /// up minor stuff like continuing the comment. 296 /// up minor stuff like continuing the comment.
297 pub fn on_enter(&self, position: FilePosition) -> Option<SourceChange> { 297 pub fn on_enter(&self, position: FilePosition) -> Option<SourceChange> {
298 let file = self.db.parse(position.file_id); 298 typing::on_enter(&self.db, position)
299 let edit = typing::on_enter(&file, position.offset)?;
300 Some(SourceChange::from_local_edit(position.file_id, edit))
301 } 299 }
302 300
303 /// Returns an edit which should be applied after `=` was typed. Primarily, 301 /// Returns an edit which should be applied after `=` was typed. Primarily,
@@ -306,14 +304,17 @@ impl Analysis {
306 pub fn on_eq_typed(&self, position: FilePosition) -> Option<SourceChange> { 304 pub fn on_eq_typed(&self, position: FilePosition) -> Option<SourceChange> {
307 let file = self.db.parse(position.file_id); 305 let file = self.db.parse(position.file_id);
308 let edit = typing::on_eq_typed(&file, position.offset)?; 306 let edit = typing::on_eq_typed(&file, position.offset)?;
309 Some(SourceChange::from_local_edit(position.file_id, edit)) 307 Some(SourceChange {
308 label: "add semicolon".to_string(),
309 source_file_edits: vec![SourceFileEdit { edit, file_id: position.file_id }],
310 file_system_edits: vec![],
311 cursor_position: None,
312 })
310 } 313 }
311 314
312 /// Returns an edit which should be applied when a dot ('.') is typed on a blank line, indenting the line appropriately. 315 /// Returns an edit which should be applied when a dot ('.') is typed on a blank line, indenting the line appropriately.
313 pub fn on_dot_typed(&self, position: FilePosition) -> Option<SourceChange> { 316 pub fn on_dot_typed(&self, position: FilePosition) -> Option<SourceChange> {
314 let file = self.db.parse(position.file_id); 317 typing::on_dot_typed(&self.db, position)
315 let edit = typing::on_dot_typed(&file, position.offset)?;
316 Some(SourceChange::from_local_edit(position.file_id, edit))
317 } 318 }
318 319
319 /// Returns a tree representation of symbols in the file. Useful to draw a 320 /// Returns a tree representation of symbols in the file. Useful to draw a
@@ -435,18 +436,6 @@ impl Analysis {
435 } 436 }
436} 437}
437 438
438impl SourceChange {
439 pub(crate) fn from_local_edit(file_id: FileId, edit: LocalEdit) -> SourceChange {
440 let file_edit = SourceFileEdit { file_id, edit: edit.edit };
441 SourceChange {
442 label: edit.label,
443 source_file_edits: vec![file_edit],
444 file_system_edits: vec![],
445 cursor_position: edit.cursor_position.map(|offset| FilePosition { offset, file_id }),
446 }
447 }
448}
449
450#[test] 439#[test]
451fn analysis_is_send() { 440fn analysis_is_send() {
452 fn is_send<T: Send>() {} 441 fn is_send<T: Send>() {}
diff --git a/crates/ra_ide_api/src/typing.rs b/crates/ra_ide_api/src/typing.rs
index b7e023d60..94b228466 100644
--- a/crates/ra_ide_api/src/typing.rs
+++ b/crates/ra_ide_api/src/typing.rs
@@ -5,31 +5,37 @@ use ra_syntax::{
5 ast::{self, AstToken}, 5 ast::{self, AstToken},
6}; 6};
7use ra_fmt::leading_indent; 7use ra_fmt::leading_indent;
8use crate::LocalEdit; 8use ra_text_edit::{TextEdit, TextEditBuilder};
9use ra_text_edit::TextEditBuilder; 9use ra_db::{FilePosition, SourceDatabase};
10use crate::{db::RootDatabase, SourceChange, SourceFileEdit};
10 11
11pub fn on_enter(file: &SourceFile, offset: TextUnit) -> Option<LocalEdit> { 12pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<SourceChange> {
12 let comment = 13 let file = db.parse(position.file_id);
13 find_leaf_at_offset(file.syntax(), offset).left_biased().and_then(ast::Comment::cast)?; 14 let comment = find_leaf_at_offset(file.syntax(), position.offset)
15 .left_biased()
16 .and_then(ast::Comment::cast)?;
14 17
15 if let ast::CommentFlavor::Multiline = comment.flavor() { 18 if let ast::CommentFlavor::Multiline = comment.flavor() {
16 return None; 19 return None;
17 } 20 }
18 21
19 let prefix = comment.prefix(); 22 let prefix = comment.prefix();
20 if offset < comment.syntax().range().start() + TextUnit::of_str(prefix) + TextUnit::from(1) { 23 if position.offset
24 < comment.syntax().range().start() + TextUnit::of_str(prefix) + TextUnit::from(1)
25 {
21 return None; 26 return None;
22 } 27 }
23 28
24 let indent = node_indent(file, comment.syntax())?; 29 let indent = node_indent(&file, comment.syntax())?;
25 let inserted = format!("\n{}{} ", indent, prefix); 30 let inserted = format!("\n{}{} ", indent, prefix);
26 let cursor_position = offset + TextUnit::of_str(&inserted); 31 let cursor_position = position.offset + TextUnit::of_str(&inserted);
27 let mut edit = TextEditBuilder::default(); 32 let mut edit = TextEditBuilder::default();
28 edit.insert(offset, inserted); 33 edit.insert(position.offset, inserted);
29 Some(LocalEdit { 34 Some(SourceChange {
30 label: "on enter".to_string(), 35 label: "on enter".to_string(),
31 edit: edit.finish(), 36 source_file_edits: vec![SourceFileEdit { edit: edit.finish(), file_id: position.file_id }],
32 cursor_position: Some(cursor_position), 37 file_system_edits: vec![],
38 cursor_position: Some(FilePosition { offset: cursor_position, file_id: position.file_id }),
33 }) 39 })
34} 40}
35 41
@@ -53,7 +59,7 @@ fn node_indent<'a>(file: &'a SourceFile, node: &SyntaxNode) -> Option<&'a str> {
53 Some(&text[pos..]) 59 Some(&text[pos..])
54} 60}
55 61
56pub fn on_eq_typed(file: &SourceFile, eq_offset: TextUnit) -> Option<LocalEdit> { 62pub fn on_eq_typed(file: &SourceFile, eq_offset: TextUnit) -> Option<TextEdit> {
57 assert_eq!(file.syntax().text().char_at(eq_offset), Some('=')); 63 assert_eq!(file.syntax().text().char_at(eq_offset), Some('='));
58 let let_stmt: &ast::LetStmt = find_node_at_offset(file.syntax(), eq_offset)?; 64 let let_stmt: &ast::LetStmt = find_node_at_offset(file.syntax(), eq_offset)?;
59 if let_stmt.has_semi() { 65 if let_stmt.has_semi() {
@@ -73,17 +79,14 @@ pub fn on_eq_typed(file: &SourceFile, eq_offset: TextUnit) -> Option<LocalEdit>
73 let offset = let_stmt.syntax().range().end(); 79 let offset = let_stmt.syntax().range().end();
74 let mut edit = TextEditBuilder::default(); 80 let mut edit = TextEditBuilder::default();
75 edit.insert(offset, ";".to_string()); 81 edit.insert(offset, ";".to_string());
76 Some(LocalEdit { 82 Some(edit.finish())
77 label: "add semicolon".to_string(),
78 edit: edit.finish(),
79 cursor_position: None,
80 })
81} 83}
82 84
83pub fn on_dot_typed(file: &SourceFile, dot_offset: TextUnit) -> Option<LocalEdit> { 85pub(crate) fn on_dot_typed(db: &RootDatabase, position: FilePosition) -> Option<SourceChange> {
84 assert_eq!(file.syntax().text().char_at(dot_offset), Some('.')); 86 let file = db.parse(position.file_id);
87 assert_eq!(file.syntax().text().char_at(position.offset), Some('.'));
85 88
86 let whitespace = find_leaf_at_offset(file.syntax(), dot_offset) 89 let whitespace = find_leaf_at_offset(file.syntax(), position.offset)
87 .left_biased() 90 .left_biased()
88 .and_then(ast::Whitespace::cast)?; 91 .and_then(ast::Whitespace::cast)?;
89 92
@@ -104,15 +107,18 @@ pub fn on_dot_typed(file: &SourceFile, dot_offset: TextUnit) -> Option<LocalEdit
104 } 107 }
105 let mut edit = TextEditBuilder::default(); 108 let mut edit = TextEditBuilder::default();
106 edit.replace( 109 edit.replace(
107 TextRange::from_to(dot_offset - current_indent_len, dot_offset), 110 TextRange::from_to(position.offset - current_indent_len, position.offset),
108 target_indent.into(), 111 target_indent.into(),
109 ); 112 );
110 let res = LocalEdit { 113 let res = SourceChange {
111 label: "reindent dot".to_string(), 114 label: "reindent dot".to_string(),
112 edit: edit.finish(), 115 source_file_edits: vec![SourceFileEdit { edit: edit.finish(), file_id: position.file_id }],
113 cursor_position: Some( 116 file_system_edits: vec![],
114 dot_offset + target_indent_len - current_indent_len + TextUnit::of_char('.'), 117 cursor_position: Some(FilePosition {
115 ), 118 offset: position.offset + target_indent_len - current_indent_len
119 + TextUnit::of_char('.'),
120 file_id: position.file_id,
121 }),
116 }; 122 };
117 Some(res) 123 Some(res)
118} 124}
@@ -121,6 +127,8 @@ pub fn on_dot_typed(file: &SourceFile, dot_offset: TextUnit) -> Option<LocalEdit
121mod tests { 127mod tests {
122 use test_utils::{add_cursor, assert_eq_text, extract_offset}; 128 use test_utils::{add_cursor, assert_eq_text, extract_offset};
123 129
130 use crate::mock_analysis::single_file;
131
124 use super::*; 132 use super::*;
125 133
126 #[test] 134 #[test]
@@ -132,7 +140,7 @@ mod tests {
132 let before = edit.finish().apply(&before); 140 let before = edit.finish().apply(&before);
133 let file = SourceFile::parse(&before); 141 let file = SourceFile::parse(&before);
134 if let Some(result) = on_eq_typed(&file, offset) { 142 if let Some(result) = on_eq_typed(&file, offset) {
135 let actual = result.edit.apply(&before); 143 let actual = result.apply(&before);
136 assert_eq_text!(after, &actual); 144 assert_eq_text!(after, &actual);
137 } else { 145 } else {
138 assert_eq_text!(&before, after) 146 assert_eq_text!(&before, after)
@@ -178,9 +186,10 @@ fn foo() {
178 let mut edit = TextEditBuilder::default(); 186 let mut edit = TextEditBuilder::default();
179 edit.insert(offset, ".".to_string()); 187 edit.insert(offset, ".".to_string());
180 let before = edit.finish().apply(&before); 188 let before = edit.finish().apply(&before);
181 let file = SourceFile::parse(&before); 189 let (analysis, file_id) = single_file(&before);
182 if let Some(result) = on_dot_typed(&file, offset) { 190 if let Some(result) = analysis.on_dot_typed(FilePosition { offset, file_id }) {
183 let actual = result.edit.apply(&before); 191 assert_eq!(result.source_file_edits.len(), 1);
192 let actual = result.source_file_edits[0].edit.apply(&before);
184 assert_eq_text!(after, &actual); 193 assert_eq_text!(after, &actual);
185 } else { 194 } else {
186 assert_eq_text!(&before, after) 195 assert_eq_text!(&before, after)
@@ -359,10 +368,12 @@ fn foo() {
359 fn test_on_enter() { 368 fn test_on_enter() {
360 fn apply_on_enter(before: &str) -> Option<String> { 369 fn apply_on_enter(before: &str) -> Option<String> {
361 let (offset, before) = extract_offset(before); 370 let (offset, before) = extract_offset(before);
362 let file = SourceFile::parse(&before); 371 let (analysis, file_id) = single_file(&before);
363 let result = on_enter(&file, offset)?; 372 let result = analysis.on_enter(FilePosition { offset, file_id })?;
364 let actual = result.edit.apply(&before); 373
365 let actual = add_cursor(&actual, result.cursor_position.unwrap()); 374 assert_eq!(result.source_file_edits.len(), 1);
375 let actual = result.source_file_edits[0].edit.apply(&before);
376 let actual = add_cursor(&actual, result.cursor_position.unwrap().offset);
366 Some(actual) 377 Some(actual)
367 } 378 }
368 379
diff --git a/crates/ra_ide_api_light/src/lib.rs b/crates/ra_ide_api_light/src/lib.rs
index 0d928745f..1c5fa0837 100644
--- a/crates/ra_ide_api_light/src/lib.rs
+++ b/crates/ra_ide_api_light/src/lib.rs
@@ -18,13 +18,6 @@ pub use crate::{
18}; 18};
19 19
20#[derive(Debug)] 20#[derive(Debug)]
21pub struct LocalEdit {
22 pub label: String,
23 pub edit: ra_text_edit::TextEdit,
24 pub cursor_position: Option<TextUnit>,
25}
26
27#[derive(Debug)]
28pub struct HighlightedRange { 21pub struct HighlightedRange {
29 pub range: TextRange, 22 pub range: TextRange,
30 pub tag: &'static str, 23 pub tag: &'static str,
@@ -36,14 +29,6 @@ pub enum Severity {
36 WeakWarning, 29 WeakWarning,
37} 30}
38 31
39#[derive(Debug)]
40pub struct Diagnostic {
41 pub range: TextRange,
42 pub msg: String,
43 pub severity: Severity,
44 pub fix: Option<LocalEdit>,
45}
46
47pub fn matching_brace(file: &SourceFile, offset: TextUnit) -> Option<TextUnit> { 32pub fn matching_brace(file: &SourceFile, offset: TextUnit) -> Option<TextUnit> {
48 const BRACES: &[SyntaxKind] = 33 const BRACES: &[SyntaxKind] =
49 &[L_CURLY, R_CURLY, L_BRACK, R_BRACK, L_PAREN, R_PAREN, L_ANGLE, R_ANGLE]; 34 &[L_CURLY, R_CURLY, L_BRACK, R_BRACK, L_PAREN, R_PAREN, L_ANGLE, R_ANGLE];