diff --git a/apps/editor/src/__test__/unit/convertor.spec.ts b/apps/editor/src/__test__/unit/convertor.spec.ts index 2ed8cdd3ef..babb0c3896 100644 --- a/apps/editor/src/__test__/unit/convertor.spec.ts +++ b/apps/editor/src/__test__/unit/convertor.spec.ts @@ -590,7 +590,8 @@ describe('Convertor', () => { | | | | |
  1. mix**ed**
| - |
  1. mixed
| + |
  1. mix*e*d
| + |
  1. mixed
| | foobaz | | ![altText](imgUrl) **mixed** | `; @@ -602,8 +603,9 @@ describe('Convertor', () => { |
  1. ordered
| | | | | - |
  1. mixed
| - |
  1. mixed
| + |
  1. mix**ed**
| + |
  1. mix*e*d
| + |
  1. mixed
| | foobaz | | ![altText](imgUrl) **mixed** | `; @@ -1075,28 +1077,81 @@ describe('Convertor', () => { }); }); - 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); + describe('emphasis that starts/ends with punctuation', () => { + it('should convert by using HTML tag when not preceded by whitespace or punctuation', () => { + const wwNodeJson = { + type: 'doc', + content: [ + { + type: 'paragraph', + content: [ + { type: 'text', text: 'a' }, + { + type: 'text', + marks: [{ type: 'emph' }], + text: '"test"', + }, + ], + }, + ], + }; + const wwNode = Node.fromJSON(schema, wwNodeJson); - const result = convertor.toMarkdownText(wwNode); + const result = convertor.toMarkdownText(wwNode); + + expect(result).toBe(`a"test"`); + }); + + it('should convert by using HTML tag when not succeeded by whitespace or punctuation', () => { + 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`); + expect(result).toBe(`"test"a`); + }); + + it('should not convert by using HTML tag when preceded by whitespace', () => { + const markdown = + 'test **"test"**'; + const expected = + 'test **"test"**'; + + assertConverting(markdown, expected); + }); + + it('should not convert by using HTML tag when preceded by punctuation', () => { + const markdown = + 'test,**"test"**'; + const expected = + 'test,**"test"**'; + + assertConverting(markdown, expected); + }); + + it('should not convert by using HTML tag when inside another emphasis', () => { + const markdown = + '**test*test***'; + const expected = + '**test*test***'; + + assertConverting(markdown, expected); + }); }); it('should convert empty line between lists of wysiwig to
', () => { diff --git a/apps/editor/src/convertors/toMarkdown/toMdConvertorState.ts b/apps/editor/src/convertors/toMarkdown/toMdConvertorState.ts index 86fd701a05..c884493235 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, isEndWithSpace, isStartWithSpace } from '@/utils/common'; +import { includes, escape, last, isStartWithSpaceOrPunct, isEndWithSpaceOrPunct, isStartWithPunct, isEndWithPunct } from '@/utils/common'; import { WwNodeType, WwMarkType } from '@t/wysiwyg'; import { @@ -10,7 +10,6 @@ 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; @@ -50,27 +49,23 @@ export default class ToMdConvertorState { return /(^|\n)$/.test(this.result); } - private isBetweenSpaces(parent: Node, index: number) { + private isNonSpecEmphasis(parent: Node, index: number) { const { content } = parent; - const isFrontNodeEndWithSpace = - index === 0 || - isEndWithSpace(content.child(index - 1).text ?? DEFAULT_TEXT_NOT_START_OR_END_WITH_SPACE); + if (index !== 0 && isStartWithPunct(content.child(index).text) && !isEndWithSpaceOrPunct(content.child(index - 1).text) ) return true; - const isRearNodeStartWithSpace = - index >= content.childCount - 1 || - isStartWithSpace(content.child(index + 1).text ?? DEFAULT_TEXT_NOT_START_OR_END_WITH_SPACE); + if (index !== content.childCount - 1 && isEndWithPunct(content.child(index).text) && !isStartWithSpaceOrPunct(content.child(index + 1).text) ) return true; - return isFrontNodeEndWithSpace && isRearNodeStartWithSpace; + return false; } private markText(mark: Mark, entering: boolean, parent: Node, index: number) { const convertor = this.getMarkConvertor(mark); if (convertor) { - const betweenSpace = this.isBetweenSpaces(parent, entering ? index : index - 1); + const nonSpecEmphasis = this.isNonSpecEmphasis(parent, entering ? index : index - 1); - const { delim, rawHTML } = convertor({ node: mark, parent, index }, entering, betweenSpace); + const { delim, rawHTML } = convertor({ node: mark, parent, index }, entering, nonSpecEmphasis); 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 8f1a190404..e991cdba4d 100644 --- a/apps/editor/src/convertors/toMarkdown/toMdConvertors.ts +++ b/apps/editor/src/convertors/toMarkdown/toMdConvertors.ts @@ -208,11 +208,11 @@ export const toMdConvertors: ToMdConvertorMap = { }; }, - strong({ node }, { entering }, betweenSpace) { + strong({ node }, { entering }, nonSpecEmphasis) { const { rawHTML } = node.attrs; let delim = '**'; - if (!betweenSpace) { + if (nonSpecEmphasis) { delim = entering ? '' : ''; } @@ -222,11 +222,11 @@ export const toMdConvertors: ToMdConvertorMap = { }; }, - emph({ node }, { entering }, betweenSpace) { + emph({ node }, { entering }, nonSpecEmphasis) { const { rawHTML } = node.attrs; let delim = '*'; - if (!betweenSpace) { + if (nonSpecEmphasis) { delim = entering ? '' : ''; } @@ -236,11 +236,11 @@ export const toMdConvertors: ToMdConvertorMap = { }; }, - strike({ node }, { entering }, betweenSpace) { + strike({ node }, { entering }, nonSpecEmphasis) { const { rawHTML } = node.attrs; let delim = '~~'; - if (!betweenSpace) { + if (nonSpecEmphasis) { delim = entering ? '' : ''; } @@ -368,7 +368,7 @@ function createMarkTypeConvertors(convertors: ToMdConvertorMap) { const markTypes = Object.keys(markTypeOptions) as WwMarkType[]; markTypes.forEach((type) => { - markTypeConvertors[type] = (nodeInfo, entering, betweenSpace) => { + markTypeConvertors[type] = (nodeInfo, entering, nonSpecEmphasis) => { const markOption = markTypeOptions[type]; const convertor = convertors[type]; @@ -378,7 +378,7 @@ function createMarkTypeConvertors(convertors: ToMdConvertorMap) { // the converter is called without parameters. const runConvertor = convertor && nodeInfo && !isUndefined(entering); const params = runConvertor - ? convertor!(nodeInfo as MarkInfo, { entering }, betweenSpace) + ? convertor!(nodeInfo as MarkInfo, { entering }, nonSpecEmphasis) : {}; return { ...params, ...markOption }; diff --git a/apps/editor/src/utils/common.ts b/apps/editor/src/utils/common.ts index 3754219237..f4dc66bd03 100644 --- a/apps/editor/src/utils/common.ts +++ b/apps/editor/src/utils/common.ts @@ -264,14 +264,35 @@ export function getSortedNumPair(valueA: number, valueB: number) { return valueA > valueB ? [valueB, valueA] : [valueA, valueB]; } -export function isStartWithSpace(text: string) { - const reStartWithSpace = /^\s(\S*)/g; +const reStartWithSpaceOrPunct = /^[\s\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,\-.\/:;<=>?@\[\]^_`{|}~]/; - return reStartWithSpace.test(text); +export function isStartWithSpaceOrPunct(text?: string) { + if (!text) return true; + + return reStartWithSpaceOrPunct.test(text); +} + +const reEndWithSpaceOrPunct = /[\s\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,\-.\/:;<=>?@\[\]^_`{|}~]$/; + +export function isEndWithSpaceOrPunct(text?: string) { + if (!text) return true; + + return reEndWithSpaceOrPunct.test(text); } -export function isEndWithSpace(text: string) { - const reEndWithSpace = /(\S*)\s$/g; +const reStartWithPunct = /^[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,\-.\/:;<=>?@\[\]^_`{|}~]/; + +export function isStartWithPunct(text?: string) { + if (!text) return false; - return reEndWithSpace.test(text); + return reStartWithPunct.test(text); } + +const reEndWithPunct = /[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,\-.\/:;<=>?@\[\]^_`{|}~]$/; + +export function isEndWithPunct(text?: string) { + if (!text) return false; + + return reEndWithPunct.test(text); +} + diff --git a/apps/editor/src/utils/constants.ts b/apps/editor/src/utils/constants.ts index 78d5adf33f..2d6cd54983 100644 --- a/apps/editor/src/utils/constants.ts +++ b/apps/editor/src/utils/constants.ts @@ -20,5 +20,3 @@ export const reBR = //i; export const reHTMLComment = /|/; export const ALTERNATIVE_TAG_FOR_BR = '

'; - -export const DEFAULT_TEXT_NOT_START_OR_END_WITH_SPACE = 'a'; diff --git a/apps/editor/types/convertor.d.ts b/apps/editor/types/convertor.d.ts index 718c1c7ade..4013134f27 100644 --- a/apps/editor/types/convertor.d.ts +++ b/apps/editor/types/convertor.d.ts @@ -114,7 +114,7 @@ export type ToMdNodeTypeConvertorMap = Partial ToMdConvertorReturnValues & ToMdMarkTypeOption; export type ToMdMarkTypeConvertorMap = Partial>; @@ -128,7 +128,7 @@ interface ToMdConvertorContext { type ToMdConvertor = ( nodeInfo: NodeInfo | MarkInfo, context: ToMdConvertorContext, - betweenSpace?: boolean + nonSpecEmphasis?: boolean ) => ToMdConvertorReturnValues; export type ToMdConvertorMap = Partial>;