Repository: nhn/tui.editor
Pull request #1791 (2021-09-06 03:16:55)

fix: user cannot insert the `$$` text due to custom inline syntax
<!-- 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
**as-is**
![images](https://user-images.githubusercontent.com/19277713/130400991-91e62cc9-e516-43dd-b437-6623fc66e91e.png)

**to-be**
![스크린샷 2021-09-06 오후 12 16 24](https://user-images.githubusercontent.com/37766175/132155728-2532ed13-4ceb-4a57-8a14-e049af0b3bf3.png)


* related issue(#1768)


---
Thank you for your contribution to TOAST UI product. 🎉 😘 ✨



Patch:
diff --git a/libs/toastmark/src/html/baseConvertors.ts b/libs/toastmark/src/html/baseConvertors.ts
index b3b0c1bc..bcdd57b9 100644
--- a/libs/toastmark/src/html/baseConvertors.ts
+++ b/libs/toastmark/src/html/baseConvertors.ts
@@ -10,6 +10,8 @@ import {
 import { escapeXml } from '../commonmark/common';
 import { filterDisallowedTags } from './tagFilter';
 
+const CUSTOM_SYNTAX_LENGTH = 4;
+
 export const baseConvertors: HTMLConvertorMap = {
   heading(node, { entering }) {
     return {
@@ -197,7 +199,9 @@ export const baseConvertors: HTMLConvertorMap = {
       try {
         return customConvertor!(node, context);
       } catch (e) {
-        console.warn(e);
+        console.warn(
+          `[@toast-ui/editor] - The error occurred when ${info} block node was parsed in markdown renderer: ${e}`
+        );
       }
     }
 
@@ -223,16 +227,34 @@ export const baseConvertors: HTMLConvertorMap = {
   },
 
   customInline(node, context, convertors) {
-    const info = (node as CustomBlockNode).info!.trim().toLowerCase();
-    const customConvertor = convertors![info];
+    const { sourcepos, info } = node as CustomBlockNode;
+    const nomalizedInfo = info.trim().toLowerCase();
+    const customConvertor = convertors![nomalizedInfo];
+    const { entering } = context;
 
     if (customConvertor) {
-      return customConvertor!(node, context);
+      try {
+        return customConvertor!(node, context);
+      } catch (e) {
+        console.warn(
+          `[@toast-ui/editor] - The error occurred when ${nomalizedInfo} inline node was parsed in markdown renderer: ${e}`
+        );
+      }
     }
 
-    return {
-      type: context.entering ? 'openTag' : 'closeTag',
-      tagName: 'span',
-    };
+    const text =
+      sourcepos![1][1] - sourcepos![0][1] + 1 === info.length + CUSTOM_SYNTAX_LENGTH
+        ? ''
+        : `${info} `;
+
+    return entering
+      ? [
+          { type: 'openTag', tagName: 'span' },
+          { type: 'text', content: `$$${text}` },
+        ]
+      : [
+          { type: 'text', content: '$$' },
+          { type: 'closeTag', tagName: 'span' },
+        ];
   },
 };
diff --git a/libs/toastmark/src/html/renderer.ts b/libs/toastmark/src/html/renderer.ts
index fa54adb4..e1310ef7 100644
--- a/libs/toastmark/src/html/renderer.ts
+++ b/libs/toastmark/src/html/renderer.ts
@@ -72,8 +72,8 @@ export class Renderer implements HTMLRenderer {
             : nodeType;
 
         if (orgConvertor) {
-          convertors[convertorType] = (node, context) => {
-            context.origin = () => orgConvertor(node, context);
+          convertors[convertorType] = (node, context, convertors) => {
+            context.origin = () => orgConvertor(node, context, convertors);
             return convertor(node, context);
           };
         } else {


Test patch:
diff --git a/libs/toastmark/src/commonmark/custom/__test__/customInline.spec.ts b/libs/toastmark/src/commonmark/custom/__test__/customInline.spec.ts
index 64ae2635..ed691eca 100644
--- a/libs/toastmark/src/commonmark/custom/__test__/customInline.spec.ts
+++ b/libs/toastmark/src/commonmark/custom/__test__/customInline.spec.ts
@@ -27,7 +27,7 @@ describe('customInline', () => {
 
     const html = renderer.render(root);
 
-    expect(html).toBe('<p>Hello <span>World</span></p>\n');
+    expect(html).toBe('<p>Hello <span>$$myInline World$$</span></p>\n');
   });
 
   it('nested markdown text example', () => {
@@ -50,7 +50,7 @@ describe('customInline', () => {
 
     const html = renderer.render(root);
 
-    expect(html).toBe('<p>Hello <span><em>World</em></span></p>\n');
+    expect(html).toBe('<p>Hello <span>$$myInline <em>World</em>$$</span></p>\n');
   });
 
   it('should be parsed as text without meta information', () => {