Skip to content

Commit 9319900

Browse files
bors[bot]Geobert Quach
and
Geobert Quach
authored
Merge #1922
1922: feat(assists): Make raw string unescaped r=matklad a=Geobert Last piece of #1730 Co-authored-by: Geobert Quach <[email protected]>
2 parents d9338df + 31663c1 commit 9319900

File tree

4 files changed

+98
-21
lines changed

4 files changed

+98
-21
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/ra_assists/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ join_to_string = "0.1.3"
1111
itertools = "0.8.0"
1212
arrayvec = "0.4.10"
1313
rustc-hash = "1.0.1"
14+
rustc_lexer = "0.1.0"
1415

1516
ra_syntax = { path = "../ra_syntax" }
1617
ra_text_edit = { path = "../ra_text_edit" }

crates/ra_assists/src/assists/raw_string.rs

Lines changed: 92 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
33
use hir::db::HirDatabase;
44
use ra_syntax::{ast::AstNode, ast::Literal, TextRange, TextUnit};
5+
use rustc_lexer;
56

67
use crate::{Assist, AssistCtx, AssistId};
78

@@ -10,13 +11,51 @@ pub(crate) fn make_raw_string(mut ctx: AssistCtx<impl HirDatabase>) -> Option<As
1011
if literal.token().kind() != ra_syntax::SyntaxKind::STRING {
1112
return None;
1213
}
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+
}
1332
ctx.add_action(AssistId("make_raw_string"), "make raw string", |edit| {
1433
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+
);
1643
});
1744
ctx.build()
1845
}
1946

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+
2059
fn find_usual_string_range(s: &str) -> Option<TextRange> {
2160
Some(TextRange::from_to(
2261
TextUnit::from(s.find('"')? as u32),
@@ -94,10 +133,10 @@ mod test {
94133
make_raw_string,
95134
r#"
96135
fn f() {
97-
let s = <|>"random string";
136+
let s = <|>"random\nstring";
98137
}
99138
"#,
100-
r#""random string""#,
139+
r#""random\nstring""#,
101140
);
102141
}
103142

@@ -107,44 +146,69 @@ mod test {
107146
make_raw_string,
108147
r#"
109148
fn f() {
110-
let s = <|>"random string";
149+
let s = <|>"random\nstring";
111150
}
112151
"#,
113-
r#"
152+
r##"
114153
fn f() {
115-
let s = <|>r"random string";
154+
let s = <|>r#"random
155+
string"#;
116156
}
117-
"#,
157+
"##,
118158
)
119159
}
120160

121161
#[test]
122-
fn make_raw_string_with_escaped_works() {
162+
fn make_raw_string_hashes_inside_works() {
123163
check_assist(
124164
make_raw_string,
125-
r#"
165+
r###"
126166
fn f() {
127-
let s = <|>"random\nstring";
167+
let s = <|>"#random##\nstring";
128168
}
129-
"#,
130-
r#"
169+
"###,
170+
r####"
131171
fn f() {
132-
let s = <|>r"random\nstring";
172+
let s = <|>r#"#random##
173+
string"#;
133174
}
134-
"#,
175+
"####,
135176
)
136177
}
137178

138179
#[test]
139-
fn make_raw_string_not_works() {
140-
check_assist_not_applicable(
180+
fn make_raw_string_closing_hashes_inside_works() {
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(
141200
make_raw_string,
142201
r#"
143202
fn f() {
144-
let s = <|>r"random string";
203+
let s = <|>"random string";
145204
}
146205
"#,
147-
);
206+
r##"
207+
fn f() {
208+
let s = <|>r#"random string"#;
209+
}
210+
"##,
211+
)
148212
}
149213

150214
#[test]
@@ -369,4 +433,14 @@ mod test {
369433
"#,
370434
);
371435
}
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+
}
372446
}

docs/user/features.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -459,17 +459,18 @@ fn foo<T: u32, F: FnOnce(T) -> T>() {}
459459
fn foo<T, F>() where T: u32, F: FnOnce(T) -> T {}
460460
```
461461

462-
- Make raw string
462+
- Make raw string unescaped
463463

464464
```rust
465465
// before:
466466
fn f() {
467-
let s = <|>"abcd";
467+
let s = <|>"ab\ncd";
468468
}
469469

470470
// after:
471471
fn f() {
472-
let s = <|>r"abcd";
472+
let s = <|>r#"ab
473+
cd"#;
473474
}
474475
```
475476

0 commit comments

Comments
 (0)