@@ -4,8 +4,9 @@ use ra_syntax::{
4
4
ast:: { self , HasQuotes , HasStringValue } ,
5
5
AstToken ,
6
6
SyntaxKind :: { RAW_STRING , STRING } ,
7
- TextSize ,
7
+ TextRange , TextSize ,
8
8
} ;
9
+ use test_utils:: mark;
9
10
10
11
use crate :: { AssistContext , AssistId , AssistKind , Assists } ;
11
12
@@ -33,8 +34,7 @@ pub(crate) fn make_raw_string(acc: &mut Assists, ctx: &AssistContext) -> Option<
33
34
"Rewrite as raw string" ,
34
35
target,
35
36
|edit| {
36
- let max_hash_streak = count_hashes ( & value) ;
37
- let hashes = "#" . repeat ( max_hash_streak + 1 ) ;
37
+ let hashes = "#" . repeat ( required_hashes ( & value) . max ( 1 ) ) ;
38
38
if matches ! ( value, Cow :: Borrowed ( _) ) {
39
39
// Avoid replacing the whole string to better position the cursor.
40
40
edit. insert ( token. syntax ( ) . text_range ( ) . start ( ) , format ! ( "r{}" , hashes) ) ;
@@ -106,7 +106,7 @@ pub(crate) fn make_usual_string(acc: &mut Assists, ctx: &AssistContext) -> Optio
106
106
pub ( crate ) fn add_hash ( acc : & mut Assists , ctx : & AssistContext ) -> Option < ( ) > {
107
107
let token = ctx. find_token_at_offset ( RAW_STRING ) ?;
108
108
let target = token. text_range ( ) ;
109
- acc. add ( AssistId ( "add_hash" , AssistKind :: Refactor ) , "Add # to raw string " , target, |edit| {
109
+ acc. add ( AssistId ( "add_hash" , AssistKind :: Refactor ) , "Add #" , target, |edit| {
110
110
edit. insert ( token. text_range ( ) . start ( ) + TextSize :: of ( 'r' ) , "#" ) ;
111
111
edit. insert ( token. text_range ( ) . end ( ) , "#" ) ;
112
112
} )
@@ -128,49 +128,58 @@ pub(crate) fn add_hash(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
128
128
// }
129
129
// ```
130
130
pub ( crate ) fn remove_hash ( acc : & mut Assists , ctx : & AssistContext ) -> Option < ( ) > {
131
- let token = ctx. find_token_at_offset ( RAW_STRING ) ?;
131
+ let token = ctx. find_token_at_offset ( RAW_STRING ) . and_then ( ast:: RawString :: cast) ?;
132
+
132
133
let text = token. text ( ) . as_str ( ) ;
133
- if text. starts_with ( "r\" " ) {
134
- // no hash to remove
134
+ if !text. starts_with ( "r#" ) && text. ends_with ( "#" ) {
135
135
return None ;
136
136
}
137
- let target = token. text_range ( ) ;
138
- acc. add (
139
- AssistId ( "remove_hash" , AssistKind :: RefactorRewrite ) ,
140
- "Remove hash from raw string" ,
141
- target,
142
- |edit| {
143
- let result = & text[ 2 ..text. len ( ) - 1 ] ;
144
- let result = if result. starts_with ( '\"' ) {
145
- // FIXME: this logic is wrong, not only the last has has to handled specially
146
- // no more hash, escape
147
- let internal_str = & result[ 1 ..result. len ( ) - 1 ] ;
148
- format ! ( "\" {}\" " , internal_str. escape_default( ) . to_string( ) )
149
- } else {
150
- result. to_owned ( )
151
- } ;
152
- edit. replace ( token. text_range ( ) , format ! ( "r{}" , result) ) ;
153
- } ,
154
- )
137
+
138
+ let existing_hashes = text. chars ( ) . skip ( 1 ) . take_while ( |& it| it == '#' ) . count ( ) ;
139
+
140
+ let text_range = token. syntax ( ) . text_range ( ) ;
141
+ let internal_text = & text[ token. text_range_between_quotes ( ) ? - text_range. start ( ) ] ;
142
+
143
+ if existing_hashes == required_hashes ( internal_text) {
144
+ mark:: hit!( cant_remove_required_hash) ;
145
+ return None ;
146
+ }
147
+
148
+ acc. add ( AssistId ( "remove_hash" , AssistKind :: RefactorRewrite ) , "Remove #" , text_range, |edit| {
149
+ edit. delete ( TextRange :: at ( text_range. start ( ) + TextSize :: of ( 'r' ) , TextSize :: of ( '#' ) ) ) ;
150
+ edit. delete ( TextRange :: new ( text_range. end ( ) - TextSize :: of ( '#' ) , text_range. end ( ) ) ) ;
151
+ } )
155
152
}
156
153
157
- fn count_hashes ( s : & str ) -> usize {
158
- let mut max_hash_streak = 0usize ;
159
- for idx in s. match_indices ( " \" #" ) . map ( |( i, _) | i) {
154
+ fn required_hashes ( s : & str ) -> usize {
155
+ let mut res = 0usize ;
156
+ for idx in s. match_indices ( '"' ) . map ( |( i, _) | i) {
160
157
let ( _, sub) = s. split_at ( idx + 1 ) ;
161
- let nb_hash = sub. chars ( ) . take_while ( |c| * c == '#' ) . count ( ) ;
162
- if nb_hash > max_hash_streak {
163
- max_hash_streak = nb_hash;
164
- }
158
+ let n_hashes = sub. chars ( ) . take_while ( |c| * c == '#' ) . count ( ) ;
159
+ res = res. max ( n_hashes + 1 )
165
160
}
166
- max_hash_streak
161
+ res
162
+ }
163
+
164
+ #[ test]
165
+ fn test_required_hashes ( ) {
166
+ assert_eq ! ( 0 , required_hashes( "abc" ) ) ;
167
+ assert_eq ! ( 0 , required_hashes( "###" ) ) ;
168
+ assert_eq ! ( 1 , required_hashes( "\" " ) ) ;
169
+ assert_eq ! ( 2 , required_hashes( "\" #abc" ) ) ;
170
+ assert_eq ! ( 0 , required_hashes( "#abc" ) ) ;
171
+ assert_eq ! ( 3 , required_hashes( "#ab\" ##c" ) ) ;
172
+ assert_eq ! ( 5 , required_hashes( "#ab\" ##\" ####c" ) ) ;
167
173
}
168
174
169
175
#[ cfg( test) ]
170
176
mod test {
171
- use super :: * ;
177
+ use test_utils:: mark;
178
+
172
179
use crate :: tests:: { check_assist, check_assist_not_applicable, check_assist_target} ;
173
180
181
+ use super :: * ;
182
+
174
183
#[ test]
175
184
fn make_raw_string_target ( ) {
176
185
check_assist_target (
@@ -372,33 +381,21 @@ string"###;
372
381
fn remove_hash_works ( ) {
373
382
check_assist (
374
383
remove_hash,
375
- r##"
376
- fn f() {
377
- let s = <|>r#"random string"#;
378
- }
379
- "## ,
380
- r#"
381
- fn f() {
382
- let s = r"random string";
383
- }
384
- "# ,
384
+ r##"fn f() { let s = <|>r#"random string"#; }"## ,
385
+ r#"fn f() { let s = r"random string"; }"# ,
385
386
)
386
387
}
387
388
388
389
#[ test]
389
- fn remove_hash_with_quote_works ( ) {
390
- check_assist (
390
+ fn cant_remove_required_hash ( ) {
391
+ mark:: check!( cant_remove_required_hash) ;
392
+ check_assist_not_applicable (
391
393
remove_hash,
392
394
r##"
393
395
fn f() {
394
396
let s = <|>r#"random"str"ing"#;
395
397
}
396
398
"## ,
397
- r#"
398
- fn f() {
399
- let s = r"random\"str\"ing";
400
- }
401
- "# ,
402
399
)
403
400
}
404
401
@@ -420,27 +417,13 @@ string"###;
420
417
}
421
418
422
419
#[ test]
423
- fn remove_hash_not_works ( ) {
424
- check_assist_not_applicable (
425
- remove_hash,
426
- r#"
427
- fn f() {
428
- let s = <|>"random string";
429
- }
430
- "# ,
431
- ) ;
420
+ fn remove_hash_doesnt_work ( ) {
421
+ check_assist_not_applicable ( remove_hash, r#"fn f() { let s = <|>"random string"; }"# ) ;
432
422
}
433
423
434
424
#[ test]
435
- fn remove_hash_no_hash_not_works ( ) {
436
- check_assist_not_applicable (
437
- remove_hash,
438
- r#"
439
- fn f() {
440
- let s = <|>r"random string";
441
- }
442
- "# ,
443
- ) ;
425
+ fn remove_hash_no_hash_doesnt_work ( ) {
426
+ check_assist_not_applicable ( remove_hash, r#"fn f() { let s = <|>r"random string"; }"# ) ;
444
427
}
445
428
446
429
#[ test]
@@ -518,14 +501,4 @@ string"###;
518
501
"# ,
519
502
) ;
520
503
}
521
-
522
- #[ test]
523
- fn count_hashes_test ( ) {
524
- assert_eq ! ( 0 , count_hashes( "abc" ) ) ;
525
- assert_eq ! ( 0 , count_hashes( "###" ) ) ;
526
- assert_eq ! ( 1 , count_hashes( "\" #abc" ) ) ;
527
- assert_eq ! ( 0 , count_hashes( "#abc" ) ) ;
528
- assert_eq ! ( 2 , count_hashes( "#ab\" ##c" ) ) ;
529
- assert_eq ! ( 4 , count_hashes( "#ab\" ##\" ####c" ) ) ;
530
- }
531
504
}
0 commit comments