Skip to content

Commit

Permalink
feat: Integrate input rules and paste rules into the core (#1997)
Browse files Browse the repository at this point in the history
* refactoring

* improve link regex

* WIP: add new markPasteRule und linkify to image mark

* move copy of inputrule to core

* trigger codeblock inputrule on enter

* refactoring

* add regex match to markpasterulematch

* refactoring

* improve link regex

* WIP: add new markPasteRule und linkify to image mark

* move copy of inputrule to core

* trigger codeblock inputrule on enter

* refactoring

* add regex match to markpasterulematch

* update linkify

* wip

* wip

* log

* wip

* remove debug code

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* rename matcher

* add data to ExtendedRegExpMatchArray

* remove logging

* add code option to marks, prevent inputrules in code mark

* remove link regex

* fix codeblock inputrule on enter

* refactoring

* refactoring

* refactoring

* refactoring

* fix position bug

* add test

* export InputRule and PasteRule

* clean up link demo

* fix types
  • Loading branch information
philippkuehn authored Oct 8, 2021
1 parent ace4964 commit 723b955
Show file tree
Hide file tree
Showing 57 changed files with 1,138 additions and 371 deletions.
1 change: 0 additions & 1 deletion demos/includeDependencies.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ prosemirror-commands
prosemirror-dropcursor
prosemirror-gapcursor
prosemirror-history
prosemirror-inputrules
prosemirror-keymap
prosemirror-model
prosemirror-schema-list
Expand Down
4 changes: 1 addition & 3 deletions demos/src/Marks/Link/Vue/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import Document from '@tiptap/extension-document'
import Paragraph from '@tiptap/extension-paragraph'
import Text from '@tiptap/extension-text'
import Link from '@tiptap/extension-link'
import Bold from '@tiptap/extension-bold'
export default {
components: {
Expand All @@ -35,14 +34,13 @@ export default {
Document,
Paragraph,
Text,
Bold,
Link.configure({
openOnClick: false,
}),
],
content: `
<p>
Wow, this editor has support for links to the whole <strong><a href="https://en.wikipedia.org/wiki/World_Wide_Web">world wide web</a></strong>. We tested a lot of URLs and I think you can add *every URL* you want. Isn’t that cool? Let’s try <a href="https://statamic.com/">another one!</a> Yep, seems to work.
Wow, this editor has support for links to the whole <a href="https://en.wikipedia.org/wiki/World_Wide_Web">world wide web</a>. We tested a lot of URLs and I think you can add *every URL* you want. Isn’t that cool? Let’s try <a href="https://statamic.com/">another one!</a> Yep, seems to work.
</p>
<p>
By default every link will get a \`rel="noopener noreferrer nofollow"\` attribute. It’s configurable though.
Expand Down
11 changes: 11 additions & 0 deletions demos/src/Nodes/CodeBlock/Vue/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,17 @@ context('/src/Nodes/CodeBlock/Vue/', () => {
})
})

it('should make a code block from backtick markdown shortcuts followed by enter', () => {
cy.get('.ProseMirror').then(([{ editor }]) => {
editor.commands.clearContent()

cy.get('.ProseMirror')
.type('```{enter}Code')
.find('pre>code')
.should('contain', 'Code')
})
})

it('reverts the markdown shortcut when pressing backspace', () => {
cy.get('.ProseMirror').then(([{ editor }]) => {
editor.commands.clearContent()
Expand Down
2 changes: 0 additions & 2 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,13 @@
],
"dependencies": {
"@types/prosemirror-commands": "^1.0.4",
"@types/prosemirror-inputrules": "^1.0.4",
"@types/prosemirror-keymap": "^1.0.4",
"@types/prosemirror-model": "^1.13.2",
"@types/prosemirror-schema-list": "^1.0.3",
"@types/prosemirror-state": "^1.2.7",
"@types/prosemirror-transform": "^1.1.4",
"@types/prosemirror-view": "^1.19.1",
"prosemirror-commands": "^1.1.11",
"prosemirror-inputrules": "^1.1.3",
"prosemirror-keymap": "^1.1.3",
"prosemirror-model": "^1.14.3",
"prosemirror-schema-list": "^1.1.6",
Expand Down
40 changes: 6 additions & 34 deletions packages/core/src/CommandManager.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { EditorState, Transaction } from 'prosemirror-state'
import { Transaction } from 'prosemirror-state'
import { Editor } from './Editor'
import createChainableState from './helpers/createChainableState'
import {
SingleCommands,
ChainedCommands,
Expand Down Expand Up @@ -106,7 +107,10 @@ export default class CommandManager {
tr,
editor,
view,
state: this.chainableState(tr, state),
state: createChainableState({
state,
transaction: tr,
}),
dispatch: shouldDispatch
? () => undefined
: undefined,
Expand All @@ -124,36 +128,4 @@ export default class CommandManager {
return props
}

public chainableState(tr: Transaction, state: EditorState): EditorState {
let { selection } = tr
let { doc } = tr
let { storedMarks } = tr

return {
...state,
schema: state.schema,
plugins: state.plugins,
apply: state.apply.bind(state),
applyTransaction: state.applyTransaction.bind(state),
reconfigure: state.reconfigure.bind(state),
toJSON: state.toJSON.bind(state),
get storedMarks() {
return storedMarks
},
get selection() {
return selection
},
get doc() {
return doc
},
get tr() {
selection = tr.selection
doc = tr.doc
storedMarks = tr.storedMarks

return tr
},
}
}

}
3 changes: 2 additions & 1 deletion packages/core/src/Editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import getText from './helpers/getText'
import isNodeEmpty from './helpers/isNodeEmpty'
import getTextSeralizersFromSchema from './helpers/getTextSeralizersFromSchema'
import createStyleTag from './utilities/createStyleTag'
import isFunction from './utilities/isFunction'
import CommandManager from './CommandManager'
import ExtensionManager from './ExtensionManager'
import EventEmitter from './EventEmitter'
Expand Down Expand Up @@ -184,7 +185,7 @@ export class Editor extends EventEmitter<EditorEvents> {
* @param handlePlugins Control how to merge the plugin into the existing plugins.
*/
public registerPlugin(plugin: Plugin, handlePlugins?: (newPlugin: Plugin, plugins: Plugin[]) => Plugin[]): void {
const plugins = typeof handlePlugins === 'function'
const plugins = isFunction(handlePlugins)
? handlePlugins(plugin, this.state.plugins)
: [...this.state.plugins, plugin]

Expand Down
5 changes: 3 additions & 2 deletions packages/core/src/Extension.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Plugin, Transaction } from 'prosemirror-state'
import { InputRule } from 'prosemirror-inputrules'
import { InputRule } from './InputRule'
import { PasteRule } from './PasteRule'
import { Editor } from './Editor'
import { Node } from './Node'
import { Mark } from './Mark'
Expand Down Expand Up @@ -81,7 +82,7 @@ declare module '@tiptap/core' {
options: Options,
editor: Editor,
parent: ParentConfig<ExtensionConfig<Options>>['addPasteRules'],
}) => Plugin[],
}) => PasteRule[],

/**
* ProseMirror plugins
Expand Down
27 changes: 16 additions & 11 deletions packages/core/src/ExtensionManager.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { keymap } from 'prosemirror-keymap'
import { Schema, Node as ProsemirrorNode } from 'prosemirror-model'
import { inputRules as inputRulesPlugin } from 'prosemirror-inputrules'
import { inputRulesPlugin } from './InputRule'
import { pasteRulesPlugin } from './PasteRule'
import { EditorView, Decoration } from 'prosemirror-view'
import { Plugin } from 'prosemirror-state'
import { Editor } from './Editor'
Expand Down Expand Up @@ -210,7 +211,12 @@ export default class ExtensionManager {
// so it feels more natural to run plugins at the end of an array first.
// That’s why we have to reverse the `extensions` array and sort again
// based on the `priority` option.
return ExtensionManager.sort([...this.extensions].reverse())
const extensions = ExtensionManager.sort([...this.extensions].reverse())

const inputRules: any[] = []
const pasteRules: any[] = []

const allPlugins = extensions
.map(extension => {
const context = {
name: extension.name,
Expand Down Expand Up @@ -248,12 +254,7 @@ export default class ExtensionManager {
)

if (this.editor.options.enableInputRules && addInputRules) {
const inputRules = addInputRules()
const inputRulePlugins = inputRules.length
? [inputRulesPlugin({ rules: inputRules })]
: []

plugins.push(...inputRulePlugins)
inputRules.push(...addInputRules())
}

const addPasteRules = getExtensionField<AnyConfig['addPasteRules']>(
Expand All @@ -263,9 +264,7 @@ export default class ExtensionManager {
)

if (this.editor.options.enablePasteRules && addPasteRules) {
const pasteRulePlugins = addPasteRules()

plugins.push(...pasteRulePlugins)
pasteRules.push(...addPasteRules())
}

const addProseMirrorPlugins = getExtensionField<AnyConfig['addProseMirrorPlugins']>(
Expand All @@ -283,6 +282,12 @@ export default class ExtensionManager {
return plugins
})
.flat()

return [
inputRulesPlugin(inputRules),
pasteRulesPlugin(pasteRules),
...allPlugins,
]
}

get attributes() {
Expand Down
Loading

0 comments on commit 723b955

Please sign in to comment.