@@ -4,7 +4,7 @@ use syntax::{AstToken, TextRange, TextSize, ast, ast::IsString};
44
55use crate :: {
66 AssistContext , AssistId , Assists ,
7- utils:: { required_hashes, string_suffix} ,
7+ utils:: { required_hashes, string_prefix , string_suffix} ,
88} ;
99
1010// Assist: make_raw_string
@@ -23,8 +23,7 @@ use crate::{
2323// }
2424// ```
2525pub ( crate ) fn make_raw_string ( acc : & mut Assists , ctx : & AssistContext < ' _ > ) -> Option < ( ) > {
26- // FIXME: This should support byte and c strings as well.
27- let token = ctx. find_token_at_offset :: < ast:: String > ( ) ?;
26+ let token = ctx. find_token_at_offset :: < ast:: AnyString > ( ) ?;
2827 if token. is_raw ( ) {
2928 return None ;
3029 }
@@ -37,14 +36,15 @@ pub(crate) fn make_raw_string(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt
3736 |edit| {
3837 let hashes = "#" . repeat ( required_hashes ( & value) . max ( 1 ) ) ;
3938 let range = token. syntax ( ) . text_range ( ) ;
39+ let raw_prefix = token. raw_prefix ( ) ;
4040 let suffix = string_suffix ( token. text ( ) ) . unwrap_or_default ( ) ;
4141 let range = TextRange :: new ( range. start ( ) , range. end ( ) - TextSize :: of ( suffix) ) ;
4242 if matches ! ( value, Cow :: Borrowed ( _) ) {
4343 // Avoid replacing the whole string to better position the cursor.
44- edit. insert ( range. start ( ) , format ! ( "r {hashes}" ) ) ;
44+ edit. insert ( range. start ( ) , format ! ( "{raw_prefix} {hashes}" ) ) ;
4545 edit. insert ( range. end ( ) , hashes) ;
4646 } else {
47- edit. replace ( range, format ! ( "r {hashes}\" {value}\" {hashes}" ) ) ;
47+ edit. replace ( range, format ! ( "{raw_prefix} {hashes}\" {value}\" {hashes}" ) ) ;
4848 }
4949 } ,
5050 )
@@ -66,7 +66,7 @@ pub(crate) fn make_raw_string(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt
6666// }
6767// ```
6868pub ( crate ) fn make_usual_string ( acc : & mut Assists , ctx : & AssistContext < ' _ > ) -> Option < ( ) > {
69- let token = ctx. find_token_at_offset :: < ast:: String > ( ) ?;
69+ let token = ctx. find_token_at_offset :: < ast:: AnyString > ( ) ?;
7070 if !token. is_raw ( ) {
7171 return None ;
7272 }
@@ -80,18 +80,22 @@ pub(crate) fn make_usual_string(acc: &mut Assists, ctx: &AssistContext<'_>) -> O
8080 // parse inside string to escape `"`
8181 let escaped = value. escape_default ( ) . to_string ( ) ;
8282 let suffix = string_suffix ( token. text ( ) ) . unwrap_or_default ( ) ;
83+ let prefix = string_prefix ( token. text ( ) ) . map_or ( "" , |s| s. trim_end_matches ( 'r' ) ) ;
8384 if let Some ( offsets) = token. quote_offsets ( )
8485 && token. text ( ) [ offsets. contents - token. syntax ( ) . text_range ( ) . start ( ) ] == escaped
8586 {
87+ let start_quote = offsets. quotes . 0 ;
88+ let start_quote =
89+ TextRange :: new ( start_quote. start ( ) + TextSize :: of ( prefix) , start_quote. end ( ) ) ;
8690 let end_quote = offsets. quotes . 1 ;
8791 let end_quote =
8892 TextRange :: new ( end_quote. start ( ) , end_quote. end ( ) - TextSize :: of ( suffix) ) ;
89- edit. replace ( offsets. quotes . 0 , "\" " ) ;
9093 edit. replace ( end_quote, "\" " ) ;
94+ edit. replace ( start_quote, "\" " ) ;
9195 return ;
9296 }
9397
94- edit. replace ( token. syntax ( ) . text_range ( ) , format ! ( "\" {escaped}\" {suffix}" ) ) ;
98+ edit. replace ( token. syntax ( ) . text_range ( ) , format ! ( "{prefix} \" {escaped}\" {suffix}" ) ) ;
9599 } ,
96100 )
97101}
@@ -112,15 +116,15 @@ pub(crate) fn make_usual_string(acc: &mut Assists, ctx: &AssistContext<'_>) -> O
112116// }
113117// ```
114118pub ( crate ) fn add_hash ( acc : & mut Assists , ctx : & AssistContext < ' _ > ) -> Option < ( ) > {
115- let token = ctx. find_token_at_offset :: < ast:: String > ( ) ?;
119+ let token = ctx. find_token_at_offset :: < ast:: AnyString > ( ) ?;
116120 if !token. is_raw ( ) {
117121 return None ;
118122 }
119123 let text_range = token. syntax ( ) . text_range ( ) ;
120124 let target = text_range;
121125 acc. add ( AssistId :: refactor ( "add_hash" ) , "Add #" , target, |edit| {
122126 let suffix = string_suffix ( token. text ( ) ) . unwrap_or_default ( ) ;
123- edit. insert ( text_range. start ( ) + TextSize :: of ( 'r' ) , "#" ) ;
127+ edit. insert ( text_range. start ( ) + TextSize :: of ( token . raw_prefix ( ) ) , "#" ) ;
124128 edit. insert ( text_range. end ( ) - TextSize :: of ( suffix) , "#" ) ;
125129 } )
126130}
@@ -141,17 +145,15 @@ pub(crate) fn add_hash(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()>
141145// }
142146// ```
143147pub ( crate ) fn remove_hash ( acc : & mut Assists , ctx : & AssistContext < ' _ > ) -> Option < ( ) > {
144- let token = ctx. find_token_at_offset :: < ast:: String > ( ) ?;
148+ let token = ctx. find_token_at_offset :: < ast:: AnyString > ( ) ?;
145149 if !token. is_raw ( ) {
146150 return None ;
147151 }
148152
149153 let text = token. text ( ) ;
150- if !text. starts_with ( "r#" ) && text. ends_with ( '#' ) {
151- return None ;
152- }
153154
154- let existing_hashes = text. chars ( ) . skip ( 1 ) . take_while ( |& it| it == '#' ) . count ( ) ;
155+ let existing_hashes =
156+ text. chars ( ) . skip ( token. raw_prefix ( ) . len ( ) ) . take_while ( |& it| it == '#' ) . count ( ) ;
155157
156158 let text_range = token. syntax ( ) . text_range ( ) ;
157159 let internal_text = & text[ token. text_range_between_quotes ( ) ? - text_range. start ( ) ] ;
@@ -163,7 +165,10 @@ pub(crate) fn remove_hash(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<
163165
164166 acc. add ( AssistId :: refactor_rewrite ( "remove_hash" ) , "Remove #" , text_range, |edit| {
165167 let suffix = string_suffix ( text) . unwrap_or_default ( ) ;
166- edit. delete ( TextRange :: at ( text_range. start ( ) + TextSize :: of ( 'r' ) , TextSize :: of ( '#' ) ) ) ;
168+ edit. delete ( TextRange :: at (
169+ text_range. start ( ) + TextSize :: of ( token. raw_prefix ( ) ) ,
170+ TextSize :: of ( '#' ) ,
171+ ) ) ;
167172 edit. delete (
168173 TextRange :: new ( text_range. end ( ) - TextSize :: of ( '#' ) , text_range. end ( ) )
169174 - TextSize :: of ( suffix) ,
@@ -224,6 +229,42 @@ string"#;
224229 )
225230 }
226231
232+ #[ test]
233+ fn make_raw_byte_string_works ( ) {
234+ check_assist (
235+ make_raw_string,
236+ r#"
237+ fn f() {
238+ let s = $0b"random\nstring";
239+ }
240+ "# ,
241+ r##"
242+ fn f() {
243+ let s = br#"random
244+ string"#;
245+ }
246+ "## ,
247+ )
248+ }
249+
250+ #[ test]
251+ fn make_raw_c_string_works ( ) {
252+ check_assist (
253+ make_raw_string,
254+ r#"
255+ fn f() {
256+ let s = $0c"random\nstring";
257+ }
258+ "# ,
259+ r##"
260+ fn f() {
261+ let s = cr#"random
262+ string"#;
263+ }
264+ "## ,
265+ )
266+ }
267+
227268 #[ test]
228269 fn make_raw_string_hashes_inside_works ( ) {
229270 check_assist (
@@ -348,6 +389,23 @@ string"###;
348389 )
349390 }
350391
392+ #[ test]
393+ fn add_hash_works_for_c_str ( ) {
394+ check_assist (
395+ add_hash,
396+ r#"
397+ fn f() {
398+ let s = $0cr"random string";
399+ }
400+ "# ,
401+ r##"
402+ fn f() {
403+ let s = cr#"random string"#;
404+ }
405+ "## ,
406+ )
407+ }
408+
351409 #[ test]
352410 fn add_hash_has_suffix_works ( ) {
353411 check_assist (
@@ -433,6 +491,15 @@ string"###;
433491 )
434492 }
435493
494+ #[ test]
495+ fn remove_hash_works_for_c_str ( ) {
496+ check_assist (
497+ remove_hash,
498+ r##"fn f() { let s = $0cr#"random string"#; }"## ,
499+ r#"fn f() { let s = cr"random string"; }"# ,
500+ )
501+ }
502+
436503 #[ test]
437504 fn remove_hash_has_suffix_works ( ) {
438505 check_assist (
@@ -529,6 +596,23 @@ string"###;
529596 )
530597 }
531598
599+ #[ test]
600+ fn make_usual_string_for_c_str ( ) {
601+ check_assist (
602+ make_usual_string,
603+ r##"
604+ fn f() {
605+ let s = $0cr#"random string"#;
606+ }
607+ "## ,
608+ r#"
609+ fn f() {
610+ let s = c"random string";
611+ }
612+ "# ,
613+ )
614+ }
615+
532616 #[ test]
533617 fn make_usual_string_has_suffix_works ( ) {
534618 check_assist (
0 commit comments