diff --git a/Cargo.lock b/Cargo.lock index 275b27775c1d..dad9b1df1d8c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -904,6 +904,7 @@ dependencies = [ "ra_syntax 0.1.0", "ra_text_edit 0.1.0", "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_lexer 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "test_utils 0.1.0", ] diff --git a/crates/ra_assists/Cargo.toml b/crates/ra_assists/Cargo.toml index 02966bbda31c..d3b6aeb36a75 100644 --- a/crates/ra_assists/Cargo.toml +++ b/crates/ra_assists/Cargo.toml @@ -11,6 +11,7 @@ join_to_string = "0.1.3" itertools = "0.8.0" arrayvec = "0.4.10" rustc-hash = "1.0.1" +rustc_lexer = "0.1.0" ra_syntax = { path = "../ra_syntax" } ra_text_edit = { path = "../ra_text_edit" } diff --git a/crates/ra_assists/src/assists/raw_string.rs b/crates/ra_assists/src/assists/raw_string.rs index 965a64c9870c..200aaa59ac4a 100644 --- a/crates/ra_assists/src/assists/raw_string.rs +++ b/crates/ra_assists/src/assists/raw_string.rs @@ -1,5 +1,6 @@ use hir::db::HirDatabase; use ra_syntax::{ast::AstNode, ast::Literal, TextRange, TextUnit}; +use rustc_lexer; use crate::{Assist, AssistCtx, AssistId}; @@ -8,13 +9,51 @@ pub(crate) fn make_raw_string(mut ctx: AssistCtx) -> Option unescaped.push(c), + Err(_) => error = Err(()), + }, + ); + if error.is_err() { + return None; + } 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"); + let max_hash_streak = count_hashes(&unescaped); + let mut hashes = String::with_capacity(max_hash_streak + 1); + for _ in 0..hashes.capacity() { + hashes.push('#'); + } + edit.replace( + literal.syntax().text_range(), + format!("r{}\"{}\"{}", hashes, unescaped, hashes), + ); }); ctx.build() } +fn count_hashes(s: &str) -> usize { + let mut max_hash_streak = 0usize; + for idx in s.match_indices("\"#").map(|(i, _)| i) { + let (_, sub) = s.split_at(idx + 1); + let nb_hash = sub.chars().take_while(|c| *c == '#').count(); + if nb_hash > max_hash_streak { + max_hash_streak = nb_hash; + } + } + max_hash_streak +} + fn find_usual_string_range(s: &str) -> Option { Some(TextRange::from_to( TextUnit::from(s.find('"')? as u32), @@ -92,10 +131,10 @@ mod test { make_raw_string, r#" fn f() { - let s = <|>"random string"; + let s = <|>"random\nstring"; } "#, - r#""random string""#, + r#""random\nstring""#, ); } @@ -105,44 +144,69 @@ mod test { make_raw_string, r#" fn f() { - let s = <|>"random string"; + let s = <|>"random\nstring"; } "#, - r#" + r##" fn f() { - let s = <|>r"random string"; + let s = <|>r#"random +string"#; } - "#, + "##, ) } #[test] - fn make_raw_string_with_escaped_works() { + fn make_raw_string_hashes_inside_works() { check_assist( make_raw_string, - r#" + r###" fn f() { - let s = <|>"random\nstring"; + let s = <|>"#random##\nstring"; } - "#, - r#" + "###, + r####" fn f() { - let s = <|>r"random\nstring"; + let s = <|>r#"#random## +string"#; } - "#, + "####, ) } #[test] - fn make_raw_string_not_works() { - check_assist_not_applicable( + fn make_raw_string_closing_hashes_inside_works() { + check_assist( + make_raw_string, + r###" + fn f() { + let s = <|>"#random\"##\nstring"; + } + "###, + r####" + fn f() { + let s = <|>r###"#random"## +string"###; + } + "####, + ) + } + + #[test] + fn make_raw_string_nothing_to_unescape_works() { + check_assist( make_raw_string, r#" fn f() { - let s = <|>r"random string"; + let s = <|>"random string"; } "#, - ); + r##" + fn f() { + let s = <|>r#"random string"#; + } + "##, + ) } #[test] @@ -367,4 +431,14 @@ mod test { "#, ); } + + #[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")); + } } diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs index 897af2b020b8..3ca3320f7aa2 100644 --- a/crates/ra_assists/src/lib.rs +++ b/crates/ra_assists/src/lib.rs @@ -340,5 +340,4 @@ mod tests { assert_eq!(assists.next().expect("expected assist").0.label, "introduce variable"); assert_eq!(assists.next().expect("expected assist").0.label, "replace with match"); } - } diff --git a/docs/user/features.md b/docs/user/features.md index eb81cba263fb..23842f40929d 100644 --- a/docs/user/features.md +++ b/docs/user/features.md @@ -445,17 +445,18 @@ fn foo T>() {} fn foo() where T: u32, F: FnOnce(T) -> T {} ``` -- Make raw string +- Make raw string unescaped ```rust // before: fn f() { - let s = <|>"abcd"; + let s = <|>"ab\ncd"; } // after: fn f() { - let s = <|>r"abcd"; + let s = <|>r#"ab +cd"#; } ```