@prosekit/extensions 0.12.2 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{drop-indicator-B1QHFb5m.js → drop-indicator-B_oMfeVP.js} +11 -10
- package/dist/drop-indicator-B_oMfeVP.js.map +1 -0
- package/dist/{enter-rule-CzWOZF_Z.js → enter-rule-D-p4ykfv.js} +1 -1
- package/dist/enter-rule-D-p4ykfv.js.map +1 -0
- package/dist/{file-DrfcSid-.js → file-iLVR0eM0.js} +3 -3
- package/dist/file-iLVR0eM0.js.map +1 -0
- package/dist/{index-oIc1a2f2.d.ts → index-cp1u4e0e.d.ts} +1 -1
- package/dist/index-cp1u4e0e.d.ts.map +1 -0
- package/dist/{input-rule-dmsb3j6w.js → input-rule-COGr_GBb.js} +1 -1
- package/dist/input-rule-COGr_GBb.js.map +1 -0
- package/dist/list/style.css +5 -5
- package/dist/list/style.css.map +1 -1
- package/dist/{mark-rule-BcLB4Uv2.js → mark-rule-CYe8zk4q.js} +6 -6
- package/dist/mark-rule-CYe8zk4q.js.map +1 -0
- package/dist/{paste-rule-pVb4sqvJ.js → paste-rule-BaDghcaU.js} +7 -7
- package/dist/paste-rule-BaDghcaU.js.map +1 -0
- package/dist/prosekit-extensions-autocomplete.d.ts +11 -3
- package/dist/prosekit-extensions-autocomplete.d.ts.map +1 -1
- package/dist/prosekit-extensions-autocomplete.js +171 -60
- package/dist/prosekit-extensions-autocomplete.js.map +1 -1
- package/dist/prosekit-extensions-background-color.d.ts +62 -0
- package/dist/prosekit-extensions-background-color.d.ts.map +1 -0
- package/dist/prosekit-extensions-background-color.js +76 -0
- package/dist/prosekit-extensions-background-color.js.map +1 -0
- package/dist/prosekit-extensions-blockquote.d.ts.map +1 -1
- package/dist/prosekit-extensions-blockquote.js +2 -2
- package/dist/prosekit-extensions-blockquote.js.map +1 -1
- package/dist/prosekit-extensions-bold.d.ts.map +1 -1
- package/dist/prosekit-extensions-bold.js +1 -1
- package/dist/prosekit-extensions-bold.js.map +1 -1
- package/dist/prosekit-extensions-code-block.d.ts +1 -1
- package/dist/prosekit-extensions-code-block.d.ts.map +1 -1
- package/dist/prosekit-extensions-code-block.js +4 -4
- package/dist/prosekit-extensions-code-block.js.map +1 -1
- package/dist/prosekit-extensions-code.d.ts.map +1 -1
- package/dist/prosekit-extensions-code.js +1 -1
- package/dist/prosekit-extensions-code.js.map +1 -1
- package/dist/prosekit-extensions-commit.d.ts +0 -1
- package/dist/prosekit-extensions-commit.d.ts.map +1 -1
- package/dist/prosekit-extensions-commit.js +1 -1
- package/dist/prosekit-extensions-commit.js.map +1 -1
- package/dist/prosekit-extensions-doc.d.ts +0 -1
- package/dist/prosekit-extensions-doc.d.ts.map +1 -1
- package/dist/prosekit-extensions-doc.js.map +1 -1
- package/dist/prosekit-extensions-drop-cursor.d.ts.map +1 -1
- package/dist/prosekit-extensions-drop-cursor.js.map +1 -1
- package/dist/prosekit-extensions-drop-indicator.d.ts +0 -1
- package/dist/prosekit-extensions-drop-indicator.d.ts.map +1 -1
- package/dist/prosekit-extensions-drop-indicator.js +1 -1
- package/dist/prosekit-extensions-enter-rule.d.ts +0 -1
- package/dist/prosekit-extensions-enter-rule.d.ts.map +1 -1
- package/dist/prosekit-extensions-enter-rule.js +1 -1
- package/dist/prosekit-extensions-file.d.ts +1 -1
- package/dist/prosekit-extensions-file.js +1 -1
- package/dist/prosekit-extensions-gap-cursor.d.ts +0 -1
- package/dist/prosekit-extensions-gap-cursor.d.ts.map +1 -1
- package/dist/prosekit-extensions-gap-cursor.js.map +1 -1
- package/dist/prosekit-extensions-hard-break.d.ts.map +1 -1
- package/dist/prosekit-extensions-hard-break.js.map +1 -1
- package/dist/prosekit-extensions-heading.d.ts.map +1 -1
- package/dist/prosekit-extensions-heading.js +7 -7
- package/dist/prosekit-extensions-heading.js.map +1 -1
- package/dist/prosekit-extensions-horizontal-rule.d.ts.map +1 -1
- package/dist/prosekit-extensions-horizontal-rule.js +1 -1
- package/dist/prosekit-extensions-horizontal-rule.js.map +1 -1
- package/dist/prosekit-extensions-image.d.ts +9 -2
- package/dist/prosekit-extensions-image.d.ts.map +1 -1
- package/dist/prosekit-extensions-image.js +17 -4
- package/dist/prosekit-extensions-image.js.map +1 -1
- package/dist/prosekit-extensions-input-rule.d.ts +0 -1
- package/dist/prosekit-extensions-input-rule.d.ts.map +1 -1
- package/dist/prosekit-extensions-input-rule.js +1 -1
- package/dist/prosekit-extensions-italic.d.ts.map +1 -1
- package/dist/prosekit-extensions-italic.js +1 -1
- package/dist/prosekit-extensions-italic.js.map +1 -1
- package/dist/prosekit-extensions-link.d.ts +0 -1
- package/dist/prosekit-extensions-link.d.ts.map +1 -1
- package/dist/prosekit-extensions-link.js +4 -4
- package/dist/prosekit-extensions-link.js.map +1 -1
- package/dist/prosekit-extensions-list.d.ts +0 -1
- package/dist/prosekit-extensions-list.d.ts.map +1 -1
- package/dist/prosekit-extensions-list.js +3 -3
- package/dist/prosekit-extensions-list.js.map +1 -1
- package/dist/prosekit-extensions-loro.d.ts +16 -17
- package/dist/prosekit-extensions-loro.d.ts.map +1 -1
- package/dist/prosekit-extensions-loro.js +14 -7
- package/dist/prosekit-extensions-loro.js.map +1 -1
- package/dist/prosekit-extensions-mark-rule.d.ts +0 -1
- package/dist/prosekit-extensions-mark-rule.d.ts.map +1 -1
- package/dist/prosekit-extensions-mark-rule.js +1 -1
- package/dist/prosekit-extensions-mention.d.ts.map +1 -1
- package/dist/prosekit-extensions-mention.js.map +1 -1
- package/dist/prosekit-extensions-mod-click-prevention.d.ts +0 -1
- package/dist/prosekit-extensions-mod-click-prevention.d.ts.map +1 -1
- package/dist/prosekit-extensions-mod-click-prevention.js.map +1 -1
- package/dist/prosekit-extensions-paragraph.d.ts.map +1 -1
- package/dist/prosekit-extensions-paragraph.js +1 -1
- package/dist/prosekit-extensions-paragraph.js.map +1 -1
- package/dist/prosekit-extensions-paste-rule.d.ts +0 -1
- package/dist/prosekit-extensions-paste-rule.d.ts.map +1 -1
- package/dist/prosekit-extensions-paste-rule.js +1 -1
- package/dist/prosekit-extensions-placeholder.d.ts.map +1 -1
- package/dist/prosekit-extensions-placeholder.js +3 -4
- package/dist/prosekit-extensions-placeholder.js.map +1 -1
- package/dist/prosekit-extensions-readonly.d.ts +0 -1
- package/dist/prosekit-extensions-readonly.d.ts.map +1 -1
- package/dist/prosekit-extensions-readonly.js.map +1 -1
- package/dist/prosekit-extensions-search.d.ts +0 -1
- package/dist/prosekit-extensions-search.d.ts.map +1 -1
- package/dist/prosekit-extensions-search.js.map +1 -1
- package/dist/prosekit-extensions-strike.d.ts +0 -1
- package/dist/prosekit-extensions-strike.d.ts.map +1 -1
- package/dist/prosekit-extensions-strike.js +3 -3
- package/dist/prosekit-extensions-strike.js.map +1 -1
- package/dist/prosekit-extensions-table.d.ts.map +1 -1
- package/dist/prosekit-extensions-table.js +1 -2
- package/dist/prosekit-extensions-text-align.d.ts +0 -1
- package/dist/prosekit-extensions-text-align.d.ts.map +1 -1
- package/dist/prosekit-extensions-text-align.js +6 -6
- package/dist/prosekit-extensions-text-align.js.map +1 -1
- package/dist/prosekit-extensions-text-color.d.ts +62 -0
- package/dist/prosekit-extensions-text-color.d.ts.map +1 -0
- package/dist/prosekit-extensions-text-color.js +76 -0
- package/dist/prosekit-extensions-text-color.js.map +1 -0
- package/dist/prosekit-extensions-text.d.ts +0 -1
- package/dist/prosekit-extensions-text.d.ts.map +1 -1
- package/dist/prosekit-extensions-text.js.map +1 -1
- package/dist/prosekit-extensions-underline.d.ts +0 -1
- package/dist/prosekit-extensions-underline.d.ts.map +1 -1
- package/dist/prosekit-extensions-virtual-selection.d.ts +0 -1
- package/dist/prosekit-extensions-virtual-selection.d.ts.map +1 -1
- package/dist/prosekit-extensions-virtual-selection.js.map +1 -1
- package/dist/prosekit-extensions-yjs.d.ts +9 -2
- package/dist/prosekit-extensions-yjs.d.ts.map +1 -1
- package/dist/prosekit-extensions-yjs.js +1 -1
- package/dist/prosekit-extensions-yjs.js.map +1 -1
- package/dist/{shiki-highlighter-chunk-rkzofy4z.d.ts → shiki-highlighter-chunk-DNNm2Vow.d.ts} +1 -1
- package/dist/shiki-highlighter-chunk-DNNm2Vow.d.ts.map +1 -0
- package/dist/shiki-highlighter-chunk.d.ts +1 -1
- package/dist/shiki-highlighter-chunk.js.map +1 -1
- package/dist/{table-BRDh_9mG.js → table-4oHfV-Ql.js} +2 -2
- package/dist/table-4oHfV-Ql.js.map +1 -0
- package/package.json +33 -18
- package/src/autocomplete/autocomplete-helpers.ts +19 -14
- package/src/autocomplete/autocomplete-plugin.ts +260 -126
- package/src/autocomplete/autocomplete-rule.ts +3 -3
- package/src/autocomplete/autocomplete.spec.ts +244 -40
- package/src/autocomplete/autocomplete.ts +9 -7
- package/src/background-color/background-color-commands.spec.ts +71 -0
- package/src/background-color/background-color-commands.ts +35 -0
- package/src/background-color/background-color-spec.spec.ts +286 -0
- package/src/background-color/background-color-spec.ts +58 -0
- package/src/background-color/background-color.ts +21 -0
- package/src/background-color/index.ts +8 -0
- package/src/blockquote/blockquote-commands.ts +1 -7
- package/src/blockquote/blockquote-keymap.spec.ts +5 -9
- package/src/blockquote/blockquote-keymap.ts +2 -7
- package/src/blockquote/blockquote-spec.ts +1 -4
- package/src/blockquote/blockquote.ts +3 -12
- package/src/blockquote/index.ts +3 -12
- package/src/bold/bold-commands.ts +1 -5
- package/src/bold/bold-input-rule.spec.ts +1 -5
- package/src/bold/bold-input-rule.ts +1 -4
- package/src/bold/bold-keymap.ts +1 -5
- package/src/bold/bold-spec.ts +1 -4
- package/src/bold/bold.ts +3 -12
- package/src/bold/index.ts +3 -12
- package/src/code/code-commands.ts +1 -5
- package/src/code/code-input-rule.ts +1 -4
- package/src/code/code-keymap.ts +1 -5
- package/src/code/code-spec.ts +1 -4
- package/src/code/code.ts +3 -12
- package/src/code/index.ts +3 -12
- package/src/code-block/code-block-commands.ts +1 -8
- package/src/code-block/code-block-highlight.ts +2 -8
- package/src/code-block/code-block-keymap.ts +2 -9
- package/src/code-block/code-block-shiki.ts +1 -4
- package/src/code-block/code-block-spec.spec.ts +4 -11
- package/src/code-block/code-block-spec.ts +1 -4
- package/src/code-block/code-block.ts +4 -16
- package/src/code-block/index.ts +5 -21
- package/src/code-block/shiki-highlighter-chunk.ts +1 -7
- package/src/code-block/shiki-highlighter.ts +1 -4
- package/src/code-block/shiki-parser.ts +1 -4
- package/src/commit/index.ts +7 -36
- package/src/doc/index.ts +1 -4
- package/src/drop-cursor/drop-cursor.ts +1 -4
- package/src/drop-cursor/index.ts +1 -5
- package/src/drop-indicator/drop-indicator-facet.ts +12 -21
- package/src/drop-indicator/index.ts +1 -5
- package/src/enter-rule/index.ts +2 -11
- package/src/file/file-paste-handler.spec.ts +3 -16
- package/src/file/index.ts +3 -16
- package/src/gap-cursor/gap-cursor.ts +1 -4
- package/src/gap-cursor/index.ts +1 -4
- package/src/hard-break/hard-break-commands.ts +1 -5
- package/src/hard-break/hard-break-keymap.spec.ts +6 -12
- package/src/hard-break/hard-break-keymap.ts +1 -4
- package/src/hard-break/hard-break-spec.ts +1 -4
- package/src/hard-break/hard-break.ts +3 -12
- package/src/hard-break/index.ts +3 -12
- package/src/heading/heading-commands.ts +1 -7
- package/src/heading/heading-keymap.spec.ts +8 -12
- package/src/heading/heading-keymap.ts +7 -14
- package/src/heading/heading-spec.ts +1 -4
- package/src/heading/heading.ts +3 -12
- package/src/heading/index.ts +3 -12
- package/src/horizontal-rule/horizontal-rule-commands.spec.ts +1 -5
- package/src/horizontal-rule/horizontal-rule-commands.ts +2 -9
- package/src/horizontal-rule/horizontal-rule-input-rule.spec.ts +5 -9
- package/src/horizontal-rule/horizontal-rule-input-rule.ts +1 -5
- package/src/horizontal-rule/horizontal-rule-spec.ts +1 -4
- package/src/horizontal-rule/horizontal-rule.ts +3 -12
- package/src/horizontal-rule/index.ts +3 -13
- package/src/image/image-commands/upload-image.spec.ts +245 -0
- package/src/image/image-commands/upload-image.ts +46 -11
- package/src/image/image-commands.ts +2 -8
- package/src/image/image-spec.ts +1 -4
- package/src/image/image-upload-handler.ts +2 -8
- package/src/image/image.ts +3 -12
- package/src/image/index.ts +3 -13
- package/src/input-rule/index.ts +2 -13
- package/src/italic/index.ts +3 -12
- package/src/italic/italic-commands.spec.ts +2 -10
- package/src/italic/italic-commands.ts +1 -5
- package/src/italic/italic-input-rule.spec.ts +1 -5
- package/src/italic/italic-input-rule.ts +1 -4
- package/src/italic/italic-keymap.ts +1 -5
- package/src/italic/italic-spec.ts +1 -4
- package/src/italic/italic.ts +3 -12
- package/src/link/index.spec.ts +10 -13
- package/src/link/index.ts +1 -5
- package/src/link/link-paste-rule.spec.ts +2 -9
- package/src/link/link-regex.spec.ts +1 -5
- package/src/list/index.ts +3 -12
- package/src/list/list-commands.ts +1 -5
- package/src/list/list-input-rules.ts +1 -4
- package/src/list/list-keymap.spec.ts +6 -10
- package/src/list/list-keymap.ts +2 -8
- package/src/list/list-plugins.ts +1 -4
- package/src/list/list-serializer.ts +2 -9
- package/src/list/list-spec.ts +3 -13
- package/src/list/list.spec.ts +10 -21
- package/src/list/list.ts +3 -12
- package/src/list/style.css +5 -5
- package/src/loro/index.ts +3 -13
- package/src/loro/loro-commands.ts +2 -8
- package/src/loro/loro-cursor-plugin.ts +21 -21
- package/src/loro/loro-keymap.ts +3 -11
- package/src/loro/loro-sync-plugin.ts +2 -8
- package/src/loro/loro-undo-plugin.ts +2 -8
- package/src/loro/loro.ts +16 -21
- package/src/mark-rule/apply.ts +3 -13
- package/src/mark-rule/mark-rule.spec.ts +2 -13
- package/src/mark-rule/mark-rule.ts +2 -13
- package/src/mark-rule/range.ts +2 -8
- package/src/mark-rule/types.ts +1 -4
- package/src/mention/index.ts +1 -8
- package/src/mod-click-prevention/index.ts +2 -9
- package/src/paragraph/paragraph-commands.ts +1 -5
- package/src/paragraph/paragraph-keymap.ts +2 -5
- package/src/paragraph/paragraph-spec.ts +1 -4
- package/src/paragraph/paragraph.ts +3 -14
- package/src/paste-rule/index.ts +2 -10
- package/src/paste-rule/mark-paste-rule.spec.ts +3 -13
- package/src/paste-rule/mark-paste-rule.ts +4 -14
- package/src/paste-rule/paste-rule-plugin.ts +2 -11
- package/src/paste-rule/paste-rule.spec.ts +4 -19
- package/src/paste-rule/split-text-by-regex.spec.ts +1 -5
- package/src/placeholder/index.ts +4 -16
- package/src/readonly/index.ts +2 -8
- package/src/search/index.ts +1 -6
- package/src/strike/index.ts +2 -2
- package/src/table/index.ts +10 -40
- package/src/table/table-commands/delete-cell-selection.spec.ts +1 -5
- package/src/table/table-commands/exit-table.spec.ts +1 -5
- package/src/table/table-commands/insert-table.spec.ts +1 -5
- package/src/table/table-commands/insert-table.ts +1 -4
- package/src/table/table-commands/move-table-column.spec.ts +1 -5
- package/src/table/table-commands/move-table-column.ts +1 -4
- package/src/table/table-commands/move-table-row.spec.ts +1 -5
- package/src/table/table-commands/move-table-row.ts +1 -4
- package/src/table/table-commands/select-table-cell.spec.ts +1 -5
- package/src/table/table-commands/select-table-column.spec.ts +1 -5
- package/src/table/table-commands/select-table-row.spec.ts +1 -5
- package/src/table/table-commands/select-table.spec.ts +1 -5
- package/src/table/table-commands/select-table.ts +1 -4
- package/src/table/table-commands.ts +8 -32
- package/src/table/table-plugins.ts +2 -8
- package/src/table/table-spec.spec.ts +2 -11
- package/src/table/table-spec.ts +2 -8
- package/src/table/table-utils.ts +2 -6
- package/src/table/table.ts +2 -8
- package/src/table/test-utils.ts +1 -4
- package/src/testing/clipboard.ts +1 -2
- package/src/testing/index.ts +9 -14
- package/src/testing/keyboard.ts +0 -30
- package/src/text/index.ts +1 -4
- package/src/text-align/index.ts +6 -6
- package/src/text-color/index.ts +3 -0
- package/src/text-color/text-color-commands.spec.ts +71 -0
- package/src/text-color/text-color-commands.ts +35 -0
- package/src/text-color/text-color-spec.spec.ts +297 -0
- package/src/text-color/text-color-spec.ts +58 -0
- package/src/text-color/text-color.ts +21 -0
- package/src/virtual-selection/index.ts +3 -14
- package/src/yjs/index.ts +5 -20
- package/src/yjs/yjs-commands.ts +2 -8
- package/src/yjs/yjs-cursor-plugin.ts +3 -5
- package/src/yjs/yjs-keymap.ts +3 -11
- package/src/yjs/yjs-sync-plugin.ts +1 -4
- package/src/yjs/yjs-types.ts +10 -0
- package/src/yjs/yjs-undo-plugin.ts +2 -8
- package/src/yjs/yjs.ts +6 -24
- package/dist/drop-indicator-B1QHFb5m.js.map +0 -1
- package/dist/enter-rule-CzWOZF_Z.js.map +0 -1
- package/dist/file-DrfcSid-.js.map +0 -1
- package/dist/index-oIc1a2f2.d.ts.map +0 -1
- package/dist/input-rule-dmsb3j6w.js.map +0 -1
- package/dist/mark-rule-BcLB4Uv2.js.map +0 -1
- package/dist/paste-rule-pVb4sqvJ.js.map +0 -1
- package/dist/shiki-highlighter-chunk-rkzofy4z.d.ts.map +0 -1
- package/dist/table-BRDh_9mG.js.map +0 -1
- package/src/testing/format-html.ts +0 -5
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
import { OBJECT_REPLACEMENT_CHARACTER } from '@prosekit/core'
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
} from '@prosekit/pm/
|
|
7
|
-
import {
|
|
8
|
-
Decoration,
|
|
9
|
-
DecorationSet,
|
|
10
|
-
} from '@prosekit/pm/view'
|
|
2
|
+
import type { ProseMirrorNode, ResolvedPos } from '@prosekit/pm/model'
|
|
3
|
+
import { Plugin, type EditorState, type Transaction } from '@prosekit/pm/state'
|
|
4
|
+
import type { Mapping } from '@prosekit/pm/transform'
|
|
5
|
+
import type { EditorView } from '@prosekit/pm/view'
|
|
6
|
+
import { Decoration, DecorationSet } from '@prosekit/pm/view'
|
|
11
7
|
|
|
12
8
|
import {
|
|
13
9
|
getPluginState,
|
|
@@ -16,9 +12,27 @@ import {
|
|
|
16
12
|
setTrMeta,
|
|
17
13
|
type PredictionPluginMatching,
|
|
18
14
|
type PredictionPluginState,
|
|
15
|
+
type PredictionTransactionMeta,
|
|
19
16
|
} from './autocomplete-helpers'
|
|
20
17
|
import type { AutocompleteRule } from './autocomplete-rule'
|
|
21
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Creates a plugin that handles autocomplete functionality.
|
|
21
|
+
*
|
|
22
|
+
* Workflow:
|
|
23
|
+
*
|
|
24
|
+
* 1. {@link handleTextInput}: called when text is going to be input, but the
|
|
25
|
+
* transaction is not yet created. Injects a new matching as a transaction
|
|
26
|
+
* meta if applicable. This is the only place to create a new matching if
|
|
27
|
+
* there is no existing matching.
|
|
28
|
+
* 2. {@link handleTransaction}: called when a transaction is going to be
|
|
29
|
+
* applied. Updates the plugin state based on the transaction. This step
|
|
30
|
+
* determines if a matching should be created, updated or removed.
|
|
31
|
+
* 3. {@link handleUpdate}: called when the editor state is updated. This is the
|
|
32
|
+
* place to call `onMatch` and register `deleteMatch` and `ignoreMatch`
|
|
33
|
+
* callbacks.
|
|
34
|
+
* 4. {@link getDecorations}: creates the decorations for the current matching.
|
|
35
|
+
*/
|
|
22
36
|
export function createAutocompletePlugin({
|
|
23
37
|
getRules,
|
|
24
38
|
}: {
|
|
@@ -31,139 +45,252 @@ export function createAutocompletePlugin({
|
|
|
31
45
|
init: (): PredictionPluginState => {
|
|
32
46
|
return { ignores: [], matching: null }
|
|
33
47
|
},
|
|
34
|
-
apply: (
|
|
35
|
-
tr
|
|
36
|
-
prevValue: PredictionPluginState,
|
|
37
|
-
oldState: EditorState,
|
|
38
|
-
newState: EditorState,
|
|
39
|
-
): PredictionPluginState => {
|
|
40
|
-
const meta = getTrMeta(tr)
|
|
41
|
-
|
|
42
|
-
// No changes
|
|
43
|
-
if (
|
|
44
|
-
!tr.docChanged
|
|
45
|
-
&& oldState.selection.eq(newState.selection)
|
|
46
|
-
&& !meta
|
|
47
|
-
) {
|
|
48
|
-
return prevValue
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// Receiving a meta means that we are ignoring a match
|
|
52
|
-
if (meta) {
|
|
53
|
-
let ignores = prevValue.ignores
|
|
54
|
-
if (!ignores.includes(meta.ignore)) {
|
|
55
|
-
ignores = [...ignores, meta.ignore]
|
|
56
|
-
}
|
|
57
|
-
return { matching: null, ignores }
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// Calculate the new ignores
|
|
61
|
-
const ignoreSet = new Set(prevValue.ignores.map(pos => tr.mapping.map(pos)))
|
|
62
|
-
|
|
63
|
-
// Calculate the new matching
|
|
64
|
-
let matching = calcPluginStateMatching(newState, getRules())
|
|
65
|
-
|
|
66
|
-
// Check if the matching should be ignored
|
|
67
|
-
if (matching && ignoreSet.has(matching.from)) {
|
|
68
|
-
matching = null
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Return the new matching and ignores
|
|
72
|
-
return { matching, ignores: Array.from(ignoreSet) }
|
|
48
|
+
apply: (tr, prevValue, oldState, newState): PredictionPluginState => {
|
|
49
|
+
return handleTransaction(tr, prevValue, oldState, newState, getRules)
|
|
73
50
|
},
|
|
74
51
|
},
|
|
75
52
|
|
|
76
53
|
view: () => ({
|
|
77
|
-
update:
|
|
78
|
-
const prevValue = getPluginState(prevState)
|
|
79
|
-
const currValue = getPluginState(view.state)
|
|
80
|
-
|
|
81
|
-
if (
|
|
82
|
-
prevValue?.matching
|
|
83
|
-
&& prevValue.matching.rule !== currValue?.matching?.rule
|
|
84
|
-
) {
|
|
85
|
-
// Deactivate the previous rule
|
|
86
|
-
prevValue.matching.rule.onLeave?.()
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
if (
|
|
90
|
-
currValue?.matching
|
|
91
|
-
&& !currValue.ignores.includes(currValue.matching.from)
|
|
92
|
-
) {
|
|
93
|
-
// Activate the current rule
|
|
94
|
-
|
|
95
|
-
const { from, to, match, rule } = currValue.matching
|
|
96
|
-
|
|
97
|
-
const textContent = view.state.doc.textBetween(
|
|
98
|
-
from,
|
|
99
|
-
to,
|
|
100
|
-
null,
|
|
101
|
-
OBJECT_REPLACEMENT_CHARACTER,
|
|
102
|
-
)
|
|
103
|
-
|
|
104
|
-
const deleteMatch = () => {
|
|
105
|
-
if (
|
|
106
|
-
view.state.doc.textBetween(
|
|
107
|
-
from,
|
|
108
|
-
to,
|
|
109
|
-
null,
|
|
110
|
-
OBJECT_REPLACEMENT_CHARACTER,
|
|
111
|
-
) === textContent
|
|
112
|
-
) {
|
|
113
|
-
view.dispatch(view.state.tr.delete(from, to))
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
const ignoreMatch = () => {
|
|
118
|
-
view.dispatch(
|
|
119
|
-
setTrMeta(view.state.tr, { ignore: from }),
|
|
120
|
-
)
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
rule.onMatch({
|
|
124
|
-
state: view.state,
|
|
125
|
-
match,
|
|
126
|
-
from,
|
|
127
|
-
to,
|
|
128
|
-
deleteMatch,
|
|
129
|
-
ignoreMatch,
|
|
130
|
-
})
|
|
131
|
-
}
|
|
132
|
-
},
|
|
54
|
+
update: handleUpdate,
|
|
133
55
|
}),
|
|
134
56
|
|
|
135
57
|
props: {
|
|
136
|
-
|
|
137
|
-
const
|
|
138
|
-
if (
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
return DecorationSet.create(state.doc, [deco])
|
|
58
|
+
handleTextInput: (view, from, to, textAdded, getTr) => {
|
|
59
|
+
const meta = handleTextInput(view, from, to, textAdded, getRules)
|
|
60
|
+
if (meta) {
|
|
61
|
+
const tr = getTr()
|
|
62
|
+
setTrMeta(tr, meta)
|
|
63
|
+
view.dispatch(tr)
|
|
64
|
+
return true
|
|
144
65
|
}
|
|
145
|
-
return
|
|
66
|
+
return false
|
|
146
67
|
},
|
|
68
|
+
decorations: getDecorations,
|
|
147
69
|
},
|
|
148
70
|
})
|
|
149
71
|
}
|
|
150
72
|
|
|
151
|
-
|
|
73
|
+
function handleTextInput(
|
|
74
|
+
view: EditorView,
|
|
75
|
+
from: number,
|
|
76
|
+
to: number,
|
|
77
|
+
textAdded: string,
|
|
78
|
+
getRules: () => AutocompleteRule[],
|
|
79
|
+
): PredictionTransactionMeta | undefined {
|
|
80
|
+
// Only handle insertions
|
|
81
|
+
if (from !== to) {
|
|
82
|
+
return
|
|
83
|
+
}
|
|
152
84
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
const $pos = state.selection.$from
|
|
85
|
+
const textBackward = getTextBackward(view.state.doc.resolve(from))
|
|
86
|
+
const textFull = textBackward + textAdded
|
|
87
|
+
const textTo = to + textAdded.length
|
|
88
|
+
const textFrom = textTo - textFull.length
|
|
158
89
|
|
|
159
|
-
const
|
|
90
|
+
const pluginState = getPluginState(view.state)
|
|
91
|
+
const ignores = pluginState?.ignores ?? []
|
|
92
|
+
|
|
93
|
+
const currMatching = matchRule(
|
|
94
|
+
view.state,
|
|
95
|
+
getRules(),
|
|
96
|
+
textFull,
|
|
97
|
+
textFrom,
|
|
98
|
+
textTo,
|
|
99
|
+
ignores,
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
if (currMatching) {
|
|
103
|
+
return { type: 'enter', matching: currMatching }
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function handleTransaction(
|
|
108
|
+
tr: Transaction,
|
|
109
|
+
prevValue: PredictionPluginState,
|
|
110
|
+
oldState: EditorState,
|
|
111
|
+
newState: EditorState,
|
|
112
|
+
getRules: () => AutocompleteRule[],
|
|
113
|
+
): PredictionPluginState {
|
|
114
|
+
const meta = getTrMeta(tr)
|
|
115
|
+
|
|
116
|
+
if (
|
|
117
|
+
!meta
|
|
118
|
+
&& !tr.docChanged
|
|
119
|
+
&& oldState.selection.eq(newState.selection)
|
|
120
|
+
) {
|
|
121
|
+
// No changes
|
|
122
|
+
return prevValue
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Handle position mapping changes
|
|
126
|
+
const ignoreSet = new Set<number>()
|
|
127
|
+
for (const ignore of prevValue.ignores) {
|
|
128
|
+
const result = tr.mapping.mapResult(ignore)
|
|
129
|
+
if (!result.deletedBefore && !result.deletedAfter) {
|
|
130
|
+
ignoreSet.add(result.pos)
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
const ignores = Array.from(ignoreSet)
|
|
134
|
+
|
|
135
|
+
const prevMatching = prevValue.matching && mapMatching(prevValue.matching, tr.mapping)
|
|
136
|
+
|
|
137
|
+
// If there is no new matching from `handleTextInput`
|
|
138
|
+
if (!meta) {
|
|
139
|
+
if (!prevMatching) {
|
|
140
|
+
return { matching: null, ignores }
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const { selection } = newState
|
|
144
|
+
// If the text selection is before the matching or after the matching,
|
|
145
|
+
// we leave the matching
|
|
146
|
+
if (selection.to < prevMatching.from || selection.from > prevMatching.to) {
|
|
147
|
+
ignores.push(prevMatching.from)
|
|
148
|
+
return { matching: null, ignores }
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Get the text between the existing matching
|
|
152
|
+
const text = getTextBetween(newState.doc, prevMatching.from, prevMatching.to)
|
|
153
|
+
// Check the text again to see if it still matches the rule
|
|
154
|
+
const currMatching = matchRule(
|
|
155
|
+
newState,
|
|
156
|
+
getRules(),
|
|
157
|
+
text,
|
|
158
|
+
prevMatching.from,
|
|
159
|
+
prevMatching.to,
|
|
160
|
+
ignores,
|
|
161
|
+
)
|
|
162
|
+
return { matching: currMatching ?? null, ignores }
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// If a new matching is being entered from `handleTextInput`
|
|
166
|
+
if (meta.type === 'enter') {
|
|
167
|
+
// Ignore the previous matching if it is not the same as the new matching
|
|
168
|
+
if (prevMatching && prevMatching.from !== meta.matching.from) {
|
|
169
|
+
ignores.push(prevMatching.from)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Return the new matching
|
|
173
|
+
return { matching: meta.matching, ignores }
|
|
174
|
+
}
|
|
160
175
|
|
|
161
|
-
|
|
176
|
+
// If a matching is being exited
|
|
177
|
+
if (meta.type === 'leave') {
|
|
178
|
+
if (prevMatching) {
|
|
179
|
+
ignores.push(prevMatching.from)
|
|
180
|
+
}
|
|
181
|
+
return { matching: null, ignores }
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
throw new Error(`Invalid transaction meta: ${meta satisfies never}`)
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function handleUpdate(view: EditorView, prevState: EditorState): void {
|
|
188
|
+
const prevValue = getPluginState(prevState)
|
|
189
|
+
const currValue = getPluginState(view.state)
|
|
190
|
+
|
|
191
|
+
if (!prevValue || !currValue) {
|
|
192
|
+
// Should not happen
|
|
193
|
+
return
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const prevMatching = prevValue.matching
|
|
197
|
+
const currMatching = currValue.matching
|
|
198
|
+
|
|
199
|
+
// Deactivate the previous rule
|
|
200
|
+
if (prevMatching && prevMatching.rule !== currMatching?.rule) {
|
|
201
|
+
prevMatching.rule.onLeave?.()
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Activate the current rule
|
|
205
|
+
if (currMatching) {
|
|
206
|
+
const { from, to, match, rule } = currMatching
|
|
207
|
+
|
|
208
|
+
const textSnapshot = getTextBetween(view.state.doc, from, to)
|
|
209
|
+
|
|
210
|
+
const deleteMatch = () => {
|
|
211
|
+
if (getTextBetween(view.state.doc, from, to) === textSnapshot) {
|
|
212
|
+
view.dispatch(view.state.tr.delete(from, to))
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const ignoreMatch = () => {
|
|
217
|
+
view.dispatch(
|
|
218
|
+
setTrMeta(view.state.tr, { type: 'leave' }),
|
|
219
|
+
)
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
rule.onMatch({
|
|
223
|
+
state: view.state,
|
|
224
|
+
match,
|
|
225
|
+
from,
|
|
226
|
+
to,
|
|
227
|
+
deleteMatch,
|
|
228
|
+
ignoreMatch,
|
|
229
|
+
})
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function getDecorations(state: EditorState): DecorationSet | null {
|
|
234
|
+
const pluginState = getPluginState(state)
|
|
235
|
+
if (pluginState?.matching) {
|
|
236
|
+
const { from, to, match } = pluginState.matching
|
|
237
|
+
const deco = Decoration.inline(from, to, {
|
|
238
|
+
'class': 'prosekit-autocomplete-match',
|
|
239
|
+
'data-autocomplete-match-text': match[0],
|
|
240
|
+
})
|
|
241
|
+
return DecorationSet.create(state.doc, [deco])
|
|
242
|
+
}
|
|
243
|
+
return null
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const MAX_MATCH = 200
|
|
247
|
+
|
|
248
|
+
/** Get the text before the given position at the current block. */
|
|
249
|
+
function getTextBackward($pos: ResolvedPos): string {
|
|
250
|
+
const parentOffset: number = $pos.parentOffset
|
|
251
|
+
return getTextBetween(
|
|
252
|
+
$pos.parent,
|
|
162
253
|
Math.max(0, parentOffset - MAX_MATCH),
|
|
163
254
|
parentOffset,
|
|
255
|
+
)
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function getTextBetween(node: ProseMirrorNode, from: number, to: number): string {
|
|
259
|
+
return node.textBetween(
|
|
260
|
+
from,
|
|
261
|
+
to,
|
|
164
262
|
null,
|
|
165
263
|
OBJECT_REPLACEMENT_CHARACTER,
|
|
166
264
|
)
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function matchRule(
|
|
268
|
+
state: EditorState,
|
|
269
|
+
rules: AutocompleteRule[],
|
|
270
|
+
text: string,
|
|
271
|
+
textFrom: number,
|
|
272
|
+
textTo: number,
|
|
273
|
+
ignores: Array<number>,
|
|
274
|
+
): PredictionPluginMatching | undefined {
|
|
275
|
+
// Find the rightmost ignore point within the text range
|
|
276
|
+
let maxIgnore = -1
|
|
277
|
+
for (const ignore of ignores) {
|
|
278
|
+
if (ignore >= textFrom && ignore < textTo && ignore > maxIgnore) {
|
|
279
|
+
maxIgnore = ignore
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// If an ignore point is within the text range, we ignore the text to the left
|
|
284
|
+
// of the ignore point (including the character right after the ignore point).
|
|
285
|
+
if (maxIgnore >= 0) {
|
|
286
|
+
const cut = maxIgnore + 1 - textFrom
|
|
287
|
+
text = text.slice(cut)
|
|
288
|
+
textFrom += cut
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
if (textFrom >= textTo || !text) {
|
|
292
|
+
return
|
|
293
|
+
}
|
|
167
294
|
|
|
168
295
|
for (const rule of rules) {
|
|
169
296
|
if (!rule.canMatch({ state })) {
|
|
@@ -171,16 +298,23 @@ function calcPluginStateMatching(
|
|
|
171
298
|
}
|
|
172
299
|
|
|
173
300
|
rule.regex.lastIndex = 0
|
|
174
|
-
const match = rule.regex.exec(
|
|
301
|
+
const match = rule.regex.exec(text)
|
|
175
302
|
if (!match) {
|
|
176
303
|
continue
|
|
177
304
|
}
|
|
178
305
|
|
|
179
|
-
const
|
|
180
|
-
const
|
|
306
|
+
const matchTo = textTo
|
|
307
|
+
const matchFrom = textFrom + match.index
|
|
181
308
|
|
|
182
|
-
return { rule, match, from, to }
|
|
309
|
+
return { rule, match, from: matchFrom, to: matchTo }
|
|
183
310
|
}
|
|
311
|
+
}
|
|
184
312
|
|
|
185
|
-
|
|
313
|
+
function mapMatching(matching: PredictionPluginMatching, mapping: Mapping): PredictionPluginMatching {
|
|
314
|
+
return {
|
|
315
|
+
rule: matching.rule,
|
|
316
|
+
match: matching.match,
|
|
317
|
+
from: mapping.map(matching.from),
|
|
318
|
+
to: mapping.map(matching.to, -1),
|
|
319
|
+
}
|
|
186
320
|
}
|
|
@@ -69,7 +69,7 @@ export interface AutocompleteRuleOptions {
|
|
|
69
69
|
* The regular expression to match against the text before the cursor. The
|
|
70
70
|
* last match before the cursor is used.
|
|
71
71
|
*
|
|
72
|
-
* For a slash menu, you might use `/(?<!\S)\/(
|
|
72
|
+
* For a slash menu, you might use `/(?<!\S)\/(\S.*)?$/u`.
|
|
73
73
|
* For a mention, you might use `/@\w*$/`
|
|
74
74
|
*/
|
|
75
75
|
regex: RegExp
|
|
@@ -87,8 +87,8 @@ export interface AutocompleteRuleOptions {
|
|
|
87
87
|
|
|
88
88
|
/**
|
|
89
89
|
* A predicate to determine if the rule can be applied in the current editor
|
|
90
|
-
* state. If not provided, it defaults to only allowing matches
|
|
91
|
-
*
|
|
90
|
+
* state. If not provided, it defaults to only allowing matches that are not
|
|
91
|
+
* inside a code block or code mark.
|
|
92
92
|
*/
|
|
93
93
|
canMatch?: CanMatchPredicate
|
|
94
94
|
}
|