diff --git a/src/extensions/base/BaseInputRules/BaseInputRules.test.ts b/src/extensions/base/BaseInputRules/BaseInputRules.test.ts
new file mode 100644
index 00000000..7ec9f761
--- /dev/null
+++ b/src/extensions/base/BaseInputRules/BaseInputRules.test.ts
@@ -0,0 +1,63 @@
+import {EditorState, TextSelection} from 'prosemirror-state';
+import {builders} from 'prosemirror-test-builder';
+import {EditorView} from 'prosemirror-view';
+
+import {ExtensionsManager} from '../../../core';
+import {CodeSpecs, codeMarkName} from '../../markdown/Code/CodeSpecs';
+import {BaseNode, BaseSchemaSpecs} from '../specs';
+
+import {BaseInputRules} from './index';
+
+const {schema, plugins} = new ExtensionsManager({
+ extensions: (builder) => builder.use(BaseSchemaSpecs, {}).use(CodeSpecs).use(BaseInputRules),
+}).build();
+
+const {doc, p, c} = builders<'doc' | 'p', 'c'>(schema, {
+ doc: {nodeType: BaseNode.Doc},
+ p: {nodeType: BaseNode.Paragraph},
+ c: {markType: codeMarkName},
+});
+
+describe('BaseInputRules ellipsis', () => {
+ it('does not replace triple dots inside inline code', () => {
+ const startDoc = doc(p(c('text')));
+ const state = EditorState.create({
+ schema,
+ doc: startDoc,
+ selection: TextSelection.create(startDoc, startDoc.tag.a),
+ plugins,
+ });
+ const view = new EditorView(document.createElement('div'), {state});
+
+ const handled = view.someProp('handleTextInput', (f: any) =>
+ f(view, startDoc.tag.a, startDoc.tag.a, '...'),
+ );
+
+ if (!handled) {
+ view.dispatch(view.state.tr.insertText('...', startDoc.tag.a, startDoc.tag.a));
+ }
+
+ expect(view.state.doc).toMatchNode(doc(p(c('text...'))));
+ });
+
+ it('replaces triple dots in regular text', () => {
+ const startDoc = doc(p('text'));
+ const state = EditorState.create({
+ schema,
+ doc: startDoc,
+ selection: TextSelection.create(startDoc, startDoc.tag.a),
+ plugins,
+ });
+ const view = new EditorView(document.createElement('div'), {state});
+
+ const handled = view.someProp('handleTextInput', (f: any) =>
+ f(view, startDoc.tag.a, startDoc.tag.a, '...'),
+ );
+
+ if (!handled) {
+ view.dispatch(view.state.tr.insertText('...', startDoc.tag.a, startDoc.tag.a));
+ }
+
+ expect(view.state.doc).toMatchNode(doc(p('text…')));
+ });
+});
diff --git a/src/extensions/base/BaseInputRules/index.ts b/src/extensions/base/BaseInputRules/index.ts
index b9ed7e02..c45f8a06 100644
--- a/src/extensions/base/BaseInputRules/index.ts
+++ b/src/extensions/base/BaseInputRules/index.ts
@@ -1,7 +1,13 @@
-import {ellipsis} from 'prosemirror-inputrules';
+import {InputRule} from 'prosemirror-inputrules';
import type {ExtensionAuto} from '../../../core';
+import {hasCodeMark} from '../../../utils/inputrules';
+
+const ellipsisInputRule = new InputRule(/\.\.\.$/, (state, match, start, end) => {
+ if (hasCodeMark(state, match, start, end)) return null;
+ return state.tr.insertText('…', start, end);
+});
export const BaseInputRules: ExtensionAuto = (builder) => {
- builder.addInputRules(() => ({rules: [ellipsis]}));
+ builder.addInputRules(() => ({rules: [ellipsisInputRule]}));
};