diff options
author | Geobert Quach <[email protected]> | 2019-09-26 20:31:45 +0100 |
---|---|---|
committer | Geobert Quach <[email protected]> | 2019-09-26 20:31:45 +0100 |
commit | 281e1071558dff3138805de49dfbb0ad91b3acd3 (patch) | |
tree | 55c2bca96fdf15f53c067dcc3b70b84e967006c4 | |
parent | 53a30d9e69ee5149e4fdb1c6fe4081281e879d0e (diff) |
feat(assists): Make raw string unescaped
-rw-r--r-- | Cargo.lock | 1 | ||||
-rw-r--r-- | crates/ra_assists/Cargo.toml | 1 | ||||
-rw-r--r-- | crates/ra_assists/src/assists/raw_string.rs | 77 | ||||
-rw-r--r-- | crates/ra_assists/src/lib.rs | 2 | ||||
-rw-r--r-- | docs/user/features.md | 15 |
5 files changed, 95 insertions, 1 deletions
diff --git a/Cargo.lock b/Cargo.lock index 275b27775..dad9b1df1 100644 --- a/Cargo.lock +++ b/Cargo.lock | |||
@@ -904,6 +904,7 @@ dependencies = [ | |||
904 | "ra_syntax 0.1.0", | 904 | "ra_syntax 0.1.0", |
905 | "ra_text_edit 0.1.0", | 905 | "ra_text_edit 0.1.0", |
906 | "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", | 906 | "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", |
907 | "rustc_lexer 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||
907 | "test_utils 0.1.0", | 908 | "test_utils 0.1.0", |
908 | ] | 909 | ] |
909 | 910 | ||
diff --git a/crates/ra_assists/Cargo.toml b/crates/ra_assists/Cargo.toml index 02966bbda..d3b6aeb36 100644 --- a/crates/ra_assists/Cargo.toml +++ b/crates/ra_assists/Cargo.toml | |||
@@ -11,6 +11,7 @@ join_to_string = "0.1.3" | |||
11 | itertools = "0.8.0" | 11 | itertools = "0.8.0" |
12 | arrayvec = "0.4.10" | 12 | arrayvec = "0.4.10" |
13 | rustc-hash = "1.0.1" | 13 | rustc-hash = "1.0.1" |
14 | rustc_lexer = "0.1.0" | ||
14 | 15 | ||
15 | ra_syntax = { path = "../ra_syntax" } | 16 | ra_syntax = { path = "../ra_syntax" } |
16 | ra_text_edit = { path = "../ra_text_edit" } | 17 | ra_text_edit = { path = "../ra_text_edit" } |
diff --git a/crates/ra_assists/src/assists/raw_string.rs b/crates/ra_assists/src/assists/raw_string.rs index 965a64c98..fe396806b 100644 --- a/crates/ra_assists/src/assists/raw_string.rs +++ b/crates/ra_assists/src/assists/raw_string.rs | |||
@@ -1,5 +1,6 @@ | |||
1 | use hir::db::HirDatabase; | 1 | use hir::db::HirDatabase; |
2 | use ra_syntax::{ast::AstNode, ast::Literal, TextRange, TextUnit}; | 2 | use ra_syntax::{ast::AstNode, ast::Literal, TextRange, TextUnit}; |
3 | use rustc_lexer; | ||
3 | 4 | ||
4 | use crate::{Assist, AssistCtx, AssistId}; | 5 | use crate::{Assist, AssistCtx, AssistId}; |
5 | 6 | ||
@@ -15,6 +16,39 @@ pub(crate) fn make_raw_string(mut ctx: AssistCtx<impl HirDatabase>) -> Option<As | |||
15 | ctx.build() | 16 | ctx.build() |
16 | } | 17 | } |
17 | 18 | ||
19 | pub(crate) fn make_raw_string_unescaped(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { | ||
20 | let literal = ctx.node_at_offset::<Literal>()?; | ||
21 | if literal.token().kind() != ra_syntax::SyntaxKind::STRING { | ||
22 | return None; | ||
23 | } | ||
24 | let token = literal.token(); | ||
25 | let text = token.text().as_str(); | ||
26 | if !text.contains(&['\\', '\r'][..]) { | ||
27 | return None; | ||
28 | } | ||
29 | let usual_string_range = find_usual_string_range(text)?; | ||
30 | ctx.add_action(AssistId("make_raw_string"), "make raw string", |edit| { | ||
31 | edit.target(literal.syntax().text_range()); | ||
32 | let start_of_inside = usual_string_range.start().to_usize() + 1; | ||
33 | let end_of_inside = usual_string_range.end().to_usize(); | ||
34 | let inside_str = &text[start_of_inside..end_of_inside]; | ||
35 | let mut unescaped = String::with_capacity(inside_str.len()); | ||
36 | let mut error = Ok(()); | ||
37 | rustc_lexer::unescape::unescape_str(inside_str, &mut |_, unescaped_char| { | ||
38 | match unescaped_char { | ||
39 | Ok(c) => unescaped.push(c), | ||
40 | Err(_) => error = Err(()), | ||
41 | } | ||
42 | }); | ||
43 | if error.is_err() { | ||
44 | eprintln!("Error unescaping string"); | ||
45 | } else { | ||
46 | edit.replace(literal.syntax().text_range(), format!("r\"{}\"", unescaped)); | ||
47 | } | ||
48 | }); | ||
49 | ctx.build() | ||
50 | } | ||
51 | |||
18 | fn find_usual_string_range(s: &str) -> Option<TextRange> { | 52 | fn find_usual_string_range(s: &str) -> Option<TextRange> { |
19 | Some(TextRange::from_to( | 53 | Some(TextRange::from_to( |
20 | TextUnit::from(s.find('"')? as u32), | 54 | TextUnit::from(s.find('"')? as u32), |
@@ -146,6 +180,49 @@ mod test { | |||
146 | } | 180 | } |
147 | 181 | ||
148 | #[test] | 182 | #[test] |
183 | fn make_raw_string_unescaped_target() { | ||
184 | check_assist_target( | ||
185 | make_raw_string_unescaped, | ||
186 | r#" | ||
187 | fn f() { | ||
188 | let s = <|>"random\nstring"; | ||
189 | } | ||
190 | "#, | ||
191 | r#""random\nstring""#, | ||
192 | ); | ||
193 | } | ||
194 | |||
195 | #[test] | ||
196 | fn make_raw_string_unescaped_works() { | ||
197 | check_assist( | ||
198 | make_raw_string_unescaped, | ||
199 | r#" | ||
200 | fn f() { | ||
201 | let s = <|>"random\nstring"; | ||
202 | } | ||
203 | "#, | ||
204 | r#" | ||
205 | fn f() { | ||
206 | let s = <|>r"random | ||
207 | string"; | ||
208 | } | ||
209 | "#, | ||
210 | ) | ||
211 | } | ||
212 | |||
213 | #[test] | ||
214 | fn make_raw_string_unescaped_dont_works() { | ||
215 | check_assist_not_applicable( | ||
216 | make_raw_string_unescaped, | ||
217 | r#" | ||
218 | fn f() { | ||
219 | let s = <|>"random string"; | ||
220 | } | ||
221 | "#, | ||
222 | ) | ||
223 | } | ||
224 | |||
225 | #[test] | ||
149 | fn add_hash_target() { | 226 | fn add_hash_target() { |
150 | check_assist_target( | 227 | check_assist_target( |
151 | add_hash, | 228 | add_hash, |
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs index 897af2b02..d1e2d3251 100644 --- a/crates/ra_assists/src/lib.rs +++ b/crates/ra_assists/src/lib.rs | |||
@@ -132,6 +132,7 @@ mod assists { | |||
132 | move_bounds::move_bounds_to_where_clause, | 132 | move_bounds::move_bounds_to_where_clause, |
133 | raw_string::add_hash, | 133 | raw_string::add_hash, |
134 | raw_string::make_raw_string, | 134 | raw_string::make_raw_string, |
135 | raw_string::make_raw_string_unescaped, | ||
135 | raw_string::make_usual_string, | 136 | raw_string::make_usual_string, |
136 | raw_string::remove_hash, | 137 | raw_string::remove_hash, |
137 | ] | 138 | ] |
@@ -340,5 +341,4 @@ mod tests { | |||
340 | assert_eq!(assists.next().expect("expected assist").0.label, "introduce variable"); | 341 | assert_eq!(assists.next().expect("expected assist").0.label, "introduce variable"); |
341 | assert_eq!(assists.next().expect("expected assist").0.label, "replace with match"); | 342 | assert_eq!(assists.next().expect("expected assist").0.label, "replace with match"); |
342 | } | 343 | } |
343 | |||
344 | } | 344 | } |
diff --git a/docs/user/features.md b/docs/user/features.md index eb81cba26..fadd4cdf1 100644 --- a/docs/user/features.md +++ b/docs/user/features.md | |||
@@ -459,6 +459,21 @@ fn f() { | |||
459 | } | 459 | } |
460 | ``` | 460 | ``` |
461 | 461 | ||
462 | - Make raw string unescaped | ||
463 | |||
464 | ```rust | ||
465 | // before: | ||
466 | fn f() { | ||
467 | let s = <|>"ab\ncd"; | ||
468 | } | ||
469 | |||
470 | // after: | ||
471 | fn f() { | ||
472 | let s = <|>r"ab | ||
473 | cd"; | ||
474 | } | ||
475 | ``` | ||
476 | |||
462 | - Make usual string | 477 | - Make usual string |
463 | 478 | ||
464 | ```rust | 479 | ```rust |