Repository: nhn/tui.editor
Pull request #2588 (2022-07-04 05:24:17)

fix: escape backslash in the middle of sentence
<!-- EDIT TITLE PLEASE -->
<!-- It should be one of them
  <ISSUE TYPE>: Short Description (<CLOSING TYPE> #<ISSUE NUMBERS>)
  ex)
  feat: add new feature (close #111)
  fix: wrong behavior (fix #111)
  chore: change build tool (ref #111)
-->

<!-- SPECIFY A ISSUE TYPE AT HEAD
  feat: A new feature
  fix: A bug fix
  docs: Documentation only changes
  style: Changes that do not affect the meaning of the code (white-space, formatting etc)
  refactor: A code change that neither fixes a bug or adds a feature
  perf: A code change that improves performance
  test: Adding missing tests
  chore: Changes to the build process or auxiliary tools and libraries such as documentation generation
-->

<!-- ADD CLOSING TYPE AND ISSUE NUMBER AT TAIL
  (<CLOSING TYPE> #<ISSUE NUMBERS>)
  close: resolve not a bug(feature, docs, etc) completely
  fix: resolve a bug completely
  ref: not fully resolved or related to
-->

### Please check if the PR fulfills these requirements
- [x] It's the right issue type on the title
- [ ] When resolving a specific issue, it's referenced in the PR's title (e.g. `fix #xxx[,#xxx]`, where "xxx" is the issue number)
- [x] The commit message follows our guidelines
- [x] Tests for the changes have been added (for bug fixes/features)
- [ ] Docs have been added/updated (for bug fixes/features)
- [ ] It does not introduce a breaking change or has a description of the breaking change

### Description

* Fixed an issue where backslashes in the middle of sentences were not escaped.
  * In WYSIWYG, when a backslash was written in the middle of a sentence like `some \<span>sentence</span>`, the backslash was not escaped and the `span` tag was escaped.
  * In this case, by escaping the backslash, the content is converted to normal.

**As-Is**

![](https://user-images.githubusercontent.com/41339744/177086689-48d7647d-c71f-42de-9ab3-1ed99cfc7a5c.gif)


**To-Be**

![](https://user-images.githubusercontent.com/41339744/177086693-90bc40a2-b539-4392-9698-de2ec5ee2d2d.gif)


---
Thank you for your contribution to TOAST UI product. 🎉 😘 ✨



Patch:
diff --git a/apps/editor/src/utils/common.ts b/apps/editor/src/utils/common.ts
index cd38e930..37542192 100644
--- a/apps/editor/src/utils/common.ts
+++ b/apps/editor/src/utils/common.ts
@@ -12,7 +12,8 @@ const reEscapeHTML = /<([a-zA-Z_][a-zA-Z0-9\-._]*)(\s|[^\\>])*\/?>|<(\/)([a-zA-Z
 const reEscapeBackSlash = /\\[!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~\\]/g;
 const reEscapePairedChars = /[*_~`]/g;
 const reMdImageSyntax = /!\[.*\]\(.*\)/g;
-const reEscapedCharInLinkSyntax = /[[\]]/g; //
+const reEscapedCharInLinkSyntax = /[[\]]/g;
+const reEscapeBackSlashInSentence = /(?:^|[^\\])\\(?!\\)/g;
 
 const XMLSPECIAL = '[&<>"]';
 const reXmlSpecial = new RegExp(XMLSPECIAL, 'g');
@@ -118,22 +119,27 @@ export function escapeTextForLink(text: string) {
 }
 
 export function escape(text: string) {
-  const replacer = (matched: string) => `\\${matched}`;
+  const aheadReplacer = (matched: string) => `\\${matched}`;
+  const behindReplacer = (matched: string) => `${matched}\\`;
 
   let escapedText = text.replace(reSpaceMoreThanOne, ' ');
 
   if (reEscapeBackSlash.test(escapedText)) {
-    escapedText = escapedText.replace(reEscapeBackSlash, replacer);
+    escapedText = escapedText.replace(reEscapeBackSlash, aheadReplacer);
   }
 
-  escapedText = escapedText.replace(reEscapePairedChars, replacer);
+  if (reEscapeBackSlashInSentence.test(escapedText)) {
+    escapedText = escapedText.replace(reEscapeBackSlashInSentence, behindReplacer);
+  }
+
+  escapedText = escapedText.replace(reEscapePairedChars, aheadReplacer);
 
   if (reEscapeHTML.test(escapedText)) {
-    escapedText = escapedText.replace(reEscapeHTML, replacer);
+    escapedText = escapedText.replace(reEscapeHTML, aheadReplacer);
   }
 
   if (isNeedEscapeText(escapedText)) {
-    escapedText = escapedText.replace(reEscapeChars, replacer);
+    escapedText = escapedText.replace(reEscapeChars, aheadReplacer);
   }
 
   return escapedText;


Test patch:
diff --git a/apps/editor/src/__test__/unit/convertor.spec.ts b/apps/editor/src/__test__/unit/convertor.spec.ts
index bef09ec5..6b98f6e0 100644
--- a/apps/editor/src/__test__/unit/convertor.spec.ts
+++ b/apps/editor/src/__test__/unit/convertor.spec.ts
@@ -1098,4 +1098,15 @@ describe('Convertor', () => {
 
     expect(result).toBe(`<strong>"test"</strong>a`);
   });
+
+  it('should escape the backslash, which is a plain chracter in the middle of a sentence', () => {
+    const markdown = source`
+      backslash \\in the middle of a sentence
+      `;
+    const expected = source`
+      backslash \\\\in the middle of a sentence
+      `;
+
+    assertConverting(markdown, expected);
+  });
 });