Repository: dprint/dprint-plugin-typescript (rust)

Issue:
Formatting bug when using multi-line strings
Hi!
seems like I ran into a problem when using multi-line strings

**Describe the bug**

dprint-plugin-typescript version: 0.88

**Input Code**

```ts
describe(
    "Im testing dprint I should be hanging \
    I should be hanging",
    async () => {
        // test
    }
)
```

**Expected Output**

```ts
describe(
    "Im testing dprint I should be hanging \
    I should be hanging",
    async () => {
        // test
    },
);

```

**Actual Output**

```ts
describe(
"Im testing dprint I should be hanging \
    I should be hanging",
    async () => {
        // test
    },
);

```

Seems like the code inside `dprint/core` doesn't take into account all the possible ways of creating multi-line strings

```rust
fn gen_from_raw_string_lines(text: &str, gen_line: impl Fn(&str) -> PrintItems) -> PrintItems {
  let add_ignore_indent = text.contains('\n');
  let mut items = PrintItems::new();
  if add_ignore_indent {
    items.push_signal(Signal::StartIgnoringIndent);
  }
  items.extend(gen_string_lines(text, gen_line));
  if add_ignore_indent {
    items.push_signal(Signal::FinishIgnoringIndent);
  }
  items
}
```

I've tried to tackle this myself but I've no idea which other crates besides `dprint-plugin-typescript` are using `dprint/core` and what would be the consequences of changing such a basic function :\

Pull request description:
fix: multi-line string indent was broken in some cases
Closes #565

Patch:
diff --git a/src/generation/generate.rs b/src/generation/generate.rs
index 58bb3df..0289dda 100644
--- a/src/generation/generate.rs
+++ b/src/generation/generate.rs
@@ -3992,22 +3992,22 @@ mod string_literal {
   use super::*;
 
   pub fn gen_non_jsx_text(string_value: &str, context: &mut Context) -> PrintItems {
-    gen_from_raw_string(&match context.config.quote_style {
+    match context.config.quote_style {
       QuoteStyle::AlwaysDouble => format_with_double(string_value),
       QuoteStyle::AlwaysSingle => format_with_single(string_value),
       QuoteStyle::PreferDouble => handle_prefer_double(string_value),
       QuoteStyle::PreferSingle => handle_prefer_single(string_value),
-    })
+    }
   }
 
   pub fn gen_jsx_text(string_value: &str, context: &mut Context) -> PrintItems {
     // JSX attributes cannot contain escaped quotes so regardless of
     // configuration, allow changing the quote style to single or
     // double depending on if it contains the opposite quote
-    gen_from_raw_string(&match context.config.jsx_quote_style {
+    match context.config.jsx_quote_style {
       JsxQuoteStyle::PreferDouble => handle_prefer_double(string_value),
       JsxQuoteStyle::PreferSingle => handle_prefer_single(string_value),
-    })
+    }
   }
 
   pub fn get_value(node: &Str, context: &mut Context) -> String {
@@ -4049,7 +4049,7 @@ mod string_literal {
     }
   }
 
-  fn handle_prefer_double(string_value: &str) -> String {
+  fn handle_prefer_double(string_value: &str) -> PrintItems {
     if double_to_single(string_value) <= 0 {
       format_with_double(string_value)
     } else {
@@ -4057,7 +4057,7 @@ mod string_literal {
     }
   }
 
-  fn handle_prefer_single(string_value: &str) -> String {
+  fn handle_prefer_single(string_value: &str) -> PrintItems {
     if double_to_single(string_value) >= 0 {
       format_with_single(string_value)
     } else {
@@ -4065,12 +4065,20 @@ mod string_literal {
     }
   }
 
-  fn format_with_double(string_value: &str) -> String {
-    format!("\"{}\"", string_value.replace('"', "\\\""))
+  fn format_with_double(string_value: &str) -> PrintItems {
+    let mut items = PrintItems::new();
+    items.push_str("\"");
+    items.extend(gen_from_raw_string(&string_value.replace('"', "\\\"")));
+    items.push_str("\"");
+    items
   }
 
-  fn format_with_single(string_value: &str) -> String {
-    format!("'{}'", string_value.replace('\'', "\\'"))
+  fn format_with_single(string_value: &str) -> PrintItems {
+    let mut items = PrintItems::new();
+    items.push_str("'");
+    items.extend(gen_from_raw_string(&string_value.replace('\'', "\\'")));
+    items.push_str("'");
+    items
   }
 
   fn double_to_single(string_value: &str) -> i32 {


Test patch:
diff --git a/tests/specs/issues/issue0565.txt b/tests/specs/issues/issue0565.txt
new file mode 100644
index 0000000..ca0dfa4
--- /dev/null
+++ b/tests/specs/issues/issue0565.txt
@@ -0,0 +1,17 @@
+== should format multi-line string and keep start of string indented ==
+asdf(
+    "Testing \
+    this out",
+    async () => {
+        // test
+    }
+);
+
+[expect]
+asdf(
+    "Testing \
+    this out",
+    async () => {
+        // test
+    },
+);