aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_assists/src/assists/raw_string.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_assists/src/assists/raw_string.rs')
-rw-r--r--crates/ra_assists/src/assists/raw_string.rs110
1 files changed, 92 insertions, 18 deletions
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
3use hir::db::HirDatabase; 3use hir::db::HirDatabase;
4use ra_syntax::{ast::AstNode, ast::Literal, TextRange, TextUnit}; 4use ra_syntax::{ast::AstNode, ast::Literal, TextRange, TextUnit};
5use rustc_lexer;
5 6
6use crate::{Assist, AssistCtx, AssistId}; 7use 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
47fn 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
20fn find_usual_string_range(s: &str) -> Option<TextRange> { 59fn 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
155string"#;
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##
173string"#;
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"##
191string"###;
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}