refactoring

This commit is contained in:
Philipp Kühn 2021-01-15 09:25:50 +01:00
parent 83b3de8711
commit a5d28a0184

View File

@ -2,78 +2,80 @@ import { Plugin, PluginKey } from 'prosemirror-state'
import { ResolvedPos } from 'prosemirror-model' import { ResolvedPos } from 'prosemirror-model'
import { Decoration, DecorationSet } from 'prosemirror-view' import { Decoration, DecorationSet } from 'prosemirror-view'
export interface Trigger {
char: string,
allowSpaces: boolean,
startOfLine: boolean,
$position: ResolvedPos,
}
// Create a matcher that matches when a specific character is typed. Useful for @mentions and #tags. // Create a matcher that matches when a specific character is typed. Useful for @mentions and #tags.
function triggerCharacter({ function triggerCharacter(config: Trigger) {
char = '@', const {
allowSpaces = false, char, allowSpaces, startOfLine, $position,
startOfLine = false, } = config
}) {
return ($position: ResolvedPos) => { // cancel if top level node
// cancel if top level node if ($position.depth <= 0) {
if ($position.depth <= 0) { return false
return false }
}
// Matching expressions used for later // Matching expressions used for later
const escapedChar = `\\${char}` const escapedChar = `\\${char}`
const suffix = new RegExp(`\\s${escapedChar}$`) const suffix = new RegExp(`\\s${escapedChar}$`)
const prefix = startOfLine ? '^' : '' const prefix = startOfLine ? '^' : ''
const regexp = allowSpaces const regexp = allowSpaces
? new RegExp(`${prefix}${escapedChar}.*?(?=\\s${escapedChar}|$)`, 'gm') ? new RegExp(`${prefix}${escapedChar}.*?(?=\\s${escapedChar}|$)`, 'gm')
: new RegExp(`${prefix}(?:^)?${escapedChar}[^\\s${escapedChar}]*`, 'gm') : new RegExp(`${prefix}(?:^)?${escapedChar}[^\\s${escapedChar}]*`, 'gm')
// Lookup the boundaries of the current node // Lookup the boundaries of the current node
const textFrom = $position.before() const textFrom = $position.before()
const textTo = $position.end() const textTo = $position.end()
const text = $position.doc.textBetween(textFrom, textTo, '\0', '\0') const text = $position.doc.textBetween(textFrom, textTo, '\0', '\0')
let match = regexp.exec(text) let match = regexp.exec(text)
let position let position
while (match !== null) { while (match !== null) {
// JavaScript doesn't have lookbehinds; this hacks a check that first character is " " // JavaScript doesn't have lookbehinds; this hacks a check that first character is " "
// or the line beginning // or the line beginning
const matchPrefix = match.input.slice(Math.max(0, match.index - 1), match.index) const matchPrefix = match.input.slice(Math.max(0, match.index - 1), match.index)
if (/^[\s\0]?$/.test(matchPrefix)) { if (/^[\s\0]?$/.test(matchPrefix)) {
// The absolute position of the match in the document // The absolute position of the match in the document
const from = match.index + $position.start() const from = match.index + $position.start()
let to = from + match[0].length let to = from + match[0].length
// Edge case handling; if spaces are allowed and we're directly in between // Edge case handling; if spaces are allowed and we're directly in between
// two triggers // two triggers
if (allowSpaces && suffix.test(text.slice(to - 1, to + 1))) { if (allowSpaces && suffix.test(text.slice(to - 1, to + 1))) {
match[0] += ' ' match[0] += ' '
to += 1 to += 1
}
// If the $position is located within the matched substring, return that range
if (from < $position.pos && to >= $position.pos) {
position = {
range: {
from,
to,
},
query: match[0].slice(char.length),
text: match[0],
}
}
} }
match = regexp.exec(text) // If the $position is located within the matched substring, return that range
if (from < $position.pos && to >= $position.pos) {
position = {
range: {
from,
to,
},
query: match[0].slice(char.length),
text: match[0],
}
}
} }
return position match = regexp.exec(text)
} }
return position
} }
export function Suggestion({ export function Suggestion({
matcher = { char = '@',
char: '@', allowSpaces = false,
allowSpaces: false, startOfLine = false,
startOfLine: false,
},
appendText = null, appendText = null,
suggestionClass = 'suggestion', suggestionClass = 'suggestion',
command = () => false, command = () => false,
@ -171,7 +173,6 @@ export function Suggestion({
}, },
state: { state: {
// Initialize the plugin's internal state. // Initialize the plugin's internal state.
init() { init() {
return { return {
@ -196,9 +197,16 @@ export function Suggestion({
// Try to match against where our cursor currently is // Try to match against where our cursor currently is
const $position = selection.$from const $position = selection.$from
const match = triggerCharacter(matcher)($position) const match = triggerCharacter({
char,
allowSpaces,
startOfLine,
$position,
})
const decorationId = (Math.random() + 1).toString(36).substr(2, 5) const decorationId = (Math.random() + 1).toString(36).substr(2, 5)
console.log({ match })
// If we found a match, update the current state to show it // If we found a match, update the current state to show it
if (match) { if (match) {
next.active = true next.active = true