diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_assists/src/raw_string.rs | 159 |
1 files changed, 59 insertions, 100 deletions
diff --git a/crates/ra_assists/src/raw_string.rs b/crates/ra_assists/src/raw_string.rs index 1cb4297a9..e00267060 100644 --- a/crates/ra_assists/src/raw_string.rs +++ b/crates/ra_assists/src/raw_string.rs | |||
@@ -1,125 +1,84 @@ | |||
1 | use hir::db::HirDatabase; | 1 | use hir::db::HirDatabase; |
2 | use ra_syntax::{ast::AstNode, ast::Literal, SyntaxText, TextRange, TextUnit}; | 2 | use ra_syntax::{ast::AstNode, ast::Literal, TextRange, TextUnit}; |
3 | 3 | ||
4 | use crate::{assist_ctx::AssistBuilder, Assist, AssistCtx, AssistId}; | 4 | use crate::{Assist, AssistCtx, AssistId}; |
5 | 5 | ||
6 | pub(crate) fn make_raw_string(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { | 6 | pub(crate) fn make_raw_string(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { |
7 | let literal = ctx.node_at_offset::<Literal>()?; | 7 | let literal = ctx.node_at_offset::<Literal>()?; |
8 | if literal.token().kind() == ra_syntax::SyntaxKind::STRING { | 8 | if literal.token().kind() != ra_syntax::SyntaxKind::STRING { |
9 | ctx.add_action(AssistId("make_raw_string"), "make raw string", |edit| { | 9 | return None; |
10 | edit.target(literal.syntax().text_range()); | ||
11 | edit.insert(literal.syntax().text_range().start(), "r"); | ||
12 | }); | ||
13 | ctx.build() | ||
14 | } else { | ||
15 | None | ||
16 | } | 10 | } |
11 | ctx.add_action(AssistId("make_raw_string"), "make raw string", |edit| { | ||
12 | edit.target(literal.syntax().text_range()); | ||
13 | edit.insert(literal.syntax().text_range().start(), "r"); | ||
14 | }); | ||
15 | ctx.build() | ||
16 | } | ||
17 | |||
18 | fn find_usual_string_range(s: &str) -> Option<TextRange> { | ||
19 | Some(TextRange::from_to( | ||
20 | TextUnit::from(s.find('"')? as u32), | ||
21 | TextUnit::from(s.rfind('"')? as u32), | ||
22 | )) | ||
17 | } | 23 | } |
18 | 24 | ||
19 | pub(crate) fn make_usual_string(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { | 25 | pub(crate) fn make_usual_string(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { |
20 | let literal = ctx.node_at_offset::<Literal>()?; | 26 | let literal = ctx.node_at_offset::<Literal>()?; |
21 | if literal.token().kind() == ra_syntax::SyntaxKind::RAW_STRING { | 27 | if literal.token().kind() != ra_syntax::SyntaxKind::RAW_STRING { |
22 | ctx.add_action(AssistId("make_usual_string"), "make usual string", |edit| { | 28 | return None; |
23 | let text = literal.syntax().text(); | ||
24 | let usual_start_pos = text.find_char('"').unwrap(); // we have a RAW_STRING | ||
25 | let end = literal.syntax().text_range().end(); | ||
26 | let mut i = 0; | ||
27 | let mut pos = 0; | ||
28 | let mut c = text.char_at(end - TextUnit::from(i)); | ||
29 | while c != Some('"') { | ||
30 | if c != None { | ||
31 | pos += 1; | ||
32 | } | ||
33 | i += 1; | ||
34 | c = text.char_at(end - TextUnit::from(i)); | ||
35 | } | ||
36 | |||
37 | edit.target(literal.syntax().text_range()); | ||
38 | edit.delete(TextRange::from_to( | ||
39 | literal.syntax().text_range().start(), | ||
40 | literal.syntax().text_range().start() + usual_start_pos, | ||
41 | )); | ||
42 | edit.delete(TextRange::from_to( | ||
43 | literal.syntax().text_range().end() - TextUnit::from(pos), | ||
44 | literal.syntax().text_range().end(), | ||
45 | )); | ||
46 | // parse inside string to escape `"` | ||
47 | let start_of_inside = usual_start_pos + TextUnit::from(1); | ||
48 | let end_of_inside = text.len() - usual_start_pos - TextUnit::from(1); | ||
49 | let inside_str = text.slice(TextRange::from_to(start_of_inside, end_of_inside)); | ||
50 | escape_double_quote( | ||
51 | edit, | ||
52 | &inside_str, | ||
53 | literal.syntax().text_range().start() + start_of_inside, | ||
54 | ); | ||
55 | }); | ||
56 | ctx.build() | ||
57 | } else { | ||
58 | None | ||
59 | } | 29 | } |
30 | let token = literal.token(); | ||
31 | let text = token.text().as_str(); | ||
32 | let usual_string_range = find_usual_string_range(text)?; | ||
33 | ctx.add_action(AssistId("make_usual_string"), "make usual string", |edit| { | ||
34 | edit.target(literal.syntax().text_range()); | ||
35 | // parse inside string to escape `"` | ||
36 | let start_of_inside = usual_string_range.start().to_usize() + 1; | ||
37 | let end_of_inside = usual_string_range.end().to_usize(); | ||
38 | let inside_str = &text[start_of_inside..end_of_inside]; | ||
39 | let escaped = inside_str.escape_default().to_string(); | ||
40 | edit.replace(literal.syntax().text_range(), format!("\"{}\"", escaped)); | ||
41 | }); | ||
42 | ctx.build() | ||
60 | } | 43 | } |
61 | 44 | ||
62 | pub(crate) fn add_hash(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { | 45 | pub(crate) fn add_hash(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { |
63 | let literal = ctx.node_at_offset::<Literal>()?; | 46 | let literal = ctx.node_at_offset::<Literal>()?; |
64 | if literal.token().kind() == ra_syntax::SyntaxKind::RAW_STRING { | 47 | if literal.token().kind() != ra_syntax::SyntaxKind::RAW_STRING { |
65 | ctx.add_action(AssistId("add_hash"), "add hash to raw string", |edit| { | 48 | return None; |
66 | edit.target(literal.syntax().text_range()); | ||
67 | edit.insert(literal.syntax().text_range().start() + TextUnit::from(1), "#"); | ||
68 | edit.insert(literal.syntax().text_range().end(), "#"); | ||
69 | }); | ||
70 | ctx.build() | ||
71 | } else { | ||
72 | None | ||
73 | } | 49 | } |
74 | } | 50 | ctx.add_action(AssistId("add_hash"), "add hash to raw string", |edit| { |
75 | 51 | edit.target(literal.syntax().text_range()); | |
76 | fn escape_double_quote(edit: &mut AssistBuilder, inside_str: &SyntaxText, offset: TextUnit) { | 52 | edit.insert(literal.syntax().text_range().start() + TextUnit::of_char('r'), "#"); |
77 | let mut start = TextUnit::from(0); | 53 | edit.insert(literal.syntax().text_range().end(), "#"); |
78 | inside_str.for_each_chunk(|chunk| { | ||
79 | let end = start + TextUnit::of_str(chunk); | ||
80 | let mut i = 0; | ||
81 | for c in chunk.to_string().chars() { | ||
82 | if c == '"' { | ||
83 | edit.insert(offset + start + TextUnit::from(i), "\\"); | ||
84 | } | ||
85 | i += 1; | ||
86 | } | ||
87 | start = end; | ||
88 | }); | 54 | }); |
55 | ctx.build() | ||
89 | } | 56 | } |
90 | 57 | ||
91 | pub(crate) fn remove_hash(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { | 58 | pub(crate) fn remove_hash(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { |
92 | let literal = ctx.node_at_offset::<Literal>()?; | 59 | let literal = ctx.node_at_offset::<Literal>()?; |
93 | if literal.token().kind() == ra_syntax::SyntaxKind::RAW_STRING { | 60 | if literal.token().kind() != ra_syntax::SyntaxKind::RAW_STRING { |
94 | if !literal.syntax().text().contains_char('#') { | 61 | return None; |
95 | return None; | 62 | } |
96 | } | 63 | let token = literal.token(); |
97 | ctx.add_action(AssistId("remove_hash"), "remove hash from raw string", |edit| { | 64 | let text = token.text().as_str(); |
98 | edit.target(literal.syntax().text_range()); | 65 | if text.starts_with("r\"") { |
99 | edit.delete(TextRange::from_to( | 66 | // no hash to remove |
100 | literal.syntax().text_range().start() + TextUnit::from(1), | 67 | return None; |
101 | literal.syntax().text_range().start() + TextUnit::from(2), | ||
102 | )); | ||
103 | edit.delete(TextRange::from_to( | ||
104 | literal.syntax().text_range().end() - TextUnit::from(1), | ||
105 | literal.syntax().text_range().end(), | ||
106 | )); | ||
107 | let text = literal.syntax().text(); | ||
108 | if text.char_at(TextUnit::from(2)) == Some('"') { | ||
109 | // no more hash after assist, need to escape any `"` in the string | ||
110 | let inside_str = text | ||
111 | .slice(TextRange::from_to(TextUnit::from(3), text.len() - TextUnit::from(2))); | ||
112 | escape_double_quote( | ||
113 | edit, | ||
114 | &inside_str, | ||
115 | literal.syntax().text_range().start() + TextUnit::from(3), | ||
116 | ); | ||
117 | } | ||
118 | }); | ||
119 | ctx.build() | ||
120 | } else { | ||
121 | None | ||
122 | } | 68 | } |
69 | ctx.add_action(AssistId("remove_hash"), "remove hash from raw string", |edit| { | ||
70 | edit.target(literal.syntax().text_range()); | ||
71 | let result = &text[2..text.len() - 1]; | ||
72 | let result = if result.starts_with("\"") { | ||
73 | // no more hash, escape | ||
74 | let internal_str = &result[1..result.len() - 1]; | ||
75 | format!("\"{}\"", internal_str.escape_default().to_string()) | ||
76 | } else { | ||
77 | result.to_owned() | ||
78 | }; | ||
79 | edit.replace(literal.syntax().text_range(), format!("r{}", result)); | ||
80 | }); | ||
81 | ctx.build() | ||
123 | } | 82 | } |
124 | 83 | ||
125 | #[cfg(test)] | 84 | #[cfg(test)] |