Skip to content

Commit

Permalink
fix: convert by using HTML tag when it is not between spaces (#2572)
Browse files Browse the repository at this point in the history
* wip: convert strong to tag when it is not between spaces

* fix: convert strong to tag when it is not between spaces

* fix: convertor that follows the same rule

* test: add test for convertor

* chore: apply code reviews
  • Loading branch information
jajugoguma committed Jun 28, 2022
1 parent 31bea65 commit 66db3f8
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 14 deletions.
41 changes: 39 additions & 2 deletions apps/editor/src/__test__/unit/convertor.spec.ts
Expand Up @@ -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';
Expand Down Expand Up @@ -595,7 +595,20 @@ describe('Convertor', () => {
| ![altText](imgUrl) **mixed**<ul><li>[linkText](linkUrl) mixed</li></ul> |
`;

assertConverting(markdown, `${markdown}\n`);
const expected = source`
| thead |
| ----- |
| <ul><li>bullet</li></ul> |
| <ol><li>ordered</li></ol> |
| <ul><li>nested<ul><li>nested</li></ul></li></ul> |
| <ul><li>nested<ul><li>nested</li><li>nested</li></ul></li></ul> |
| <ol><li>mix<strong>ed</strong><ul><li><strong>mix</strong>ed</li></ul></li></ol> |
| <ol><li>mix<i>ed</i><ul><li><strong>mix</strong>ed</li></ul></li></ol> |
| foo<ul><li>bar</li></ul>baz |
| ![altText](imgUrl) **mixed**<ul><li>[linkText](linkUrl) mixed</li></ul> |
`;

assertConverting(markdown, `${expected}\n`);
});

it('table with unmatched html list', () => {
Expand Down Expand Up @@ -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(`<strong>"test"</strong>a`);
});
});
21 changes: 19 additions & 2 deletions 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 {
Expand All @@ -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;
Expand Down Expand Up @@ -49,11 +50,27 @@ export default class ToMdConvertorState {
return /(^|\n)$/.test(this.result);
}

private isBetweenSpaces(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);

const isRearNodeStartWithSpace =
index >= content.childCount - 1 ||
isStartWithSpace(content.child(index + 1).text ?? DEFAULT_TEXT_NOT_START_OR_END_WITH_SPACE);

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);
const betweenSpace = this.isBetweenSpaces(parent, entering ? index : index - 1);

const { delim, rawHTML } = convertor({ node: mark, parent, index }, entering, betweenSpace);

return (rawHTML as string) || (delim as string);
}
Expand Down
33 changes: 25 additions & 8 deletions apps/editor/src/convertors/toMarkdown/toMdConvertors.ts
Expand Up @@ -208,29 +208,44 @@ export const toMdConvertors: ToMdConvertorMap = {
};
},

strong({ node }, { entering }) {
strong({ node }, { entering }, betweenSpace) {
const { rawHTML } = node.attrs;
let delim = '**';

if (!betweenSpace) {
delim = entering ? '<strong>' : '</strong>';
}

return {
delim: '**',
delim,
rawHTML: entering ? getOpenRawHTML(rawHTML) : getCloseRawHTML(rawHTML),
};
},

emph({ node }, { entering }) {
emph({ node }, { entering }, betweenSpace) {
const { rawHTML } = node.attrs;
let delim = '*';

if (!betweenSpace) {
delim = entering ? '<em>' : '</em>';
}

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 = entering ? '<del>' : '</del>';
}

return {
delim: '~~',
delim,
rawHTML: entering ? getOpenRawHTML(rawHTML) : getCloseRawHTML(rawHTML),
};
},
Expand Down Expand Up @@ -353,7 +368,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];

Expand All @@ -362,7 +377,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 };
};
Expand Down
12 changes: 12 additions & 0 deletions apps/editor/src/utils/common.ts
Expand Up @@ -257,3 +257,15 @@ export function assign(targetObj: Record<string, any>, obj: Record<string, any>
export function getSortedNumPair(valueA: number, valueB: number) {
return valueA > valueB ? [valueB, valueA] : [valueA, valueB];
}

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);
}
2 changes: 2 additions & 0 deletions apps/editor/src/utils/constants.ts
Expand Up @@ -20,3 +20,5 @@ export const reBR = /<br\s*\/*>/i;
export const reHTMLComment = /<! ---->|<!--(?:-?[^>-])(?:-?[^-])*-->/;

export const ALTERNATIVE_TAG_FOR_BR = '</p><p>';

export const DEFAULT_TEXT_NOT_START_OR_END_WITH_SPACE = 'a';
6 changes: 4 additions & 2 deletions apps/editor/types/convertor.d.ts
Expand Up @@ -111,7 +111,8 @@ export type ToMdNodeTypeConvertorMap = Partial<Record<WwNodeType, ToMdNodeTypeCo

type ToMdMarkTypeConvertor = (
nodeInfo?: MarkInfo,
entering?: boolean
entering?: boolean,
betweenSpace?: boolean
) => ToMdConvertorReturnValues & ToMdMarkTypeOption;

export type ToMdMarkTypeConvertorMap = Partial<Record<WwMarkType, ToMdMarkTypeConvertor>>;
Expand All @@ -124,7 +125,8 @@ interface ToMdConvertorContext {

type ToMdConvertor = (
nodeInfo: NodeInfo | MarkInfo,
context: ToMdConvertorContext
context: ToMdConvertorContext,
betweenSpace?: boolean
) => ToMdConvertorReturnValues;

export type ToMdConvertorMap = Partial<Record<WwNodeType | MdNodeType, ToMdConvertor>>;
Expand Down

0 comments on commit 66db3f8

Please sign in to comment.