diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2019-10-08 10:52:22 +0100 |
---|---|---|
committer | GitHub <[email protected]> | 2019-10-08 10:52:22 +0100 |
commit | 93199002af05f3a3dfd274fe10633372f2471b73 (patch) | |
tree | 7200deed270b234ad2d4c9bcef32242dff46aaba /crates | |
parent | d9338dfa98964c0dac8fc082c3d9201807feced0 (diff) | |
parent | 31663c1368aedfdc52aaea7b54de0097c13cf889 (diff) |
Merge #1922
1922: feat(assists): Make raw string unescaped r=matklad a=Geobert
Last piece of https://github.com/rust-analyzer/rust-analyzer/issues/1730
Co-authored-by: Geobert Quach <[email protected]>
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_assists/Cargo.toml | 1 | ||||
-rw-r--r-- | crates/ra_assists/src/assists/raw_string.rs | 110 |
2 files changed, 93 insertions, 18 deletions
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 388ee7e97..2d2e31e51 100644 --- a/crates/ra_assists/src/assists/raw_string.rs +++ b/crates/ra_assists/src/assists/raw_string.rs | |||
@@ -2,6 +2,7 @@ | |||
2 | 2 | ||
3 | use hir::db::HirDatabase; | 3 | use hir::db::HirDatabase; |
4 | use ra_syntax::{ast::AstNode, ast::Literal, TextRange, TextUnit}; | 4 | use ra_syntax::{ast::AstNode, ast::Literal, TextRange, TextUnit}; |
5 | use rustc_lexer; | ||
5 | 6 | ||
6 | use crate::{Assist, AssistCtx, AssistId}; | 7 | use crate::{Assist, AssistCtx, AssistId}; |
7 | 8 | ||
@@ -10,13 +11,51 @@ pub(crate) fn make_raw_string(mut ctx: AssistCtx<impl HirDatabase>) -> Option<As | |||
10 | if literal.token().kind() != ra_syntax::SyntaxKind::STRING { | 11 | if literal.token().kind() != ra_syntax::SyntaxKind::STRING { |
11 | return None; | 12 | return None; |
12 | } | 13 | } |
14 | let token = literal.token(); | ||
15 | let text = token.text().as_str(); | ||
16 | let usual_string_range = find_usual_string_range(text)?; | ||
17 | let start_of_inside = usual_string_range.start().to_usize() + 1; | ||
18 | let end_of_inside = usual_string_range.end().to_usize(); | ||
19 | let inside_str = &text[start_of_inside..end_of_inside]; | ||
20 | let mut unescaped = String::with_capacity(inside_str.len()); | ||
21 | let mut error = Ok(()); | ||
22 | rustc_lexer::unescape::unescape_str( | ||
23 | inside_str, | ||
24 | &mut |_, unescaped_char| match unescaped_char { | ||
25 | Ok(c) => unescaped.push(c), | ||
26 | Err(_) => error = Err(()), | ||
27 | }, | ||
28 | ); | ||
29 | if error.is_err() { | ||
30 | return None; | ||
31 | } | ||
13 | ctx.add_action(AssistId("make_raw_string"), "make raw string", |edit| { | 32 | ctx.add_action(AssistId("make_raw_string"), "make raw string", |edit| { |
14 | edit.target(literal.syntax().text_range()); | 33 | edit.target(literal.syntax().text_range()); |
15 | edit.insert(literal.syntax().text_range().start(), "r"); | 34 | let max_hash_streak = count_hashes(&unescaped); |
35 | let mut hashes = String::with_capacity(max_hash_streak + 1); | ||
36 | for _ in 0..hashes.capacity() { | ||
37 | hashes.push('#'); | ||
38 | } | ||
39 | edit.replace( | ||
40 | literal.syntax().text_range(), | ||
41 | format!("r{}\"{}\"{}", hashes, unescaped, hashes), | ||
42 | ); | ||
16 | }); | 43 | }); |
17 | ctx.build() | 44 | ctx.build() |
18 | } | 45 | } |
19 | 46 | ||
47 | fn count_hashes(s: &str) -> usize { | ||
48 | let mut max_hash_streak = 0usize; | ||
49 | for idx in s.match_indices("\"#").map(|(i, _)| i) { | ||
50 | let (_, sub) = s.split_at(idx + 1); | ||
51 | let nb_hash = sub.chars().take_while(|c| *c == '#').count(); | ||
52 | if nb_hash > max_hash_streak { | ||
53 | max_hash_streak = nb_hash; | ||
54 | } | ||
55 | } | ||
56 | max_hash_streak | ||
57 | } | ||
58 | |||
20 | fn find_usual_string_range(s: &str) -> Option<TextRange> { | 59 | fn find_usual_string_range(s: &str) -> Option<TextRange> { |
21 | Some(TextRange::from_to( | 60 | Some(TextRange::from_to( |
22 | TextUnit::from(s.find('"')? as u32), | 61 | TextUnit::from(s.find('"')? as u32), |
@@ -94,10 +133,10 @@ mod test { | |||
94 | make_raw_string, | 133 | make_raw_string, |
95 | r#" | 134 | r#" |
96 | fn f() { | 135 | fn f() { |
97 | let s = <|>"random string"; | 136 | let s = <|>"random\nstring"; |
98 | } | 137 | } |
99 | "#, | 138 | "#, |
100 | r#""random string""#, | 139 | r#""random\nstring""#, |
101 | ); | 140 | ); |
102 | } | 141 | } |
103 | 142 | ||
@@ -107,44 +146,69 @@ mod test { | |||
107 | make_raw_string, | 146 | make_raw_string, |
108 | r#" | 147 | r#" |
109 | fn f() { | 148 | fn f() { |
110 | let s = <|>"random string"; | 149 | let s = <|>"random\nstring"; |
111 | } | 150 | } |
112 | "#, | 151 | "#, |
113 | r#" | 152 | r##" |
114 | fn f() { | 153 | fn f() { |
115 | let s = <|>r"random string"; | 154 | let s = <|>r#"random |
155 | string"#; | ||
116 | } | 156 | } |
117 | "#, | 157 | "##, |
118 | ) | 158 | ) |
119 | } | 159 | } |
120 | 160 | ||
121 | #[test] | 161 | #[test] |
122 | fn make_raw_string_with_escaped_works() { | 162 | fn make_raw_string_hashes_inside_works() { |
123 | check_assist( | 163 | check_assist( |
124 | make_raw_string, | 164 | make_raw_string, |
125 | r#" | 165 | r###" |
126 | fn f() { | 166 | fn f() { |
127 | let s = <|>"random\nstring"; | 167 | let s = <|>"#random##\nstring"; |
128 | } | 168 | } |
129 | "#, | 169 | "###, |
130 | r#" | 170 | r####" |
131 | fn f() { | 171 | fn f() { |
132 | let s = <|>r"random\nstring"; | 172 | let s = <|>r#"#random## |
173 | string"#; | ||
133 | } | 174 | } |
134 | "#, | 175 | "####, |
135 | ) | 176 | ) |
136 | } | 177 | } |
137 | 178 | ||
138 | #[test] | 179 | #[test] |
139 | fn make_raw_string_not_works() { | 180 | fn make_raw_string_closing_hashes_inside_works() { |
140 | check_assist_not_applicable( | 181 | check_assist( |
182 | make_raw_string, | ||
183 | r###" | ||
184 | fn f() { | ||
185 | let s = <|>"#random\"##\nstring"; | ||
186 | } | ||
187 | "###, | ||
188 | r####" | ||
189 | fn f() { | ||
190 | let s = <|>r###"#random"## | ||
191 | string"###; | ||
192 | } | ||
193 | "####, | ||
194 | ) | ||
195 | } | ||
196 | |||
197 | #[test] | ||
198 | fn make_raw_string_nothing_to_unescape_works() { | ||
199 | check_assist( | ||
141 | make_raw_string, | 200 | make_raw_string, |
142 | r#" | 201 | r#" |
143 | fn f() { | 202 | fn f() { |
144 | let s = <|>r"random string"; | 203 | let s = <|>"random string"; |
145 | } | 204 | } |
146 | "#, | 205 | "#, |
147 | ); | 206 | r##" |
207 | fn f() { | ||
208 | let s = <|>r#"random string"#; | ||
209 | } | ||
210 | "##, | ||
211 | ) | ||
148 | } | 212 | } |
149 | 213 | ||
150 | #[test] | 214 | #[test] |
@@ -369,4 +433,14 @@ mod test { | |||
369 | "#, | 433 | "#, |
370 | ); | 434 | ); |
371 | } | 435 | } |
436 | |||
437 | #[test] | ||
438 | fn count_hashes_test() { | ||
439 | assert_eq!(0, count_hashes("abc")); | ||
440 | assert_eq!(0, count_hashes("###")); | ||
441 | assert_eq!(1, count_hashes("\"#abc")); | ||
442 | assert_eq!(0, count_hashes("#abc")); | ||
443 | assert_eq!(2, count_hashes("#ab\"##c")); | ||
444 | assert_eq!(4, count_hashes("#ab\"##\"####c")); | ||
445 | } | ||
372 | } | 446 | } |