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