Skip to content

Commit

Permalink
Restore MarkdownSerializerState + add test
Browse files Browse the repository at this point in the history
  • Loading branch information
antoine committed Jun 8, 2023
1 parent b4871cc commit 5efc72f
Show file tree
Hide file tree
Showing 6 changed files with 137 additions and 1 deletion.
4 changes: 4 additions & 0 deletions __tests__/serialize.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ describe('serialize', () => {
expect(serialize('My <strong> example </strong>')).toEqual('My **example** ');
expect(serialize('My <em> example </em>')).toEqual('My *example* ');
});
test('trim inline', () => {
expect(serialize('My<strong>, example</strong>')).toEqual('My, **example**');
expect(serialize('My<em>. example</em>')).toEqual('My. *example*');
});
});
describe('nodes', () => {
test('paragraph', () => {
Expand Down
22 changes: 22 additions & 0 deletions __tests__/util.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { shiftDelim, trimInline } from "../src/util/markdown";

describe('util' ,() => {
describe('trimInline', () => {
test('full', () => {
expect(trimInline('*abcde*', 0, 6)).toEqual('*abcde*');
});
test('left', () => {
expect(trimInline('a*, bcde*', '*', 1, 8)).toEqual('a, *bcde*');
});
test('right', () => {
expect(trimInline('*abcd ,*e', '*', 0, 7)).toEqual('*abcd* ,e');
});
test('meet', () => {
expect(trimInline('e*,,*e', '*', 1, 4)).toEqual('e,,e');
});
});
test('shiftDelim', () => {
expect(shiftDelim('e**abcd', '**', 1, 1)).toEqual('ea**bcd');
expect(shiftDelim('e**abcd', '**', 1, -1)).toEqual('**eabcd');
})
})
1 change: 1 addition & 0 deletions example/src/content.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ ___

***

Aenean ut eros et nisl sagittis vestibulum. Donec vitae orci sed dolor rutrum auctor. Vestibulum rutrum, mi nec elementum vehicula, eros quam gravida nisl, id fringilla neque ante vel mi.

## Emphasis

Expand Down
2 changes: 1 addition & 1 deletion src/serialize/MarkdownSerializer.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { MarkdownSerializerState } from "prosemirror-markdown";
import { MarkdownSerializerState } from './state';
import HTMLMark from "../extensions/marks/html";
import HTMLNode from "../extensions/nodes/html";
import { getMarkdownSpec } from "../util/extensions";
Expand Down
55 changes: 55 additions & 0 deletions src/serialize/state.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { MarkdownSerializerState as BaseMarkdownSerializerState } from "prosemirror-markdown";
import { trimInline } from "../util/markdown";


/**
* Override default MarkdownSerializerState to:
* - handle commonmark delimiters (https://spec.commonmark.org/0.29/#left-flanking-delimiter-run)
*/
export class MarkdownSerializerState extends BaseMarkdownSerializerState {

constructor(nodes, marks, options) {
super(nodes, marks, options ?? {});
this.inlines = [];
}

render(node, parent, index) {
super.render(node, parent, index);
const top = this.inlines[this.inlines.length - 1];
if(top?.start && top?.end) {
const { delimiter, start, end } = this.normalizeInline(top);
this.out = trimInline(this.out, delimiter, start, end);
this.inlines.pop();
}
}

markString(mark, open, parent, index) {
const info = this.marks[mark.type.name]
if(info.expelEnclosingWhitespace) {
if(open) {
this.inlines.push({
start: this.out.length,
delimiter: info.open,
});
} else {
const top = this.inlines.pop();
this.inlines.push({
...top,
end: this.out.length,
});
}
}
return super.markString(mark, open, parent, index);
}

normalizeInline(inline) {
let { start, end } = inline;
while(this.out.charAt(start).match(/\s/)) {
start++;
}
return {
...inline,
start,
}
}
}
54 changes: 54 additions & 0 deletions src/util/markdown.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import markdownit from 'markdown-it';


function scanDelims(text, pos) {
const state = new (markdownit().inline.State)(text, null, null, []);
return state.scanDelims(pos, true);
}

export function shiftDelim(text, delim, start, offset) {
let res = text.substring(0, start) + text.substring(start + delim.length);
res = res.substring(0, start + offset) + delim + res.substring(start + offset);
return res;
}

function trimStart(text, delim, from, to) {
let pos = from, res = text;
while(pos < to) {
if(scanDelims(res, pos).can_open) {
break;
}
res = shiftDelim(res, delim, pos, 1);
pos++;
}
return { text: res, from: pos, to }
}

function trimEnd(text, delim, from, to) {
let pos = to, res = text;
while(pos > from) {
if(scanDelims(res, pos).can_close) {
break;
}
res = shiftDelim(res, delim, pos, -1);
pos--;
}
return { text: res, from, to: pos }
}

export function trimInline(text, delim, from, to) {
let state = {
text,
from,
to,
}

state = trimStart(state.text, delim, state.from, state.to);
state = trimEnd(state.text, delim, state.from, state.to);

if(state.to - state.from < delim.length + 1) {
state.text = state.text.substring(0, state.from) + state.text.substring(state.to + delim.length);
}

return state.text;
}

0 comments on commit 5efc72f

Please sign in to comment.