aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorVille Penttinen <[email protected]>2019-02-07 18:00:58 +0000
committerVille Penttinen <[email protected]>2019-02-07 18:34:33 +0000
commit6cbf83c9462b993f2fa5b3c4334dd469c182c8af (patch)
treeac6bc4ebc0638cbf1b8aa44fddd3bbfd64a75eeb /crates
parentd0fd05142f45fb69a9dc64bfd55d2453b1927b9c (diff)
Add new assist to remove dbg!() calls
This fixes #758. Currently we try to maintain the cursor position relative to the statement under cursor, if the cursor is inside the dbg! macro call. Meaning: let foo = dbg!(some.complex<|>().expression()); Should turn into: let foo = some.complex<|>().expression(); With the cursor staying in place.
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_assists/src/lib.rs2
-rw-r--r--crates/ra_assists/src/remove_dbg.rs124
2 files changed, 126 insertions, 0 deletions
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs
index 062dd8804..555af51bc 100644
--- a/crates/ra_assists/src/lib.rs
+++ b/crates/ra_assists/src/lib.rs
@@ -73,6 +73,7 @@ mod fill_match_arms;
73mod introduce_variable; 73mod introduce_variable;
74mod replace_if_let_with_match; 74mod replace_if_let_with_match;
75mod split_import; 75mod split_import;
76mod remove_dbg;
76fn all_assists<DB: HirDatabase>() -> &'static [fn(AssistCtx<DB>) -> Option<Assist>] { 77fn all_assists<DB: HirDatabase>() -> &'static [fn(AssistCtx<DB>) -> Option<Assist>] {
77 &[ 78 &[
78 add_derive::add_derive, 79 add_derive::add_derive,
@@ -83,6 +84,7 @@ fn all_assists<DB: HirDatabase>() -> &'static [fn(AssistCtx<DB>) -> Option<Assis
83 introduce_variable::introduce_variable, 84 introduce_variable::introduce_variable,
84 replace_if_let_with_match::replace_if_let_with_match, 85 replace_if_let_with_match::replace_if_let_with_match,
85 split_import::split_import, 86 split_import::split_import,
87 remove_dbg::remove_dbg,
86 ] 88 ]
87} 89}
88 90
diff --git a/crates/ra_assists/src/remove_dbg.rs b/crates/ra_assists/src/remove_dbg.rs
new file mode 100644
index 000000000..6609abab2
--- /dev/null
+++ b/crates/ra_assists/src/remove_dbg.rs
@@ -0,0 +1,124 @@
1use hir::db::HirDatabase;
2use ra_syntax::{
3 ast::{self, AstNode},
4 TextUnit,
5 SyntaxKind::{
6 L_PAREN, R_PAREN, L_CURLY, R_CURLY, L_BRACK, R_BRACK, EXCL
7 },
8};
9use crate::{AssistCtx, Assist};
10
11pub(crate) fn remove_dbg(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
12 let macro_call = ctx.node_at_offset::<ast::MacroCall>()?;
13
14 if !is_valid_macrocall(macro_call, "dbg")? {
15 return None;
16 }
17
18 let macro_range = macro_call.syntax().range();
19
20 // If the cursor is inside the macrocall, we'll try to maintain
21 // the cursor position by subtracting the length of dbg!( from the start
22 // of the filerange, otherwise we'll default to using the start of the macrocall
23 let cursor_pos = {
24 let file_range = ctx.frange.range;
25
26 let offset_start = file_range
27 .start()
28 .checked_sub(macro_range.start())
29 .unwrap_or_else(|| TextUnit::from(0));
30
31 let dbg_size = dbg!(TextUnit::of_str("dbg!("));
32
33 if offset_start > dbg_size {
34 file_range.start() - dbg_size
35 } else {
36 macro_range.start()
37 }
38 };
39
40 let macro_content = {
41 let macro_args = macro_call.token_tree()?.syntax();
42 let _ = dbg!(macro_args.text());
43 let range = dbg!(macro_args.range());
44 let start = range.start() + TextUnit::of_char('(');
45 let end = range.end() - TextUnit::of_char(')');
46
47 macro_args.text().slice(start..end).to_string()
48 };
49
50 ctx.build("remove dbg!()", |edit| {
51 edit.replace(macro_range, macro_content);
52 edit.set_cursor(cursor_pos);
53 })
54}
55
56/// Verifies that the given macro_call actually matches the given name
57/// and contains proper ending tokens
58fn is_valid_macrocall(macro_call: &ast::MacroCall, macro_name: &str) -> Option<bool> {
59 let path = macro_call.path()?;
60 let name_ref = path.segment()?.name_ref()?;
61
62 // Make sure it is actually a dbg-macrocall, dbg followed by !
63 let excl = path.syntax().next_sibling()?;
64
65 if name_ref.text() != macro_name || excl.kind() != EXCL {
66 return None;
67 }
68
69 let node = macro_call.token_tree()?.syntax();
70 let first_child = node.first_child()?;
71 let last_child = node.last_child()?;
72
73 match (first_child.kind(), last_child.kind()) {
74 (L_PAREN, R_PAREN) | (L_BRACK, R_BRACK) | (L_CURLY, R_CURLY) => Some(true),
75 _ => Some(false),
76 }
77}
78
79#[cfg(test)]
80mod tests {
81 use super::*;
82 use crate::helpers::{check_assist, check_assist_not_applicable};
83
84 #[test]
85 fn test_remove_dbg() {
86 check_assist(remove_dbg, "<|>dbg!(1 + 1)", "<|>1 + 1");
87
88 check_assist(remove_dbg, "dbg!<|>((1 + 1))", "<|>(1 + 1)");
89
90 check_assist(remove_dbg, "dbg!(1 <|>+ 1)", "1 <|>+ 1");
91
92 check_assist(remove_dbg, "let _ = <|>dbg!(1 + 1)", "let _ = <|>1 + 1");
93
94 check_assist(
95 remove_dbg,
96 "
97fn foo(n: usize) {
98 if let Some(_) = dbg!(n.<|>checked_sub(4)) {
99 // ...
100 }
101}
102",
103 "
104fn foo(n: usize) {
105 if let Some(_) = n.<|>checked_sub(4) {
106 // ...
107 }
108}
109",
110 );
111 }
112 #[test]
113 fn test_remove_dbg_with_brackets_and_braces() {
114 check_assist(remove_dbg, "dbg![<|>1 + 1]", "<|>1 + 1");
115 check_assist(remove_dbg, "dbg!{<|>1 + 1}", "<|>1 + 1");
116 }
117
118 #[test]
119 fn test_remove_dbg_not_applicable() {
120 check_assist_not_applicable(remove_dbg, "<|>vec![1, 2, 3]");
121 check_assist_not_applicable(remove_dbg, "<|>dbg(5, 6, 7)");
122 check_assist_not_applicable(remove_dbg, "<|>dbg!(5, 6, 7");
123 }
124}