aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock1
-rw-r--r--crates/ra_assists/Cargo.toml1
-rw-r--r--crates/ra_assists/src/assists/raw_string.rs110
-rw-r--r--docs/user/features.md7
4 files changed, 98 insertions, 21 deletions
diff --git a/Cargo.lock b/Cargo.lock
index a5a43cffc..e9383a89a 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"
11itertools = "0.8.0" 11itertools = "0.8.0"
12arrayvec = "0.4.10" 12arrayvec = "0.4.10"
13rustc-hash = "1.0.1" 13rustc-hash = "1.0.1"
14rustc_lexer = "0.1.0"
14 15
15ra_syntax = { path = "../ra_syntax" } 16ra_syntax = { path = "../ra_syntax" }
16ra_text_edit = { path = "../ra_text_edit" } 17ra_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
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}
diff --git a/docs/user/features.md b/docs/user/features.md
index 757a02838..8b7a8d7fc 100644
--- a/docs/user/features.md
+++ b/docs/user/features.md
@@ -459,17 +459,18 @@ fn foo<T: u32, F: FnOnce(T) -> T>() {}
459fn foo<T, F>() where T: u32, F: FnOnce(T) -> T {} 459fn foo<T, F>() where T: u32, F: FnOnce(T) -> T {}
460``` 460```
461 461
462- Make raw string 462- Make raw string unescaped
463 463
464```rust 464```rust
465// before: 465// before:
466fn f() { 466fn f() {
467 let s = <|>"abcd"; 467 let s = <|>"ab\ncd";
468} 468}
469 469
470// after: 470// after:
471fn f() { 471fn f() {
472 let s = <|>r"abcd"; 472 let s = <|>r#"ab
473cd"#;
473} 474}
474``` 475```
475 476