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

Issue:
Consider wide unicode characters in `lineWidth`
**Describe the bug**

- dprint-plugin-typescript version: 0.78.0
- lineWidth: 80
- indentWidth: 2
- useTabs: false

**Input Code**

```tsx
const a = () => (
  <div>
    대충 한국어로 아무 말이나 적고 있습니다. '아무말'은 표준국어대사전에 등재되지 않은 단어이므로 '아무 말'로 띄어 씁니다. 아무 말일까요? 누군가에게는 적절한 더미 텍스트일 수도 있고 누군가에게는 그냥 지루한 문단일 수 있습니다. 문장이 긴데 보여질 폭은 좁을 때 어디에서 줄바꿈을 해야 할지는 중요한 이슈가 되어 왔습니다. 한국어는 띄어쓰기가 자주 등장하는 만큼 단어 사이를 끊는 것을 싫어하지만 끊는 부분에 타우마타와카탕이항아코아우아우오타마테아투리푸카카피키마웅아호로누쿠포카이웨누아키타나타후 같은 단어가 존재하면 엄청난 공백이 등장하게 됩니다. 직접 줄바꿈을 삽입하자는 의견도 나오지만 다양한 스크린 폭에 대응되지 못하는 것을 탐탁치 않아 하는 사람도 있습니다. 대충 한국어로 아무 말이나 적었습니다.
  </div>
);
```

**Expected Output**

```tsx
const a = () => (
  <div>
    대충 한국어로 아무 말이나 적고 있습니다. '아무말'은 표준국어대사전에
    등재되지 않은 단어이므로 '아무 말'로 띄어 씁니다. 아무 말일까요?
    누군가에게는 적절한 더미 텍스트일 수도 있고 누군가에게는 그냥 지루한 문단일
    수 있습니다. 문장이 긴데 보여질 폭은 좁을 때 어디에서 줄바꿈을 해야 할지는
    중요한 이슈가 되어 왔습니다. 한국어는 띄어쓰기가 자주 등장하는 만큼 단어
    사이를 끊는 것을 싫어하지만 끊는 부분에
    타우마타와카탕이항아코아우아우오타마테아투리푸카카피키마웅아호로누쿠포카이웨누아키타나타후
    같은 단어가 존재하면 엄청난 공백이 등장하게 됩니다. 직접 줄바꿈을 삽입하자는
    의견도 나오지만 다양한 스크린 폭에 대응되지 못하는 것을 탐탁치 않아 하는
    사람도 있습니다. 대충 한국어로 아무 말이나 적었습니다.
  </div>
);
```
Got from [Prettier playground].

**Actual Output**

```tsx
const a = () => (
  <div>
    대충 한국어로 아무 말이나 적고 있습니다. '아무말'은 표준국어대사전에 등재되지 않은 단어이므로 '아무 말'로 띄어 씁니다. 아무
    말일까요? 누군가에게는 적절한 더미 텍스트일 수도 있고 누군가에게는 그냥 지루한 문단일 수 있습니다. 문장이 긴데 보여질 폭은 좁을 때
    어디에서 줄바꿈을 해야 할지는 중요한 이슈가 되어 왔습니다. 한국어는 띄어쓰기가 자주 등장하는 만큼 단어 사이를 끊는 것을 싫어하지만
    끊는 부분에 타우마타와카탕이항아코아우아우오타마테아투리푸카카피키마웅아호로누쿠포카이웨누아키타나타후 같은 단어가 존재하면 엄청난 공백이
    등장하게 됩니다. 직접 줄바꿈을 삽입하자는 의견도 나오지만 다양한 스크린 폭에 대응되지 못하는 것을 탐탁치 않아 하는 사람도 있습니다.
    대충 한국어로 아무 말이나 적었습니다.
  </div>
);
```
Got from [dprint playground].

CJK and Emoji characters are usually considered as 2 half-width characters. I think, but I'm unsure, that other dprint plugins (or dprint itself) also have this problem.

[Prettier playground]: https://prettier.io/playground/#N4Igxg9gdgLgprEAuc0DOMAEBDTBeTACgEp8A+IgHSk0wB4ATASwDczrbbAAZsErZzQHVXAtrWAW0cA6HZkAio4BZuzIAXOwC7jgDIbMgQAnABzWZAEeOBWocATTYBOmgHSYA5NNknAAuOZAOiuAASZFdANQOAQCcALo5kCPLYBrxwBgtgAcnMQClR60ALpuF5QAHu8TMZC3FAEXbhTEBAUYNjaTlAH3HAGTrAFLGAfkxAECbAB1rAABq3QBiawBSm1UAICf5MQBVmwA-uzEBZRcASocAOpazMQAwhwBHmrQ0yytrMQA-awFOGzH9AC46mwA5u0P6BrT0jTGXAUvH5TEAWOsAHZsxAF57AG9HACcnMQFvl60BAicARccxAGVbMYUAUVrdAHEHMQBBJwAoPYAJ+pegBdVwA+o5hAAar-jqgERJvJNeSACKHyphfMlACpjW2MQmEdSSgAeRwAMdejAAnjgB+Jry7QAaq3VADOdgB8FzDhTBOeSAH07MIApBrqgGCal6Aa6HhLT-AzeXVAAG9gBDejyAAYXAA1jgAnO+WAATHAC5zgFWF+SAW1WJIAVeYkiuNgBIx+XKwAyixJADRLgBrOwAeKxqNYAeVcAJQvKwCjYxJABxromKgAP5wA3yxr5IALseKEjd8sU8sAI2uYQCYNWFhOjAC4T3lpgBcuzCAEdHALUzgByGzCAV5rAIw9B08dKqmEAlC3pTCAQcnAIgTAJBL0AvQOAUPHaeS6oANccANTUjRSm8WYfSARNGml1ADYLgAbOu4eLiARXGAphAN1d9MwgswgAWFwCDC4ANOeCEkw26cgBj2kY6Bu8AQicSZBTKFSACdHcRx6AB6ZhsajEAA3CAAA0IAQAADjATDoMgoDYAATghEAAO4AAqIQgaDICA2AADYodgACe2FgQARgh2BgAA1nAMAAMrYAAtnAAAyTBQHAyAAGb4WgcDkZRNF0fREFURxADmyAwAhACuAkgHATFkXADAMKprHYFAEmydgElwAAYhACFMdgMDQdpOHYLJMAQKBIAABYwExeEAOoOUw8BoGJYBwPRWGeawnlEThYBoKRIAcfxCEwGhlESaZPF8QpABWaAAB70ZJeFwAAirJEDwEleH8WBYkIdFOFkdgKl4fZEEIRxMCuUwDAwA5yAABwAAxlch-GuZREE4Q1cDRSwXFgQAjgV8BxZB2EoNgaAALScapqn2QhcAzUw21xXpiVILxJUKfxTFMNJclndleWzVxx3JWBMA1S1bUdUgABMz2UUweGSQAwhATFHYpaAAKz2bJ-EACo1YtJ2lSALDyQAklA6mwPRYCNVBACCGP0TARE5cV-EAL7k0AA
[dprint playground]: https://dprint.dev/playground/#code/MYewdgzgLgBAhjAvDAFASiQPlQKBjAHgBMBLAN0z3xkABmwStmZAdVcFtawFtHAdDpkBFRwFm6ZAFzsAu44AyGmIEAJwAc1MQBHjgVqHAE02ATpoB0MAOS9+awALjMQDorgAEm2NQDUDgEAnAC6MxAjy2Aa8cAYLYAHJmIClR3YAum1oMAD3Zw18tTkARdtYYQEBRpVVeAUAfccAZOsAUsYB+GEAQJsAHWsAAGotAGJrAFKbxQAgJxhhAFWbAD+6YQFlFwBKhwA6lmJhADCHAEeaZKQzs-JhAD9rAU4aYR0ALjpLADm73RqaZBRUYUcBS8cEYQBY6wAdmmEAXnsAb0cAJyZhAW+XdQECJwBFxmEAZVphWQBRWi0AcQZhAEEnAFB7ACfqzwBdVwB9RmEADVccCoBESYSJUEgAihzIweyhQAqYzNVCxWAUQoAHkcADHWQwAJ44AfiZs80AGqsFQAznYAfBZgnhgJkEgB9OmCAKQaCoBgmrOgGuh1j4xxE+kFQABvYAQ3qsgAGFwANY4AJzsFgAExwAuc4BVhcEgFtVriAFXmuMLVYASMcFosAMotcQA0S4AazsAHitSqWAHlXACULosAo2NcQAca+xUoAD+cAN8tSwSAC7HUlwLYLhILACNrMEAmDUeViQwAuE7Z8YAXLpggBHRwC1M4AchpggFeawCMPUtrAScjBAJQtkRggEHJwCIE083mdAL0DgFDx-GYgqADXHADU1bWE6s5MEUgETRko1QA2C4AGzoOVhogEVxpwwQDdXYSYMyYIAFhcAgwuADTnXFwYLOTIAY9raciL9CYbE40SEojEgAnR+FUAgAelIFBwaAA3EA/language/typescript

Pull request:
feat: use Unicode Standard Annex #11 rules for line width calculation
Closes #452

Patch:
diff --git a/Cargo.lock b/Cargo.lock
index 2c0b792..274aa8e 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -49,9 +49,9 @@ dependencies = [
 
 [[package]]
 name = "anyhow"
-version = "1.0.64"
+version = "1.0.66"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b9a8f622bcf6ff3df478e9deba3e03e4e04b300f8e6a139e192c05fa3490afc7"
+checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6"
 
 [[package]]
 name = "ast_node"
@@ -208,15 +208,16 @@ dependencies = [
 
 [[package]]
 name = "dprint-core"
-version = "0.59.0"
+version = "0.60.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "84600c297cc99fc088a9a916286d71915c988fa3a6f1bbc994ad9b93dde80c03"
+checksum = "2c762da282ebc7635f7918898e26d50e2b282378977dc7b3786364ac12065a71"
 dependencies = [
  "anyhow",
  "bumpalo",
  "indexmap",
  "rustc-hash",
  "serde",
+ "unicode-width",
 ]
 
 [[package]]
@@ -364,9 +365,9 @@ dependencies = [
 
 [[package]]
 name = "indexmap"
-version = "1.9.1"
+version = "1.9.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e"
+checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399"
 dependencies = [
  "autocfg",
  "hashbrown",
@@ -639,9 +640,9 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.40"
+version = "1.0.47"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7"
+checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725"
 dependencies = [
  "unicode-ident",
 ]
@@ -743,18 +744,18 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
 
 [[package]]
 name = "serde"
-version = "1.0.144"
+version = "1.0.149"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0f747710de3dcd43b88c9168773254e809d8ddbdf9653b84e2554ab219f17860"
+checksum = "256b9932320c590e707b94576e3cc1f7c9024d0ee6612dfbcf1cb106cbe8e055"
 dependencies = [
  "serde_derive",
 ]
 
 [[package]]
 name = "serde_derive"
-version = "1.0.144"
+version = "1.0.149"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "94ed3a816fb1d101812f83e789f888322c34e291f894f19590dc310963e87a00"
+checksum = "b4eae9b04cbffdfd550eb462ed33bc6a1b68c935127d008b27444d08380f94e4"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -974,9 +975,9 @@ dependencies = [
 
 [[package]]
 name = "syn"
-version = "1.0.98"
+version = "1.0.105"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd"
+checksum = "60b9b43d45702de4c839cb9b51d9f529c5dd26a4aff255b42b1ebc03e88ee908"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1094,9 +1095,9 @@ dependencies = [
 
 [[package]]
 name = "unicode-width"
-version = "0.1.9"
+version = "0.1.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
+checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
 
 [[package]]
 name = "unreachable"
diff --git a/Cargo.toml b/Cargo.toml
index a9e29c9..25d2ad6 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -27,7 +27,7 @@ tracing = ["dprint-core/tracing"]
 [dependencies]
 anyhow = "1.0.64"
 deno_ast = { version = "0.21.0", features = ["view"] }
-dprint-core = { version = "0.59.0", features = ["formatting"] }
+dprint-core = { version = "0.60.0", features = ["formatting"] }
 rustc-hash = "1.1.0"
 serde = { version = "1.0.144", features = ["derive"] }
 serde_json = { version = "1.0", optional = true }
diff --git a/dprint.json b/dprint.json
index 9aa6577..65bd1b8 100644
--- a/dprint.json
+++ b/dprint.json
@@ -11,10 +11,10 @@
     "**/target"
   ],
   "plugins": [
-    "https://plugins.dprint.dev/typescript-0.71.2.wasm",
-    "https://plugins.dprint.dev/json-0.15.5.wasm",
-    "https://plugins.dprint.dev/markdown-0.14.0.wasm",
+    "https://plugins.dprint.dev/typescript-0.78.0.wasm",
+    "https://plugins.dprint.dev/json-0.16.0.wasm",
+    "https://plugins.dprint.dev/markdown-0.14.3.wasm",
     "https://plugins.dprint.dev/toml-0.5.4.wasm",
-    "https://plugins.dprint.dev/exec-0.3.1.json@9351b67ec7a6b58a69201c2834cba38cb3d191080aefc6422fb1320f03c8fc4d"
+    "https://plugins.dprint.dev/exec-0.3.2.json@8efbbb3fcfbdf84142c3c438fbdeaf1637152a020032127c837b2b14e23261c3"
   ]
 }
diff --git a/src/generation/generate.rs b/src/generation/generate.rs
index 09d6952..ec4a354 100644
--- a/src/generation/generate.rs
+++ b/src/generation/generate.rs
@@ -3902,7 +3902,10 @@ fn gen_string_literal<'a>(node: &'a Str, context: &mut Context<'a>) -> PrintItem
 
   fn get_string_value(node: &Str, context: &mut Context) -> String {
     let raw_string_text = node.text_fast(context.program);
-    let string_value = raw_string_text.chars().skip(1).take(raw_string_text.chars().count() - 2).collect::<String>();
+    if raw_string_text.len() <= 2 {
+      return String::new();
+    }
+    let string_value = &raw_string_text[1..raw_string_text.len() - 1];
     let is_double_quote = raw_string_text.starts_with('"');
 
     return match is_double_quote {


Test patch:
diff --git a/tests/specs/issues/issue0452.txt b/tests/specs/issues/issue0452.txt
new file mode 100644
index 0000000..3302087
--- /dev/null
+++ b/tests/specs/issues/issue0452.txt
@@ -0,0 +1,28 @@
+~~ deno: true ~~
+== should calculate line width according to unicode standard annex #11 rules ==
+const a = () => (
+  <div>
+    대충 한국어로 아무 말이나 적고 있습니다. '아무말'은 표준국어대사전에 등재되지 않은 단어이므로 '아무 말'로 띄어 씁니다. 아무
+    말일까요? 누군가에게는 적절한 더미 텍스트일 수도 있고 누군가에게는 그냥 지루한 문단일 수 있습니다. 문장이 긴데 보여질 폭은 좁을 때
+    어디에서 줄바꿈을 해야 할지는 중요한 이슈가 되어 왔습니다. 한국어는 띄어쓰기가 자주 등장하는 만큼 단어 사이를 끊는 것을 싫어하지만
+    끊는 부분에 타우마타와카탕이항아코아우아우오타마테아투리푸카카피키마웅아호로누쿠포카이웨누아키타나타후 같은 단어가 존재하면 엄청난 공백이
+    등장하게 됩니다. 직접 줄바꿈을 삽입하자는 의견도 나오지만 다양한 스크린 폭에 대응되지 못하는 것을 탐탁치 않아 하는 사람도 있습니다.
+    대충 한국어로 아무 말이나 적었습니다.
+  </div>
+);
+
+[expect]
+const a = () => (
+  <div>
+    대충 한국어로 아무 말이나 적고 있습니다. '아무말'은 표준국어대사전에
+    등재되지 않은 단어이므로 '아무 말'로 띄어 씁니다. 아무 말일까요?
+    누군가에게는 적절한 더미 텍스트일 수도 있고 누군가에게는 그냥 지루한 문단일
+    수 있습니다. 문장이 긴데 보여질 폭은 좁을 때 어디에서 줄바꿈을 해야 할지는
+    중요한 이슈가 되어 왔습니다. 한국어는 띄어쓰기가 자주 등장하는 만큼 단어
+    사이를 끊는 것을 싫어하지만 끊는 부분에
+    타우마타와카탕이항아코아우아우오타마테아투리푸카카피키마웅아호로누쿠포카이웨누아키타나타후
+    같은 단어가 존재하면 엄청난 공백이 등장하게 됩니다. 직접 줄바꿈을 삽입하자는
+    의견도 나오지만 다양한 스크린 폭에 대응되지 못하는 것을 탐탁치 않아 하는
+    사람도 있습니다. 대충 한국어로 아무 말이나 적었습니다.
+  </div>
+);
diff --git a/tests/specs/literals/StringLiteral/StringLiteral_All.txt b/tests/specs/literals/StringLiteral/StringLiteral_All.txt
index bd64a2b..efdc2a6 100644
--- a/tests/specs/literals/StringLiteral/StringLiteral_All.txt
+++ b/tests/specs/literals/StringLiteral/StringLiteral_All.txt
@@ -1,8 +1,12 @@
 == should not replace escaped character with actual character ==
 const t = "\t"  ;
+const u = "";
+const v = "1";
 
 [expect]
 const t = "\t";
+const u = "";
+const v = "1";
 
 == should not replace actual character with escaped character ==
 const t = "	";