mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-14 06:03:58 +00:00
Merge #5427
5427: More precise ranges in remove hashes assist r=matklad a=matklad
bors r+
🤖
Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com>
This commit is contained in:
commit
de8c5fc09b
1 changed files with 52 additions and 79 deletions
|
@ -4,8 +4,9 @@ use ra_syntax::{
|
||||||
ast::{self, HasQuotes, HasStringValue},
|
ast::{self, HasQuotes, HasStringValue},
|
||||||
AstToken,
|
AstToken,
|
||||||
SyntaxKind::{RAW_STRING, STRING},
|
SyntaxKind::{RAW_STRING, STRING},
|
||||||
TextSize,
|
TextRange, TextSize,
|
||||||
};
|
};
|
||||||
|
use test_utils::mark;
|
||||||
|
|
||||||
use crate::{AssistContext, AssistId, AssistKind, Assists};
|
use crate::{AssistContext, AssistId, AssistKind, Assists};
|
||||||
|
|
||||||
|
@ -33,8 +34,7 @@ pub(crate) fn make_raw_string(acc: &mut Assists, ctx: &AssistContext) -> Option<
|
||||||
"Rewrite as raw string",
|
"Rewrite as raw string",
|
||||||
target,
|
target,
|
||||||
|edit| {
|
|edit| {
|
||||||
let max_hash_streak = count_hashes(&value);
|
let hashes = "#".repeat(required_hashes(&value).max(1));
|
||||||
let hashes = "#".repeat(max_hash_streak + 1);
|
|
||||||
if matches!(value, Cow::Borrowed(_)) {
|
if matches!(value, Cow::Borrowed(_)) {
|
||||||
// Avoid replacing the whole string to better position the cursor.
|
// Avoid replacing the whole string to better position the cursor.
|
||||||
edit.insert(token.syntax().text_range().start(), format!("r{}", hashes));
|
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
|
||||||
pub(crate) fn add_hash(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
pub(crate) fn add_hash(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||||
let token = ctx.find_token_at_offset(RAW_STRING)?;
|
let token = ctx.find_token_at_offset(RAW_STRING)?;
|
||||||
let target = token.text_range();
|
let target = token.text_range();
|
||||||
acc.add(AssistId("add_hash", AssistKind::Refactor), "Add # to raw string", target, |edit| {
|
acc.add(AssistId("add_hash", AssistKind::Refactor), "Add #", target, |edit| {
|
||||||
edit.insert(token.text_range().start() + TextSize::of('r'), "#");
|
edit.insert(token.text_range().start() + TextSize::of('r'), "#");
|
||||||
edit.insert(token.text_range().end(), "#");
|
edit.insert(token.text_range().end(), "#");
|
||||||
})
|
})
|
||||||
|
@ -128,49 +128,58 @@ pub(crate) fn add_hash(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||||
// }
|
// }
|
||||||
// ```
|
// ```
|
||||||
pub(crate) fn remove_hash(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
pub(crate) fn remove_hash(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||||
let token = ctx.find_token_at_offset(RAW_STRING)?;
|
let token = ctx.find_token_at_offset(RAW_STRING).and_then(ast::RawString::cast)?;
|
||||||
|
|
||||||
let text = token.text().as_str();
|
let text = token.text().as_str();
|
||||||
if text.starts_with("r\"") {
|
if !text.starts_with("r#") && text.ends_with("#") {
|
||||||
// no hash to remove
|
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let target = token.text_range();
|
|
||||||
acc.add(
|
let existing_hashes = text.chars().skip(1).take_while(|&it| it == '#').count();
|
||||||
AssistId("remove_hash", AssistKind::RefactorRewrite),
|
|
||||||
"Remove hash from raw string",
|
let text_range = token.syntax().text_range();
|
||||||
target,
|
let internal_text = &text[token.text_range_between_quotes()? - text_range.start()];
|
||||||
|edit| {
|
|
||||||
let result = &text[2..text.len() - 1];
|
if existing_hashes == required_hashes(internal_text) {
|
||||||
let result = if result.starts_with('\"') {
|
mark::hit!(cant_remove_required_hash);
|
||||||
// FIXME: this logic is wrong, not only the last has has to handled specially
|
return None;
|
||||||
// no more hash, escape
|
}
|
||||||
let internal_str = &result[1..result.len() - 1];
|
|
||||||
format!("\"{}\"", internal_str.escape_default().to_string())
|
acc.add(AssistId("remove_hash", AssistKind::RefactorRewrite), "Remove #", text_range, |edit| {
|
||||||
} else {
|
edit.delete(TextRange::at(text_range.start() + TextSize::of('r'), TextSize::of('#')));
|
||||||
result.to_owned()
|
edit.delete(TextRange::new(text_range.end() - TextSize::of('#'), text_range.end()));
|
||||||
};
|
})
|
||||||
edit.replace(token.text_range(), format!("r{}", result));
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn count_hashes(s: &str) -> usize {
|
fn required_hashes(s: &str) -> usize {
|
||||||
let mut max_hash_streak = 0usize;
|
let mut res = 0usize;
|
||||||
for idx in s.match_indices("\"#").map(|(i, _)| i) {
|
for idx in s.match_indices('"').map(|(i, _)| i) {
|
||||||
let (_, sub) = s.split_at(idx + 1);
|
let (_, sub) = s.split_at(idx + 1);
|
||||||
let nb_hash = sub.chars().take_while(|c| *c == '#').count();
|
let n_hashes = sub.chars().take_while(|c| *c == '#').count();
|
||||||
if nb_hash > max_hash_streak {
|
res = res.max(n_hashes + 1)
|
||||||
max_hash_streak = nb_hash;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
max_hash_streak
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_required_hashes() {
|
||||||
|
assert_eq!(0, required_hashes("abc"));
|
||||||
|
assert_eq!(0, required_hashes("###"));
|
||||||
|
assert_eq!(1, required_hashes("\""));
|
||||||
|
assert_eq!(2, required_hashes("\"#abc"));
|
||||||
|
assert_eq!(0, required_hashes("#abc"));
|
||||||
|
assert_eq!(3, required_hashes("#ab\"##c"));
|
||||||
|
assert_eq!(5, required_hashes("#ab\"##\"####c"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use test_utils::mark;
|
||||||
|
|
||||||
use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
|
use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn make_raw_string_target() {
|
fn make_raw_string_target() {
|
||||||
check_assist_target(
|
check_assist_target(
|
||||||
|
@ -372,33 +381,21 @@ string"###;
|
||||||
fn remove_hash_works() {
|
fn remove_hash_works() {
|
||||||
check_assist(
|
check_assist(
|
||||||
remove_hash,
|
remove_hash,
|
||||||
r##"
|
r##"fn f() { let s = <|>r#"random string"#; }"##,
|
||||||
fn f() {
|
r#"fn f() { let s = r"random string"; }"#,
|
||||||
let s = <|>r#"random string"#;
|
|
||||||
}
|
|
||||||
"##,
|
|
||||||
r#"
|
|
||||||
fn f() {
|
|
||||||
let s = r"random string";
|
|
||||||
}
|
|
||||||
"#,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn remove_hash_with_quote_works() {
|
fn cant_remove_required_hash() {
|
||||||
check_assist(
|
mark::check!(cant_remove_required_hash);
|
||||||
|
check_assist_not_applicable(
|
||||||
remove_hash,
|
remove_hash,
|
||||||
r##"
|
r##"
|
||||||
fn f() {
|
fn f() {
|
||||||
let s = <|>r#"random"str"ing"#;
|
let s = <|>r#"random"str"ing"#;
|
||||||
}
|
}
|
||||||
"##,
|
"##,
|
||||||
r#"
|
|
||||||
fn f() {
|
|
||||||
let s = r"random\"str\"ing";
|
|
||||||
}
|
|
||||||
"#,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -420,27 +417,13 @@ string"###;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn remove_hash_not_works() {
|
fn remove_hash_doesnt_work() {
|
||||||
check_assist_not_applicable(
|
check_assist_not_applicable(remove_hash, r#"fn f() { let s = <|>"random string"; }"#);
|
||||||
remove_hash,
|
|
||||||
r#"
|
|
||||||
fn f() {
|
|
||||||
let s = <|>"random string";
|
|
||||||
}
|
|
||||||
"#,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn remove_hash_no_hash_not_works() {
|
fn remove_hash_no_hash_doesnt_work() {
|
||||||
check_assist_not_applicable(
|
check_assist_not_applicable(remove_hash, r#"fn f() { let s = <|>r"random string"; }"#);
|
||||||
remove_hash,
|
|
||||||
r#"
|
|
||||||
fn f() {
|
|
||||||
let s = <|>r"random string";
|
|
||||||
}
|
|
||||||
"#,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -518,14 +501,4 @@ string"###;
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn count_hashes_test() {
|
|
||||||
assert_eq!(0, count_hashes("abc"));
|
|
||||||
assert_eq!(0, count_hashes("###"));
|
|
||||||
assert_eq!(1, count_hashes("\"#abc"));
|
|
||||||
assert_eq!(0, count_hashes("#abc"));
|
|
||||||
assert_eq!(2, count_hashes("#ab\"##c"));
|
|
||||||
assert_eq!(4, count_hashes("#ab\"##\"####c"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue