/
autoinput.js
108 lines (94 loc) · 3.84 KB
/
autoinput.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
import {BlockQuote, OrderedList, BulletList, CodeBlock, Heading} from "../model"
import {defineOption} from "../edit"
import {InputRule, addInputRule, removeInputRule} from "./inputrules"
// :: Object<InputRule>
// Base set of input rules, enabled by default when `autoInput` is set
// to `true`.
export const autoInputRules = Object.create(null)
// :: union<bool, [union<string, Object<?InputRule>>]> #path=autoInput #kind=option
// Controls the [input rules](#InputRule) initially active in the
// editor. Pass an array of sources, which can be either the string
// `"schema"`, to add rules [registered](#SchemaItem.register) on the
// schema items (under the namespace `"autoInput"`), or an object
// containing input rules. To remove previously included rules, you
// can add an object that maps their name to `null`.
//
// The value `false` (the default) is a shorthand for no input rules,
// and the value `true` for `["schema", autoInputRules]`.
defineOption("autoInput", false, function(pm, val) {
if (pm.mod.autoInput) {
pm.mod.autoInput.forEach(rule => removeInputRule(pm, rule))
pm.mod.autoInput = null
}
if (val) {
if (val === true) val = ["schema", autoInputRules]
let rules = Object.create(null), list = pm.mod.autoInput = []
val.forEach(spec => {
if (spec === "schema") {
pm.schema.registry("autoInput", (name, rule, type, typeName) => {
let rname = typeName + ":" + name, handler = rule.handler
if (handler.bind) handler = handler.bind(type)
rules[rname] = new InputRule(rule.match, rule.filter, handler)
})
} else {
for (let name in spec) {
let val = spec[name]
if (val == null) delete rules[name]
else rules[name] = val
}
}
})
for (let name in rules) {
addInputRule(pm, rules[name])
list.push(rules[name])
}
}
})
autoInputRules.emDash = new InputRule(/--$/, "-", "—")
autoInputRules.openDoubleQuote = new InputRule(/(?:^|[\s\{\[\(\<'"\u2018\u201C])(")$/, '"', "“")
autoInputRules.closeDoubleQuote = new InputRule(/"$/, '"', "”")
autoInputRules.openSingleQuote = new InputRule(/(?:^|[\s\{\[\(\<'"\u2018\u201C])(')$/, "'", "‘")
autoInputRules.closeSingleQuote = new InputRule(/'$/, "'", "’")
BlockQuote.register("autoInput", "startBlockQuote", new InputRule(
/^\s*> $/, " ",
function(pm, _, pos) { wrapAndJoin(pm, pos, this) }
))
OrderedList.register("autoInput", "startOrderedList", new InputRule(
/^(\d+)\. $/, " ",
function(pm, match, pos) {
wrapAndJoin(pm, pos, this, {order: +match[1]},
node => node.childCount + node.attrs.order == +match[1])
}
))
BulletList.register("autoInput", "startBulletList", new InputRule(
/^\s*([-+*]) $/, " ",
function(pm, match, pos) {
let bullet = match[1]
wrapAndJoin(pm, pos, this, null, node => node.attrs.bullet == bullet)
}
))
CodeBlock.register("autoInput", "startCodeBlock", new InputRule(
/^```$/, "`",
function(pm, _, pos) { setAs(pm, pos, this, {params: ""}) }
))
Heading.registerComputed("autoInput", "startHeading", type => {
let re = new RegExp("^(#{1," + type.maxLevel + "}) $")
return new InputRule(re, " ", function(pm, match, pos) {
setAs(pm, pos, this, {level: match[1].length})
})
})
function wrapAndJoin(pm, pos, type, attrs = null, predicate = null) {
let $pos = pm.doc.resolve(pos), d1 = $pos.depth - 1
let sibling = $pos.index(d1) > 0 && $pos.node(d1).child($pos.index(d1) - 1)
let join = sibling && sibling.type == type && (!predicate || predicate(sibling))
let start = pos - $pos.parentOffset
let tr = pm.tr.delete(start, pos).wrap(start, start, type, attrs)
if (join) tr.join($pos.before())
tr.apply()
}
function setAs(pm, pos, type, attrs) {
let $pos = pm.doc.resolve(pos), start = pos - $pos.parentOffset
pm.tr.delete(start, pos)
.setBlockType(start, start, type, attrs)
.apply()
}