misc.js
6.31 KB
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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
export function bind(f) {
let args = Array.prototype.slice.call(arguments, 1)
return function(){return f.apply(null, args)}
}
export function copyObj(obj, target, overwrite) {
if (!target) target = {}
for (let prop in obj)
if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop)))
target[prop] = obj[prop]
return target
}
// Counts the column offset in a string, taking tabs into account.
// Used mostly to find indentation.
export function countColumn(string, end, tabSize, startIndex, startValue) {
if (end == null) {
end = string.search(/[^\s\u00a0]/)
if (end == -1) end = string.length
}
for (let i = startIndex || 0, n = startValue || 0;;) {
let nextTab = string.indexOf("\t", i)
if (nextTab < 0 || nextTab >= end)
return n + (end - i)
n += nextTab - i
n += tabSize - (n % tabSize)
i = nextTab + 1
}
}
export class Delayed {
constructor() {this.id = null}
set(ms, f) {
clearTimeout(this.id)
this.id = setTimeout(f, ms)
}
}
export function indexOf(array, elt) {
for (let i = 0; i < array.length; ++i)
if (array[i] == elt) return i
return -1
}
// Number of pixels added to scroller and sizer to hide scrollbar
export let scrollerGap = 30
// Returned or thrown by various protocols to signal 'I'm not
// handling this'.
export let Pass = {toString: function(){return "CodeMirror.Pass"}}
// Reused option objects for setSelection & friends
export let sel_dontScroll = {scroll: false}, sel_mouse = {origin: "*mouse"}, sel_move = {origin: "+move"}
// The inverse of countColumn -- find the offset that corresponds to
// a particular column.
export function findColumn(string, goal, tabSize) {
for (let pos = 0, col = 0;;) {
let nextTab = string.indexOf("\t", pos)
if (nextTab == -1) nextTab = string.length
let skipped = nextTab - pos
if (nextTab == string.length || col + skipped >= goal)
return pos + Math.min(skipped, goal - col)
col += nextTab - pos
col += tabSize - (col % tabSize)
pos = nextTab + 1
if (col >= goal) return pos
}
}
let spaceStrs = [""]
export function spaceStr(n) {
while (spaceStrs.length <= n)
spaceStrs.push(lst(spaceStrs) + " ")
return spaceStrs[n]
}
export function lst(arr) { return arr[arr.length-1] }
export function map(array, f) {
let out = []
for (let i = 0; i < array.length; i++) out[i] = f(array[i], i)
return out
}
export function insertSorted(array, value, score) {
let pos = 0, priority = score(value)
while (pos < array.length && score(array[pos]) <= priority) pos++
array.splice(pos, 0, value)
}
function nothing() {}
export function createObj(base, props) {
let inst
if (Object.create) {
inst = Object.create(base)
} else {
nothing.prototype = base
inst = new nothing()
}
if (props) copyObj(props, inst)
return inst
}
let nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/
export function isWordCharBasic(ch) {
return /\w/.test(ch) || ch > "\x80" &&
(ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch))
}
export function isWordChar(ch, helper) {
if (!helper) return isWordCharBasic(ch)
if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) return true
return helper.test(ch)
}
export function isEmpty(obj) {
for (let n in obj) if (obj.hasOwnProperty(n) && obj[n]) return false
return true
}
// Extending unicode characters. A series of a non-extending char +
// any number of extending chars is treated as a single unit as far
// as editing and measuring is concerned. This is not fully correct,
// since some scripts/fonts/browsers also treat other configurations
// of code points as a group.
let extendingChars = /[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065e\u0670\u06d6-\u06dc\u06de-\u06e4\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0900-\u0902\u093c\u0941-\u0948\u094d\u0951-\u0955\u0962\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81\u0a82\u0abc\u0ac1-\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86\u0f87\u0f90-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039\u103a\u103d\u103e\u1058\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085\u1086\u108d\u109d\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193b\u1a17\u1a18\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80\u1b81\u1ba2-\u1ba5\u1ba8\u1ba9\u1c2c-\u1c33\u1c36\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1dc0-\u1de6\u1dfd-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\ua672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua825\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31\uaa32\uaa35\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe5\uabe8\uabed\udc00-\udfff\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]/
export function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch) }
// Returns a number from the range [`0`; `str.length`] unless `pos` is outside that range.
export function skipExtendingChars(str, pos, dir) {
while ((dir < 0 ? pos > 0 : pos < str.length) && isExtendingChar(str.charAt(pos))) pos += dir
return pos
}
// Returns the value from the range [`from`; `to`] that satisfies
// `pred` and is closest to `from`. Assumes that at least `to` satisfies `pred`.
export function findFirst(pred, from, to) {
for (;;) {
if (Math.abs(from - to) <= 1) return pred(from) ? from : to
let mid = Math.floor((from + to) / 2)
if (pred(mid)) to = mid
else from = mid
}
}