From b6d55290a1e416e68bb258bb1e19861692476bd2 Mon Sep 17 00:00:00 2001 From: Geobert Quach Date: Sun, 15 Sep 2019 18:51:34 +0100 Subject: [PATCH] feat(assists): raw string <-> usual string manipulation Fixes #1730 --- crates/ra_assists/src/lib.rs | 5 + crates/ra_assists/src/raw_string.rs | 326 ++++++++++++++++++++++++++++ docs/user/features.md | 56 +++++ 3 files changed, 387 insertions(+) create mode 100644 crates/ra_assists/src/raw_string.rs diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs index 66cf325243..756acf4153 100644 --- a/crates/ra_assists/src/lib.rs +++ b/crates/ra_assists/src/lib.rs @@ -96,6 +96,7 @@ mod fill_match_arms; mod merge_match_arms; mod introduce_variable; mod inline_local_variable; +mod raw_string; mod replace_if_let_with_match; mod split_import; mod remove_dbg; @@ -125,6 +126,10 @@ fn all_assists() -> &'static [fn(AssistCtx) -> Option) -> Option { + let literal = ctx.node_at_offset::()?; + if literal.token().kind() == ra_syntax::SyntaxKind::STRING { + ctx.add_action(AssistId("make_raw_string"), "make raw string", |edit| { + edit.target(literal.syntax().text_range()); + edit.insert(literal.syntax().text_range().start(), "r"); + }); + ctx.build() + } else { + None + } +} + +pub(crate) fn make_usual_string(mut ctx: AssistCtx) -> Option { + let literal = ctx.node_at_offset::()?; + if literal.token().kind() == ra_syntax::SyntaxKind::RAW_STRING { + ctx.add_action(AssistId("make_usual_string"), "make usual string", |edit| { + let text = literal.syntax().text(); + let usual_start_pos = text.find_char('"').unwrap(); // we have a RAW_STRING + let end = literal.syntax().text_range().end(); + dbg!(&end); + let mut i = 0; + let mut pos = 0; + let mut c = text.char_at(end - TextUnit::from(i)); + while c != Some('"') { + if c != None { + pos += 1; + } + i += 1; + c = text.char_at(end - TextUnit::from(i)); + } + + edit.target(literal.syntax().text_range()); + edit.delete(TextRange::from_to( + literal.syntax().text_range().start(), + literal.syntax().text_range().start() + usual_start_pos, + )); + edit.delete(TextRange::from_to( + literal.syntax().text_range().end() - TextUnit::from(pos), + literal.syntax().text_range().end(), + )); + }); + ctx.build() + } else { + None + } +} + +pub(crate) fn add_hash(mut ctx: AssistCtx) -> Option { + let literal = ctx.node_at_offset::()?; + if literal.token().kind() == ra_syntax::SyntaxKind::RAW_STRING { + ctx.add_action(AssistId("add_hash"), "add hash to raw string", |edit| { + edit.target(literal.syntax().text_range()); + edit.insert(literal.syntax().text_range().start() + TextUnit::from(1), "#"); + edit.insert(literal.syntax().text_range().end(), "#"); + }); + ctx.build() + } else { + None + } +} + +pub(crate) fn remove_hash(mut ctx: AssistCtx) -> Option { + let literal = ctx.node_at_offset::()?; + if literal.token().kind() == ra_syntax::SyntaxKind::RAW_STRING { + if !literal.syntax().text().contains_char('#') { + return None; + } + ctx.add_action(AssistId("remove_hash"), "remove hash from raw string", |edit| { + edit.target(literal.syntax().text_range()); + edit.delete(TextRange::from_to( + literal.syntax().text_range().start() + TextUnit::from(1), + literal.syntax().text_range().start() + TextUnit::from(2), + )); + edit.delete(TextRange::from_to( + literal.syntax().text_range().end() - TextUnit::from(1), + literal.syntax().text_range().end(), + )); + }); + ctx.build() + } else { + None + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::helpers::{check_assist, check_assist_not_applicable, check_assist_target}; + + #[test] + fn make_raw_string_target() { + check_assist_target( + make_raw_string, + r#" + fn f() { + let s = <|>"random string"; + } + "#, + r#""random string""#, + ); + } + + #[test] + fn make_raw_string_works() { + check_assist( + make_raw_string, + r#" + fn f() { + let s = <|>"random string"; + } + "#, + r#" + fn f() { + let s = <|>r"random string"; + } + "#, + ) + } + + #[test] + fn make_raw_string_not_works() { + check_assist_not_applicable( + make_raw_string, + r#" + fn f() { + let s = <|>r"random string"; + } + "#, + ); + } + + #[test] + fn add_hash_target() { + check_assist_target( + add_hash, + r#" + fn f() { + let s = <|>r"random string"; + } + "#, + r#"r"random string""#, + ); + } + + #[test] + fn add_hash_works() { + check_assist( + add_hash, + r#" + fn f() { + let s = <|>r"random string"; + } + "#, + r##" + fn f() { + let s = <|>r#"random string"#; + } + "##, + ) + } + + #[test] + fn add_more_hash_works() { + check_assist( + add_hash, + r##" + fn f() { + let s = <|>r#"random string"#; + } + "##, + r###" + fn f() { + let s = <|>r##"random string"##; + } + "###, + ) + } + + #[test] + fn add_hash_not_works() { + check_assist_not_applicable( + add_hash, + r#" + fn f() { + let s = <|>"random string"; + } + "#, + ); + } + + #[test] + fn remove_hash_target() { + check_assist_target( + remove_hash, + r##" + fn f() { + let s = <|>r#"random string"#; + } + "##, + r##"r#"random string"#"##, + ); + } + + #[test] + fn remove_hash_works() { + check_assist( + remove_hash, + r##" + fn f() { + let s = <|>r#"random string"#; + } + "##, + r#" + fn f() { + let s = <|>r"random string"; + } + "#, + ) + } + + #[test] + fn remove_more_hash_works() { + check_assist( + remove_hash, + r###" + fn f() { + let s = <|>r##"random string"##; + } + "###, + r##" + fn f() { + let s = <|>r#"random string"#; + } + "##, + ) + } + + #[test] + fn remove_hash_not_works() { + check_assist_not_applicable( + remove_hash, + r#" + fn f() { + let s = <|>"random string"; + } + "#, + ); + } + + #[test] + fn remove_hash_no_hash_not_works() { + check_assist_not_applicable( + remove_hash, + r#" + fn f() { + let s = <|>r"random string"; + } + "#, + ); + } + + #[test] + fn make_usual_string_target() { + check_assist_target( + make_usual_string, + r##" + fn f() { + let s = <|>r#"random string"#; + } + "##, + r##"r#"random string"#"##, + ); + } + + #[test] + fn make_usual_string_works() { + check_assist( + make_usual_string, + r##" + fn f() { + let s = <|>r#"random string"#; + } + "##, + r#" + fn f() { + let s = <|>"random string"; + } + "#, + ) + } + + #[test] + fn make_usual_string_more_hash_works() { + check_assist( + make_usual_string, + r###" + fn f() { + let s = <|>r##"random string"##; + } + "###, + r##" + fn f() { + let s = <|>"random string"; + } + "##, + ) + } + + #[test] + fn make_usual_string_not_works() { + check_assist_not_applicable( + make_usual_string, + r#" + fn f() { + let s = <|>"random string"; + } + "#, + ); + } +} diff --git a/docs/user/features.md b/docs/user/features.md index 1034a51172..93e565315e 100644 --- a/docs/user/features.md +++ b/docs/user/features.md @@ -445,6 +445,62 @@ fn foo T>() {} fn foo() where T: u32, F: FnOnce(T) -> T {} ``` +- Make raw string + +```rust +// before: +fn f() { + let s = <|>"abcd"; +} + +// after: +fn f() { + let s = <|>r"abcd"; +} +``` + +- Make usual string + +```rust +// before: +fn f() { + let s = <|>r#"abcd"#; +} + +// after: +fn f() { + let s = <|>"abcd"; +} +``` + +- Add hash + +```rust +// before: +fn f() { + let s = <|>r"abcd"; +} + +// after: +fn f() { + let s = <|>r#"abcd"#; +} +``` + +- Remove hash + +```rust +// before: +fn f() { + let s = <|>r#"abcd"#; +} + +// after: +fn f() { + let s = <|>r"abcd"; +} +``` + ### Magic Completions In addition to usual reference completion, rust-analyzer provides some ✨magic✨