aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_assists/src/assists/raw_string.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_assists/src/assists/raw_string.rs')
-rw-r--r--crates/ra_assists/src/assists/raw_string.rs104
1 files changed, 53 insertions, 51 deletions
diff --git a/crates/ra_assists/src/assists/raw_string.rs b/crates/ra_assists/src/assists/raw_string.rs
index 2d2e31e51..ea756d1ca 100644
--- a/crates/ra_assists/src/assists/raw_string.rs
+++ b/crates/ra_assists/src/assists/raw_string.rs
@@ -1,17 +1,16 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use hir::db::HirDatabase; 3use hir::db::HirDatabase;
4use ra_syntax::{ast::AstNode, ast::Literal, TextRange, TextUnit}; 4use ra_syntax::{
5 SyntaxKind::{RAW_STRING, STRING},
6 TextRange, TextUnit,
7};
5use rustc_lexer; 8use rustc_lexer;
6 9
7use crate::{Assist, AssistCtx, AssistId}; 10use crate::{Assist, AssistCtx, AssistId};
8 11
9pub(crate) fn make_raw_string(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { 12pub(crate) fn make_raw_string(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
10 let literal = ctx.node_at_offset::<Literal>()?; 13 let token = ctx.find_token_at_offset(STRING)?;
11 if literal.token().kind() != ra_syntax::SyntaxKind::STRING {
12 return None;
13 }
14 let token = literal.token();
15 let text = token.text().as_str(); 14 let text = token.text().as_str();
16 let usual_string_range = find_usual_string_range(text)?; 15 let usual_string_range = find_usual_string_range(text)?;
17 let start_of_inside = usual_string_range.start().to_usize() + 1; 16 let start_of_inside = usual_string_range.start().to_usize() + 1;
@@ -30,85 +29,52 @@ pub(crate) fn make_raw_string(mut ctx: AssistCtx<impl HirDatabase>) -> Option<As
30 return None; 29 return None;
31 } 30 }
32 ctx.add_action(AssistId("make_raw_string"), "make raw string", |edit| { 31 ctx.add_action(AssistId("make_raw_string"), "make raw string", |edit| {
33 edit.target(literal.syntax().text_range()); 32 edit.target(token.text_range());
34 let max_hash_streak = count_hashes(&unescaped); 33 let max_hash_streak = count_hashes(&unescaped);
35 let mut hashes = String::with_capacity(max_hash_streak + 1); 34 let mut hashes = String::with_capacity(max_hash_streak + 1);
36 for _ in 0..hashes.capacity() { 35 for _ in 0..hashes.capacity() {
37 hashes.push('#'); 36 hashes.push('#');
38 } 37 }
39 edit.replace( 38 edit.replace(token.text_range(), format!("r{}\"{}\"{}", hashes, unescaped, hashes));
40 literal.syntax().text_range(),
41 format!("r{}\"{}\"{}", hashes, unescaped, hashes),
42 );
43 }); 39 });
44 ctx.build() 40 ctx.build()
45} 41}
46 42
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
59fn find_usual_string_range(s: &str) -> Option<TextRange> {
60 Some(TextRange::from_to(
61 TextUnit::from(s.find('"')? as u32),
62 TextUnit::from(s.rfind('"')? as u32),
63 ))
64}
65
66pub(crate) fn make_usual_string(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { 43pub(crate) fn make_usual_string(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
67 let literal = ctx.node_at_offset::<Literal>()?; 44 let token = ctx.find_token_at_offset(RAW_STRING)?;
68 if literal.token().kind() != ra_syntax::SyntaxKind::RAW_STRING {
69 return None;
70 }
71 let token = literal.token();
72 let text = token.text().as_str(); 45 let text = token.text().as_str();
73 let usual_string_range = find_usual_string_range(text)?; 46 let usual_string_range = find_usual_string_range(text)?;
74 ctx.add_action(AssistId("make_usual_string"), "make usual string", |edit| { 47 ctx.add_action(AssistId("make_usual_string"), "make usual string", |edit| {
75 edit.target(literal.syntax().text_range()); 48 edit.target(token.text_range());
76 // parse inside string to escape `"` 49 // parse inside string to escape `"`
77 let start_of_inside = usual_string_range.start().to_usize() + 1; 50 let start_of_inside = usual_string_range.start().to_usize() + 1;
78 let end_of_inside = usual_string_range.end().to_usize(); 51 let end_of_inside = usual_string_range.end().to_usize();
79 let inside_str = &text[start_of_inside..end_of_inside]; 52 let inside_str = &text[start_of_inside..end_of_inside];
80 let escaped = inside_str.escape_default().to_string(); 53 let escaped = inside_str.escape_default().to_string();
81 edit.replace(literal.syntax().text_range(), format!("\"{}\"", escaped)); 54 edit.replace(token.text_range(), format!("\"{}\"", escaped));
82 }); 55 });
83 ctx.build() 56 ctx.build()
84} 57}
85 58
86pub(crate) fn add_hash(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { 59pub(crate) fn add_hash(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
87 let literal = ctx.node_at_offset::<Literal>()?; 60 let token = ctx.find_token_at_offset(RAW_STRING)?;
88 if literal.token().kind() != ra_syntax::SyntaxKind::RAW_STRING {
89 return None;
90 }
91 ctx.add_action(AssistId("add_hash"), "add hash to raw string", |edit| { 61 ctx.add_action(AssistId("add_hash"), "add hash to raw string", |edit| {
92 edit.target(literal.syntax().text_range()); 62 edit.target(token.text_range());
93 edit.insert(literal.syntax().text_range().start() + TextUnit::of_char('r'), "#"); 63 edit.insert(token.text_range().start() + TextUnit::of_char('r'), "#");
94 edit.insert(literal.syntax().text_range().end(), "#"); 64 edit.insert(token.text_range().end(), "#");
95 }); 65 });
96 ctx.build() 66 ctx.build()
97} 67}
98 68
99pub(crate) fn remove_hash(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { 69pub(crate) fn remove_hash(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
100 let literal = ctx.node_at_offset::<Literal>()?; 70 let token = ctx.find_token_at_offset(RAW_STRING)?;
101 if literal.token().kind() != ra_syntax::SyntaxKind::RAW_STRING {
102 return None;
103 }
104 let token = literal.token();
105 let text = token.text().as_str(); 71 let text = token.text().as_str();
106 if text.starts_with("r\"") { 72 if text.starts_with("r\"") {
107 // no hash to remove 73 // no hash to remove
108 return None; 74 return None;
109 } 75 }
110 ctx.add_action(AssistId("remove_hash"), "remove hash from raw string", |edit| { 76 ctx.add_action(AssistId("remove_hash"), "remove hash from raw string", |edit| {
111 edit.target(literal.syntax().text_range()); 77 edit.target(token.text_range());
112 let result = &text[2..text.len() - 1]; 78 let result = &text[2..text.len() - 1];
113 let result = if result.starts_with("\"") { 79 let result = if result.starts_with("\"") {
114 // no more hash, escape 80 // no more hash, escape
@@ -117,11 +83,30 @@ pub(crate) fn remove_hash(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist
117 } else { 83 } else {
118 result.to_owned() 84 result.to_owned()
119 }; 85 };
120 edit.replace(literal.syntax().text_range(), format!("r{}", result)); 86 edit.replace(token.text_range(), format!("r{}", result));
121 }); 87 });
122 ctx.build() 88 ctx.build()
123} 89}
124 90
91fn count_hashes(s: &str) -> usize {
92 let mut max_hash_streak = 0usize;
93 for idx in s.match_indices("\"#").map(|(i, _)| i) {
94 let (_, sub) = s.split_at(idx + 1);
95 let nb_hash = sub.chars().take_while(|c| *c == '#').count();
96 if nb_hash > max_hash_streak {
97 max_hash_streak = nb_hash;
98 }
99 }
100 max_hash_streak
101}
102
103fn find_usual_string_range(s: &str) -> Option<TextRange> {
104 Some(TextRange::from_to(
105 TextUnit::from(s.find('"')? as u32),
106 TextUnit::from(s.rfind('"')? as u32),
107 ))
108}
109
125#[cfg(test)] 110#[cfg(test)]
126mod test { 111mod test {
127 use super::*; 112 use super::*;
@@ -159,6 +144,23 @@ string"#;
159 } 144 }
160 145
161 #[test] 146 #[test]
147 fn make_raw_string_works_inside_macros() {
148 check_assist(
149 make_raw_string,
150 r#"
151 fn f() {
152 format!(<|>"x = {}", 92)
153 }
154 "#,
155 r##"
156 fn f() {
157 format!(<|>r#"x = {}"#, 92)
158 }
159 "##,
160 )
161 }
162
163 #[test]
162 fn make_raw_string_hashes_inside_works() { 164 fn make_raw_string_hashes_inside_works() {
163 check_assist( 165 check_assist(
164 make_raw_string, 166 make_raw_string,