show-hint.js
406 lines
| 1 | !(function (t) { |
| 2 | "object" == typeof exports && "object" == typeof module ? t(require("../../lib/codemirror")) : "function" == typeof define && define.amd ? define(["../../lib/codemirror"], t) : t(CodeMirror); |
| 3 | })(function (T) { |
| 4 | "use strict"; |
| 5 | var F = "CodeMirror-hint-active"; |
| 6 | function n(t, e) { |
| 7 | var i; |
| 8 | (this.cm = t), |
| 9 | (this.options = e), |
| 10 | (this.widget = null), |
| 11 | (this.debounce = 0), |
| 12 | (this.tick = 0), |
| 13 | (this.startPos = this.cm.getCursor("start")), |
| 14 | (this.startLen = this.cm.getLine(this.startPos.line).length - this.cm.getSelection().length), |
| 15 | this.options.updateOnCursorActivity && |
| 16 | t.on( |
| 17 | "cursorActivity", |
| 18 | ((i = this).activityFunc = function () { |
| 19 | i.cursorActivity(); |
| 20 | }) |
| 21 | ); |
| 22 | } |
| 23 | (T.showHint = function (t, e, i) { |
| 24 | if (!e) return t.showHint(i); |
| 25 | i && i.async && (e.async = !0); |
| 26 | var n = { hint: e }; |
| 27 | if (i) for (var o in i) n[o] = i[o]; |
| 28 | return t.showHint(n); |
| 29 | }), |
| 30 | T.defineExtension("showHint", function (t) { |
| 31 | t = (function (t, e, i) { |
| 32 | var n = t.options.hintOptions, |
| 33 | o = {}; |
| 34 | for (s in c) o[s] = c[s]; |
| 35 | if (n) for (var s in n) void 0 !== n[s] && (o[s] = n[s]); |
| 36 | if (i) for (var s in i) void 0 !== i[s] && (o[s] = i[s]); |
| 37 | o.hint.resolve && (o.hint = o.hint.resolve(t, e)); |
| 38 | return o; |
| 39 | })(this, this.getCursor("start"), t); |
| 40 | var e = this.listSelections(); |
| 41 | if (!(1 < e.length)) { |
| 42 | if (this.somethingSelected()) { |
| 43 | if (!t.hint.supportsSelection) return; |
| 44 | for (var i = 0; i < e.length; i++) if (e[i].head.line != e[i].anchor.line) return; |
| 45 | } |
| 46 | this.state.completionActive && this.state.completionActive.close(); |
| 47 | t = this.state.completionActive = new n(this, t); |
| 48 | t.options.hint && (T.signal(this, "startCompletion", this), t.update(!0)); |
| 49 | } |
| 50 | }), |
| 51 | T.defineExtension("closeHint", function () { |
| 52 | this.state.completionActive && this.state.completionActive.close(); |
| 53 | }); |
| 54 | var o = |
| 55 | window.requestAnimationFrame || |
| 56 | function (t) { |
| 57 | return setTimeout(t, 1e3 / 60); |
| 58 | }, |
| 59 | s = window.cancelAnimationFrame || clearTimeout; |
| 60 | function M(t) { |
| 61 | return "string" == typeof t ? t : t.text; |
| 62 | } |
| 63 | function O(t, e) { |
| 64 | for (; e && e != t; ) { |
| 65 | if ("LI" === e.nodeName.toUpperCase() && e.parentNode == t) return e; |
| 66 | e = e.parentNode; |
| 67 | } |
| 68 | } |
| 69 | function i(o, t) { |
| 70 | (this.id = "cm-complete-" + Math.floor(Math.random(1e6))), (this.completion = o), (this.data = t), (this.picked = !1); |
| 71 | var i = this, |
| 72 | s = o.cm, |
| 73 | c = s.getInputField().ownerDocument, |
| 74 | r = c.defaultView || c.parentWindow, |
| 75 | l = (this.hints = c.createElement("ul")); |
| 76 | l.setAttribute("role", "listbox"), l.setAttribute("aria-expanded", "true"), (l.id = this.id); |
| 77 | var e = o.cm.options.theme; |
| 78 | (l.className = "CodeMirror-hints " + e), (this.selectedHint = t.selectedHint || 0); |
| 79 | for (var n = t.list, h = 0; h < n.length; ++h) { |
| 80 | var a = l.appendChild(c.createElement("li")), |
| 81 | u = n[h], |
| 82 | d = "CodeMirror-hint" + (h != this.selectedHint ? "" : " " + F); |
| 83 | null != u.className && (d = u.className + " " + d), |
| 84 | (a.className = d), |
| 85 | h == this.selectedHint && a.setAttribute("aria-selected", "true"), |
| 86 | (a.id = this.id + "-" + h), |
| 87 | a.setAttribute("role", "option"), |
| 88 | u.render ? u.render(a, t, u) : a.appendChild(c.createTextNode(u.displayText || M(u))), |
| 89 | (a.hintId = h); |
| 90 | } |
| 91 | var f = o.options.container || c.body, |
| 92 | p = s.cursorCoords(o.options.alignWithWord ? t.from : null), |
| 93 | m = p.left, |
| 94 | g = p.bottom, |
| 95 | v = !0, |
| 96 | y = 0, |
| 97 | b = 0; |
| 98 | f !== c.body && |
| 99 | ((w = (C = -1 !== ["absolute", "relative", "fixed"].indexOf(r.getComputedStyle(f).position) ? f : f.offsetParent).getBoundingClientRect()), |
| 100 | (H = c.body.getBoundingClientRect()), |
| 101 | (y = w.left - H.left - C.scrollLeft), |
| 102 | (b = w.top - H.top - C.scrollTop)), |
| 103 | (l.style.left = m - y + "px"), |
| 104 | (l.style.top = g - b + "px"); |
| 105 | var e = r.innerWidth || Math.max(c.body.offsetWidth, c.documentElement.offsetWidth), |
| 106 | w = r.innerHeight || Math.max(c.body.offsetHeight, c.documentElement.offsetHeight); |
| 107 | f.appendChild(l), s.getInputField().setAttribute("aria-autocomplete", "list"), s.getInputField().setAttribute("aria-owns", this.id), s.getInputField().setAttribute("aria-activedescendant", this.id + "-" + this.selectedHint); |
| 108 | var A, |
| 109 | H = o.options.moveOnOverlap ? l.getBoundingClientRect() : new DOMRect(), |
| 110 | C = !!o.options.paddingForScrollbar && l.scrollHeight > l.clientHeight + 1; |
| 111 | setTimeout(function () { |
| 112 | A = s.getScrollInfo(); |
| 113 | }), |
| 114 | 0 < H.bottom - w && |
| 115 | ((f = H.bottom - H.top), |
| 116 | 0 < p.top - (p.bottom - H.top) - f |
| 117 | ? ((l.style.top = (g = p.top - f - b) + "px"), (v = !1)) |
| 118 | : w < f && |
| 119 | ((l.style.height = w - 5 + "px"), |
| 120 | (l.style.top = (g = p.bottom - H.top - b) + "px"), |
| 121 | (x = s.getCursor()), |
| 122 | t.from.ch != x.ch && ((p = s.cursorCoords(x)), (l.style.left = (m = p.left - y) + "px"), (H = l.getBoundingClientRect())))); |
| 123 | var k, |
| 124 | x = H.right - e; |
| 125 | if ((C && (x += s.display.nativeBarWidth), 0 < x && (H.right - H.left > e && ((l.style.width = e - 5 + "px"), (x -= H.right - H.left - e)), (l.style.left = (m = p.left - x - y) + "px")), C)) |
| 126 | for (var S = l.firstChild; S; S = S.nextSibling) S.style.paddingRight = s.display.nativeBarWidth + "px"; |
| 127 | s.addKeyMap( |
| 128 | (this.keyMap = (function (t, n) { |
| 129 | var o = { |
| 130 | Up: function () { |
| 131 | n.moveFocus(-1); |
| 132 | }, |
| 133 | Down: function () { |
| 134 | n.moveFocus(1); |
| 135 | }, |
| 136 | PageUp: function () { |
| 137 | n.moveFocus(1 - n.menuSize(), !0); |
| 138 | }, |
| 139 | PageDown: function () { |
| 140 | n.moveFocus(n.menuSize() - 1, !0); |
| 141 | }, |
| 142 | Home: function () { |
| 143 | n.setFocus(0); |
| 144 | }, |
| 145 | End: function () { |
| 146 | n.setFocus(n.length - 1); |
| 147 | }, |
| 148 | Enter: n.pick, |
| 149 | Tab: n.pick, |
| 150 | Esc: n.close, |
| 151 | }; |
| 152 | /Mac/.test(navigator.platform) && |
| 153 | ((o["Ctrl-P"] = function () { |
| 154 | n.moveFocus(-1); |
| 155 | }), |
| 156 | (o["Ctrl-N"] = function () { |
| 157 | n.moveFocus(1); |
| 158 | })); |
| 159 | var e = t.options.customKeys, |
| 160 | s = e ? {} : o; |
| 161 | function i(t, e) { |
| 162 | var i = |
| 163 | "string" != typeof e |
| 164 | ? function (t) { |
| 165 | return e(t, n); |
| 166 | } |
| 167 | : o.hasOwnProperty(e) |
| 168 | ? o[e] |
| 169 | : e; |
| 170 | s[t] = i; |
| 171 | } |
| 172 | if (e) for (var c in e) e.hasOwnProperty(c) && i(c, e[c]); |
| 173 | var r = t.options.extraKeys; |
| 174 | if (r) for (var c in r) r.hasOwnProperty(c) && i(c, r[c]); |
| 175 | return s; |
| 176 | })(o, { |
| 177 | moveFocus: function (t, e) { |
| 178 | i.changeActive(i.selectedHint + t, e); |
| 179 | }, |
| 180 | setFocus: function (t) { |
| 181 | i.changeActive(t); |
| 182 | }, |
| 183 | menuSize: function () { |
| 184 | return i.screenAmount(); |
| 185 | }, |
| 186 | length: n.length, |
| 187 | close: function () { |
| 188 | o.close(); |
| 189 | }, |
| 190 | pick: function () { |
| 191 | i.pick(); |
| 192 | }, |
| 193 | data: t, |
| 194 | })) |
| 195 | ), |
| 196 | o.options.closeOnUnfocus && |
| 197 | (s.on( |
| 198 | "blur", |
| 199 | (this.onBlur = function () { |
| 200 | k = setTimeout(function () { |
| 201 | o.close(); |
| 202 | }, 100); |
| 203 | }) |
| 204 | ), |
| 205 | s.on( |
| 206 | "focus", |
| 207 | (this.onFocus = function () { |
| 208 | clearTimeout(k); |
| 209 | }) |
| 210 | )), |
| 211 | s.on( |
| 212 | "scroll", |
| 213 | (this.onScroll = function () { |
| 214 | var t = s.getScrollInfo(), |
| 215 | e = s.getWrapperElement().getBoundingClientRect(); |
| 216 | A = A || s.getScrollInfo(); |
| 217 | var i = g + A.top - t.top, |
| 218 | n = i - (r.pageYOffset || (c.documentElement || c.body).scrollTop); |
| 219 | if ((v || (n += l.offsetHeight), n <= e.top || n >= e.bottom)) return o.close(); |
| 220 | (l.style.top = i + "px"), (l.style.left = m + A.left - t.left + "px"); |
| 221 | }) |
| 222 | ), |
| 223 | T.on(l, "dblclick", function (t) { |
| 224 | t = O(l, t.target || t.srcElement); |
| 225 | t && null != t.hintId && (i.changeActive(t.hintId), i.pick()); |
| 226 | }), |
| 227 | T.on(l, "click", function (t) { |
| 228 | t = O(l, t.target || t.srcElement); |
| 229 | t && null != t.hintId && (i.changeActive(t.hintId), o.options.completeOnSingleClick && i.pick()); |
| 230 | }), |
| 231 | T.on(l, "mousedown", function () { |
| 232 | setTimeout(function () { |
| 233 | s.focus(); |
| 234 | }, 20); |
| 235 | }); |
| 236 | C = this.getSelectedHintRange(); |
| 237 | return (0 === C.from && 0 === C.to) || this.scrollToActive(), T.signal(t, "select", n[this.selectedHint], l.childNodes[this.selectedHint]), !0; |
| 238 | } |
| 239 | function r(t, e, i, n) { |
| 240 | t.async ? t(e, n, i) : (i = t(e, i)) && i.then ? i.then(n) : n(i); |
| 241 | } |
| 242 | (n.prototype = { |
| 243 | close: function () { |
| 244 | this.active() && |
| 245 | ((this.cm.state.completionActive = null), |
| 246 | (this.tick = null), |
| 247 | this.options.updateOnCursorActivity && this.cm.off("cursorActivity", this.activityFunc), |
| 248 | this.widget && this.data && T.signal(this.data, "close"), |
| 249 | this.widget && this.widget.close(), |
| 250 | T.signal(this.cm, "endCompletion", this.cm)); |
| 251 | }, |
| 252 | active: function () { |
| 253 | return this.cm.state.completionActive == this; |
| 254 | }, |
| 255 | pick: function (t, e) { |
| 256 | var i = t.list[e], |
| 257 | n = this; |
| 258 | this.cm.operation(function () { |
| 259 | i.hint ? i.hint(n.cm, t, i) : n.cm.replaceRange(M(i), i.from || t.from, i.to || t.to, "complete"), T.signal(t, "pick", i), n.cm.scrollIntoView(); |
| 260 | }), |
| 261 | this.options.closeOnPick && this.close(); |
| 262 | }, |
| 263 | cursorActivity: function () { |
| 264 | this.debounce && (s(this.debounce), (this.debounce = 0)); |
| 265 | var t = this.startPos; |
| 266 | this.data && (t = this.data.from); |
| 267 | var e, |
| 268 | i = this.cm.getCursor(), |
| 269 | n = this.cm.getLine(i.line); |
| 270 | i.line != this.startPos.line || n.length - i.ch != this.startLen - this.startPos.ch || i.ch < t.ch || this.cm.somethingSelected() || !i.ch || this.options.closeCharacters.test(n.charAt(i.ch - 1)) |
| 271 | ? this.close() |
| 272 | : (((e = this).debounce = o(function () { |
| 273 | e.update(); |
| 274 | })), |
| 275 | this.widget && this.widget.disable()); |
| 276 | }, |
| 277 | update: function (e) { |
| 278 | var i, n; |
| 279 | null != this.tick && |
| 280 | ((n = ++(i = this).tick), |
| 281 | r(this.options.hint, this.cm, this.options, function (t) { |
| 282 | i.tick == n && i.finishUpdate(t, e); |
| 283 | })); |
| 284 | }, |
| 285 | finishUpdate: function (t, e) { |
| 286 | this.data && T.signal(this.data, "update"); |
| 287 | e = (this.widget && this.widget.picked) || (e && this.options.completeSingle); |
| 288 | this.widget && this.widget.close(), (this.data = t) && t.list.length && (e && 1 == t.list.length ? this.pick(t, 0) : ((this.widget = new i(this, t)), T.signal(t, "shown"))); |
| 289 | }, |
| 290 | }), |
| 291 | (i.prototype = { |
| 292 | close: function () { |
| 293 | var t; |
| 294 | this.completion.widget == this && |
| 295 | ((this.completion.widget = null), |
| 296 | this.hints.parentNode && this.hints.parentNode.removeChild(this.hints), |
| 297 | this.completion.cm.removeKeyMap(this.keyMap), |
| 298 | (t = this.completion.cm.getInputField()).removeAttribute("aria-activedescendant"), |
| 299 | t.removeAttribute("aria-owns"), |
| 300 | (t = this.completion.cm), |
| 301 | this.completion.options.closeOnUnfocus && (t.off("blur", this.onBlur), t.off("focus", this.onFocus)), |
| 302 | t.off("scroll", this.onScroll)); |
| 303 | }, |
| 304 | disable: function () { |
| 305 | this.completion.cm.removeKeyMap(this.keyMap); |
| 306 | var t = this; |
| 307 | (this.keyMap = { |
| 308 | Enter: function () { |
| 309 | t.picked = !0; |
| 310 | }, |
| 311 | }), |
| 312 | this.completion.cm.addKeyMap(this.keyMap); |
| 313 | }, |
| 314 | pick: function () { |
| 315 | this.completion.pick(this.data, this.selectedHint); |
| 316 | }, |
| 317 | changeActive: function (t, e) { |
| 318 | t >= this.data.list.length ? (t = e ? this.data.list.length - 1 : 0) : t < 0 && (t = e ? 0 : this.data.list.length - 1), |
| 319 | this.selectedHint != t && |
| 320 | ((e = this.hints.childNodes[this.selectedHint]) && ((e.className = e.className.replace(" " + F, "")), e.removeAttribute("aria-selected")), |
| 321 | ((e = this.hints.childNodes[(this.selectedHint = t)]).className += " " + F), |
| 322 | e.setAttribute("aria-selected", "true"), |
| 323 | this.completion.cm.getInputField().setAttribute("aria-activedescendant", e.id), |
| 324 | this.scrollToActive(), |
| 325 | T.signal(this.data, "select", this.data.list[this.selectedHint], e)); |
| 326 | }, |
| 327 | scrollToActive: function () { |
| 328 | var t = this.getSelectedHintRange(), |
| 329 | e = this.hints.childNodes[t.from], |
| 330 | i = this.hints.childNodes[t.to], |
| 331 | t = this.hints.firstChild; |
| 332 | e.offsetTop < this.hints.scrollTop |
| 333 | ? (this.hints.scrollTop = e.offsetTop - t.offsetTop) |
| 334 | : i.offsetTop + i.offsetHeight > this.hints.scrollTop + this.hints.clientHeight && (this.hints.scrollTop = i.offsetTop + i.offsetHeight - this.hints.clientHeight + t.offsetTop); |
| 335 | }, |
| 336 | screenAmount: function () { |
| 337 | return Math.floor(this.hints.clientHeight / this.hints.firstChild.offsetHeight) || 1; |
| 338 | }, |
| 339 | getSelectedHintRange: function () { |
| 340 | var t = this.completion.options.scrollMargin || 0; |
| 341 | return { from: Math.max(0, this.selectedHint - t), to: Math.min(this.data.list.length - 1, this.selectedHint + t) }; |
| 342 | }, |
| 343 | }), |
| 344 | T.registerHelper("hint", "auto", { |
| 345 | resolve: function (t, e) { |
| 346 | var i, |
| 347 | c = t.getHelpers(e, "hint"); |
| 348 | if (c.length) { |
| 349 | e = function (t, n, o) { |
| 350 | var s = (function (t, e) { |
| 351 | if (!t.somethingSelected()) return e; |
| 352 | for (var i = [], n = 0; n < e.length; n++) e[n].supportsSelection && i.push(e[n]); |
| 353 | return i; |
| 354 | })(t, c); |
| 355 | !(function e(i) { |
| 356 | if (i == s.length) return n(null); |
| 357 | r(s[i], t, o, function (t) { |
| 358 | t && 0 < t.list.length ? n(t) : e(i + 1); |
| 359 | }); |
| 360 | })(0); |
| 361 | }; |
| 362 | return (e.async = !0), (e.supportsSelection = !0), e; |
| 363 | } |
| 364 | return (i = t.getHelper(t.getCursor(), "hintWords")) |
| 365 | ? function (t) { |
| 366 | return T.hint.fromList(t, { words: i }); |
| 367 | } |
| 368 | : T.hint.anyword |
| 369 | ? function (t, e) { |
| 370 | return T.hint.anyword(t, e); |
| 371 | } |
| 372 | : function () {}; |
| 373 | }, |
| 374 | }), |
| 375 | T.registerHelper("hint", "fromList", function (t, e) { |
| 376 | var i, |
| 377 | n = t.getCursor(), |
| 378 | o = t.getTokenAt(n), |
| 379 | s = T.Pos(n.line, o.start), |
| 380 | t = n; |
| 381 | o.start < n.ch && /\w/.test(o.string.charAt(n.ch - o.start - 1)) ? (i = o.string.substr(0, n.ch - o.start)) : ((i = ""), (s = n)); |
| 382 | for (var c = [], r = 0; r < e.words.length; r++) { |
| 383 | var l = e.words[r]; |
| 384 | l.slice(0, i.length) == i && c.push(l); |
| 385 | } |
| 386 | if (c.length) return { list: c, from: s, to: t }; |
| 387 | }), |
| 388 | (T.commands.autocomplete = T.showHint); |
| 389 | var c = { |
| 390 | hint: T.hint.auto, |
| 391 | completeSingle: !0, |
| 392 | alignWithWord: !0, |
| 393 | closeCharacters: /[\s()\[\]{};:>,]/, |
| 394 | closeOnPick: !0, |
| 395 | closeOnUnfocus: !0, |
| 396 | updateOnCursorActivity: !0, |
| 397 | completeOnSingleClick: !0, |
| 398 | container: null, |
| 399 | customKeys: null, |
| 400 | extraKeys: null, |
| 401 | paddingForScrollbar: !0, |
| 402 | moveOnOverlap: !0, |
| 403 | }; |
| 404 | T.defineOption("hintOptions", null); |
| 405 | }); |
| 406 |