]> git.gir.st - VimFx.git/blob - extension/lib/commands.coffee
Improve automatic insert mode
[VimFx.git] / extension / lib / commands.coffee
1 ###
2 # Copyright Anton Khodakivskiy 2012, 2013, 2014.
3 # Copyright Simon Lydell 2013, 2014.
4 # Copyright Wang Zhuochun 2013, 2014.
5 #
6 # This file is part of VimFx.
7 #
8 # VimFx is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation, either version 3 of the License, or
11 # (at your option) any later version.
12 #
13 # VimFx is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
17 #
18 # You should have received a copy of the GNU General Public License
19 # along with VimFx. If not, see <http://www.gnu.org/licenses/>.
20 ###
21
22 notation = require('vim-like-key-notation')
23 { Marker } = require('./marker')
24 legacy = require('./legacy')
25 utils = require('./utils')
26 help = require('./help')
27 _ = require('./l10n')
28 { getPref
29 , setPref
30 , isPrefSet } = require('./prefs')
31
32 { isProperLink, isTextInputElement, isContentEditable } = utils
33
34 { classes: Cc, interfaces: Ci, utils: Cu } = Components
35
36 # “Selecting an element” means “focusing and selecting the text, if any, of an
37 # element”.
38
39 # Select the Address Bar.
40 command_focus = (vim) ->
41 # This function works even if the Address Bar has been removed.
42 vim.rootWindow.focusAndSelectUrlBar()
43
44 # Select the Search Bar.
45 command_focus_search = (vim) ->
46 # The `.webSearch()` method opens a search engine in a tab if the Search Bar
47 # has been removed. Therefore we first check if it exists.
48 if vim.rootWindow.BrowserSearch.searchBar
49 vim.rootWindow.BrowserSearch.webSearch()
50
51 helper_paste = (vim) ->
52 url = vim.rootWindow.readFromClipboard()
53 postData = null
54 if not utils.isURL(url) and submission = utils.browserSearchSubmission(url)
55 url = submission.uri.spec
56 { postData } = submission
57 return {url, postData}
58
59 # Go to or search for the contents of the system clipboard.
60 command_paste = (vim) ->
61 { url, postData } = helper_paste(vim)
62 vim.rootWindow.gBrowser.loadURIWithFlags(url, null, null, null, postData)
63
64 # Go to or search for the contents of the system clipboard in a new tab.
65 command_paste_tab = (vim) ->
66 { url, postData } = helper_paste(vim)
67 vim.rootWindow.gBrowser.selectedTab =
68 vim.rootWindow.gBrowser.addTab(url, null, null, postData, null, false)
69
70 # Copy the current URL to the system clipboard.
71 command_yank = (vim) ->
72 utils.writeToClipboard(vim.window.location.href)
73
74 # Reload the current tab, possibly from cache.
75 command_reload = (vim) ->
76 vim.rootWindow.BrowserReload()
77
78 # Reload the current tab, skipping cache.
79 command_reload_force = (vim) ->
80 vim.rootWindow.BrowserReloadSkipCache()
81
82 # Reload all tabs, possibly from cache.
83 command_reload_all = (vim) ->
84 vim.rootWindow.gBrowser.reloadAllTabs()
85
86 # Reload all tabs, skipping cache.
87 command_reload_all_force = (vim) ->
88 for tab in vim.rootWindow.gBrowser.visibleTabs
89 window = tab.linkedBrowser.contentWindow
90 window.location.reload(true)
91
92 # Stop loading the current tab.
93 command_stop = (vim) ->
94 vim.window.stop()
95
96 # Stop loading all tabs.
97 command_stop_all = (vim) ->
98 for tab in vim.rootWindow.gBrowser.visibleTabs
99 window = tab.linkedBrowser.contentWindow
100 window.stop()
101
102 # Scroll to the top of the page.
103 command_scroll_to_top = (vim) ->
104 vim.rootWindow.goDoCommand('cmd_scrollTop')
105
106 # Scroll to the bottom of the page.
107 command_scroll_to_bottom = (vim) ->
108 vim.rootWindow.goDoCommand('cmd_scrollBottom')
109
110 # Scroll down a bit.
111 command_scroll_down = (vim, event, count) ->
112 step = getPref('scroll_step_lines') * count
113 utils.simulateWheel(vim.window, 0, +step, utils.WHEEL_MODE_LINE)
114
115 # Scroll up a bit.
116 command_scroll_up = (vim, event, count) ->
117 step = getPref('scroll_step_lines') * count
118 utils.simulateWheel(vim.window, 0, -step, utils.WHEEL_MODE_LINE)
119
120 # Scroll left a bit.
121 command_scroll_left = (vim, event, count) ->
122 step = getPref('scroll_step_lines') * count
123 utils.simulateWheel(vim.window, -step, 0, utils.WHEEL_MODE_LINE)
124
125 # Scroll right a bit.
126 command_scroll_right = (vim, event, count) ->
127 step = getPref('scroll_step_lines') * count
128 utils.simulateWheel(vim.window, +step, 0, utils.WHEEL_MODE_LINE)
129
130 # Scroll down half a page.
131 command_scroll_half_page_down = (vim, event, count) ->
132 utils.simulateWheel(vim.window, 0, +0.5 * count, utils.WHEEL_MODE_PAGE)
133
134 # Scroll up half a page.
135 command_scroll_half_page_up = (vim, event, count) ->
136 utils.simulateWheel(vim.window, 0, -0.5 * count, utils.WHEEL_MODE_PAGE)
137
138 # Scroll down full a page.
139 command_scroll_page_down = (vim, event, count) ->
140 utils.simulateWheel(vim.window, 0, +1 * count, utils.WHEEL_MODE_PAGE)
141
142 # Scroll up full a page.
143 command_scroll_page_up = (vim, event, count) ->
144 utils.simulateWheel(vim.window, 0, -1 * count, utils.WHEEL_MODE_PAGE)
145
146 # Open a new tab and select the Address Bar.
147 command_open_tab = (vim) ->
148 vim.rootWindow.BrowserOpenTab()
149
150 absoluteTabIndex = (relativeIndex, gBrowser) ->
151 tabs = gBrowser.visibleTabs
152 { selectedTab } = gBrowser
153
154 currentIndex = tabs.indexOf(selectedTab)
155 absoluteIndex = currentIndex + relativeIndex
156 numTabs = tabs.length
157
158 wrap = (Math.abs(relativeIndex) == 1)
159 if wrap
160 absoluteIndex %%= numTabs
161 else
162 absoluteIndex = Math.max(0, absoluteIndex)
163 absoluteIndex = Math.min(absoluteIndex, numTabs - 1)
164
165 return absoluteIndex
166
167 helper_switch_tab = (direction, vim, event, count) ->
168 { gBrowser } = vim.rootWindow
169 gBrowser.selectTabAtIndex(absoluteTabIndex(direction * count, gBrowser))
170
171 # Switch to the previous tab.
172 command_tab_prev = helper_switch_tab.bind(undefined, -1)
173
174 # Switch to the next tab.
175 command_tab_next = helper_switch_tab.bind(undefined, +1)
176
177 helper_move_tab = (direction, vim, event, count) ->
178 { gBrowser } = vim.rootWindow
179 { selectedTab } = gBrowser
180 { pinned } = selectedTab
181
182 index = absoluteTabIndex(direction * count, gBrowser)
183
184 if index < gBrowser._numPinnedTabs
185 gBrowser.pinTab(selectedTab) unless pinned
186 else
187 gBrowser.unpinTab(selectedTab) if pinned
188
189 gBrowser.moveTabTo(selectedTab, index)
190
191 # Move the current tab backward.
192 command_tab_move_left = helper_move_tab.bind(undefined, -1)
193
194 # Move the current tab forward.
195 command_tab_move_right = helper_move_tab.bind(undefined, +1)
196
197 # Load the home page.
198 command_home = (vim) ->
199 vim.rootWindow.BrowserHome()
200
201 # Switch to the first tab.
202 command_tab_first = (vim) ->
203 vim.rootWindow.gBrowser.selectTabAtIndex(0)
204
205 # Switch to the first non-pinned tab.
206 command_tab_first_non_pinned = (vim) ->
207 firstNonPinned = vim.rootWindow.gBrowser._numPinnedTabs
208 vim.rootWindow.gBrowser.selectTabAtIndex(firstNonPinned)
209
210 # Switch to the last tab.
211 command_tab_last = (vim) ->
212 vim.rootWindow.gBrowser.selectTabAtIndex(-1)
213
214 # Toggle Pin Tab.
215 command_toggle_pin_tab = (vim) ->
216 currentTab = vim.rootWindow.gBrowser.selectedTab
217
218 if currentTab.pinned
219 vim.rootWindow.gBrowser.unpinTab(currentTab)
220 else
221 vim.rootWindow.gBrowser.pinTab(currentTab)
222
223 # Duplicate current tab.
224 command_duplicate_tab = (vim) ->
225 { gBrowser } = vim.rootWindow
226 gBrowser.duplicateTab(gBrowser.selectedTab)
227
228 # Close all tabs from current to the end.
229 command_close_tabs_to_end = (vim) ->
230 { gBrowser } = vim.rootWindow
231 gBrowser.removeTabsToTheEndFrom(gBrowser.selectedTab)
232
233 # Close all tabs except the current.
234 command_close_other_tabs = (vim) ->
235 { gBrowser } = vim.rootWindow
236 gBrowser.removeAllTabsBut(gBrowser.selectedTab)
237
238 # Close current tab.
239 command_close_tab = (vim, event, count) ->
240 { gBrowser } = vim.rootWindow
241 return if gBrowser.selectedTab.pinned
242 currentIndex = gBrowser.visibleTabs.indexOf(gBrowser.selectedTab)
243 for tab in gBrowser.visibleTabs[currentIndex...(currentIndex + count)]
244 gBrowser.removeTab(tab)
245
246 # Restore last closed tab.
247 command_restore_tab = (vim, event, count) ->
248 vim.rootWindow.undoCloseTab() for [1..count]
249
250 # Follow links, focus text inputs and click buttons with hint markers.
251 command_follow = (vim, event, count) ->
252 filter = (element, getElementShape) ->
253 semantic = true
254 switch
255 when isProperLink(element)
256 type = 'link'
257 when isTextInputElement(element), isContentEditable(element)
258 type = 'text'
259 when element.tabIndex > -1
260 type = 'clickable'
261 unless element.nodeName in ['A', 'INPUT', 'BUTTON', 'SELECT']
262 semantic = false
263 when element.hasAttribute('onclick') or
264 element.hasAttribute('onmousedown') or
265 element.hasAttribute('onmouseup') or
266 element.hasAttribute('oncommand') or
267 element.getAttribute('role') in ['link', 'button'] or
268 # Twitter special-case.
269 element.classList.contains('js-new-tweets-bar')
270 type = 'clickable'
271 semantic = false
272 # Putting markers on `<label>` elements is generally redundant, because
273 # its `<input>` gets one. However, some sites hide the actual `<input>`
274 # but keeps the `<label>` to click, either for styling purposes or to keep
275 # the `<input>` hidden until it is used. In those cases we should add a
276 # marker for the `<label>`.
277 when element.nodeName == 'LABEL'
278 if element.htmlFor
279 input = element.ownerDocument.getElementById(element.htmlFor)
280 if input and not getElementShape(input)
281 type = 'clickable'
282 # Elements that have “button” somewhere in the class might be clickable,
283 # unless they contain a real link or button in which case they likely are
284 # “button-wrapper”s. (`<SVG element>.className` is not a string!)
285 when typeof element.className == 'string' and
286 element.className.toLowerCase().contains('button')
287 unless element.querySelector('a, button')
288 type = 'clickable'
289 semantic = false
290 # When viewing an image it should get a marker to toggle zoom.
291 when element.ownerDocument.body.childElementCount == 1 and
292 element.nodeName == 'IMG' and
293 (element.classList.contains('overflowing') or
294 element.classList.contains('shrinkToFit'))
295 type = 'clickable'
296 return unless type
297 return unless shape = getElementShape(element)
298 return new Marker(element, shape, {semantic, type})
299
300 callback = (marker) ->
301 { element } = marker
302 element.focus()
303 last = (count == 1)
304 if not last and marker.type == 'link'
305 vim.rootWindow.gBrowser.loadOneTab(element.href, {
306 inBackground: true
307 relatedToCurrent: true
308 })
309 else
310 if element.target == '_blank'
311 targetReset = element.target
312 element.target = ''
313 utils.simulateClick(element)
314 element.target = targetReset if targetReset
315 count--
316 return (not last and marker.type != 'text')
317
318 vim.enterMode('hints', filter, callback)
319
320 # Like command_follow but multiple times.
321 command_follow_multiple = (vim, event) ->
322 command_follow(vim, event, Infinity)
323
324 # Follow links in a new background tab with hint markers.
325 command_follow_in_tab = (vim, event, count, inBackground = true) ->
326 filter = (element, getElementShape) ->
327 return unless isProperLink(element)
328 return unless shape = getElementShape(element)
329 return new Marker(element, shape, {semantic: true})
330
331 callback = (marker) ->
332 last = (count == 1)
333 vim.rootWindow.gBrowser.loadOneTab(marker.element.href, {
334 inBackground: if last then inBackground else true
335 relatedToCurrent: true
336 })
337 count--
338 return not last
339
340 vim.enterMode('hints', filter, callback)
341
342 # Follow links in a new foreground tab with hint markers.
343 command_follow_in_focused_tab = (vim, event, count) ->
344 command_follow_in_tab(vim, event, count, false)
345
346 # Copy the URL or text of a markable element to the system clipboard.
347 command_marker_yank = (vim) ->
348 filter = (element, getElementShape) ->
349 type = switch
350 when isProperLink(element) then 'link'
351 when isTextInputElement(element) then 'textInput'
352 when isContentEditable(element) then 'contenteditable'
353 return unless type
354 return unless shape = getElementShape(element)
355 return new Marker(element, shape, {semantic: true, type})
356
357 callback = (marker) ->
358 { element } = marker
359 text = switch marker.type
360 when 'link' then element.href
361 when 'textInput' then element.value
362 when 'contenteditable' then element.textContent
363 utils.writeToClipboard(text)
364
365 vim.enterMode('hints', filter, callback)
366
367 # Focus element with hint markers.
368 command_marker_focus = (vim) ->
369 filter = (element, getElementShape) ->
370 return unless element.tabIndex > -1
371 return unless shape = getElementShape(element)
372 return new Marker(element, shape, {semantic: true})
373
374 callback = (marker) ->
375 { element } = marker
376 element.focus()
377 element.select?()
378
379 vim.enterMode('hints', filter, callback)
380
381 # Search for the prev/next patterns in the following attributes of the element.
382 # `rel` should be kept as the first attribute, since the standard way of marking
383 # up prev/next links (`rel="prev"` and `rel="next"`) should be favored. Even
384 # though some of these attributes only allow a fixed set of keywords, we
385 # pattern-match them anyways since lots of sites don’t follow the spec and use
386 # the attributes arbitrarily.
387 attrs = ['rel', 'role', 'data-tooltip', 'aria-label']
388 helper_follow_pattern = (type, vim) ->
389 { document } = vim.window
390
391 # If there’s a `<link rel=prev/next>` element we use that.
392 for link in document.head.getElementsByTagName('link')
393 # Also support `rel=previous`, just like Google.
394 if type == link.rel.toLowerCase().replace(/^previous$/, 'prev')
395 vim.rootWindow.gBrowser.loadURI(link.href)
396 return
397
398 # Otherwise we look for a link or button on the page that seems to go to the
399 # previous or next page.
400 candidates = document.querySelectorAll('a, button')
401 patterns = utils.splitListString(getPref("#{ type }_patterns"))
402 if matchingLink = utils.getBestPatternMatch(patterns, attrs, candidates)
403 utils.simulateClick(matchingLink)
404
405 # Follow previous page.
406 command_follow_prev = helper_follow_pattern.bind(undefined, 'prev')
407
408 # Follow next page.
409 command_follow_next = helper_follow_pattern.bind(undefined, 'next')
410
411 # Go up one level in the URL hierarchy.
412 command_go_up_path = (vim, event, count) ->
413 { pathname } = vim.window.location
414 vim.window.location.pathname = pathname.replace(
415 /// (?: /[^/]+ ){1,#{ count }} /?$ ///, ''
416 )
417
418 # Go up to root of the URL hierarchy.
419 command_go_to_root = (vim) ->
420 vim.window.location.href = vim.window.location.origin
421
422 helper_go_history = (num, vim, event, count) ->
423 { index } = vim.rootWindow.getWebNavigation().sessionHistory
424 { history } = vim.window
425 num *= count
426 num = Math.max(num, -index)
427 num = Math.min(num, history.length - 1 - index)
428 return if num == 0
429 history.go(num)
430
431 # Go back in history.
432 command_back = helper_go_history.bind(undefined, -1)
433
434 # Go forward in history.
435 command_forward = helper_go_history.bind(undefined, +1)
436
437 findStorage = {lastSearchString: ''}
438
439 helper_find = (highlight, vim) ->
440 findBar = vim.rootWindow.gBrowser.getFindBar()
441
442 findBar.onFindCommand()
443 findBar._findField.focus()
444 findBar._findField.select()
445
446 return unless highlightButton = findBar.getElement('highlight')
447 if highlightButton.checked != highlight
448 highlightButton.click()
449
450 # Open the find bar, making sure that hightlighting is off.
451 command_find = helper_find.bind(undefined, false)
452
453 # Open the find bar, making sure that hightlighting is on.
454 command_find_hl = helper_find.bind(undefined, true)
455
456 helper_find_again = (direction, vim) ->
457 findBar = vim.rootWindow.gBrowser.getFindBar()
458 if findStorage.lastSearchString.length > 0
459 findBar._findField.value = findStorage.lastSearchString
460 findBar.onFindAgainCommand(direction)
461
462 # Search for the last pattern.
463 command_find_next = helper_find_again.bind(undefined, false)
464
465 # Search for the last pattern backwards.
466 command_find_prev = helper_find_again.bind(undefined, true)
467
468 # Enter insert mode.
469 command_insert_mode = (vim) ->
470 vim.enterMode('insert')
471
472 # Quote next keypress (pass it through to the page).
473 command_quote = (vim, event, count) ->
474 vim.enterMode('insert', count)
475
476 # Display the Help Dialog.
477 command_help = (vim) ->
478 help.injectHelp(vim.window.document, require('./modes'))
479
480 # Open and select the Developer Toolbar.
481 command_dev = (vim) ->
482 vim.rootWindow.DeveloperToolbar.show(true) # focus
483
484 command_Esc = (vim, event) ->
485 utils.blurActiveElement(vim.window)
486
487 # Blur active XUL control.
488 callback = -> event.originalTarget?.ownerDocument?.activeElement?.blur()
489 vim.window.setTimeout(callback, 0)
490
491 help.removeHelp(vim.window.document)
492
493 vim.rootWindow.DeveloperToolbar.hide()
494
495 vim.rootWindow.gBrowser.getFindBar().close()
496
497 vim.rootWindow.TabView.hide()
498
499
500 class Command
501 constructor: (@group, @name, @func, keys) ->
502 @prefName = "commands.#{ @name }.keys"
503 @keyValues =
504 if isPrefSet(@prefName)
505 try JSON.parse(getPref(@prefName))
506 catch then []
507 else
508 keys
509 for key, index in @keyValues when typeof key == 'string'
510 @keyValues[index] = legacy.convertKey(key)
511
512 keys: (value) ->
513 if value == undefined
514 return @keyValues
515 else
516 @keyValues = value
517 setPref(@prefName, JSON.stringify(value))
518
519 help: -> _("help_command_#{ @name }")
520
521 match: (str, numbers = null) ->
522 for key in @keys()
523 key = utils.normalizedKey(key)
524 if key.startsWith(str)
525 # When letter 0 follows after a number, it is considered as number 0
526 # instead of a valid command.
527 continue if key == '0' and numbers
528
529 count = parseInt(numbers[numbers.length - 1], 10) if numbers
530 count = if count > 1 then count else 1
531
532 return {match: true, exact: (key == str), command: this, count}
533
534
535 # coffeelint: disable=max_line_length
536 commands = [
537 new Command('urls', 'focus', command_focus, [['o']])
538 new Command('urls', 'focus_search', command_focus_search, [['O']])
539 new Command('urls', 'paste', command_paste, [['p']])
540 new Command('urls', 'paste_tab', command_paste_tab, [['P']])
541 new Command('urls', 'marker_yank', command_marker_yank, [['y', 'f']])
542 new Command('urls', 'marker_focus', command_marker_focus, [['v', 'f']])
543 new Command('urls', 'yank', command_yank, [['y', 'y']])
544 new Command('urls', 'reload', command_reload, [['r']])
545 new Command('urls', 'reload_force', command_reload_force, [['R']])
546 new Command('urls', 'reload_all', command_reload_all, [['a', 'r']])
547 new Command('urls', 'reload_all_force', command_reload_all_force, [['a', 'R']])
548 new Command('urls', 'stop', command_stop, [['s']])
549 new Command('urls', 'stop_all', command_stop_all, [['a', 's']])
550
551 new Command('nav', 'scroll_to_top', command_scroll_to_top , [['g', 'g']])
552 new Command('nav', 'scroll_to_bottom', command_scroll_to_bottom, [['G']])
553 new Command('nav', 'scroll_down', command_scroll_down, [['j']])
554 new Command('nav', 'scroll_up', command_scroll_up, [['k']])
555 new Command('nav', 'scroll_left', command_scroll_left, [['h']])
556 new Command('nav', 'scroll_right', command_scroll_right , [['l']])
557 new Command('nav', 'scroll_half_page_down', command_scroll_half_page_down, [['d']])
558 new Command('nav', 'scroll_half_page_up', command_scroll_half_page_up, [['u']])
559 new Command('nav', 'scroll_page_down', command_scroll_page_down, [['<space>']])
560 new Command('nav', 'scroll_page_up', command_scroll_page_up, [['<s-space>']])
561
562 new Command('tabs', 'open_tab', command_open_tab, [['t']])
563 new Command('tabs', 'tab_prev', command_tab_prev, [['J'], ['g', 'T']])
564 new Command('tabs', 'tab_next', command_tab_next, [['K'], ['g', 't']])
565 new Command('tabs', 'tab_move_left', command_tab_move_left, [['g', 'J']])
566 new Command('tabs', 'tab_move_right', command_tab_move_right, [['g', 'K']])
567 new Command('tabs', 'home', command_home, [['g', 'h']])
568 new Command('tabs', 'tab_first', command_tab_first, [['g', 'H'], ['g', '0']])
569 new Command('tabs', 'tab_first_non_pinned', command_tab_first_non_pinned, [['g', '^']])
570 new Command('tabs', 'tab_last', command_tab_last, [['g', 'L'], ['g', '$']])
571 new Command('tabs', 'toggle_pin_tab', command_toggle_pin_tab, [['g', 'p']])
572 new Command('tabs', 'duplicate_tab', command_duplicate_tab, [['y', 't']])
573 new Command('tabs', 'close_tabs_to_end', command_close_tabs_to_end, [['g', 'x', '$']])
574 new Command('tabs', 'close_other_tabs', command_close_other_tabs, [['g', 'x', 'a']])
575 new Command('tabs', 'close_tab', command_close_tab, [['x']])
576 new Command('tabs', 'restore_tab', command_restore_tab, [['X']])
577
578 new Command('browse', 'follow', command_follow, [['f']])
579 new Command('browse', 'follow_in_tab', command_follow_in_tab, [['F']])
580 new Command('browse', 'follow_in_focused_tab', command_follow_in_focused_tab, [['g', 'f']])
581 new Command('browse', 'follow_multiple', command_follow_multiple, [['a', 'f']])
582 new Command('browse', 'follow_previous', command_follow_prev, [['[']])
583 new Command('browse', 'follow_next', command_follow_next, [[']']])
584 new Command('browse', 'go_up_path', command_go_up_path, [['g', 'u']])
585 new Command('browse', 'go_to_root', command_go_to_root, [['g', 'U']])
586 new Command('browse', 'back', command_back, [['H']])
587 new Command('browse', 'forward', command_forward, [['L']])
588
589 new Command('misc', 'find', command_find, [['/']])
590 new Command('misc', 'find_hl', command_find_hl, [['a', '/']])
591 new Command('misc', 'find_next', command_find_next, [['n']])
592 new Command('misc', 'find_prev', command_find_prev, [['N']])
593 new Command('misc', 'insert_mode', command_insert_mode, [['i']])
594 new Command('misc', 'quote', command_quote, [['I']])
595 new Command('misc', 'help', command_help, [['?']])
596 new Command('misc', 'dev', command_dev, [[':']])
597
598 escapeCommand =
599 new Command('misc', 'Esc', command_Esc, [['<escape>']])
600 ]
601 # coffeelint: enable=max_line_length
602
603 searchForMatchingCommand = (keys) ->
604 for index in [0...keys.length] by 1
605 str = keys[index..].join('')
606 numbers = keys[0..index].join('').match(/[1-9]\d*/g)
607 for command in commands
608 return match if match = command.match(str, numbers)
609 return {match: false}
610
611 exports.commands = commands
612 exports.searchForMatchingCommand = searchForMatchingCommand
613 exports.escapeCommand = escapeCommand
614 exports.Command = Command
615 exports.findStorage = findStorage
Imprint / Impressum