├── README.md ├── index.html ├── kakoune-explain.css ├── kakoune-explain.js ├── kakoune-logo.svg ├── keys.css ├── keys.html ├── keys.js ├── react-dom.development.js └── react.development.js /README.md: -------------------------------------------------------------------------------- 1 | # Kakoune Explain 2 | 3 | Interactive tool: 4 | https://delapouite.github.io/kakoune-explain/ 5 | 6 | Cheatsheet: 7 | https://delapouite.github.io/kakoune-explain/keys.html 8 | 9 | ## Discussions 10 | 11 | ### `count` 12 | 13 | - [Allow counts for text object start/end selections](https://github.com/mawww/kakoune/issues/795) 14 | 15 | ### `0` 16 | 17 | - [Add `0` commands to pipe-all with NUL](https://github.com/mawww/kakoune/issues/3331) 18 | 19 | ### `g.` 20 | 21 | - [Restore multiple-selections upon hitting `g.`](https://github.com/mawww/kakoune/issues/3037) 22 | 23 | ### `` 24 | 25 | - [`` command should not append spaces for empty lines](https://github.com/mawww/kakoune/issues/1169) 26 | 27 | ### `t` 28 | 29 | - [`` behavior with `t`](https://github.com/mawww/kakoune/issues/1637) 30 | 31 | ### `w`, `` 32 | 33 | - [`w` and `` do not work as expected when end of selection falls on end of word](https://github.com/mawww/kakoune/issues/3132) 34 | 35 | ### `x` 36 | 37 | - [Change slightly how `x`/`X` works](https://github.com/mawww/kakoune/issues/2590) 38 | 39 | ### `` 40 | 41 | - [`` should always prompt for a combining operator](https://github.com/mawww/kakoune/issues/3374) 42 | - [`` does not work with empty registers](https://github.com/mawww/kakoune/issues/3315) 43 | 44 | ### `%` 45 | 46 | - [`%` should not move cursor](https://github.com/mawww/kakoune/issues/2982) 47 | 48 | ### `` 49 | 50 | - [`` behavior with `t`](https://github.com/mawww/kakoune/issues/1637) 51 | 52 | ### `!`, `` 53 | 54 | - [Select the data inserted by the `!` and `` primitives](https://github.com/mawww/kakoune/issues/1468) -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Kakoune Explain 6 | 7 | 8 | 9 |
10 |

Kakoune Explain (alpha)

11 | 12 |
13 | 14 |
15 | Mode: 16 | | Count: 17 |
18 |

key(s)

19 |
20 |
21 | 22 |

Examples

23 |

Click one of these lines to fill the textarea

24 |
    25 |
  • fuf}dOf=f;<esc>P
  • 26 |
  • xsb|l<ret>2E<a-'>
  • 27 |
  • Qxs,<ret>a<ret><esc><a-x>s:<ret>&<esc><space>jq
  • 28 |
  • "_dihello<esc>"ap%|sort<ret><a-m>
  • 29 |
30 | 31 |

About

32 |
33 |
34 |  ___          
35 | (__ \         
36 |   / /         
37 |  .' '·.       
38 | '      ”      
39 | ╰       /\_/| 
40 |  | .         \
41 |  ╰_J`    | | |
42 |      ' \__- _/
43 |      \_\   \_\
44 | 		
45 |
46 |

This tool lets you type kakoune keys in the textarea at the top and translates them into plain English commands.

47 |

For more illustrated examples see Kakoune TV.

48 |

49 | Check keys.asciidoc, 50 | keys.html or 51 | the keyboard cheat sheet for help. 52 |

53 |

Enjoy! – Delapouite

54 |
55 |
56 |
57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /kakoune-explain.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: #333; 3 | color: #eee; 4 | font-family: Arial, Verdana, sans-serif; 5 | } 6 | 7 | a { 8 | color: #fff; 9 | } 10 | 11 | h1 { 12 | text-align: center; 13 | } 14 | 15 | h2 { 16 | margin-top: 2em; 17 | text-align: center; 18 | } 19 | 20 | main { 21 | margin: 0 auto; 22 | max-width: 940px; 23 | padding: 1em; 24 | } 25 | 26 | #kakoune-logo { 27 | height: 60px; 28 | vertical-align: middle; 29 | } 30 | 31 | #keys { 32 | background: #000; 33 | border: 2px solid #eee; 34 | border-radius: 3px; 35 | color: #eee; 36 | display: inline-block; 37 | font-size: 1.5em; 38 | margin-bottom: .5em; 39 | padding: .5em; 40 | text-align: left; 41 | width: 100%; 42 | } 43 | 44 | kbd { 45 | background: #f5f5f5; 46 | border-color: #ddd #bbb #bbb #ddd; 47 | border-radius: 3px; 48 | border-style: solid; 49 | border-width: 1px 1px 2px; 50 | color: #000; 51 | font-size: 1.2em; 52 | font-weight: bold; 53 | padding: .2em .5em; 54 | white-space: nowrap; 55 | } 56 | 57 | .macro { 58 | left: -1em; 59 | position: absolute; 60 | } 61 | 62 | kbd.kbd-count { 63 | background-color: #fff; 64 | border-color: #fff; 65 | } 66 | 67 | kbd.kbd-insert { 68 | background-color: #99a969; 69 | border-color: #99a969; 70 | } 71 | 72 | kbd.kbd-prompt { 73 | background-color: #1c758e; 74 | border-color: #1c758e; 75 | } 76 | 77 | kbd.kbd-lock { 78 | background-color: #1c758e; 79 | border-color: #1c758e; 80 | } 81 | 82 | kbd.kbd-reg { 83 | background-color: #ddaf3c; 84 | border-color: #ddaf3c; 85 | } 86 | 87 | /* */ 88 | kbd.kbd-validator { 89 | background-color: #999; 90 | border-color: #999; 91 | } 92 | 93 | dt { 94 | clear: left; 95 | float: left; 96 | margin-right: 1em; 97 | position: relative; 98 | } 99 | 100 | dd { 101 | margin-bottom: 1em; 102 | margin-left: 11em; 103 | white-space: nowrap; 104 | } 105 | 106 | dd em { 107 | background: #222; 108 | color: #CCC; 109 | padding: 0 5px; 110 | } 111 | 112 | .cancelled { 113 | opacity: 0.5; 114 | } 115 | 116 | dd.cancelled::after { 117 | content: ' ✖'; 118 | display: inline; 119 | } 120 | 121 | #examples { 122 | list-style: none; 123 | margin: 0; 124 | padding: 0; 125 | } 126 | 127 | #examples code { 128 | cursor: pointer; 129 | } 130 | 131 | 132 | #about { 133 | display: flex; 134 | } 135 | 136 | #about pre { 137 | margin-right: 2em; 138 | } 139 | -------------------------------------------------------------------------------- /kakoune-explain.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | (function () { 4 | 5 | const insertKeys = { 6 | 'a': 'insert [text] after selected text', 7 | 'A': 'insert [text] at line end', 8 | 'c': 'change selected text to [text]', 9 | '': 'change selected text to [text] without yanking', 10 | 'i': 'insert [text] before selected text', 11 | 'I': 'insert [text] at line begin', 12 | 'o': 'insert [text] on new line below', 13 | 'O': 'insert [text] on new line above', 14 | } 15 | 16 | const promptKeys = { 17 | '': 'keep selections matching regex [text]', 18 | '': 'keep selections not matching regex [text]', 19 | 's': 'select regex [text] matches in selected text', 20 | 'S': 'split selected text on regex [text] matches', 21 | '/': 'select next given regex [text] match', 22 | '': 'select previous given regex [text] match', 23 | '!': 'insert external [text] command output', 24 | '': 'append external [text] command output', 25 | '$': 'keep selections where external [text] command succeed', 26 | '|': 'pipe selections through external [text] command and replace', 27 | '': 'pipe selections through external [text] command and ignore', 28 | } 29 | 30 | const chooseKeys = { 31 | 'f': 'select to next [text] character included', 32 | 'F': 'extend to next [text] character included', 33 | '': 'select to previous [text] character included', 34 | '': 'extend to previous [text] character included', 35 | 'g': 'go to [text]', 36 | 'G': 'extend to [text]', 37 | 'r': 'replace with character [text]', 38 | 't': 'select to next [text] character', 39 | 'T': 'extend to next [text] character', 40 | '': 'select to previous [text] character', 41 | '': 'extend to previous [text] character', 42 | 'v': 'view [text]', 43 | '': 'combine selections from register ([text])', 44 | '': 'combine selections to register ([text])', 45 | '': 'select around object [text]', 46 | '': 'select inner object [text]', 47 | '[': 'select to surrounding object begin [text]', 48 | ']': 'select to surrounding object end [text]', 49 | '{': 'extend to surrounding object begin [text]', 50 | '}': 'extend to surrounding object end [text]', 51 | '': 'select to inner surrounding object begin [text]', 52 | '': 'select to inner surrounding object end [text]', 53 | '': 'extend to inner surrounding object begin [text]', 54 | '': 'extend to inner surrounding object end [text]', 55 | } 56 | 57 | const lockKeys = { 58 | 'V': 'view (lock) [text]', 59 | } 60 | 61 | const keys = { 62 | 'b': 'select to previous word start', 63 | 'B': 'extend to previous word start', 64 | '': 'select to previous WORD start', 65 | '': 'extend to previous WORD start', 66 | 'C': 'copy selection on next lines', 67 | '': 'copy selection on previous lines', 68 | 'd': 'delete selected text to [text]', 69 | '': 'delete selected text without yanking', 70 | 'e': 'select to next word end', 71 | 'E': 'extend to next word end', 72 | '': 'select to next WORD end', 73 | '': 'extend to next WORD end', 74 | 'g': 'go to line', 75 | 'h': 'move left ←', 76 | 'H': 'extend left ⇐', 77 | '': 'select to line begin', 78 | '': 'extend to line begin', 79 | 'j': 'move down ↓', 80 | 'J': 'extend down ⇓', 81 | '': 'join lines', 82 | '': 'join lines and select spaces', 83 | 'k': 'move up ↑', 84 | 'K': 'extend up ⇑', 85 | 'l': 'move right →', 86 | 'L': 'extend right ⇒', 87 | '': 'select to line end', 88 | '': 'extend to line end', 89 | 'm': 'select to next matching character', 90 | 'M': 'extend to next matching character', 91 | '': 'select to previous matching character', 92 | '': 'extend to previous matching character', 93 | 'n': 'select next current search pattern match', 94 | 'N': 'extend with next current search pattern match', 95 | '': 'select previous current search pattern match', 96 | '': 'extend with previous current search pattern match', 97 | '': 'add new empty line below ↓', 98 | '': 'add new empty line above ↑', 99 | 'p': 'paste [text] after selected text', 100 | 'P': 'paste [text] before selected text', 101 | '': 'paste every yanked selection after selected text', 102 | '': 'paste every yanked selection before selected text', 103 | 'q': 'replay recorded macro ▶', 104 | 'R': 'replace selected text with yanked text', 105 | '': 'replace selected text with every yanked text', 106 | '': 'split selections on line boundaries', 107 | '': 'select first and last characters of each selection', 108 | 'u': 'undo ←', 109 | 'U': 'redo →', 110 | '': 'move backward in history ←', 111 | '': 'move forward in history →', 112 | 'w': 'select to next word start', 113 | 'W': 'extend to next word start', 114 | '': 'select to next WORD start', 115 | '': 'extend to next WORD start', 116 | 'x': 'select line', 117 | 'X': 'extend line', 118 | '': 'extend selections to whole lines', 119 | '': 'crop selections to whole lines', 120 | 'y': 'yank selected text to [text]', 121 | 'z': 'restore selections from register', 122 | 'Z': 'save selections to register', 123 | '': 'remove all selections except main', 124 | '': 'remove main selection', 125 | '&': 'align selection cursors', 126 | '': 'copy indentation', 127 | '~': 'convert to upper case in selections', 128 | '`': 'convert to lower case in selections', 129 | '': 'swap case in selections', 130 | '*': 'set search pattern to main selection content', 131 | '': 'set search pattern to main selection content, do not detect words', 132 | '%': 'select whole buffer', 133 | '.': 'repeat last insert command', 134 | '': 'repeat last object select / character find', 135 | '<': 'dedent', 136 | '': 'dedent, not including incomplete indent', 137 | '>': 'indent', 138 | '': 'indent, including empty lines', 139 | ")": 'rotate main selection forward →', 140 | "(": 'rotate main selection backward ←', 141 | '': 'rotate selections content forward →', 142 | '': 'rotate selections content backward ←', 143 | ';': 'reduce selections to their cursor', 144 | '': 'swap selections cursor and anchor', 145 | '': 'reduce selections to their cursor', 146 | '': 'swap selections cursor and anchor', 147 | '': 'ensure selection cursor is after anchor', 148 | '\\': 'disable hooks', 149 | '_': 'trim selections', 150 | '': 'merge continuous selections together', 151 | ',': 'user mode', 152 | } 153 | 154 | function tokenize (keys) { 155 | return escapeB(keys) 156 | .replace(/<((c-.|a-.)|(c-|a-)?(ret|space|tab|lt|gt|backspace|esc|up|down|left|right|pageup|pagedown|home|end|backtab|del))>|./g, '¤$&') 157 | .split('¤') 158 | .slice(1) 159 | } 160 | 161 | function annotate (tokens) { 162 | var t // current token 163 | var mode = 'n' 164 | 165 | // insert and prompt buffers have to be separate because of oneshot 166 | var insertBuffer = [] 167 | var promptBuffer = [] 168 | var countBuffer = [] 169 | var lockBuffer = [] 170 | 171 | var op 172 | 173 | var usingRegister = false 174 | var macroRecording = false 175 | // triggered by 176 | var oneShot = false 177 | 178 | /* will be filled with log object like this 179 | { 180 | cancelled: bool, 181 | macro: bool, 182 | // left 183 | count: * 184 | reg: ? 185 | op: ? 186 | key: ? 187 | insert: * 188 | prompt: * 189 | validator: ? / 190 | // right 191 | dt: * 192 | } */ 193 | var logs = [] 194 | 195 | // shortcut 196 | function push(key, dt) { 197 | logs.push({ key, dt, macro: macroRecording }) 198 | } 199 | 200 | while (t = tokens.shift()) { 201 | 202 | switch (mode) { 203 | 204 | // insert mode 205 | case 'i': 206 | if (t === '') { 207 | logs[logs.length - 1].validator = t 208 | insertBuffer = [] 209 | mode = 'n' 210 | } else if (t === '' || t === '') { 211 | insertBuffer = [] 212 | push(t, 'escape to normal mode for a single command') 213 | mode = 'n' 214 | oneShot = true 215 | continue 216 | } else if (t !== '' && t !== '') { 217 | insertBuffer.push(t) 218 | logs[logs.length - 1].insert = insertBuffer.join('') 219 | continue 220 | } 221 | break 222 | 223 | // prompt mode 224 | case 'p': 225 | if (t === '' || t === '') { 226 | logs[logs.length - 1].validator = t 227 | logs[logs.length - 1].cancelled = t === '', 228 | promptBuffer = [] 229 | mode = 'n' 230 | } else { 231 | promptBuffer.push(t) 232 | logs[logs.length - 1].prompt = promptBuffer.join('') 233 | continue 234 | } 235 | break 236 | 237 | // choose mode 238 | case 'c': 239 | if (!usingRegister) { 240 | logs.push({ 241 | macro: macroRecording, 242 | op: op[0], 243 | prompt: t, 244 | dt: op[1] 245 | }) 246 | } else { 247 | usingRegister = t 248 | } 249 | mode = 'n' 250 | continue 251 | 252 | // view lock mode 253 | case 'V': 254 | if (t === '') { 255 | logs[logs.length - 1].validator = t 256 | lockBuffer = [] 257 | mode = 'n' 258 | } else { 259 | lockBuffer.push(t) 260 | logs[logs.length - 1].lock = lockBuffer.join('') 261 | continue 262 | } 263 | break 264 | 265 | // normal mode 266 | default: 267 | if (insertKeys[t]) { 268 | mode = 'i' 269 | logs.push({ 270 | macro: macroRecording, 271 | op: t, 272 | insert: '…', 273 | dt: insertKeys[t], 274 | }) 275 | } else if (promptKeys[t]) { 276 | mode = 'p' 277 | logs.push({ 278 | macro: macroRecording, 279 | op: t, 280 | prompt: '…', 281 | dt: promptKeys[t], 282 | }) 283 | } else if (chooseKeys[t]) { 284 | if (countBuffer.length && t === 'g') { 285 | push(t, keys[t]) 286 | } else { 287 | op = [t, chooseKeys[t]] 288 | mode = 'c' 289 | } 290 | } else if (lockKeys[t]) { 291 | mode = 'V' 292 | logs.push({ 293 | macro: macroRecording, 294 | op: t, 295 | lock: '…', 296 | dt: lockKeys[t], 297 | }) 298 | } else if (keys[t]) { 299 | push(t, keys[t]) 300 | } else if (!isNaN(Number(t)) && t !== ' ') { 301 | // beware of spaces: Number(' ') === 0 302 | countBuffer.push(t) 303 | } else { 304 | // special keys 305 | switch (t) { 306 | case '': 307 | push(t, 'stop macro recording ◼') 308 | macroRecording = false 309 | break 310 | 311 | case '"': 312 | usingRegister = true 313 | mode = 'c' 314 | continue 315 | 316 | case 'Q': 317 | if (macroRecording) { 318 | push(t, 'stop macro recording ◼') 319 | } else { 320 | push(t, 'start macro recording in [text] ⚫') 321 | } 322 | macroRecording = !macroRecording 323 | break 324 | 325 | case '': 326 | case '': 327 | if (insertBuffer.length) { 328 | logs.push({ 329 | macro: macroRecording, 330 | op: op[0], 331 | insert: insertBuffer.join(''), 332 | dt: op[1] 333 | }) 334 | insertBuffer = [] 335 | } 336 | push(t, 'select to line ' + (t === 'home' ? 'begin' : 'end')) 337 | break 338 | 339 | default: 340 | console.debug('unknown token', t) 341 | } 342 | } 343 | break 344 | } 345 | 346 | if (oneShot) { 347 | oneShot = false 348 | mode = 'i' 349 | logs.push({ 350 | macro: macroRecording, 351 | op: '', 352 | insert: insertBuffer.join(''), 353 | dt: 'insert [text]', 354 | }) 355 | } 356 | 357 | // count 358 | 359 | if (countBuffer.length && isNaN(Number(t))) { 360 | logs[logs.length - 1].count = countBuffer.join('') 361 | countBuffer = [] 362 | } 363 | 364 | // register 365 | if (usingRegister) { 366 | logs[logs.length - 1].reg = usingRegister 367 | usingRegister = false 368 | } 369 | } 370 | 371 | return { 372 | logs, 373 | mode, 374 | countBuffer, 375 | } 376 | } 377 | 378 | function getRegName (reg, key) { 379 | if (!reg) { 380 | switch (key) { 381 | case 'd': 382 | case 'y': 383 | case 'p': 384 | case 'P': return '" (default yank)' 385 | case 'q': 386 | case 'Q': return '@ (default macro)' 387 | case 'z': 388 | case 'Z': return '^ (default mark)' 389 | } 390 | } 391 | const regs = { 392 | '%': '% (current buffer name)', 393 | '.': '. (current selection contents)', 394 | '#': '# (selection indices)', 395 | '_': '_ (null)', 396 | '/': '/ (default search)', 397 | '@': '@ (default macro)', 398 | '^': '^ (default mark)', 399 | '|': '| (default shell)', 400 | ':': ': (last entered command)', 401 | } 402 | return regs[reg] || reg 403 | } 404 | 405 | function getGotoName (str, key = '') { 406 | if (key.toLowerCase() !== 'g') return str 407 | const gotos = { 408 | 'g': 'buffer top', 409 | 'k': 'buffer top', 410 | 'l': 'line end', 411 | 'h': 'line begin', 412 | 'i': 'line non blank start', 413 | 'j': 'buffer bottom', 414 | 't': 'window top', 415 | 'b': 'window bottom', 416 | 'c': 'window center', 417 | 'a': 'last buffer', 418 | '.': 'last buffer change', 419 | } 420 | return gotos[str] || 'INVALID' 421 | } 422 | 423 | function getViewName (str, key = '') { 424 | if (key.toLowerCase() !== 'v') return str 425 | const views = { 426 | 'v': 'center cursor vertically', 427 | 'c': 'center cursor vertically', 428 | 'm': 'center cursor horizontally', 429 | 't': 'cursor on top', 430 | 'b': 'cursor on bottom', 431 | 'h': 'scroll left', 432 | 'j': 'scroll down', 433 | 'k': 'scroll up', 434 | 'l': 'scroll right', 435 | } 436 | return views[str] || 'INVALID' 437 | } 438 | 439 | function getModeName (str) { 440 | const modes = { 441 | 'i':'insert (type to return to normal mode)', 442 | 'p':'prompt (type or to return to normal mode)', 443 | 'c':'enter key', 444 | 'V':'view lock (type to return to normal mode)', 445 | } 446 | return modes[str] || 'normal' 447 | } 448 | 449 | function getComboName (str) { 450 | const combos = { 451 | 'a': 'append', 452 | 'u': 'union', 453 | 'i': 'intersection', 454 | '<': 'select leftmost cursor', 455 | '>': 'select rightmost cursor', 456 | '+': 'select longest', 457 | '-': 'select shortest', 458 | } 459 | return combos[str] || 'INVALID' 460 | } 461 | 462 | function escapeB (keys) { 463 | return keys 464 | } 465 | 466 | // UI 467 | 468 | const $ = document.querySelector.bind(document) 469 | const h = document.createElement.bind(document) 470 | 471 | function createDl (annotations) { 472 | const dl = h('dl') 473 | 474 | annotations.forEach((a) => { 475 | dl.appendChild(createDt(a)) 476 | dl.appendChild(createDd(a)) 477 | }) 478 | 479 | return dl 480 | } 481 | 482 | // left part with keys 483 | function createDt (a) { 484 | const dt = h('dt') 485 | let kbd 486 | let macro 487 | 488 | function createKbd (c, k) { 489 | kbd = h('kbd') 490 | kbd.textContent = c 491 | if (k) { 492 | kbd.classList.add(`kbd-${k}`) 493 | } 494 | dt.appendChild(kbd) 495 | } 496 | 497 | // macro track 498 | if (a.key === 'Q') { 499 | if (a.dt.indexOf('start') !== -1) { 500 | macro = h('span') 501 | macro.textContent = '╔' 502 | macro.classList.add('macro') 503 | } else { 504 | macro = h('span') 505 | macro.textContent = '╚' 506 | macro.classList.add('macro') 507 | } 508 | } else if (a.macro) { 509 | macro = h('span') 510 | macro.textContent = '║' 511 | macro.classList.add('macro') 512 | } 513 | if (a.macro && a.key === '') { 514 | macro = h('span') 515 | macro.textContent = '╚' 516 | macro.classList.add('macro') 517 | } 518 | if (macro) dt.appendChild(macro) 519 | 520 | if (a.count) createKbd(a.count, 'count') 521 | if (a.reg) { 522 | createKbd('"') 523 | createKbd(a.reg, 'reg') 524 | } 525 | if (a.key) createKbd(a.key) 526 | if (a.op) createKbd(a.op) 527 | if (a.insert) createKbd(a.insert, 'insert') 528 | if (a.prompt) createKbd(a.prompt, 'prompt') 529 | if (a.lock) createKbd(a.lock, 'lock') 530 | if (a.validator) createKbd(a.validator, 'validator') 531 | // TODO handle INVALID 532 | if (a.cancelled) dt.classList.add('cancelled') 533 | 534 | return dt 535 | } 536 | 537 | // right part with english translation 538 | function createDd (a) { 539 | var dd = h('dd') 540 | if (a.dt.indexOf('[text]') === -1) { 541 | dd.textContent = a.dt 542 | } else { 543 | var m = a.dt.split('[text]') 544 | var pre = h('span') 545 | pre.textContent = m[0] 546 | 547 | var text = h('em') 548 | text.textContent = a.insert 549 | || (a.op && 'gG'.includes(a.op) && getGotoName(a.prompt, a.op)) 550 | || (a.op && 'v'.includes(a.op) && getViewName(a.prompt, a.op)) 551 | || (a.op && ['', ''].includes(a.op) && getComboName(a.prompt)) 552 | || a.prompt || a.lock 553 | || `${getRegName(a.reg, a.key)} register` 554 | 555 | var post = h('span') 556 | post.textContent = m[1] 557 | 558 | dd.appendChild(pre) 559 | dd.appendChild(text) 560 | dd.appendChild(post) 561 | } 562 | if (a.count) { 563 | if (a.key === 'g') { 564 | let line = h('em') 565 | line.textContent = a.count 566 | dd.appendChild(line) 567 | } else { 568 | let count = h('strong') 569 | count.textContent = ` (${a.count} times)` 570 | dd.appendChild(count) 571 | } 572 | } 573 | if (a.cancelled) dd.classList.add('cancelled') 574 | 575 | return dd 576 | } 577 | 578 | function render () { 579 | const keys = $('#keys').value.trim() 580 | const tokens = tokenize(keys) 581 | 582 | $('#total').textContent = 583 | tokens.length > 1 584 | ? `${tokens.length} keys` 585 | : tokens.length === 1 586 | ? '1 key' 587 | : 'no keys' 588 | 589 | const { logs, mode, countBuffer } = annotate(tokens) 590 | $('#mode').textContent = getModeName(mode) 591 | $('#count').textContent = countBuffer.join('') || 0 592 | $('#count-holder').style.display = (!countBuffer.length) ? 'none' : 'inline' 593 | 594 | const annotations = $('#annotations') 595 | annotations.innerHTML = '' 596 | annotations.appendChild(createDl(logs)) 597 | 598 | // sharing 599 | window.location.hash = encodeURIComponent(keys) 600 | } 601 | 602 | $('#examples').onclick = function (event) { 603 | if (event.target.nodeName !== 'CODE') return 604 | $('#keys').value = event.target.textContent 605 | render() 606 | } 607 | 608 | $('#keys').onkeyup = render 609 | $('#keys').onchange = render 610 | if (window.location.hash) 611 | $('#keys').value = decodeURIComponent(window.location.hash.slice(1)) 612 | 613 | // init 614 | render() 615 | 616 | })() 617 | -------------------------------------------------------------------------------- /kakoune-logo.svg: -------------------------------------------------------------------------------- 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 | 51 | 53 | 54 | 55 | 56 | 57 | 59 | 61 | 62 | 63 | 64 | 65 | 67 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 79 | 81 | 83 | 85 | 86 | 87 | 88 | 90 | 91 | 93 | 95 | 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /keys.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | font-family: Arial, Verdana, sans-serif; 3 | font-size: 12px; 4 | } 5 | 6 | label { 7 | font-weight: bold; 8 | white-space: nowrap; 9 | } 10 | 11 | input, span.query { 12 | margin: 0 1em; 13 | } 14 | 15 | span.query { 16 | cursor: pointer; 17 | } 18 | 19 | table { 20 | width: 100%; 21 | } 22 | 23 | th { 24 | font-size: 12px; 25 | padding: .2em .8em; 26 | } 27 | 28 | td { 29 | background: #f9f9f9; 30 | font-size: 12px; 31 | padding: .8em; 32 | } 33 | 34 | td.exist { 35 | background: #eee; 36 | } 37 | 38 | div.tags { 39 | color: #666; 40 | } 41 | -------------------------------------------------------------------------------- /keys.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Kakoune keys 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | -------------------------------------------------------------------------------- /keys.js: -------------------------------------------------------------------------------- 1 | /* global React, ReactDOM */ 2 | const h = React.createElement 3 | 4 | const keys = [ 5 | { 6 | key: 'a', 7 | normal: { 8 | base: 'insert after selection | →', 9 | alt: 'select the whole object', 10 | }, 11 | prompt: { 12 | ctrl: 'go to line start | ←', 13 | }, 14 | goto: { 15 | base: 'last buffer | jump', 16 | }, 17 | object: { 18 | base: 'angle block | object', 19 | }, 20 | combine: { 21 | base: 'append', 22 | }, 23 | }, 24 | { 25 | key: 'A', 26 | normal: { 27 | base: 'insert at line end | →', 28 | }, 29 | }, 30 | { 31 | key: 'b', 32 | normal: { 33 | base: 'select to previous word start | ← count', 34 | alt: 'select to previous WORD start | ← count', 35 | ctrl: 'scroll one page up | ↑ count', 36 | }, 37 | prompt: { 38 | ctrl: 'move to previous char | ←', 39 | alt: 'go to previous word start | ←' 40 | }, 41 | goto: { 42 | base: 'window bottom | ↓', 43 | }, 44 | view: { 45 | base: 'cursor on bottom | ↓', 46 | }, 47 | object: { 48 | base: 'parenthesis block | object', 49 | }, 50 | }, 51 | { 52 | key: 'B', 53 | normal: { 54 | base: 'extend to previous word start | ← count', 55 | alt: 'extend to previous WORD start | ← count', 56 | }, 57 | prompt: { 58 | alt: 'go to previous WORD start | ←' 59 | }, 60 | object: { 61 | base: 'braces block | object', 62 | }, 63 | }, 64 | { 65 | key: 'c', 66 | normal: { 67 | base: 'change selection content | register-" | di', 68 | alt: 'change selection content (not yanking) | i', 69 | }, 70 | goto: { 71 | base: 'window center', 72 | }, 73 | view: { 74 | base: 'center cursor vertically', 75 | }, 76 | object: { 77 | base: 'custom desc | object', 78 | }, 79 | }, 80 | { 81 | key: 'C', 82 | normal: { 83 | base: 'copy selection on next line | ↓ count', 84 | alt: 'copy selection on previous line | ↑ count opposite', 85 | }, 86 | }, 87 | { 88 | key: 'd', 89 | normal: { 90 | base: 'delete selection content | register-"', 91 | alt: 'delete selection content (not yanking)', 92 | ctrl: 'scroll half a page down | ↓ count', 93 | }, 94 | prompt: { 95 | ctrl: 'delete char under cursor', 96 | alt: 'delete to previous word end | ←' 97 | }, 98 | }, 99 | { 100 | key: 'D', 101 | prompt: { 102 | alt: 'delete to previous WORD end | ←' 103 | }, 104 | }, 105 | { 106 | key: 'e', 107 | normal: { 108 | base: 'select to next word end | → count', 109 | alt: 'select to next WORD end | → count', 110 | }, 111 | prompt: { 112 | ctrl: 'go to line end | →', 113 | alt: 'go to next word end | →' 114 | }, 115 | goto: { 116 | base: 'buffer end | ↓→ jump | %;', 117 | }, 118 | }, 119 | { 120 | key: 'E', 121 | normal: { 122 | base: 'extend to next word end | → count', 123 | alt: 'extend to next WORD end | → count', 124 | }, 125 | prompt: { 126 | alt: 'go to next WORD end | →' 127 | } 128 | }, 129 | { 130 | key: 'f', 131 | normal: { 132 | base: 'select to next char included | → count | tL', 133 | alt: 'select to previous char included | ← count opposite | H', 134 | ctrl: 'scroll one page down | ↓ count', 135 | }, 136 | prompt: { 137 | ctrl: 'move to next char | →', 138 | alt: 'go to next word start | →', 139 | }, 140 | goto: { 141 | base: 'included file | jump', 142 | }, 143 | }, 144 | { 145 | key: 'F', 146 | normal: { 147 | base: 'extend to next char included | → count | TL', 148 | alt: 'extend to previous char included | ← count opposite | H', 149 | }, 150 | prompt: { 151 | alt: 'go to next WORD start | →' 152 | } 153 | }, 154 | { 155 | key: 'g', 156 | normal: { 157 | base: 'go to location | count jump', 158 | }, 159 | goto: { 160 | base: 'buffer top | ↑← | %;', 161 | }, 162 | object: { 163 | base: 'grave quote string | object', 164 | }, 165 | }, 166 | { 167 | key: 'G', 168 | normal: { 169 | base: 'extend to location', 170 | }, 171 | }, 172 | { 173 | key: 'h', 174 | normal: { 175 | base: 'move left | ← count | ', 176 | alt: 'select to line start | ← | ', 177 | }, 178 | prompt: { 179 | ctrl: 'delete char before cursor | ←', 180 | }, 181 | goto: { 182 | base: 'line start | ← | x;', 183 | }, 184 | view: { 185 | base: 'scroll left | ←', 186 | }, 187 | }, 188 | { 189 | key: 'H', 190 | normal: { 191 | base: 'extend left | ← count | ', 192 | alt: 'extend to line start | ← | ', 193 | }, 194 | }, 195 | { 196 | key: 'i', 197 | normal: { 198 | base: 'insert before selection | ←', 199 | alt: 'select inner object', 200 | ctrl: 'jump forward | ↓→ count', 201 | }, 202 | goto: { 203 | base: 'line non blank start | ←→', 204 | }, 205 | object: { 206 | base: 'indent | object', 207 | }, 208 | combine: { 209 | base: 'intersection', 210 | }, 211 | }, 212 | { 213 | key: 'I', 214 | normal: { 215 | base: 'insert at line start | ←', 216 | }, 217 | }, 218 | { 219 | key: 'j', 220 | normal: { 221 | base: 'move down | ↓ count | ', 222 | alt: 'join lines', 223 | }, 224 | goto: { 225 | base: 'buffer bottom | ↓ jump', 226 | }, 227 | view: { 228 | base: 'scroll down | ↓', 229 | }, 230 | }, 231 | { 232 | key: 'J', 233 | normal: { 234 | base: 'extend down | ↓ count | ', 235 | alt: 'join lines and select spaces', 236 | }, 237 | }, 238 | { 239 | key: 'k', 240 | normal: { 241 | base: 'move up | ↑ count | ', 242 | alt: 'keep selections matching given regex', 243 | }, 244 | prompt: { 245 | ctrl: 'delete to line end | →', 246 | }, 247 | goto: { 248 | base: 'buffer top | ↑← jump | %;', 249 | }, 250 | view: { 251 | base: 'scroll up | ↑', 252 | }, 253 | }, 254 | { 255 | key: 'K', 256 | normal: { 257 | base: 'extend up | ↑ count | ', 258 | alt: 'keep selections not matching given regex', 259 | }, 260 | }, 261 | { 262 | key: 'l', 263 | normal: { 264 | base: 'move right | → count | ', 265 | alt: 'select to line end | → | ', 266 | ctrl: 'clear screen', 267 | }, 268 | goto: { 269 | base: 'line end | → | xh', 270 | }, 271 | view: { 272 | base: 'scroll right | →', 273 | }, 274 | }, 275 | { 276 | key: 'L', 277 | normal: { 278 | base: 'extend right | → count | ', 279 | alt: 'extend to line end | → | ', 280 | }, 281 | }, 282 | { 283 | key: 'm', 284 | normal: { 285 | base: 'select to next matching char | →', 286 | alt: 'select to previous matching char | ← opposite', 287 | }, 288 | view: { 289 | base: 'center cursor horizontally', 290 | }, 291 | }, 292 | { 293 | key: 'M', 294 | normal: { 295 | base: 'extend to next matching char | →', 296 | alt: 'extend to previous matching char | ← opposite', 297 | }, 298 | }, 299 | { 300 | key: 'n', 301 | normal: { 302 | base: 'select next search pattern match | → count', 303 | alt: 'select previous search pattern match | ← count opposite', 304 | }, 305 | prompt: { 306 | ctrl: 'next history entry | ↓→', 307 | }, 308 | insert: { 309 | ctrl: 'next completion candidate | ↓→', 310 | }, 311 | object: { 312 | base: 'number | object', 313 | }, 314 | }, 315 | { 316 | key: 'N', 317 | normal: { 318 | base: 'extend with next search pattern match | → count', 319 | alt: 'extend with previous search pattern match | ← count opposite', 320 | }, 321 | }, 322 | { 323 | key: 'o', 324 | normal: { 325 | base: 'insert on a new line below | ↓ count', 326 | alt: 'add an empty line below cursor | ↓ count', 327 | ctrl: 'jump backward | ↑← count', 328 | }, 329 | insert: { 330 | ctrl: 'toggle autocompletion for this insert session', 331 | }, 332 | prompt: { 333 | ctrl: 'toggle autocompletion for this prompt', 334 | }, 335 | }, 336 | { 337 | key: 'O', 338 | normal: { 339 | base: 'insert on a new line above | ↑ count opposite', 340 | alt: 'add an empty line above cursor | ↑ count opposite', 341 | }, 342 | }, 343 | { 344 | key: 'p', 345 | normal: { 346 | base: 'paste after | →', 347 | alt: 'paste all after | →', 348 | }, 349 | prompt: { 350 | ctrl: 'previous history entry | ↑←', 351 | }, 352 | insert: { 353 | ctrl: 'previous completion candidate | ↑←', 354 | }, 355 | }, 356 | { 357 | key: 'P', 358 | normal: { 359 | base: 'paste before | ← opposite', 360 | alt: 'paste all before | ← opposite', 361 | }, 362 | object: { 363 | base: 'paragraph | object', 364 | }, 365 | }, 366 | { 367 | key: 'q', 368 | normal: { 369 | base: 'replay recorded macro | count register-@', 370 | }, 371 | object: { 372 | base: 'single quote string | object', 373 | }, 374 | }, 375 | { 376 | key: 'Q', 377 | normal: { 378 | base: 'start or end macro recording | register-@', 379 | }, 380 | object: { 381 | base: 'double quote string | object', 382 | }, 383 | }, 384 | { 385 | key: 'r', 386 | normal: { 387 | base: 'replace with char', 388 | }, 389 | insert: { 390 | ctrl: 'insert given register | register-"', 391 | }, 392 | prompt: { 393 | ctrl: 'insert given register | register-"', 394 | }, 395 | object: { 396 | base: 'brackets block | object', 397 | }, 398 | }, 399 | { 400 | key: 'R', 401 | normal: { 402 | base: 'replace selection content with paired yank text', 403 | alt: 'replace selection content with all yank text', 404 | }, 405 | }, 406 | { 407 | key: 's', 408 | normal: { 409 | base: 'select on regex', 410 | alt: 'split on line end', 411 | ctrl: 'push current selections on jump list', 412 | }, 413 | object: { 414 | base: 'sentence | object', 415 | }, 416 | }, 417 | { 418 | key: 'S', 419 | normal: { 420 | base: 'split on line boundaries', 421 | alt: 'select first and last characters of each selection', 422 | }, 423 | }, 424 | { 425 | key: 't', 426 | normal: { 427 | base: 'select till next char | → count | fH', 428 | alt: 'select till previous char | ← count opposite | L', 429 | }, 430 | goto: { 431 | base: 'window top | ↑', 432 | }, 433 | view: { 434 | base: 'cursor on top | ↑', 435 | }, 436 | }, 437 | { 438 | key: 'T', 439 | normal: { 440 | base: 'extend to next char | → count | FH', 441 | alt: 'extend to previous char | ← count opposite | L', 442 | }, 443 | }, 444 | { 445 | key: 'u', 446 | normal: { 447 | base: 'undo | count', 448 | alt: 'move backward in history | ↑← count', 449 | ctrl: 'scroll half a page up | ↑ count', 450 | }, 451 | insert: { 452 | ctrl: 'commit changes up to now as a single undo group', 453 | }, 454 | prompt: { 455 | ctrl: 'delete to line start | ←', 456 | }, 457 | object: { 458 | base: 'argument | object', 459 | }, 460 | combine: { 461 | base: 'union', 462 | }, 463 | }, 464 | { 465 | key: 'U', 466 | normal: { 467 | base: 'redo | count opposite', 468 | alt: 'move forward in history | ↓→ count opposite', 469 | }, 470 | }, 471 | { 472 | key: 'v', 473 | normal: { 474 | base: 'enter view mode | count', 475 | }, 476 | insert: { 477 | ctrl: 'insert raw keystroke', 478 | }, 479 | prompt: { 480 | ctrl: 'insert raw keystroke', 481 | }, 482 | view: { 483 | base: 'center cursor vertically', 484 | }, 485 | }, 486 | { 487 | key: 'V', 488 | normal: { 489 | base: 'lock view mode', 490 | }, 491 | }, 492 | { 493 | key: 'w', 494 | normal: { 495 | base: 'select to next word start | → count', 496 | alt: 'select to next WORD start | → count', 497 | }, 498 | prompt: { 499 | ctrl: 'delete to previous word start | ←', 500 | }, 501 | object: { 502 | base: 'word | object', 503 | }, 504 | }, 505 | { 506 | key: 'W', 507 | normal: { 508 | base: 'extend to next word start | → count', 509 | alt: 'extend to next WORD start | → count', 510 | }, 511 | prompt: { 512 | ctrl: 'delete to previous WORD start | ←', 513 | }, 514 | object: { 515 | base: 'WORD | object', 516 | }, 517 | }, 518 | { 519 | key: 'x', 520 | normal: { 521 | base: 'select line | count', 522 | alt: 'extend selections to whole lines', 523 | }, 524 | insert: { 525 | ctrl: 'choose completion mode', 526 | }, 527 | }, 528 | { 529 | key: 'X', 530 | normal: { 531 | base: 'extend line | ↓ count', 532 | alt: 'crop selections to whole lines', 533 | }, 534 | }, 535 | { 536 | key: 'y', 537 | normal: { 538 | base: 'yank | register-"', 539 | }, 540 | prompt: { 541 | ctrl: 'insert clipboard before cursor', 542 | }, 543 | }, 544 | { 545 | key: 'Y', 546 | }, 547 | { 548 | key: 'z', 549 | normal: { 550 | base: 'restore selections | register-^', 551 | alt: 'combine selections from register | register-^', 552 | }, 553 | }, 554 | { 555 | key: 'Z', 556 | normal: { 557 | base: 'save selections | register-^', 558 | alt: 'combine selections to register | register-^', 559 | }, 560 | }, 561 | { 562 | key: '!', 563 | normal: { 564 | base: 'insert command output | register-|', 565 | alt: 'append command output | register-|', 566 | }, 567 | prompt: { 568 | alt: 'expand the typed expansions', 569 | } 570 | }, 571 | { 572 | key: '$', 573 | normal: { 574 | base: 575 | 'pipe each selection through shell command and keep the ones whose command succeed | register-|', 576 | }, 577 | }, 578 | { 579 | key: '|', 580 | normal: { 581 | base: 582 | 'pipe each selection through filter and replace with output | register-|', 583 | alt: 'pipe each selection through filter and ignore output | register-|', 584 | }, 585 | register: { 586 | base: 'shell command', 587 | }, 588 | }, 589 | { 590 | key: '&', 591 | normal: { 592 | base: 'align cursors', 593 | alt: 'copy indentation', 594 | }, 595 | }, 596 | { 597 | key: '@', 598 | normal: { 599 | base: 'convert tabs to spaces', 600 | alt: 'convert spaces to tabs', 601 | }, 602 | register: { 603 | base: 'macro', 604 | }, 605 | }, 606 | { 607 | key: '<', 608 | normal: { 609 | base: 'deindent | ← count', 610 | alt: 'deindent not including incomplete indent | ←', 611 | }, 612 | object: { 613 | base: 'angle block | object', 614 | }, 615 | combine: { 616 | base: 'leftmost cursor', 617 | }, 618 | }, 619 | { 620 | key: '>', 621 | normal: { 622 | base: 'indent | → count opposite', 623 | alt: 'indent including empty lines | → opposite', 624 | }, 625 | object: { 626 | base: 'angle block | object', 627 | }, 628 | combine: { 629 | base: 'rightmost cursor', 630 | }, 631 | }, 632 | { 633 | key: '%', 634 | normal: { 635 | base: 'select whole buffer', 636 | }, 637 | register: { 638 | base: 'buffer name', 639 | }, 640 | }, 641 | { 642 | key: "'", 643 | object: { 644 | base: 'single quote string | object', 645 | }, 646 | }, 647 | { 648 | key: '"', 649 | normal: { 650 | base: 'choose register', 651 | }, 652 | register: { 653 | base: 'yank / paste / replace', 654 | }, 655 | object: { 656 | base: 'double quote string | object', 657 | }, 658 | }, 659 | { 660 | key: '; ', 661 | normal: { 662 | base: 'reduce to cursor', 663 | alt: 'flip selection direction', 664 | }, 665 | insert: { 666 | alt: 'escape to normal mode for single command', 667 | }, 668 | }, 669 | { 670 | key: '*', 671 | normal: { 672 | base: 'set search register to main selection content | register-/', 673 | alt: 674 | 'set search register to main selection content (do not detect word) | register-/', 675 | }, 676 | }, 677 | { 678 | key: '/', 679 | normal: { 680 | base: 'select next given regex match | → jump register-/', 681 | alt: 'select previous given regex match | ← jump register-/ opposite', 682 | }, 683 | register: { 684 | base: 'search', 685 | }, 686 | }, 687 | { 688 | key: '?', 689 | normal: { 690 | base: 'extend with next given regex match | → jump register-/', 691 | alt: 'extend with previous given regex match | ← jump register-/ opposite', 692 | }, 693 | }, 694 | { 695 | key: '[', 696 | normal: { 697 | base: 'select to object start | ←', 698 | alt: 'select to inner object start | ←', 699 | }, 700 | object: { 701 | base: 'brackets block | object', 702 | }, 703 | }, 704 | { 705 | key: ']', 706 | normal: { 707 | base: 'select to object end | →', 708 | alt: 'select to inner object end | →', 709 | }, 710 | object: { 711 | base: 'brackets block | object', 712 | }, 713 | }, 714 | { 715 | key: '{', 716 | normal: { 717 | base: 'extend to object start | ←', 718 | alt: 'extend to inner object start | ←', 719 | }, 720 | object: { 721 | base: 'braces block | object', 722 | }, 723 | }, 724 | { 725 | key: '}', 726 | normal: { 727 | base: 'extend to object end | →', 728 | alt: 'extend to inner object end | →', 729 | }, 730 | object: { 731 | base: 'braces block | object', 732 | }, 733 | }, 734 | { 735 | key: '(', 736 | normal: { 737 | base: 'rotate main selection backward | ←', 738 | alt: 'rotate selections contents backward | ←', 739 | }, 740 | object: { 741 | base: 'parenthesis block | object', 742 | }, 743 | }, 744 | { 745 | key: ')', 746 | normal: { 747 | base: 'rotate main selection forward | →', 748 | alt: 'rotate selections contents forward | →', 749 | }, 750 | object: { 751 | base: 'parenthesis block | object', 752 | }, 753 | }, 754 | { 755 | key: '`', 756 | normal: { 757 | base: 'convert to lower case', 758 | alt: 'swap case', 759 | }, 760 | object: { 761 | base: 'grave quote string', 762 | }, 763 | }, 764 | { 765 | key: '~', 766 | normal: { 767 | base: 'convert to upper case', 768 | }, 769 | }, 770 | { 771 | key: ',', 772 | normal: { 773 | base: 'user mappings', 774 | }, 775 | }, 776 | { 777 | key: '.', 778 | normal: { 779 | base: 'repeat last insert command', 780 | alt: 'repeat last object / char find', 781 | }, 782 | goto: { 783 | base: 'last buffer change | jump', 784 | }, 785 | register: { 786 | base: 'selection content', 787 | }, 788 | }, 789 | { 790 | key: '\\', 791 | normal: { 792 | base: 'disable hooks', 793 | }, 794 | }, 795 | { 796 | key: ':', 797 | normal: { 798 | base: 'enter command prompt', 799 | alt: 'ensure cursor is after anchor', 800 | }, 801 | register: { 802 | base: 'last entered command', 803 | }, 804 | }, 805 | { 806 | key: '#', 807 | register: { 808 | base: 'selection index', 809 | }, 810 | }, 811 | { 812 | key: '^', 813 | register: { 814 | base: 'mark', 815 | }, 816 | }, 817 | { 818 | key: '_', 819 | normal: { 820 | base: 'trim', 821 | alt: 'merge contiguous selections together', 822 | }, 823 | }, 824 | { 825 | key: '=', 826 | normal: { 827 | base: '', 828 | }, 829 | }, 830 | { 831 | key: '-', 832 | combine: { 833 | base: 'shortest', 834 | }, 835 | }, 836 | { 837 | key: '+', 838 | combine: { 839 | base: 'longest', 840 | }, 841 | }, 842 | { 843 | key: '', 844 | normal: { 845 | base: 'move up | ↑ count | k', 846 | }, 847 | insert: { 848 | base: 'move up | ↑', 849 | }, 850 | prompt: { 851 | base: 'previous history entry | ↑←', 852 | }, 853 | }, 854 | { 855 | key: '', 856 | normal: { 857 | base: 'extend up | ↑ count | K', 858 | }, 859 | }, 860 | { 861 | key: '', 862 | normal: { 863 | base: 'move down | ↓ count | j', 864 | }, 865 | insert: { 866 | base: 'move down | ↓', 867 | }, 868 | prompt: { 869 | base: 'next history entry | ↓→', 870 | }, 871 | }, 872 | { 873 | key: '', 874 | normal: { 875 | base: 'extend down | ↓ count | J', 876 | }, 877 | }, 878 | { 879 | key: '', 880 | normal: { 881 | base: 'move left | ← count | h', 882 | }, 883 | insert: { 884 | base: 'move left | ←', 885 | }, 886 | prompt: { 887 | base: 'move left | ←', 888 | }, 889 | }, 890 | { 891 | key: '', 892 | normal: { 893 | base: 'extend left | ← count | H', 894 | }, 895 | }, 896 | { 897 | key: '', 898 | normal: { 899 | base: 'move right | → count | l', 900 | }, 901 | insert: { 902 | base: 'move right | →', 903 | }, 904 | prompt: { 905 | base: 'move right | →', 906 | }, 907 | }, 908 | { 909 | key: '', 910 | normal: { 911 | base: 'extend right | → count | L', 912 | }, 913 | }, 914 | { 915 | key: '', 916 | normal: { 917 | base: 'select to line start | ← | ', 918 | }, 919 | prompt: { 920 | base: 'go to line start | ←', 921 | }, 922 | insert: { 923 | base: 'go to line start | ←', 924 | }, 925 | }, 926 | { 927 | key: '', 928 | normal: { 929 | base: 'extend to line start | ← | ', 930 | }, 931 | }, 932 | { 933 | key: '', 934 | normal: { 935 | base: 'select to line end | → | ', 936 | }, 937 | prompt: { 938 | base: 'go to line end | →', 939 | }, 940 | insert: { 941 | base: 'go to line end | →', 942 | }, 943 | }, 944 | { 945 | key: '', 946 | normal: { 947 | base: 'extend to line end | → | ', 948 | }, 949 | }, 950 | { 951 | key: '', 952 | normal: { 953 | base: 'scroll one page up | ↑ count', 954 | }, 955 | }, 956 | { 957 | key: '', 958 | normal: { 959 | base: 'scroll one page down | ↓ count', 960 | }, 961 | }, 962 | { 963 | key: '', 964 | normal: { 965 | base: 'remove all selections except main | count', 966 | alt: 'remove main selection | count', 967 | }, 968 | object: { 969 | base: 'whitespaces | object', 970 | }, 971 | }, 972 | { 973 | key: '', 974 | normal: { 975 | base: 'remove count', 976 | }, 977 | insert: { 978 | base: 'delete char before cursor', 979 | }, 980 | prompt: { 981 | base: 'delete char before cursor', 982 | }, 983 | }, 984 | { 985 | key: '', 986 | normal: { 987 | base: 'end macro recording', 988 | }, 989 | insert: { 990 | base: 'leave insert mode', 991 | }, 992 | prompt: { 993 | base: 'leave prompt', 994 | }, 995 | goto: { 996 | base: 'leave goto mode', 997 | }, 998 | view: { 999 | base: 'leave view mode', 1000 | }, 1001 | object: { 1002 | base: 'cancel', 1003 | }, 1004 | register: { 1005 | base: 'cancel', 1006 | }, 1007 | combine: { 1008 | base: 'cancel', 1009 | }, 1010 | }, 1011 | { 1012 | key: '', 1013 | prompt: { 1014 | base: 'select next completion candidate | ↓→', 1015 | }, 1016 | }, 1017 | { 1018 | key: '', 1019 | prompt: { 1020 | base: 'select previous completion candidate | ↑←', 1021 | }, 1022 | }, 1023 | { 1024 | key: '', 1025 | insert: { 1026 | base: 'delete char under cursor', 1027 | }, 1028 | prompt: { 1029 | base: 'delete char under cursor', 1030 | }, 1031 | }, 1032 | { 1033 | key: '', 1034 | prompt: { 1035 | base: 'validate', 1036 | }, 1037 | }, 1038 | ] 1039 | 1040 | // top form 1041 | 1042 | let query = '' 1043 | const queries = [ 1044 | 'move', 1045 | 'extend', 1046 | 'select ', 1047 | 'insert', 1048 | 'jump', 1049 | 'scroll', 1050 | 'buffer', 1051 | 'line', 1052 | 'word', 1053 | 'char', 1054 | 'cursor', 1055 | 'object', 1056 | 'count', 1057 | 'register', 1058 | 'opposite', 1059 | '←', 1060 | '→', 1061 | '↑', 1062 | '↓', 1063 | ] 1064 | 1065 | const search = ({ target }) => { 1066 | query = target.value 1067 | refresh() 1068 | } 1069 | 1070 | const modes = { 1071 | normal: ['base', 'alt', 'ctrl'], 1072 | insert: ['base', 'alt', 'ctrl'], 1073 | prompt: ['base', 'alt', 'ctrl'], 1074 | goto: ['base'], 1075 | view: ['base'], 1076 | object: ['base'], 1077 | register: ['base'], 1078 | combine: ['base'], 1079 | } 1080 | 1081 | const getKeysCount = (mode, modKey) => 1082 | keys.filter(k => k[mode] && k[mode][modKey]).length 1083 | 1084 | const hiddenCols = new Set(['insertbase', 'insertalt', 'promptbase']) 1085 | 1086 | // checkbox click 1087 | const toggleCol = col => { 1088 | hiddenCols.has(col) ? hiddenCols.delete(col) : hiddenCols.add(col) 1089 | refresh() 1090 | } 1091 | 1092 | let alwaysDisplay = false 1093 | 1094 | const toggleLines = () => { 1095 | alwaysDisplay = !alwaysDisplay 1096 | refresh() 1097 | } 1098 | 1099 | const shouldDisplayKey = key => { 1100 | return ( 1101 | alwaysDisplay || 1102 | Object.keys(key) 1103 | .filter(k => k !== 'name') 1104 | .some(m => Object.values(key[m]).some(v => v.match(query))) 1105 | ) 1106 | } 1107 | 1108 | // react 1109 | 1110 | const Form = () => 1111 | h( 1112 | 'form', 1113 | {}, 1114 | h('a', { href: 'index.html' }, 'Kakoune explain'), 1115 | h( 1116 | 'label', 1117 | {}, 1118 | ' Search:', 1119 | h('input', { 1120 | type: 'search', 1121 | placeholder: 'query', 1122 | onChange: search, 1123 | value: query, 1124 | }), 1125 | ), 1126 | h( 1127 | 'label', 1128 | {}, 1129 | 'Hide non matching lines:', 1130 | h('input', { 1131 | type: 'checkbox', 1132 | onChange: toggleLines, 1133 | checked: !alwaysDisplay, 1134 | }), 1135 | ), 1136 | h( 1137 | 'span', 1138 | {}, 1139 | h('label', {}, 'Example queries:'), 1140 | queries.map(q => 1141 | h( 1142 | 'span', 1143 | { 1144 | className: 'query', 1145 | key: q, 1146 | onClick: () => search({ target: { value: q } }), 1147 | }, 1148 | q, 1149 | ), 1150 | ), 1151 | ), 1152 | ) 1153 | 1154 | const Th = ({ mode, modKey }) => { 1155 | return h( 1156 | 'th', 1157 | { key: modKey }, 1158 | h( 1159 | 'label', 1160 | {}, 1161 | modKey, 1162 | h('input', { 1163 | type: 'checkbox', 1164 | onChange: () => toggleCol(mode + modKey), 1165 | checked: !hiddenCols.has(mode + modKey), 1166 | title: getKeysCount(mode, modKey), 1167 | }), 1168 | ), 1169 | ) 1170 | } 1171 | 1172 | const Thead = () => 1173 | h( 1174 | 'thead', 1175 | {}, 1176 | h( 1177 | 'tr', 1178 | {}, 1179 | h('th'), 1180 | Object.entries(modes).map(([m, v]) => 1181 | h('th', { key: m, colSpan: v.length }, m), 1182 | ), 1183 | ), 1184 | h( 1185 | 'tr', 1186 | {}, 1187 | h('th'), 1188 | Object.entries(modes).map(([m, v]) => 1189 | v.map(v => h(Th, { mode: m, modKey: v })), 1190 | ), 1191 | ), 1192 | ) 1193 | 1194 | const Td = ({ k, mode, modKey }) => { 1195 | const text = (k[mode] && k[mode][modKey]) || '' 1196 | const hidden = hiddenCols.has(mode + modKey) || !text.match(query) 1197 | const [def, tags, primitives] = text.split(' |') 1198 | return h( 1199 | 'td', 1200 | { key: modKey, className: k[mode] && k[mode][modKey] ? 'exist' : '' }, 1201 | !hidden && def && h('div', {}, def), 1202 | !hidden && tags && h('div', { className: 'tags' }, !hidden && tags), 1203 | !hidden && 1204 | primitives && 1205 | h('div', { className: 'tags' }, !hidden && primitives), 1206 | ) 1207 | } 1208 | 1209 | const Tbody = () => 1210 | h( 1211 | 'tbody', 1212 | {}, 1213 | keys.map( 1214 | k => 1215 | shouldDisplayKey(k) && 1216 | h( 1217 | 'tr', 1218 | { key: k.key }, 1219 | h('th', {}, k.key), 1220 | Object.entries(modes).map(([m, v]) => 1221 | v.map(v => h(Td, { k, mode: m, modKey: v })), 1222 | ), 1223 | ), 1224 | ), 1225 | ) 1226 | 1227 | const Table = () => h('table', {}, h(Thead), h(Tbody)) 1228 | 1229 | const App = () => h('div', {}, h(Form), h(Table)) 1230 | 1231 | const refresh = () => ReactDOM.render(h(App), document.getElementById('mount')) 1232 | 1233 | refresh() 1234 | -------------------------------------------------------------------------------- /react.development.js: -------------------------------------------------------------------------------- 1 | /** @license React v16.1.1 2 | * react.development.js 3 | * 4 | * Copyright (c) 2013-present, Facebook, Inc. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE file in the root directory of this source tree. 8 | */ 9 | 10 | 'use strict'; 11 | 12 | (function (global, factory) { 13 | typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : 14 | typeof define === 'function' && define.amd ? define(factory) : 15 | (global.React = factory()); 16 | }(this, (function () { 'use strict'; 17 | 18 | /* 19 | object-assign 20 | (c) Sindre Sorhus 21 | @license MIT 22 | */ 23 | 24 | 25 | /* eslint-disable no-unused-vars */ 26 | var getOwnPropertySymbols = Object.getOwnPropertySymbols; 27 | var hasOwnProperty = Object.prototype.hasOwnProperty; 28 | var propIsEnumerable = Object.prototype.propertyIsEnumerable; 29 | 30 | function toObject(val) { 31 | if (val === null || val === undefined) { 32 | throw new TypeError('Object.assign cannot be called with null or undefined'); 33 | } 34 | 35 | return Object(val); 36 | } 37 | 38 | function shouldUseNative() { 39 | try { 40 | if (!Object.assign) { 41 | return false; 42 | } 43 | 44 | // Detect buggy property enumeration order in older V8 versions. 45 | 46 | // https://bugs.chromium.org/p/v8/issues/detail?id=4118 47 | var test1 = new String('abc'); // eslint-disable-line no-new-wrappers 48 | test1[5] = 'de'; 49 | if (Object.getOwnPropertyNames(test1)[0] === '5') { 50 | return false; 51 | } 52 | 53 | // https://bugs.chromium.org/p/v8/issues/detail?id=3056 54 | var test2 = {}; 55 | for (var i = 0; i < 10; i++) { 56 | test2['_' + String.fromCharCode(i)] = i; 57 | } 58 | var order2 = Object.getOwnPropertyNames(test2).map(function (n) { 59 | return test2[n]; 60 | }); 61 | if (order2.join('') !== '0123456789') { 62 | return false; 63 | } 64 | 65 | // https://bugs.chromium.org/p/v8/issues/detail?id=3056 66 | var test3 = {}; 67 | 'abcdefghijklmnopqrst'.split('').forEach(function (letter) { 68 | test3[letter] = letter; 69 | }); 70 | if (Object.keys(Object.assign({}, test3)).join('') !== 71 | 'abcdefghijklmnopqrst') { 72 | return false; 73 | } 74 | 75 | return true; 76 | } catch (err) { 77 | // We don't expect any of the above to throw, but better to be safe. 78 | return false; 79 | } 80 | } 81 | 82 | var objectAssign$1 = shouldUseNative() ? Object.assign : function (target, source) { 83 | var from; 84 | var to = toObject(target); 85 | var symbols; 86 | 87 | for (var s = 1; s < arguments.length; s++) { 88 | from = Object(arguments[s]); 89 | 90 | for (var key in from) { 91 | if (hasOwnProperty.call(from, key)) { 92 | to[key] = from[key]; 93 | } 94 | } 95 | 96 | if (getOwnPropertySymbols) { 97 | symbols = getOwnPropertySymbols(from); 98 | for (var i = 0; i < symbols.length; i++) { 99 | if (propIsEnumerable.call(from, symbols[i])) { 100 | to[symbols[i]] = from[symbols[i]]; 101 | } 102 | } 103 | } 104 | } 105 | 106 | return to; 107 | }; 108 | 109 | // TODO: this is special because it gets imported during build. 110 | 111 | var ReactVersion = '16.1.1'; 112 | 113 | /** 114 | * WARNING: DO NOT manually require this module. 115 | * This is a replacement for `invariant(...)` used by the error code system 116 | * and will _only_ be required by the corresponding babel pass. 117 | * It always throws. 118 | */ 119 | 120 | /** 121 | * Copyright (c) 2013-present, Facebook, Inc. 122 | * 123 | * This source code is licensed under the MIT license found in the 124 | * LICENSE file in the root directory of this source tree. 125 | * 126 | */ 127 | 128 | 129 | 130 | /** 131 | * Use invariant() to assert state which your program assumes to be true. 132 | * 133 | * Provide sprintf-style format (only %s is supported) and arguments 134 | * to provide information about what broke and what you were 135 | * expecting. 136 | * 137 | * The invariant message will be stripped in production, but the invariant 138 | * will remain to ensure logic does not differ in production. 139 | */ 140 | 141 | var validateFormat = function validateFormat(format) {}; 142 | 143 | { 144 | validateFormat = function validateFormat(format) { 145 | if (format === undefined) { 146 | throw new Error('invariant requires an error message argument'); 147 | } 148 | }; 149 | } 150 | 151 | function invariant(condition, format, a, b, c, d, e, f) { 152 | validateFormat(format); 153 | 154 | if (!condition) { 155 | var error; 156 | if (format === undefined) { 157 | error = new Error('Minified exception occurred; use the non-minified dev environment ' + 'for the full error message and additional helpful warnings.'); 158 | } else { 159 | var args = [a, b, c, d, e, f]; 160 | var argIndex = 0; 161 | error = new Error(format.replace(/%s/g, function () { 162 | return args[argIndex++]; 163 | })); 164 | error.name = 'Invariant Violation'; 165 | } 166 | 167 | error.framesToPop = 1; // we don't care about invariant's own frame 168 | throw error; 169 | } 170 | } 171 | 172 | var invariant_1$1 = invariant; 173 | 174 | // Exports React.Fragment 175 | var enableReactFragment = false; 176 | // Exports ReactDOM.createRoot 177 | 178 | 179 | 180 | // Mutating mode (React DOM, React ART, React Native): 181 | 182 | // Experimental noop mode (currently unused): 183 | 184 | // Experimental persistent mode (CS): 185 | 186 | 187 | // Only used in www builds. 188 | 189 | /** 190 | * Copyright (c) 2013-present, Facebook, Inc. 191 | * 192 | * This source code is licensed under the MIT license found in the 193 | * LICENSE file in the root directory of this source tree. 194 | * 195 | */ 196 | 197 | 198 | 199 | var emptyObject = {}; 200 | 201 | { 202 | Object.freeze(emptyObject); 203 | } 204 | 205 | var emptyObject_1 = emptyObject; 206 | 207 | /** 208 | * Forked from fbjs/warning: 209 | * https://github.com/facebook/fbjs/blob/e66ba20ad5be433eb54423f2b097d829324d9de6/packages/fbjs/src/__forks__/warning.js 210 | * 211 | * Only change is we use console.warn instead of console.error, 212 | * and do nothing when 'console' is not supported. 213 | * This really simplifies the code. 214 | * --- 215 | * Similar to invariant but only logs a warning if the condition is not met. 216 | * This can be used to log issues in development environments in critical 217 | * paths. Removing the logging code for production environments will keep the 218 | * same logic and follow the same code paths. 219 | */ 220 | 221 | var lowPriorityWarning = function () {}; 222 | 223 | { 224 | var printWarning = function (format) { 225 | for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { 226 | args[_key - 1] = arguments[_key]; 227 | } 228 | 229 | var argIndex = 0; 230 | var message = 'Warning: ' + format.replace(/%s/g, function () { 231 | return args[argIndex++]; 232 | }); 233 | if (typeof console !== 'undefined') { 234 | console.warn(message); 235 | } 236 | try { 237 | // --- Welcome to debugging React --- 238 | // This error was thrown as a convenience so that you can use this stack 239 | // to find the callsite that caused this warning to fire. 240 | throw new Error(message); 241 | } catch (x) {} 242 | }; 243 | 244 | lowPriorityWarning = function (condition, format) { 245 | if (format === undefined) { 246 | throw new Error('`warning(condition, format, ...args)` requires a warning ' + 'message argument'); 247 | } 248 | if (!condition) { 249 | for (var _len2 = arguments.length, args = Array(_len2 > 2 ? _len2 - 2 : 0), _key2 = 2; _key2 < _len2; _key2++) { 250 | args[_key2 - 2] = arguments[_key2]; 251 | } 252 | 253 | printWarning.apply(undefined, [format].concat(args)); 254 | } 255 | }; 256 | } 257 | 258 | var lowPriorityWarning$1 = lowPriorityWarning; 259 | 260 | /** 261 | * Copyright (c) 2013-present, Facebook, Inc. 262 | * 263 | * This source code is licensed under the MIT license found in the 264 | * LICENSE file in the root directory of this source tree. 265 | * 266 | * 267 | */ 268 | 269 | function makeEmptyFunction(arg) { 270 | return function () { 271 | return arg; 272 | }; 273 | } 274 | 275 | /** 276 | * This function accepts and discards inputs; it has no side effects. This is 277 | * primarily useful idiomatically for overridable function endpoints which 278 | * always need to be callable, since JS lacks a null-call idiom ala Cocoa. 279 | */ 280 | var emptyFunction = function emptyFunction() {}; 281 | 282 | emptyFunction.thatReturns = makeEmptyFunction; 283 | emptyFunction.thatReturnsFalse = makeEmptyFunction(false); 284 | emptyFunction.thatReturnsTrue = makeEmptyFunction(true); 285 | emptyFunction.thatReturnsNull = makeEmptyFunction(null); 286 | emptyFunction.thatReturnsThis = function () { 287 | return this; 288 | }; 289 | emptyFunction.thatReturnsArgument = function (arg) { 290 | return arg; 291 | }; 292 | 293 | var emptyFunction_1 = emptyFunction; 294 | 295 | /** 296 | * Copyright (c) 2014-present, Facebook, Inc. 297 | * 298 | * This source code is licensed under the MIT license found in the 299 | * LICENSE file in the root directory of this source tree. 300 | * 301 | */ 302 | 303 | 304 | 305 | 306 | 307 | /** 308 | * Similar to invariant but only logs a warning if the condition is not met. 309 | * This can be used to log issues in development environments in critical 310 | * paths. Removing the logging code for production environments will keep the 311 | * same logic and follow the same code paths. 312 | */ 313 | 314 | var warning = emptyFunction_1; 315 | 316 | { 317 | var printWarning$1 = function printWarning(format) { 318 | for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { 319 | args[_key - 1] = arguments[_key]; 320 | } 321 | 322 | var argIndex = 0; 323 | var message = 'Warning: ' + format.replace(/%s/g, function () { 324 | return args[argIndex++]; 325 | }); 326 | if (typeof console !== 'undefined') { 327 | console.error(message); 328 | } 329 | try { 330 | // --- Welcome to debugging React --- 331 | // This error was thrown as a convenience so that you can use this stack 332 | // to find the callsite that caused this warning to fire. 333 | throw new Error(message); 334 | } catch (x) {} 335 | }; 336 | 337 | warning = function warning(condition, format) { 338 | if (format === undefined) { 339 | throw new Error('`warning(condition, format, ...args)` requires a warning ' + 'message argument'); 340 | } 341 | 342 | if (format.indexOf('Failed Composite propType: ') === 0) { 343 | return; // Ignore CompositeComponent proptype check. 344 | } 345 | 346 | if (!condition) { 347 | for (var _len2 = arguments.length, args = Array(_len2 > 2 ? _len2 - 2 : 0), _key2 = 2; _key2 < _len2; _key2++) { 348 | args[_key2 - 2] = arguments[_key2]; 349 | } 350 | 351 | printWarning$1.apply(undefined, [format].concat(args)); 352 | } 353 | }; 354 | } 355 | 356 | var warning_1$1 = warning; 357 | 358 | var didWarnStateUpdateForUnmountedComponent = {}; 359 | 360 | function warnNoop(publicInstance, callerName) { 361 | { 362 | var constructor = publicInstance.constructor; 363 | var componentName = constructor && (constructor.displayName || constructor.name) || 'ReactClass'; 364 | var warningKey = componentName + '.' + callerName; 365 | if (didWarnStateUpdateForUnmountedComponent[warningKey]) { 366 | return; 367 | } 368 | warning_1$1(false, '%s(...): Can only update a mounted or mounting component. ' + 'This usually means you called %s() on an unmounted component. ' + 'This is a no-op.\n\nPlease check the code for the %s component.', callerName, callerName, componentName); 369 | didWarnStateUpdateForUnmountedComponent[warningKey] = true; 370 | } 371 | } 372 | 373 | /** 374 | * This is the abstract API for an update queue. 375 | */ 376 | var ReactNoopUpdateQueue = { 377 | /** 378 | * Checks whether or not this composite component is mounted. 379 | * @param {ReactClass} publicInstance The instance we want to test. 380 | * @return {boolean} True if mounted, false otherwise. 381 | * @protected 382 | * @final 383 | */ 384 | isMounted: function (publicInstance) { 385 | return false; 386 | }, 387 | 388 | /** 389 | * Forces an update. This should only be invoked when it is known with 390 | * certainty that we are **not** in a DOM transaction. 391 | * 392 | * You may want to call this when you know that some deeper aspect of the 393 | * component's state has changed but `setState` was not called. 394 | * 395 | * This will not invoke `shouldComponentUpdate`, but it will invoke 396 | * `componentWillUpdate` and `componentDidUpdate`. 397 | * 398 | * @param {ReactClass} publicInstance The instance that should rerender. 399 | * @param {?function} callback Called after component is updated. 400 | * @param {?string} callerName name of the calling function in the public API. 401 | * @internal 402 | */ 403 | enqueueForceUpdate: function (publicInstance, callback, callerName) { 404 | warnNoop(publicInstance, 'forceUpdate'); 405 | }, 406 | 407 | /** 408 | * Replaces all of the state. Always use this or `setState` to mutate state. 409 | * You should treat `this.state` as immutable. 410 | * 411 | * There is no guarantee that `this.state` will be immediately updated, so 412 | * accessing `this.state` after calling this method may return the old value. 413 | * 414 | * @param {ReactClass} publicInstance The instance that should rerender. 415 | * @param {object} completeState Next state. 416 | * @param {?function} callback Called after component is updated. 417 | * @param {?string} callerName name of the calling function in the public API. 418 | * @internal 419 | */ 420 | enqueueReplaceState: function (publicInstance, completeState, callback, callerName) { 421 | warnNoop(publicInstance, 'replaceState'); 422 | }, 423 | 424 | /** 425 | * Sets a subset of the state. This only exists because _pendingState is 426 | * internal. This provides a merging strategy that is not available to deep 427 | * properties which is confusing. TODO: Expose pendingState or don't use it 428 | * during the merge. 429 | * 430 | * @param {ReactClass} publicInstance The instance that should rerender. 431 | * @param {object} partialState Next partial state to be merged with state. 432 | * @param {?function} callback Called after component is updated. 433 | * @param {?string} Name of the calling function in the public API. 434 | * @internal 435 | */ 436 | enqueueSetState: function (publicInstance, partialState, callback, callerName) { 437 | warnNoop(publicInstance, 'setState'); 438 | } 439 | }; 440 | 441 | /** 442 | * Base class helpers for the updating state of a component. 443 | */ 444 | function Component(props, context, updater) { 445 | this.props = props; 446 | this.context = context; 447 | this.refs = emptyObject_1; 448 | // We initialize the default updater but the real one gets injected by the 449 | // renderer. 450 | this.updater = updater || ReactNoopUpdateQueue; 451 | } 452 | 453 | Component.prototype.isReactComponent = {}; 454 | 455 | /** 456 | * Sets a subset of the state. Always use this to mutate 457 | * state. You should treat `this.state` as immutable. 458 | * 459 | * There is no guarantee that `this.state` will be immediately updated, so 460 | * accessing `this.state` after calling this method may return the old value. 461 | * 462 | * There is no guarantee that calls to `setState` will run synchronously, 463 | * as they may eventually be batched together. You can provide an optional 464 | * callback that will be executed when the call to setState is actually 465 | * completed. 466 | * 467 | * When a function is provided to setState, it will be called at some point in 468 | * the future (not synchronously). It will be called with the up to date 469 | * component arguments (state, props, context). These values can be different 470 | * from this.* because your function may be called after receiveProps but before 471 | * shouldComponentUpdate, and this new state, props, and context will not yet be 472 | * assigned to this. 473 | * 474 | * @param {object|function} partialState Next partial state or function to 475 | * produce next partial state to be merged with current state. 476 | * @param {?function} callback Called after state is updated. 477 | * @final 478 | * @protected 479 | */ 480 | Component.prototype.setState = function (partialState, callback) { 481 | !(typeof partialState === 'object' || typeof partialState === 'function' || partialState == null) ? invariant_1$1(false, 'setState(...): takes an object of state variables to update or a function which returns an object of state variables.') : void 0; 482 | this.updater.enqueueSetState(this, partialState, callback, 'setState'); 483 | }; 484 | 485 | /** 486 | * Forces an update. This should only be invoked when it is known with 487 | * certainty that we are **not** in a DOM transaction. 488 | * 489 | * You may want to call this when you know that some deeper aspect of the 490 | * component's state has changed but `setState` was not called. 491 | * 492 | * This will not invoke `shouldComponentUpdate`, but it will invoke 493 | * `componentWillUpdate` and `componentDidUpdate`. 494 | * 495 | * @param {?function} callback Called after update is complete. 496 | * @final 497 | * @protected 498 | */ 499 | Component.prototype.forceUpdate = function (callback) { 500 | this.updater.enqueueForceUpdate(this, callback, 'forceUpdate'); 501 | }; 502 | 503 | /** 504 | * Deprecated APIs. These APIs used to exist on classic React classes but since 505 | * we would like to deprecate them, we're not going to move them over to this 506 | * modern base class. Instead, we define a getter that warns if it's accessed. 507 | */ 508 | { 509 | var deprecatedAPIs = { 510 | isMounted: ['isMounted', 'Instead, make sure to clean up subscriptions and pending requests in ' + 'componentWillUnmount to prevent memory leaks.'], 511 | replaceState: ['replaceState', 'Refactor your code to use setState instead (see ' + 'https://github.com/facebook/react/issues/3236).'] 512 | }; 513 | var defineDeprecationWarning = function (methodName, info) { 514 | Object.defineProperty(Component.prototype, methodName, { 515 | get: function () { 516 | lowPriorityWarning$1(false, '%s(...) is deprecated in plain JavaScript React classes. %s', info[0], info[1]); 517 | return undefined; 518 | } 519 | }); 520 | }; 521 | for (var fnName in deprecatedAPIs) { 522 | if (deprecatedAPIs.hasOwnProperty(fnName)) { 523 | defineDeprecationWarning(fnName, deprecatedAPIs[fnName]); 524 | } 525 | } 526 | } 527 | 528 | /** 529 | * Base class helpers for the updating state of a component. 530 | */ 531 | function PureComponent(props, context, updater) { 532 | // Duplicated from Component. 533 | this.props = props; 534 | this.context = context; 535 | this.refs = emptyObject_1; 536 | // We initialize the default updater but the real one gets injected by the 537 | // renderer. 538 | this.updater = updater || ReactNoopUpdateQueue; 539 | } 540 | 541 | function ComponentDummy() {} 542 | ComponentDummy.prototype = Component.prototype; 543 | var pureComponentPrototype = PureComponent.prototype = new ComponentDummy(); 544 | pureComponentPrototype.constructor = PureComponent; 545 | // Avoid an extra prototype jump for these methods. 546 | objectAssign$1(pureComponentPrototype, Component.prototype); 547 | pureComponentPrototype.isPureReactComponent = true; 548 | 549 | function AsyncComponent(props, context, updater) { 550 | // Duplicated from Component. 551 | this.props = props; 552 | this.context = context; 553 | this.refs = emptyObject_1; 554 | // We initialize the default updater but the real one gets injected by the 555 | // renderer. 556 | this.updater = updater || ReactNoopUpdateQueue; 557 | } 558 | 559 | var asyncComponentPrototype = AsyncComponent.prototype = new ComponentDummy(); 560 | asyncComponentPrototype.constructor = AsyncComponent; 561 | // Avoid an extra prototype jump for these methods. 562 | objectAssign$1(asyncComponentPrototype, Component.prototype); 563 | asyncComponentPrototype.unstable_isAsyncReactComponent = true; 564 | asyncComponentPrototype.render = function () { 565 | return this.props.children; 566 | }; 567 | 568 | /** 569 | * Keeps track of the current owner. 570 | * 571 | * The current owner is the component who should own any components that are 572 | * currently being constructed. 573 | */ 574 | var ReactCurrentOwner = { 575 | /** 576 | * @internal 577 | * @type {ReactComponent} 578 | */ 579 | current: null 580 | }; 581 | 582 | var hasOwnProperty$1 = Object.prototype.hasOwnProperty; 583 | 584 | // The Symbol used to tag the ReactElement type. If there is no native Symbol 585 | // nor polyfill, then a plain number is used for performance. 586 | var REACT_ELEMENT_TYPE$1 = typeof Symbol === 'function' && Symbol['for'] && Symbol['for']('react.element') || 0xeac7; 587 | 588 | var RESERVED_PROPS = { 589 | key: true, 590 | ref: true, 591 | __self: true, 592 | __source: true 593 | }; 594 | 595 | var specialPropKeyWarningShown; 596 | var specialPropRefWarningShown; 597 | 598 | function hasValidRef(config) { 599 | { 600 | if (hasOwnProperty$1.call(config, 'ref')) { 601 | var getter = Object.getOwnPropertyDescriptor(config, 'ref').get; 602 | if (getter && getter.isReactWarning) { 603 | return false; 604 | } 605 | } 606 | } 607 | return config.ref !== undefined; 608 | } 609 | 610 | function hasValidKey(config) { 611 | { 612 | if (hasOwnProperty$1.call(config, 'key')) { 613 | var getter = Object.getOwnPropertyDescriptor(config, 'key').get; 614 | if (getter && getter.isReactWarning) { 615 | return false; 616 | } 617 | } 618 | } 619 | return config.key !== undefined; 620 | } 621 | 622 | function defineKeyPropWarningGetter(props, displayName) { 623 | var warnAboutAccessingKey = function () { 624 | if (!specialPropKeyWarningShown) { 625 | specialPropKeyWarningShown = true; 626 | warning_1$1(false, '%s: `key` is not a prop. Trying to access it will result ' + 'in `undefined` being returned. If you need to access the same ' + 'value within the child component, you should pass it as a different ' + 'prop. (https://fb.me/react-special-props)', displayName); 627 | } 628 | }; 629 | warnAboutAccessingKey.isReactWarning = true; 630 | Object.defineProperty(props, 'key', { 631 | get: warnAboutAccessingKey, 632 | configurable: true 633 | }); 634 | } 635 | 636 | function defineRefPropWarningGetter(props, displayName) { 637 | var warnAboutAccessingRef = function () { 638 | if (!specialPropRefWarningShown) { 639 | specialPropRefWarningShown = true; 640 | warning_1$1(false, '%s: `ref` is not a prop. Trying to access it will result ' + 'in `undefined` being returned. If you need to access the same ' + 'value within the child component, you should pass it as a different ' + 'prop. (https://fb.me/react-special-props)', displayName); 641 | } 642 | }; 643 | warnAboutAccessingRef.isReactWarning = true; 644 | Object.defineProperty(props, 'ref', { 645 | get: warnAboutAccessingRef, 646 | configurable: true 647 | }); 648 | } 649 | 650 | /** 651 | * Factory method to create a new React element. This no longer adheres to 652 | * the class pattern, so do not use new to call it. Also, no instanceof check 653 | * will work. Instead test $$typeof field against Symbol.for('react.element') to check 654 | * if something is a React Element. 655 | * 656 | * @param {*} type 657 | * @param {*} key 658 | * @param {string|object} ref 659 | * @param {*} self A *temporary* helper to detect places where `this` is 660 | * different from the `owner` when React.createElement is called, so that we 661 | * can warn. We want to get rid of owner and replace string `ref`s with arrow 662 | * functions, and as long as `this` and owner are the same, there will be no 663 | * change in behavior. 664 | * @param {*} source An annotation object (added by a transpiler or otherwise) 665 | * indicating filename, line number, and/or other information. 666 | * @param {*} owner 667 | * @param {*} props 668 | * @internal 669 | */ 670 | var ReactElement = function (type, key, ref, self, source, owner, props) { 671 | var element = { 672 | // This tag allow us to uniquely identify this as a React Element 673 | $$typeof: REACT_ELEMENT_TYPE$1, 674 | 675 | // Built-in properties that belong on the element 676 | type: type, 677 | key: key, 678 | ref: ref, 679 | props: props, 680 | 681 | // Record the component responsible for creating this element. 682 | _owner: owner 683 | }; 684 | 685 | { 686 | // The validation flag is currently mutative. We put it on 687 | // an external backing store so that we can freeze the whole object. 688 | // This can be replaced with a WeakMap once they are implemented in 689 | // commonly used development environments. 690 | element._store = {}; 691 | 692 | // To make comparing ReactElements easier for testing purposes, we make 693 | // the validation flag non-enumerable (where possible, which should 694 | // include every environment we run tests in), so the test framework 695 | // ignores it. 696 | Object.defineProperty(element._store, 'validated', { 697 | configurable: false, 698 | enumerable: false, 699 | writable: true, 700 | value: false 701 | }); 702 | // self and source are DEV only properties. 703 | Object.defineProperty(element, '_self', { 704 | configurable: false, 705 | enumerable: false, 706 | writable: false, 707 | value: self 708 | }); 709 | // Two elements created in two different places should be considered 710 | // equal for testing purposes and therefore we hide it from enumeration. 711 | Object.defineProperty(element, '_source', { 712 | configurable: false, 713 | enumerable: false, 714 | writable: false, 715 | value: source 716 | }); 717 | if (Object.freeze) { 718 | Object.freeze(element.props); 719 | Object.freeze(element); 720 | } 721 | } 722 | 723 | return element; 724 | }; 725 | 726 | /** 727 | * Create and return a new ReactElement of the given type. 728 | * See https://reactjs.org/docs/react-api.html#createelement 729 | */ 730 | function createElement(type, config, children) { 731 | var propName; 732 | 733 | // Reserved names are extracted 734 | var props = {}; 735 | 736 | var key = null; 737 | var ref = null; 738 | var self = null; 739 | var source = null; 740 | 741 | if (config != null) { 742 | if (hasValidRef(config)) { 743 | ref = config.ref; 744 | } 745 | if (hasValidKey(config)) { 746 | key = '' + config.key; 747 | } 748 | 749 | self = config.__self === undefined ? null : config.__self; 750 | source = config.__source === undefined ? null : config.__source; 751 | // Remaining properties are added to a new props object 752 | for (propName in config) { 753 | if (hasOwnProperty$1.call(config, propName) && !RESERVED_PROPS.hasOwnProperty(propName)) { 754 | props[propName] = config[propName]; 755 | } 756 | } 757 | } 758 | 759 | // Children can be more than one argument, and those are transferred onto 760 | // the newly allocated props object. 761 | var childrenLength = arguments.length - 2; 762 | if (childrenLength === 1) { 763 | props.children = children; 764 | } else if (childrenLength > 1) { 765 | var childArray = Array(childrenLength); 766 | for (var i = 0; i < childrenLength; i++) { 767 | childArray[i] = arguments[i + 2]; 768 | } 769 | { 770 | if (Object.freeze) { 771 | Object.freeze(childArray); 772 | } 773 | } 774 | props.children = childArray; 775 | } 776 | 777 | // Resolve default props 778 | if (type && type.defaultProps) { 779 | var defaultProps = type.defaultProps; 780 | for (propName in defaultProps) { 781 | if (props[propName] === undefined) { 782 | props[propName] = defaultProps[propName]; 783 | } 784 | } 785 | } 786 | { 787 | if (key || ref) { 788 | if (typeof props.$$typeof === 'undefined' || props.$$typeof !== REACT_ELEMENT_TYPE$1) { 789 | var displayName = typeof type === 'function' ? type.displayName || type.name || 'Unknown' : type; 790 | if (key) { 791 | defineKeyPropWarningGetter(props, displayName); 792 | } 793 | if (ref) { 794 | defineRefPropWarningGetter(props, displayName); 795 | } 796 | } 797 | } 798 | } 799 | return ReactElement(type, key, ref, self, source, ReactCurrentOwner.current, props); 800 | } 801 | 802 | /** 803 | * Return a function that produces ReactElements of a given type. 804 | * See https://reactjs.org/docs/react-api.html#createfactory 805 | */ 806 | 807 | 808 | function cloneAndReplaceKey(oldElement, newKey) { 809 | var newElement = ReactElement(oldElement.type, newKey, oldElement.ref, oldElement._self, oldElement._source, oldElement._owner, oldElement.props); 810 | 811 | return newElement; 812 | } 813 | 814 | /** 815 | * Clone and return a new ReactElement using element as the starting point. 816 | * See https://reactjs.org/docs/react-api.html#cloneelement 817 | */ 818 | function cloneElement(element, config, children) { 819 | var propName; 820 | 821 | // Original props are copied 822 | var props = objectAssign$1({}, element.props); 823 | 824 | // Reserved names are extracted 825 | var key = element.key; 826 | var ref = element.ref; 827 | // Self is preserved since the owner is preserved. 828 | var self = element._self; 829 | // Source is preserved since cloneElement is unlikely to be targeted by a 830 | // transpiler, and the original source is probably a better indicator of the 831 | // true owner. 832 | var source = element._source; 833 | 834 | // Owner will be preserved, unless ref is overridden 835 | var owner = element._owner; 836 | 837 | if (config != null) { 838 | if (hasValidRef(config)) { 839 | // Silently steal the ref from the parent. 840 | ref = config.ref; 841 | owner = ReactCurrentOwner.current; 842 | } 843 | if (hasValidKey(config)) { 844 | key = '' + config.key; 845 | } 846 | 847 | // Remaining properties override existing props 848 | var defaultProps; 849 | if (element.type && element.type.defaultProps) { 850 | defaultProps = element.type.defaultProps; 851 | } 852 | for (propName in config) { 853 | if (hasOwnProperty$1.call(config, propName) && !RESERVED_PROPS.hasOwnProperty(propName)) { 854 | if (config[propName] === undefined && defaultProps !== undefined) { 855 | // Resolve default props 856 | props[propName] = defaultProps[propName]; 857 | } else { 858 | props[propName] = config[propName]; 859 | } 860 | } 861 | } 862 | } 863 | 864 | // Children can be more than one argument, and those are transferred onto 865 | // the newly allocated props object. 866 | var childrenLength = arguments.length - 2; 867 | if (childrenLength === 1) { 868 | props.children = children; 869 | } else if (childrenLength > 1) { 870 | var childArray = Array(childrenLength); 871 | for (var i = 0; i < childrenLength; i++) { 872 | childArray[i] = arguments[i + 2]; 873 | } 874 | props.children = childArray; 875 | } 876 | 877 | return ReactElement(element.type, key, ref, self, source, owner, props); 878 | } 879 | 880 | /** 881 | * Verifies the object is a ReactElement. 882 | * See https://reactjs.org/docs/react-api.html#isvalidelement 883 | * @param {?object} object 884 | * @return {boolean} True if `object` is a valid component. 885 | * @final 886 | */ 887 | function isValidElement(object) { 888 | return typeof object === 'object' && object !== null && object.$$typeof === REACT_ELEMENT_TYPE$1; 889 | } 890 | 891 | var ReactDebugCurrentFrame = {}; 892 | 893 | { 894 | // Component that is being worked on 895 | ReactDebugCurrentFrame.getCurrentStack = null; 896 | 897 | ReactDebugCurrentFrame.getStackAddendum = function () { 898 | var impl = ReactDebugCurrentFrame.getCurrentStack; 899 | if (impl) { 900 | return impl(); 901 | } 902 | return null; 903 | }; 904 | } 905 | 906 | var ITERATOR_SYMBOL = typeof Symbol === 'function' && Symbol.iterator; 907 | var FAUX_ITERATOR_SYMBOL = '@@iterator'; // Before Symbol spec. 908 | // The Symbol used to tag the ReactElement type. If there is no native Symbol 909 | // nor polyfill, then a plain number is used for performance. 910 | var REACT_ELEMENT_TYPE = typeof Symbol === 'function' && Symbol['for'] && Symbol['for']('react.element') || 0xeac7; 911 | var REACT_PORTAL_TYPE = typeof Symbol === 'function' && Symbol['for'] && Symbol['for']('react.portal') || 0xeaca; 912 | var SEPARATOR = '.'; 913 | var SUBSEPARATOR = ':'; 914 | 915 | /** 916 | * Escape and wrap key so it is safe to use as a reactid 917 | * 918 | * @param {string} key to be escaped. 919 | * @return {string} the escaped key. 920 | */ 921 | function escape(key) { 922 | var escapeRegex = /[=:]/g; 923 | var escaperLookup = { 924 | '=': '=0', 925 | ':': '=2' 926 | }; 927 | var escapedString = ('' + key).replace(escapeRegex, function (match) { 928 | return escaperLookup[match]; 929 | }); 930 | 931 | return '$' + escapedString; 932 | } 933 | 934 | /** 935 | * TODO: Test that a single child and an array with one item have the same key 936 | * pattern. 937 | */ 938 | 939 | var didWarnAboutMaps = false; 940 | 941 | var userProvidedKeyEscapeRegex = /\/+/g; 942 | function escapeUserProvidedKey(text) { 943 | return ('' + text).replace(userProvidedKeyEscapeRegex, '$&/'); 944 | } 945 | 946 | var POOL_SIZE = 10; 947 | var traverseContextPool = []; 948 | function getPooledTraverseContext(mapResult, keyPrefix, mapFunction, mapContext) { 949 | if (traverseContextPool.length) { 950 | var traverseContext = traverseContextPool.pop(); 951 | traverseContext.result = mapResult; 952 | traverseContext.keyPrefix = keyPrefix; 953 | traverseContext.func = mapFunction; 954 | traverseContext.context = mapContext; 955 | traverseContext.count = 0; 956 | return traverseContext; 957 | } else { 958 | return { 959 | result: mapResult, 960 | keyPrefix: keyPrefix, 961 | func: mapFunction, 962 | context: mapContext, 963 | count: 0 964 | }; 965 | } 966 | } 967 | 968 | function releaseTraverseContext(traverseContext) { 969 | traverseContext.result = null; 970 | traverseContext.keyPrefix = null; 971 | traverseContext.func = null; 972 | traverseContext.context = null; 973 | traverseContext.count = 0; 974 | if (traverseContextPool.length < POOL_SIZE) { 975 | traverseContextPool.push(traverseContext); 976 | } 977 | } 978 | 979 | /** 980 | * @param {?*} children Children tree container. 981 | * @param {!string} nameSoFar Name of the key path so far. 982 | * @param {!function} callback Callback to invoke with each child found. 983 | * @param {?*} traverseContext Used to pass information throughout the traversal 984 | * process. 985 | * @return {!number} The number of children in this subtree. 986 | */ 987 | function traverseAllChildrenImpl(children, nameSoFar, callback, traverseContext) { 988 | var type = typeof children; 989 | 990 | if (type === 'undefined' || type === 'boolean') { 991 | // All of the above are perceived as null. 992 | children = null; 993 | } 994 | 995 | if (children === null || type === 'string' || type === 'number' || 996 | // The following is inlined from ReactElement. This means we can optimize 997 | // some checks. React Fiber also inlines this logic for similar purposes. 998 | type === 'object' && children.$$typeof === REACT_ELEMENT_TYPE || type === 'object' && children.$$typeof === REACT_PORTAL_TYPE) { 999 | callback(traverseContext, children, 1000 | // If it's the only child, treat the name as if it was wrapped in an array 1001 | // so that it's consistent if the number of children grows. 1002 | nameSoFar === '' ? SEPARATOR + getComponentKey(children, 0) : nameSoFar); 1003 | return 1; 1004 | } 1005 | 1006 | var child; 1007 | var nextName; 1008 | var subtreeCount = 0; // Count of children found in the current subtree. 1009 | var nextNamePrefix = nameSoFar === '' ? SEPARATOR : nameSoFar + SUBSEPARATOR; 1010 | 1011 | if (Array.isArray(children)) { 1012 | for (var i = 0; i < children.length; i++) { 1013 | child = children[i]; 1014 | nextName = nextNamePrefix + getComponentKey(child, i); 1015 | subtreeCount += traverseAllChildrenImpl(child, nextName, callback, traverseContext); 1016 | } 1017 | } else { 1018 | var iteratorFn = ITERATOR_SYMBOL && children[ITERATOR_SYMBOL] || children[FAUX_ITERATOR_SYMBOL]; 1019 | if (typeof iteratorFn === 'function') { 1020 | { 1021 | // Warn about using Maps as children 1022 | if (iteratorFn === children.entries) { 1023 | warning_1$1(didWarnAboutMaps, 'Using Maps as children is unsupported and will likely yield ' + 'unexpected results. Convert it to a sequence/iterable of keyed ' + 'ReactElements instead.%s', ReactDebugCurrentFrame.getStackAddendum()); 1024 | didWarnAboutMaps = true; 1025 | } 1026 | } 1027 | 1028 | var iterator = iteratorFn.call(children); 1029 | var step; 1030 | var ii = 0; 1031 | while (!(step = iterator.next()).done) { 1032 | child = step.value; 1033 | nextName = nextNamePrefix + getComponentKey(child, ii++); 1034 | subtreeCount += traverseAllChildrenImpl(child, nextName, callback, traverseContext); 1035 | } 1036 | } else if (type === 'object') { 1037 | var addendum = ''; 1038 | { 1039 | addendum = ' If you meant to render a collection of children, use an array ' + 'instead.' + ReactDebugCurrentFrame.getStackAddendum(); 1040 | } 1041 | var childrenString = '' + children; 1042 | invariant_1$1(false, 'Objects are not valid as a React child (found: %s).%s', childrenString === '[object Object]' ? 'object with keys {' + Object.keys(children).join(', ') + '}' : childrenString, addendum); 1043 | } 1044 | } 1045 | 1046 | return subtreeCount; 1047 | } 1048 | 1049 | /** 1050 | * Traverses children that are typically specified as `props.children`, but 1051 | * might also be specified through attributes: 1052 | * 1053 | * - `traverseAllChildren(this.props.children, ...)` 1054 | * - `traverseAllChildren(this.props.leftPanelChildren, ...)` 1055 | * 1056 | * The `traverseContext` is an optional argument that is passed through the 1057 | * entire traversal. It can be used to store accumulations or anything else that 1058 | * the callback might find relevant. 1059 | * 1060 | * @param {?*} children Children tree object. 1061 | * @param {!function} callback To invoke upon traversing each child. 1062 | * @param {?*} traverseContext Context for traversal. 1063 | * @return {!number} The number of children in this subtree. 1064 | */ 1065 | function traverseAllChildren(children, callback, traverseContext) { 1066 | if (children == null) { 1067 | return 0; 1068 | } 1069 | 1070 | return traverseAllChildrenImpl(children, '', callback, traverseContext); 1071 | } 1072 | 1073 | /** 1074 | * Generate a key string that identifies a component within a set. 1075 | * 1076 | * @param {*} component A component that could contain a manual key. 1077 | * @param {number} index Index that is used if a manual key is not provided. 1078 | * @return {string} 1079 | */ 1080 | function getComponentKey(component, index) { 1081 | // Do some typechecking here since we call this blindly. We want to ensure 1082 | // that we don't block potential future ES APIs. 1083 | if (typeof component === 'object' && component !== null && component.key != null) { 1084 | // Explicit key 1085 | return escape(component.key); 1086 | } 1087 | // Implicit key determined by the index in the set 1088 | return index.toString(36); 1089 | } 1090 | 1091 | function forEachSingleChild(bookKeeping, child, name) { 1092 | var func = bookKeeping.func, 1093 | context = bookKeeping.context; 1094 | 1095 | func.call(context, child, bookKeeping.count++); 1096 | } 1097 | 1098 | /** 1099 | * Iterates through children that are typically specified as `props.children`. 1100 | * 1101 | * See https://reactjs.org/docs/react-api.html#react.children.foreach 1102 | * 1103 | * The provided forEachFunc(child, index) will be called for each 1104 | * leaf child. 1105 | * 1106 | * @param {?*} children Children tree container. 1107 | * @param {function(*, int)} forEachFunc 1108 | * @param {*} forEachContext Context for forEachContext. 1109 | */ 1110 | function forEachChildren(children, forEachFunc, forEachContext) { 1111 | if (children == null) { 1112 | return children; 1113 | } 1114 | var traverseContext = getPooledTraverseContext(null, null, forEachFunc, forEachContext); 1115 | traverseAllChildren(children, forEachSingleChild, traverseContext); 1116 | releaseTraverseContext(traverseContext); 1117 | } 1118 | 1119 | function mapSingleChildIntoContext(bookKeeping, child, childKey) { 1120 | var result = bookKeeping.result, 1121 | keyPrefix = bookKeeping.keyPrefix, 1122 | func = bookKeeping.func, 1123 | context = bookKeeping.context; 1124 | 1125 | 1126 | var mappedChild = func.call(context, child, bookKeeping.count++); 1127 | if (Array.isArray(mappedChild)) { 1128 | mapIntoWithKeyPrefixInternal(mappedChild, result, childKey, emptyFunction_1.thatReturnsArgument); 1129 | } else if (mappedChild != null) { 1130 | if (isValidElement(mappedChild)) { 1131 | mappedChild = cloneAndReplaceKey(mappedChild, 1132 | // Keep both the (mapped) and old keys if they differ, just as 1133 | // traverseAllChildren used to do for objects as children 1134 | keyPrefix + (mappedChild.key && (!child || child.key !== mappedChild.key) ? escapeUserProvidedKey(mappedChild.key) + '/' : '') + childKey); 1135 | } 1136 | result.push(mappedChild); 1137 | } 1138 | } 1139 | 1140 | function mapIntoWithKeyPrefixInternal(children, array, prefix, func, context) { 1141 | var escapedPrefix = ''; 1142 | if (prefix != null) { 1143 | escapedPrefix = escapeUserProvidedKey(prefix) + '/'; 1144 | } 1145 | var traverseContext = getPooledTraverseContext(array, escapedPrefix, func, context); 1146 | traverseAllChildren(children, mapSingleChildIntoContext, traverseContext); 1147 | releaseTraverseContext(traverseContext); 1148 | } 1149 | 1150 | /** 1151 | * Maps children that are typically specified as `props.children`. 1152 | * 1153 | * See https://reactjs.org/docs/react-api.html#react.children.map 1154 | * 1155 | * The provided mapFunction(child, key, index) will be called for each 1156 | * leaf child. 1157 | * 1158 | * @param {?*} children Children tree container. 1159 | * @param {function(*, int)} func The map function. 1160 | * @param {*} context Context for mapFunction. 1161 | * @return {object} Object containing the ordered map of results. 1162 | */ 1163 | function mapChildren(children, func, context) { 1164 | if (children == null) { 1165 | return children; 1166 | } 1167 | var result = []; 1168 | mapIntoWithKeyPrefixInternal(children, result, null, func, context); 1169 | return result; 1170 | } 1171 | 1172 | /** 1173 | * Count the number of children that are typically specified as 1174 | * `props.children`. 1175 | * 1176 | * See https://reactjs.org/docs/react-api.html#react.children.count 1177 | * 1178 | * @param {?*} children Children tree container. 1179 | * @return {number} The number of children. 1180 | */ 1181 | function countChildren(children, context) { 1182 | return traverseAllChildren(children, emptyFunction_1.thatReturnsNull, null); 1183 | } 1184 | 1185 | /** 1186 | * Flatten a children object (typically specified as `props.children`) and 1187 | * return an array with appropriately re-keyed children. 1188 | * 1189 | * See https://reactjs.org/docs/react-api.html#react.children.toarray 1190 | */ 1191 | function toArray(children) { 1192 | var result = []; 1193 | mapIntoWithKeyPrefixInternal(children, result, null, emptyFunction_1.thatReturnsArgument); 1194 | return result; 1195 | } 1196 | 1197 | /** 1198 | * Returns the first child in a collection of children and verifies that there 1199 | * is only one child in the collection. 1200 | * 1201 | * See https://reactjs.org/docs/react-api.html#react.children.only 1202 | * 1203 | * The current implementation of this function assumes that a single child gets 1204 | * passed without a wrapper, but the purpose of this helper function is to 1205 | * abstract away the particular structure of children. 1206 | * 1207 | * @param {?object} children Child collection structure. 1208 | * @return {ReactElement} The first and only `ReactElement` contained in the 1209 | * structure. 1210 | */ 1211 | function onlyChild(children) { 1212 | !isValidElement(children) ? invariant_1$1(false, 'React.Children.only expected to receive a single React element child.') : void 0; 1213 | return children; 1214 | } 1215 | 1216 | var describeComponentFrame = function (name, source, ownerName) { 1217 | return '\n in ' + (name || 'Unknown') + (source ? ' (at ' + source.fileName.replace(/^.*[\\\/]/, '') + ':' + source.lineNumber + ')' : ownerName ? ' (created by ' + ownerName + ')' : ''); 1218 | }; 1219 | 1220 | function getComponentName(fiber) { 1221 | var type = fiber.type; 1222 | 1223 | if (typeof type === 'string') { 1224 | return type; 1225 | } 1226 | if (typeof type === 'function') { 1227 | return type.displayName || type.name; 1228 | } 1229 | return null; 1230 | } 1231 | 1232 | /** 1233 | * Copyright (c) 2013-present, Facebook, Inc. 1234 | * 1235 | * This source code is licensed under the MIT license found in the 1236 | * LICENSE file in the root directory of this source tree. 1237 | */ 1238 | 1239 | 1240 | 1241 | var ReactPropTypesSecret$1 = 'SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED'; 1242 | 1243 | var ReactPropTypesSecret_1 = ReactPropTypesSecret$1; 1244 | 1245 | /** 1246 | * Copyright (c) 2013-present, Facebook, Inc. 1247 | * 1248 | * This source code is licensed under the MIT license found in the 1249 | * LICENSE file in the root directory of this source tree. 1250 | */ 1251 | 1252 | 1253 | 1254 | { 1255 | var invariant$2 = invariant_1$1; 1256 | var warning$2 = warning_1$1; 1257 | var ReactPropTypesSecret = ReactPropTypesSecret_1; 1258 | var loggedTypeFailures = {}; 1259 | } 1260 | 1261 | /** 1262 | * Assert that the values match with the type specs. 1263 | * Error messages are memorized and will only be shown once. 1264 | * 1265 | * @param {object} typeSpecs Map of name to a ReactPropType 1266 | * @param {object} values Runtime values that need to be type-checked 1267 | * @param {string} location e.g. "prop", "context", "child context" 1268 | * @param {string} componentName Name of the component for error messages. 1269 | * @param {?Function} getStack Returns the component stack. 1270 | * @private 1271 | */ 1272 | function checkPropTypes(typeSpecs, values, location, componentName, getStack) { 1273 | { 1274 | for (var typeSpecName in typeSpecs) { 1275 | if (typeSpecs.hasOwnProperty(typeSpecName)) { 1276 | var error; 1277 | // Prop type validation may throw. In case they do, we don't want to 1278 | // fail the render phase where it didn't fail before. So we log it. 1279 | // After these have been cleaned up, we'll let them throw. 1280 | try { 1281 | // This is intentionally an invariant that gets caught. It's the same 1282 | // behavior as without this statement except with a better message. 1283 | invariant$2(typeof typeSpecs[typeSpecName] === 'function', '%s: %s type `%s` is invalid; it must be a function, usually from ' + 'the `prop-types` package, but received `%s`.', componentName || 'React class', location, typeSpecName, typeof typeSpecs[typeSpecName]); 1284 | error = typeSpecs[typeSpecName](values, typeSpecName, componentName, location, null, ReactPropTypesSecret); 1285 | } catch (ex) { 1286 | error = ex; 1287 | } 1288 | warning$2(!error || error instanceof Error, '%s: type specification of %s `%s` is invalid; the type checker ' + 'function must return `null` or an `Error` but returned a %s. ' + 'You may have forgotten to pass an argument to the type checker ' + 'creator (arrayOf, instanceOf, objectOf, oneOf, oneOfType, and ' + 'shape all require an argument).', componentName || 'React class', location, typeSpecName, typeof error); 1289 | if (error instanceof Error && !(error.message in loggedTypeFailures)) { 1290 | // Only monitor this failure once because there tends to be a lot of the 1291 | // same error. 1292 | loggedTypeFailures[error.message] = true; 1293 | 1294 | var stack = getStack ? getStack() : ''; 1295 | 1296 | warning$2(false, 'Failed %s type: %s%s', location, error.message, stack != null ? stack : ''); 1297 | } 1298 | } 1299 | } 1300 | } 1301 | } 1302 | 1303 | var checkPropTypes_1$1 = checkPropTypes; 1304 | 1305 | /** 1306 | * ReactElementValidator provides a wrapper around a element factory 1307 | * which validates the props passed to the element. This is intended to be 1308 | * used only in DEV and could be replaced by a static type checker for languages 1309 | * that support it. 1310 | */ 1311 | 1312 | { 1313 | var currentlyValidatingElement = null; 1314 | 1315 | var getDisplayName = function (element) { 1316 | if (element == null) { 1317 | return '#empty'; 1318 | } else if (typeof element === 'string' || typeof element === 'number') { 1319 | return '#text'; 1320 | } else if (typeof element.type === 'string') { 1321 | return element.type; 1322 | } else if (element.type === REACT_FRAGMENT_TYPE$1) { 1323 | return 'React.Fragment'; 1324 | } else { 1325 | return element.type.displayName || element.type.name || 'Unknown'; 1326 | } 1327 | }; 1328 | 1329 | var getStackAddendum = function () { 1330 | var stack = ''; 1331 | if (currentlyValidatingElement) { 1332 | var name = getDisplayName(currentlyValidatingElement); 1333 | var owner = currentlyValidatingElement._owner; 1334 | stack += describeComponentFrame(name, currentlyValidatingElement._source, owner && getComponentName(owner)); 1335 | } 1336 | stack += ReactDebugCurrentFrame.getStackAddendum() || ''; 1337 | return stack; 1338 | }; 1339 | 1340 | var REACT_FRAGMENT_TYPE$1 = typeof Symbol === 'function' && Symbol['for'] && Symbol['for']('react.fragment') || 0xeacb; 1341 | 1342 | var VALID_FRAGMENT_PROPS = new Map([['children', true], ['key', true]]); 1343 | } 1344 | 1345 | var ITERATOR_SYMBOL$1 = typeof Symbol === 'function' && Symbol.iterator; 1346 | var FAUX_ITERATOR_SYMBOL$1 = '@@iterator'; // Before Symbol spec. 1347 | 1348 | function getDeclarationErrorAddendum() { 1349 | if (ReactCurrentOwner.current) { 1350 | var name = getComponentName(ReactCurrentOwner.current); 1351 | if (name) { 1352 | return '\n\nCheck the render method of `' + name + '`.'; 1353 | } 1354 | } 1355 | return ''; 1356 | } 1357 | 1358 | function getSourceInfoErrorAddendum(elementProps) { 1359 | if (elementProps !== null && elementProps !== undefined && elementProps.__source !== undefined) { 1360 | var source = elementProps.__source; 1361 | var fileName = source.fileName.replace(/^.*[\\\/]/, ''); 1362 | var lineNumber = source.lineNumber; 1363 | return '\n\nCheck your code at ' + fileName + ':' + lineNumber + '.'; 1364 | } 1365 | return ''; 1366 | } 1367 | 1368 | /** 1369 | * Warn if there's no key explicitly set on dynamic arrays of children or 1370 | * object keys are not valid. This allows us to keep track of children between 1371 | * updates. 1372 | */ 1373 | var ownerHasKeyUseWarning = {}; 1374 | 1375 | function getCurrentComponentErrorInfo(parentType) { 1376 | var info = getDeclarationErrorAddendum(); 1377 | 1378 | if (!info) { 1379 | var parentName = typeof parentType === 'string' ? parentType : parentType.displayName || parentType.name; 1380 | if (parentName) { 1381 | info = '\n\nCheck the top-level render call using <' + parentName + '>.'; 1382 | } 1383 | } 1384 | return info; 1385 | } 1386 | 1387 | /** 1388 | * Warn if the element doesn't have an explicit key assigned to it. 1389 | * This element is in an array. The array could grow and shrink or be 1390 | * reordered. All children that haven't already been validated are required to 1391 | * have a "key" property assigned to it. Error statuses are cached so a warning 1392 | * will only be shown once. 1393 | * 1394 | * @internal 1395 | * @param {ReactElement} element Element that requires a key. 1396 | * @param {*} parentType element's parent's type. 1397 | */ 1398 | function validateExplicitKey(element, parentType) { 1399 | if (!element._store || element._store.validated || element.key != null) { 1400 | return; 1401 | } 1402 | element._store.validated = true; 1403 | 1404 | var currentComponentErrorInfo = getCurrentComponentErrorInfo(parentType); 1405 | if (ownerHasKeyUseWarning[currentComponentErrorInfo]) { 1406 | return; 1407 | } 1408 | ownerHasKeyUseWarning[currentComponentErrorInfo] = true; 1409 | 1410 | // Usually the current owner is the offender, but if it accepts children as a 1411 | // property, it may be the creator of the child that's responsible for 1412 | // assigning it a key. 1413 | var childOwner = ''; 1414 | if (element && element._owner && element._owner !== ReactCurrentOwner.current) { 1415 | // Give the component that originally created this child. 1416 | childOwner = ' It was passed a child from ' + getComponentName(element._owner) + '.'; 1417 | } 1418 | 1419 | currentlyValidatingElement = element; 1420 | { 1421 | warning_1$1(false, 'Each child in an array or iterator should have a unique "key" prop.' + '%s%s See https://fb.me/react-warning-keys for more information.%s', currentComponentErrorInfo, childOwner, getStackAddendum()); 1422 | } 1423 | currentlyValidatingElement = null; 1424 | } 1425 | 1426 | /** 1427 | * Ensure that every element either is passed in a static location, in an 1428 | * array with an explicit keys property defined, or in an object literal 1429 | * with valid key property. 1430 | * 1431 | * @internal 1432 | * @param {ReactNode} node Statically passed child of any type. 1433 | * @param {*} parentType node's parent's type. 1434 | */ 1435 | function validateChildKeys(node, parentType) { 1436 | if (typeof node !== 'object') { 1437 | return; 1438 | } 1439 | if (Array.isArray(node)) { 1440 | for (var i = 0; i < node.length; i++) { 1441 | var child = node[i]; 1442 | if (isValidElement(child)) { 1443 | validateExplicitKey(child, parentType); 1444 | } 1445 | } 1446 | } else if (isValidElement(node)) { 1447 | // This element was passed in a valid location. 1448 | if (node._store) { 1449 | node._store.validated = true; 1450 | } 1451 | } else if (node) { 1452 | var iteratorFn = ITERATOR_SYMBOL$1 && node[ITERATOR_SYMBOL$1] || node[FAUX_ITERATOR_SYMBOL$1]; 1453 | if (typeof iteratorFn === 'function') { 1454 | // Entry iterators used to provide implicit keys, 1455 | // but now we print a separate warning for them later. 1456 | if (iteratorFn !== node.entries) { 1457 | var iterator = iteratorFn.call(node); 1458 | var step; 1459 | while (!(step = iterator.next()).done) { 1460 | if (isValidElement(step.value)) { 1461 | validateExplicitKey(step.value, parentType); 1462 | } 1463 | } 1464 | } 1465 | } 1466 | } 1467 | } 1468 | 1469 | /** 1470 | * Given an element, validate that its props follow the propTypes definition, 1471 | * provided by the type. 1472 | * 1473 | * @param {ReactElement} element 1474 | */ 1475 | function validatePropTypes(element) { 1476 | var componentClass = element.type; 1477 | if (typeof componentClass !== 'function') { 1478 | return; 1479 | } 1480 | var name = componentClass.displayName || componentClass.name; 1481 | var propTypes = componentClass.propTypes; 1482 | 1483 | if (propTypes) { 1484 | currentlyValidatingElement = element; 1485 | checkPropTypes_1$1(propTypes, element.props, 'prop', name, getStackAddendum); 1486 | currentlyValidatingElement = null; 1487 | } 1488 | if (typeof componentClass.getDefaultProps === 'function') { 1489 | warning_1$1(componentClass.getDefaultProps.isReactClassApproved, 'getDefaultProps is only used on classic React.createClass ' + 'definitions. Use a static property named `defaultProps` instead.'); 1490 | } 1491 | } 1492 | 1493 | /** 1494 | * Given a fragment, validate that it can only be provided with fragment props 1495 | * @param {ReactElement} fragment 1496 | */ 1497 | function validateFragmentProps(fragment) { 1498 | currentlyValidatingElement = fragment; 1499 | 1500 | var _iteratorNormalCompletion = true; 1501 | var _didIteratorError = false; 1502 | var _iteratorError = undefined; 1503 | 1504 | try { 1505 | for (var _iterator = Object.keys(fragment.props)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { 1506 | var key = _step.value; 1507 | 1508 | if (!VALID_FRAGMENT_PROPS.has(key)) { 1509 | warning_1$1(false, 'Invalid prop `%s` supplied to `React.Fragment`. ' + 'React.Fragment can only have `key` and `children` props.%s', key, getStackAddendum()); 1510 | break; 1511 | } 1512 | } 1513 | } catch (err) { 1514 | _didIteratorError = true; 1515 | _iteratorError = err; 1516 | } finally { 1517 | try { 1518 | if (!_iteratorNormalCompletion && _iterator['return']) { 1519 | _iterator['return'](); 1520 | } 1521 | } finally { 1522 | if (_didIteratorError) { 1523 | throw _iteratorError; 1524 | } 1525 | } 1526 | } 1527 | 1528 | if (fragment.ref !== null) { 1529 | warning_1$1(false, 'Invalid attribute `ref` supplied to `React.Fragment`.%s', getStackAddendum()); 1530 | } 1531 | 1532 | currentlyValidatingElement = null; 1533 | } 1534 | 1535 | function createElementWithValidation(type, props, children) { 1536 | var validType = typeof type === 'string' || typeof type === 'function' || typeof type === 'symbol' || typeof type === 'number'; 1537 | // We warn in this case but don't throw. We expect the element creation to 1538 | // succeed and there will likely be errors in render. 1539 | if (!validType) { 1540 | var info = ''; 1541 | if (type === undefined || typeof type === 'object' && type !== null && Object.keys(type).length === 0) { 1542 | info += ' You likely forgot to export your component from the file ' + "it's defined in, or you might have mixed up default and named imports."; 1543 | } 1544 | 1545 | var sourceInfo = getSourceInfoErrorAddendum(props); 1546 | if (sourceInfo) { 1547 | info += sourceInfo; 1548 | } else { 1549 | info += getDeclarationErrorAddendum(); 1550 | } 1551 | 1552 | info += getStackAddendum() || ''; 1553 | 1554 | warning_1$1(false, 'React.createElement: type is invalid -- expected a string (for ' + 'built-in components) or a class/function (for composite ' + 'components) but got: %s.%s', type == null ? type : typeof type, info); 1555 | } 1556 | 1557 | var element = createElement.apply(this, arguments); 1558 | 1559 | // The result can be nullish if a mock or a custom function is used. 1560 | // TODO: Drop this when these are no longer allowed as the type argument. 1561 | if (element == null) { 1562 | return element; 1563 | } 1564 | 1565 | // Skip key warning if the type isn't valid since our key validation logic 1566 | // doesn't expect a non-string/function type and can throw confusing errors. 1567 | // We don't want exception behavior to differ between dev and prod. 1568 | // (Rendering will throw with a helpful message and as soon as the type is 1569 | // fixed, the key warnings will appear.) 1570 | if (validType) { 1571 | for (var i = 2; i < arguments.length; i++) { 1572 | validateChildKeys(arguments[i], type); 1573 | } 1574 | } 1575 | 1576 | if (typeof type === 'symbol' && type === REACT_FRAGMENT_TYPE$1) { 1577 | validateFragmentProps(element); 1578 | } else { 1579 | validatePropTypes(element); 1580 | } 1581 | 1582 | return element; 1583 | } 1584 | 1585 | function createFactoryWithValidation(type) { 1586 | var validatedFactory = createElementWithValidation.bind(null, type); 1587 | // Legacy hook TODO: Warn if this is accessed 1588 | validatedFactory.type = type; 1589 | 1590 | { 1591 | Object.defineProperty(validatedFactory, 'type', { 1592 | enumerable: false, 1593 | get: function () { 1594 | lowPriorityWarning$1(false, 'Factory.type is deprecated. Access the class directly ' + 'before passing it to createFactory.'); 1595 | Object.defineProperty(this, 'type', { 1596 | value: type 1597 | }); 1598 | return type; 1599 | } 1600 | }); 1601 | } 1602 | 1603 | return validatedFactory; 1604 | } 1605 | 1606 | function cloneElementWithValidation(element, props, children) { 1607 | var newElement = cloneElement.apply(this, arguments); 1608 | for (var i = 2; i < arguments.length; i++) { 1609 | validateChildKeys(arguments[i], newElement.type); 1610 | } 1611 | validatePropTypes(newElement); 1612 | return newElement; 1613 | } 1614 | 1615 | var REACT_FRAGMENT_TYPE = typeof Symbol === 'function' && Symbol['for'] && Symbol['for']('react.fragment') || 0xeacb; 1616 | 1617 | var React = { 1618 | Children: { 1619 | map: mapChildren, 1620 | forEach: forEachChildren, 1621 | count: countChildren, 1622 | toArray: toArray, 1623 | only: onlyChild 1624 | }, 1625 | 1626 | Component: Component, 1627 | PureComponent: PureComponent, 1628 | unstable_AsyncComponent: AsyncComponent, 1629 | 1630 | createElement: createElementWithValidation, 1631 | cloneElement: cloneElementWithValidation, 1632 | createFactory: createFactoryWithValidation, 1633 | isValidElement: isValidElement, 1634 | 1635 | version: ReactVersion, 1636 | 1637 | __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: { 1638 | ReactCurrentOwner: ReactCurrentOwner, 1639 | // Used by renderers to avoid bundling object-assign twice in UMD bundles: 1640 | assign: objectAssign$1 1641 | } 1642 | }; 1643 | 1644 | if (enableReactFragment) { 1645 | React.Fragment = REACT_FRAGMENT_TYPE; 1646 | } 1647 | 1648 | { 1649 | objectAssign$1(React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED, { 1650 | // These should not be included in production. 1651 | ReactDebugCurrentFrame: ReactDebugCurrentFrame, 1652 | // Shim for React DOM 16.0.0 which still destructured (but not used) this. 1653 | // TODO: remove in React 17.0. 1654 | ReactComponentTreeHook: {} 1655 | }); 1656 | } 1657 | 1658 | 1659 | 1660 | var React$2 = Object.freeze({ 1661 | default: React 1662 | }); 1663 | 1664 | var React$3 = ( React$2 && React ) || React$2; 1665 | 1666 | // TODO: decide on the top-level export form. 1667 | // This is hacky but makes it work with both Rollup and Jest. 1668 | var react = React$3['default'] ? React$3['default'] : React$3; 1669 | 1670 | return react; 1671 | 1672 | }))); 1673 | --------------------------------------------------------------------------------