Skip to content

Commit a6dd38e

Browse files
authored
feat: support custom escape regexp (#337)
1 parent 988df7c commit a6dd38e

File tree

5 files changed

+67
-5
lines changed

5 files changed

+67
-5
lines changed

demo/EscapeConfig.stories.tsx

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import React from 'react';
2+
3+
// eslint-disable-next-line import/no-extraneous-dependencies
4+
import type {ComponentMeta, Story} from '@storybook/react';
5+
6+
import {Playground as PlaygroundComponent, PlaygroundProps} from './Playground';
7+
import {parseLocation} from './location';
8+
import {initialMdContent} from './md-content';
9+
10+
export default {
11+
title: 'Markdown Editor',
12+
component: PlaygroundComponent,
13+
} as ComponentMeta<any>;
14+
15+
type PlaygroundStoryProps = Pick<PlaygroundProps, 'initialEditor'> & {
16+
commonEscapeRegexp: string;
17+
startOfLineEscapeRegexp: string;
18+
};
19+
export const EscapeConfig: Story<PlaygroundStoryProps> = ({
20+
commonEscapeRegexp,
21+
startOfLineEscapeRegexp,
22+
...props
23+
}) => (
24+
<PlaygroundComponent
25+
{...props}
26+
initial={parseLocation() || initialMdContent}
27+
escapeConfig={{
28+
commonEscape: new RegExp(commonEscapeRegexp),
29+
startOfLineEscape: new RegExp(startOfLineEscapeRegexp),
30+
}}
31+
/>
32+
);
33+
34+
EscapeConfig.args = {
35+
initialEditor: 'wysiwyg',
36+
commonEscapeRegexp: '^$',
37+
startOfLineEscapeRegexp: '^$',
38+
};

demo/Playground.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import {
1515
useMarkdownEditor,
1616
wysiwygToolbarConfigs,
1717
} from '../src';
18-
import type {ToolbarActionData} from '../src/bundle/Editor';
18+
import type {EscapeConfig, ToolbarActionData} from '../src/bundle/Editor';
1919
import {FoldingHeading} from '../src/extensions/yfm/FoldingHeading';
2020
import {Math} from '../src/extensions/yfm/Math';
2121
import {Mermaid} from '../src/extensions/yfm/Mermaid';
@@ -74,6 +74,7 @@ export type PlaygroundProps = {
7474
initialSplitModeEnabled?: boolean;
7575
renderPreviewDefined?: boolean;
7676
height?: CSSProperties['height'];
77+
escapeConfig?: EscapeConfig;
7778
onChangeEditorType?: (mode: MarkdownEditorMode) => void;
7879
onChangeSplitModeEnabled?: (splitModeEnabled: boolean) => void;
7980
};
@@ -100,6 +101,7 @@ export const Playground = React.memo<PlaygroundProps>((props) => {
100101
stickyToolbar,
101102
renderPreviewDefined,
102103
height,
104+
escapeConfig,
103105
} = props;
104106
const [editorMode, setEditorMode] = React.useState<MarkdownEditorMode>(
105107
initialEditor ?? 'wysiwyg',
@@ -135,6 +137,7 @@ export const Playground = React.memo<PlaygroundProps>((props) => {
135137
initialSplitModeEnabled: initialSplitModeEnabled,
136138
initialToolbarVisible: true,
137139
splitMode: splitModeOrientation,
140+
escapeConfig: escapeConfig,
138141
renderPreview: renderPreviewDefined ? renderPreview : undefined,
139142
fileUploadHandler,
140143
prepareRawMarkup: prepareRawMarkup

src/bundle/Editor.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,11 @@ export type MarkupConfig = {
126126
languageData?: YfmLangOptions['languageData'];
127127
};
128128

129+
export type EscapeConfig = {
130+
commonEscape?: RegExp;
131+
startOfLineEscape?: RegExp;
132+
};
133+
129134
export type EditorOptions = Pick<
130135
WysiwygEditorOptions,
131136
'allowHTML' | 'linkify' | 'linkifyTlds' | 'extensions'
@@ -158,6 +163,7 @@ export type EditorOptions = Pick<
158163
/** @deprecated Put extra extensions via MarkdownEditorMarkupConfig */
159164
extraMarkupExtensions?: CodemirrorExtension[];
160165
markupConfig?: MarkupConfig;
166+
escapeConfig?: EscapeConfig;
161167
};
162168

163169
/** @internal */
@@ -171,6 +177,7 @@ export class EditorImpl extends SafeEventEmitter<EventMapInt> implements EditorI
171177
#wysiwygEditor?: WysiwygEditor;
172178
#markupEditor?: MarkupEditor;
173179
#markupConfig: MarkupConfig;
180+
#escapeConfig?: EscapeConfig;
174181

175182
readonly #preset: EditorPreset;
176183
#allowHTML?: boolean;
@@ -272,6 +279,7 @@ export class EditorImpl extends SafeEventEmitter<EventMapInt> implements EditorI
272279
allowHTML: this.#allowHTML,
273280
linkify: this.#linkify,
274281
linkifyTlds: this.#linkifyTlds,
282+
escapeConfig: this.#escapeConfig,
275283
onChange: () => this.emit('rerender-toolbar', null),
276284
onDocChange: () => this.emit('change', null),
277285
});
@@ -340,6 +348,7 @@ export class EditorImpl extends SafeEventEmitter<EventMapInt> implements EditorI
340348
opts.needToSetDimensionsForUploadedImages,
341349
);
342350
this.#prepareRawMarkup = opts.prepareRawMarkup;
351+
this.#escapeConfig = opts.escapeConfig;
343352
}
344353

345354
// ---> implements CodeEditor
@@ -483,6 +492,7 @@ export class EditorImpl extends SafeEventEmitter<EventMapInt> implements EditorI
483492
private shouldReplaceMarkupEditorValue(markupValue: string, wysiwygValue: string) {
484493
const serializedEditorMarkup = this.#wysiwygEditor?.serializer.serialize(
485494
this.#wysiwygEditor.parser.parse(markupValue),
495+
this.#escapeConfig,
486496
);
487497
return serializedEditorMarkup?.trim() !== wysiwygValue.trim();
488498
}

src/core/Editor.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ import {logTransactionMetrics} from './utils/metrics';
1616

1717
type OnChange = (editor: WysiwygEditor) => void;
1818

19+
type EscapeConfig = {
20+
commonEscape?: RegExp;
21+
startOfLineEscape?: RegExp;
22+
};
23+
1924
export type WysiwygEditorOptions = {
2025
domElem?: Element;
2126
/** markdown markup */
@@ -26,6 +31,7 @@ export type WysiwygEditorOptions = {
2631
allowHTML?: boolean;
2732
linkify?: boolean;
2833
linkifyTlds?: string | string[];
34+
escapeConfig?: EscapeConfig;
2935
/** Call on any state change (move cursor, change selection, etc...) */
3036
onChange?: OnChange;
3137
/** Call only if document change */
@@ -38,6 +44,7 @@ export class WysiwygEditor implements CommonEditor, ActionStorage {
3844
#parser: Parser;
3945
#actions: ActionsManager;
4046
#contentHandler: ContentHandler;
47+
#escapeConfig?: EscapeConfig;
4148

4249
get dom() {
4350
return this.#view.dom;
@@ -68,6 +75,7 @@ export class WysiwygEditor implements CommonEditor, ActionStorage {
6875
mdPreset,
6976
linkify,
7077
linkifyTlds,
78+
escapeConfig,
7179
onChange,
7280
onDocChange,
7381
}: WysiwygEditorOptions) {
@@ -117,6 +125,7 @@ export class WysiwygEditor implements CommonEditor, ActionStorage {
117125
this.#serializer = serializer;
118126
this.#parser = parser;
119127
this.#contentHandler = new WysiwygContentHandler(this.#view, parser);
128+
this.#escapeConfig = escapeConfig;
120129
}
121130

122131
action<T extends keyof WysiwygEditor.Actions>(actionName: T): WysiwygEditor.Actions[T] {
@@ -132,7 +141,7 @@ export class WysiwygEditor implements CommonEditor, ActionStorage {
132141
}
133142

134143
getValue(): MarkupString {
135-
return this.#serializer.serialize(this.#view.state.doc);
144+
return this.#serializer.serialize(this.#view.state.doc, this.#escapeConfig);
136145
}
137146

138147
isEmpty(): boolean {

src/core/markdown/MarkdownSerializer.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -296,9 +296,11 @@ export class MarkdownSerializerState {
296296
// content. If `startOfLine` is true, also escape characters that
297297
// have special meaning only at the start of the line.
298298
esc(str, startOfLine) {
299-
// TODO: add a setting which characters need to be escaped
300-
str = str.replace(/[`\^+*\\\|~\[\]\{\}<>\$]/g, '\\$&');
301-
if (startOfLine) str = str.replace(/^[:#\-*+>]/, '\\$&').replace(/^(\s*\d+)\./, '$1\\.');
299+
const escRegexp = this.options?.commonEscape || /[`\^+*\\\|~\[\]\{\}<>\$]/g;
300+
const startOfLineEscRegexp = this.options?.startOfLineEscape || /^[:#\-*+>]/;
301+
302+
str = str.replace(escRegexp, '\\$&');
303+
if (startOfLine) str = str.replace(startOfLineEscRegexp, '\\$&').replace(/^(\s*\d+)\./, '$1\\.');
302304
return str;
303305
}
304306

0 commit comments

Comments
 (0)