Skip to content

Commit

Permalink
fix: non-spec emphasis (fix nhn#1806 nhn#2708)
Browse files Browse the repository at this point in the history
  • Loading branch information
kshnurov committed Sep 19, 2022
1 parent 06355b6 commit 58d92ea
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 53 deletions.
92 changes: 69 additions & 23 deletions apps/editor/src/__test__/unit/convertor.spec.ts
Expand Up @@ -590,7 +590,8 @@ describe('Convertor', () => {
| <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**ed**<ul><li>**mix**ed</li></ul></li></ol> |
| <ol><li>mix<i>ed</i><ul><li><strong>mix</strong>ed</li></ul></li></ol> |
| <ol><li>mix*e*d<ul><li>*mix*ed</li></ul></li></ol> |
| <ol><li>mix<em>ed</em><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> |
`;
Expand All @@ -602,8 +603,9 @@ describe('Convertor', () => {
| <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> |
| <ol><li>mix**ed**<ul><li>**mix**ed</li></ul></li></ol> |
| <ol><li>mix*e*d<ul><li>*mix*ed</li></ul></li></ol> |
| <ol><li>mix<em>ed</em><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> |
`;
Expand Down Expand Up @@ -1075,28 +1077,72 @@ 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<em>"test"</em>`);
});

expect(result).toBe(`<strong>"test"</strong>a`);
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(`<strong>"test"</strong>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 convert empty line between lists of wysiwig to <br>', () => {
Expand Down
19 changes: 7 additions & 12 deletions 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 {
Expand All @@ -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;
Expand Down Expand Up @@ -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);
}
Expand Down
16 changes: 8 additions & 8 deletions apps/editor/src/convertors/toMarkdown/toMdConvertors.ts
Expand Up @@ -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 ? '<strong>' : '</strong>';
}

Expand All @@ -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 ? '<em>' : '</em>';
}

Expand All @@ -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 ? '<del>' : '</del>';
}

Expand Down Expand Up @@ -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];

Expand All @@ -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 };
Expand Down
33 changes: 27 additions & 6 deletions apps/editor/src/utils/common.ts
Expand Up @@ -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);
}

2 changes: 0 additions & 2 deletions apps/editor/src/utils/constants.ts
Expand Up @@ -20,5 +20,3 @@ 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';
4 changes: 2 additions & 2 deletions apps/editor/types/convertor.d.ts
Expand Up @@ -114,7 +114,7 @@ export type ToMdNodeTypeConvertorMap = Partial<Record<WwNodeType, ToMdNodeTypeCo
type ToMdMarkTypeConvertor = (
nodeInfo?: MarkInfo,
entering?: boolean,
betweenSpace?: boolean
nonSpecEmphasis?: boolean
) => ToMdConvertorReturnValues & ToMdMarkTypeOption;

export type ToMdMarkTypeConvertorMap = Partial<Record<WwMarkType, ToMdMarkTypeConvertor>>;
Expand All @@ -128,7 +128,7 @@ interface ToMdConvertorContext {
type ToMdConvertor = (
nodeInfo: NodeInfo | MarkInfo,
context: ToMdConvertorContext,
betweenSpace?: boolean
nonSpecEmphasis?: boolean
) => ToMdConvertorReturnValues;

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

0 comments on commit 58d92ea

Please sign in to comment.