From d22044a2e93427fb46e4364f226e7777b247a8d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8C=80=EC=97=B0?= Date: Tue, 14 Jun 2022 14:53:50 +0900 Subject: [PATCH 1/5] wip: convert strong to tag when it is not between spaces --- .../toMarkdown/toMdConvertorState.ts | 37 ++++++++++++++++++- .../convertors/toMarkdown/toMdConvertors.ts | 17 +++++++-- apps/editor/src/utils/common.ts | 10 +++++ apps/editor/types/convertor.d.ts | 6 ++- 4 files changed, 62 insertions(+), 8 deletions(-) diff --git a/apps/editor/src/convertors/toMarkdown/toMdConvertorState.ts b/apps/editor/src/convertors/toMarkdown/toMdConvertorState.ts index 544a186f58..0d279fbe19 100644 --- a/apps/editor/src/convertors/toMarkdown/toMdConvertorState.ts +++ b/apps/editor/src/convertors/toMarkdown/toMdConvertorState.ts @@ -1,6 +1,6 @@ import { Node, Mark } from 'prosemirror-model'; -import { includes, escape, last } from '@/utils/common'; +import { includes, escape, last, isEndWithSpace, isStartWithSpace } from '@/utils/common'; import { WwNodeType, WwMarkType } from '@t/wysiwyg'; import { @@ -49,11 +49,44 @@ export default class ToMdConvertorState { return /(^|\n)$/.test(this.result); } + private isBeteewnSpaces(parent: Node, index: number) { + const { content } = parent; + // const frontText = content.child(index - 1).text; + // const rearText = content.child(index + 1).text; + + const isFrontNodeEndWithSpace = + index === 0 || isEndWithSpace(content.child(index - 1).text ?? 'a'); + + if (index !== 0) { + console.log(index, isFrontNodeEndWithSpace); + } + + const isRearNodeStartWithSpace = + index >= content.childCount - 1 || isStartWithSpace(content.child(index + 1).text ?? 'a'); + + if (index - 1 >= 0 && index + 1 <= content.childCount - 1) { + console.log( + isEndWithSpace(content.child(index - 1).text ?? 'a'), + isStartWithSpace(content.child(index + 1).text ?? 'a'), + isFrontNodeEndWithSpace, + isRearNodeStartWithSpace, + content + ); + } + + return isFrontNodeEndWithSpace && isRearNodeStartWithSpace; + } + private markText(mark: Mark, entering: boolean, parent: Node, index: number) { const convertor = this.getMarkConvertor(mark); if (convertor) { - const { delim, rawHTML } = convertor({ node: mark, parent, index }, entering); + if (mark.type.name === 'strong') { + // debugger; + } + const betweenSpace = this.isBeteewnSpaces(parent, entering ? index : index - 1); + + const { delim, rawHTML } = convertor({ node: mark, parent, index }, entering, betweenSpace); return (rawHTML as string) || (delim as string); } diff --git a/apps/editor/src/convertors/toMarkdown/toMdConvertors.ts b/apps/editor/src/convertors/toMarkdown/toMdConvertors.ts index 71066e7b18..c8655736d1 100644 --- a/apps/editor/src/convertors/toMarkdown/toMdConvertors.ts +++ b/apps/editor/src/convertors/toMarkdown/toMdConvertors.ts @@ -208,11 +208,18 @@ export const toMdConvertors: ToMdConvertorMap = { }; }, - strong({ node }, { entering }) { + strong({ node }, { entering }, betweenSpace) { const { rawHTML } = node.attrs; + let delim; + + if (betweenSpace) { + delim = '**'; + } else { + delim = entering ? '' : ''; + } return { - delim: '**', + delim, rawHTML: entering ? getOpenRawHTML(rawHTML) : getCloseRawHTML(rawHTML), }; }, @@ -353,7 +360,7 @@ function createMarkTypeConvertors(convertors: ToMdConvertorMap) { const markTypes = Object.keys(markTypeOptions) as WwMarkType[]; markTypes.forEach((type) => { - markTypeConvertors[type] = (nodeInfo, entering) => { + markTypeConvertors[type] = (nodeInfo, entering, betweenSpace) => { const markOption = markTypeOptions[type]; const convertor = convertors[type]; @@ -362,7 +369,9 @@ function createMarkTypeConvertors(convertors: ToMdConvertorMap) { // When calling the converter without using `delim` and `rawHTML` values, // the converter is called without parameters. const runConvertor = convertor && nodeInfo && !isUndefined(entering); - const params = runConvertor ? convertor!(nodeInfo as MarkInfo, { entering }) : {}; + const params = runConvertor + ? convertor!(nodeInfo as MarkInfo, { entering }, betweenSpace) + : {}; return { ...params, ...markOption }; }; diff --git a/apps/editor/src/utils/common.ts b/apps/editor/src/utils/common.ts index 343eecc8d7..cf1a6034ec 100644 --- a/apps/editor/src/utils/common.ts +++ b/apps/editor/src/utils/common.ts @@ -13,6 +13,8 @@ const reEscapeBackSlash = /\\[!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~\\]/g; const reEscapePairedChars = /[*_~`]/g; const reMdImageSyntax = /!\[.*\]\(.*\)/g; const reEscapedCharInLinkSyntax = /[[\]]/g; // +const reEndWithSpace = /(\S*)\s$/g; +const reStartWithSpace = /^\s(\S*)/g; const XMLSPECIAL = '[&<>"]'; const reXmlSpecial = new RegExp(XMLSPECIAL, 'g'); @@ -257,3 +259,11 @@ export function assign(targetObj: Record, obj: Record export function getSortedNumPair(valueA: number, valueB: number) { return valueA > valueB ? [valueB, valueA] : [valueA, valueB]; } + +export function isStartWithSpace(text: string) { + return reStartWithSpace.test(text); +} + +export function isEndWithSpace(text: string) { + return reEndWithSpace.test(text); +} diff --git a/apps/editor/types/convertor.d.ts b/apps/editor/types/convertor.d.ts index e8ef7d5fc0..d44db7d00d 100644 --- a/apps/editor/types/convertor.d.ts +++ b/apps/editor/types/convertor.d.ts @@ -111,7 +111,8 @@ export type ToMdNodeTypeConvertorMap = Partial ToMdConvertorReturnValues & ToMdMarkTypeOption; export type ToMdMarkTypeConvertorMap = Partial>; @@ -124,7 +125,8 @@ interface ToMdConvertorContext { type ToMdConvertor = ( nodeInfo: NodeInfo | MarkInfo, - context: ToMdConvertorContext + context: ToMdConvertorContext, + betweenSpace?: boolean ) => ToMdConvertorReturnValues; export type ToMdConvertorMap = Partial>; From 627996ab3f877bdeb6490aa6bf1c13beeea22edd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8C=80=EC=97=B0?= Date: Mon, 20 Jun 2022 16:36:44 +0900 Subject: [PATCH 2/5] fix: convert strong to tag when it is not between spaces --- .../toMarkdown/toMdConvertorState.ts | 23 ++----------------- apps/editor/src/utils/common.ts | 6 +++-- 2 files changed, 6 insertions(+), 23 deletions(-) diff --git a/apps/editor/src/convertors/toMarkdown/toMdConvertorState.ts b/apps/editor/src/convertors/toMarkdown/toMdConvertorState.ts index 0d279fbe19..54beb8cd34 100644 --- a/apps/editor/src/convertors/toMarkdown/toMdConvertorState.ts +++ b/apps/editor/src/convertors/toMarkdown/toMdConvertorState.ts @@ -49,31 +49,15 @@ export default class ToMdConvertorState { return /(^|\n)$/.test(this.result); } - private isBeteewnSpaces(parent: Node, index: number) { + private isBetweenSpaces(parent: Node, index: number) { const { content } = parent; - // const frontText = content.child(index - 1).text; - // const rearText = content.child(index + 1).text; const isFrontNodeEndWithSpace = index === 0 || isEndWithSpace(content.child(index - 1).text ?? 'a'); - if (index !== 0) { - console.log(index, isFrontNodeEndWithSpace); - } - const isRearNodeStartWithSpace = index >= content.childCount - 1 || isStartWithSpace(content.child(index + 1).text ?? 'a'); - if (index - 1 >= 0 && index + 1 <= content.childCount - 1) { - console.log( - isEndWithSpace(content.child(index - 1).text ?? 'a'), - isStartWithSpace(content.child(index + 1).text ?? 'a'), - isFrontNodeEndWithSpace, - isRearNodeStartWithSpace, - content - ); - } - return isFrontNodeEndWithSpace && isRearNodeStartWithSpace; } @@ -81,10 +65,7 @@ export default class ToMdConvertorState { const convertor = this.getMarkConvertor(mark); if (convertor) { - if (mark.type.name === 'strong') { - // debugger; - } - const betweenSpace = this.isBeteewnSpaces(parent, entering ? index : index - 1); + const betweenSpace = this.isBetweenSpaces(parent, entering ? index : index - 1); const { delim, rawHTML } = convertor({ node: mark, parent, index }, entering, betweenSpace); diff --git a/apps/editor/src/utils/common.ts b/apps/editor/src/utils/common.ts index cf1a6034ec..cd38e93083 100644 --- a/apps/editor/src/utils/common.ts +++ b/apps/editor/src/utils/common.ts @@ -13,8 +13,6 @@ const reEscapeBackSlash = /\\[!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~\\]/g; const reEscapePairedChars = /[*_~`]/g; const reMdImageSyntax = /!\[.*\]\(.*\)/g; const reEscapedCharInLinkSyntax = /[[\]]/g; // -const reEndWithSpace = /(\S*)\s$/g; -const reStartWithSpace = /^\s(\S*)/g; const XMLSPECIAL = '[&<>"]'; const reXmlSpecial = new RegExp(XMLSPECIAL, 'g'); @@ -261,9 +259,13 @@ export function getSortedNumPair(valueA: number, valueB: number) { } export function isStartWithSpace(text: string) { + const reStartWithSpace = /^\s(\S*)/g; + return reStartWithSpace.test(text); } export function isEndWithSpace(text: string) { + const reEndWithSpace = /(\S*)\s$/g; + return reEndWithSpace.test(text); } From 11a4b4c38cc9362c3980d403a6e67021cda25859 Mon Sep 17 00:00:00 2001 From: Kim DaeYeon Date: Tue, 21 Jun 2022 17:19:19 +0900 Subject: [PATCH 3/5] fix: convertor that follows the same rule --- .../convertors/toMarkdown/toMdConvertors.ts | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/apps/editor/src/convertors/toMarkdown/toMdConvertors.ts b/apps/editor/src/convertors/toMarkdown/toMdConvertors.ts index c8655736d1..b618b3419d 100644 --- a/apps/editor/src/convertors/toMarkdown/toMdConvertors.ts +++ b/apps/editor/src/convertors/toMarkdown/toMdConvertors.ts @@ -224,20 +224,34 @@ export const toMdConvertors: ToMdConvertorMap = { }; }, - emph({ node }, { entering }) { + emph({ node }, { entering }, betweenSpace) { const { rawHTML } = node.attrs; + let delim; + + if (betweenSpace) { + delim = '*'; + } else { + delim = entering ? '' : ''; + } return { - delim: '*', + delim, rawHTML: entering ? getOpenRawHTML(rawHTML) : getCloseRawHTML(rawHTML), }; }, - strike({ node }, { entering }) { + strike({ node }, { entering }, betweenSpace) { const { rawHTML } = node.attrs; + let delim; + + if (betweenSpace) { + delim = '~~'; + } else { + delim = entering ? '' : ''; + } return { - delim: '~~', + delim, rawHTML: entering ? getOpenRawHTML(rawHTML) : getCloseRawHTML(rawHTML), }; }, From 159e47a9a267e7899a875ab1530ec22d48a205b1 Mon Sep 17 00:00:00 2001 From: jajugoguma Date: Wed, 22 Jun 2022 15:36:12 +0900 Subject: [PATCH 4/5] test: add test for convertor --- .../src/__test__/unit/convertor.spec.ts | 41 ++++++++++++++++++- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/apps/editor/src/__test__/unit/convertor.spec.ts b/apps/editor/src/__test__/unit/convertor.spec.ts index 52702f2650..bef09ec536 100644 --- a/apps/editor/src/__test__/unit/convertor.spec.ts +++ b/apps/editor/src/__test__/unit/convertor.spec.ts @@ -2,7 +2,7 @@ import { source, oneLineTrim } from 'common-tags'; import { Context, MdNode, Parser, HTMLConvertorMap } from '@toast-ui/toastmark'; -import { Schema } from 'prosemirror-model'; +import { Node, Schema } from 'prosemirror-model'; import { createSpecs } from '@/wysiwyg/specCreator'; import Convertor from '@/convertors/convertor'; @@ -595,7 +595,20 @@ describe('Convertor', () => { | ![altText](imgUrl) **mixed**
  • [linkText](linkUrl) mixed
| `; - assertConverting(markdown, `${markdown}\n`); + const expected = source` + | thead | + | ----- | + |
  • bullet
| + |
  1. ordered
| + |
  • nested
    • nested
| + |
  • nested
    • nested
    • nested
| + |
  1. mixed
    • mixed
| + |
  1. mixed
    • mixed
| + | foo
  • bar
baz | + | ![altText](imgUrl) **mixed**
  • [linkText](linkUrl) mixed
| + `; + + assertConverting(markdown, `${expected}\n`); }); it('table with unmatched html list', () => { @@ -1061,4 +1074,28 @@ describe('Convertor', () => { assertConverting(markdown, markdown); }); }); + + it('should convert by using HTML tag when delimiter is not preceded an alphanumeric', () => { + const wwNodeJson = { + type: 'doc', + content: [ + { + type: 'paragraph', + content: [ + { + type: 'text', + marks: [{ type: 'strong' }], + text: '"test"', + }, + { type: 'text', text: 'a' }, + ], + }, + ], + }; + const wwNode = Node.fromJSON(schema, wwNodeJson); + + const result = convertor.toMarkdownText(wwNode); + + expect(result).toBe(`"test"a`); + }); }); From 9dd5e9e592fed3c93c212bcd4542dcc08e29fc92 Mon Sep 17 00:00:00 2001 From: jajugoguma Date: Mon, 27 Jun 2022 16:55:11 +0900 Subject: [PATCH 5/5] chore: apply code reviews --- .../toMarkdown/toMdConvertorState.ts | 7 +++++-- .../convertors/toMarkdown/toMdConvertors.ts | 18 ++++++------------ apps/editor/src/utils/constants.ts | 2 ++ 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/apps/editor/src/convertors/toMarkdown/toMdConvertorState.ts b/apps/editor/src/convertors/toMarkdown/toMdConvertorState.ts index 54beb8cd34..99991a348f 100644 --- a/apps/editor/src/convertors/toMarkdown/toMdConvertorState.ts +++ b/apps/editor/src/convertors/toMarkdown/toMdConvertorState.ts @@ -10,6 +10,7 @@ import { FirstDelimFn, InfoForPosSync, } from '@t/convertor'; +import { DEFAULT_TEXT_NOT_START_OR_END_WITH_SPACE } from '@/utils/constants'; export default class ToMdConvertorState { private readonly nodeTypeConvertors: ToMdNodeTypeConvertorMap; @@ -53,10 +54,12 @@ export default class ToMdConvertorState { const { content } = parent; const isFrontNodeEndWithSpace = - index === 0 || isEndWithSpace(content.child(index - 1).text ?? 'a'); + index === 0 || + isEndWithSpace(content.child(index - 1).text ?? DEFAULT_TEXT_NOT_START_OR_END_WITH_SPACE); const isRearNodeStartWithSpace = - index >= content.childCount - 1 || isStartWithSpace(content.child(index + 1).text ?? 'a'); + index >= content.childCount - 1 || + isStartWithSpace(content.child(index + 1).text ?? DEFAULT_TEXT_NOT_START_OR_END_WITH_SPACE); return isFrontNodeEndWithSpace && isRearNodeStartWithSpace; } diff --git a/apps/editor/src/convertors/toMarkdown/toMdConvertors.ts b/apps/editor/src/convertors/toMarkdown/toMdConvertors.ts index b618b3419d..8f1a190404 100644 --- a/apps/editor/src/convertors/toMarkdown/toMdConvertors.ts +++ b/apps/editor/src/convertors/toMarkdown/toMdConvertors.ts @@ -210,11 +210,9 @@ export const toMdConvertors: ToMdConvertorMap = { strong({ node }, { entering }, betweenSpace) { const { rawHTML } = node.attrs; - let delim; + let delim = '**'; - if (betweenSpace) { - delim = '**'; - } else { + if (!betweenSpace) { delim = entering ? '' : ''; } @@ -226,11 +224,9 @@ export const toMdConvertors: ToMdConvertorMap = { emph({ node }, { entering }, betweenSpace) { const { rawHTML } = node.attrs; - let delim; + let delim = '*'; - if (betweenSpace) { - delim = '*'; - } else { + if (!betweenSpace) { delim = entering ? '' : ''; } @@ -242,11 +238,9 @@ export const toMdConvertors: ToMdConvertorMap = { strike({ node }, { entering }, betweenSpace) { const { rawHTML } = node.attrs; - let delim; + let delim = '~~'; - if (betweenSpace) { - delim = '~~'; - } else { + if (!betweenSpace) { delim = entering ? '' : ''; } diff --git a/apps/editor/src/utils/constants.ts b/apps/editor/src/utils/constants.ts index 2d6cd54983..78d5adf33f 100644 --- a/apps/editor/src/utils/constants.ts +++ b/apps/editor/src/utils/constants.ts @@ -20,3 +20,5 @@ export const reBR = //i; export const reHTMLComment = /|/; export const ALTERNATIVE_TAG_FOR_BR = '

'; + +export const DEFAULT_TEXT_NOT_START_OR_END_WITH_SPACE = 'a';