2
2
3
3
use hir:: db:: HirDatabase ;
4
4
use ra_syntax:: { ast:: AstNode , ast:: Literal , TextRange , TextUnit } ;
5
+ use rustc_lexer;
5
6
6
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
11
if literal. token ( ) . kind ( ) != ra_syntax:: SyntaxKind :: STRING {
11
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
32
ctx. add_action ( AssistId ( "make_raw_string" ) , "make raw string" , |edit| {
14
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
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
59
fn find_usual_string_range ( s : & str ) -> Option < TextRange > {
21
60
Some ( TextRange :: from_to (
22
61
TextUnit :: from ( s. find ( '"' ) ? as u32 ) ,
@@ -94,10 +133,10 @@ mod test {
94
133
make_raw_string,
95
134
r#"
96
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
146
make_raw_string,
108
147
r#"
109
148
fn f() {
110
- let s = <|>"random string ";
149
+ let s = <|>"random\nstring ";
111
150
}
112
151
"# ,
113
- r#"
152
+ r## "
114
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
161
#[ test]
122
- fn make_raw_string_with_escaped_works ( ) {
162
+ fn make_raw_string_hashes_inside_works ( ) {
123
163
check_assist (
124
164
make_raw_string,
125
- r#"
165
+ r### "
126
166
fn f() {
127
- let s = <|>"random\nstring";
167
+ let s = <|>"# random## \nstring";
128
168
}
129
- "# ,
130
- r#"
169
+ "### ,
170
+ r#### "
131
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
179
#[ 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 (
141
200
make_raw_string,
142
201
r#"
143
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
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
}
0 commit comments