2 # Copyright Anton Khodakivskiy 2013, 2014.
3 # Copyright Simon Lydell 2013, 2014.
4 # Copyright Wang Zhuochun 2014.
6 # This file is part of VimFx.
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.
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.
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/>.
23 , findStorage } = require('./commands')
24 defaults = require('./defaults')
25 help = require('./help')
26 hints = require('./hints')
27 translate = require('./l10n')
28 { rotateOverlappingMarkers } = require('./marker')
29 utils = require('./utils')
31 { interfaces: Ci } = Components
33 XULDocument = Ci.nsIDOMXULDocument
35 # Helper to create modes in a DRY way.
36 mode = (modeName, obj, commands) ->
37 obj.name = translate.bind(null, "mode.#{ modeName }")
38 obj.order = defaults.mode_order[modeName]
40 for commandName, fn of commands
41 pref = "mode.#{ modeName }.#{ commandName }"
42 obj.commands[commandName] =
43 pref: defaults.BRANCH + pref
45 category: defaults.categoryMap[pref]
46 description: translate.bind(null, pref)
47 order: defaults.command_order[pref]
48 exports[modeName] = obj
56 help.removeHelp(vim.rootWindow)
58 onInput: (args, match) ->
59 { vim, storage, event } = args
61 autoInsertMode = (match.focus != null)
62 if match.type == 'none' or (autoInsertMode and not match.force)
65 match.command.run(args) if match.type == 'full'
67 # At this point the match is either full, partial or part of a count. Then
68 # we always want to suppress, except for one case: The Escape key.
70 # - It allows for stopping the loading of the page.
71 # - It allows for closing many custom dialogs (and perhaps other things
72 # -- Esc is a very commonly used key).
73 # - It is not passed if Esc is used for `command_Esc` and we’re blurring
74 # an element. That allows for blurring an input in a custom dialog
75 # without closing the dialog too.
76 # - There are two reasons we might suppress it in other modes. If some
77 # custom dialog of a website is open, we should be able to cancel hint
78 # markers on it without closing it. Secondly, otherwise cancelling hint
79 # markers on Google causes its search bar to be focused.
80 # - It may only be suppressed in web pages, not in browser chrome. That
81 # allows for reseting the location bar when blurring it, and closing
82 # dialogs such as the “bookmark this page” dialog (<c-d>).
83 document = event.originalTarget.ownerDocument
84 inBrowserChrome = (document instanceof XULDocument)
85 if match.keyStr == '<escape>' and (not autoInsertMode or inBrowserChrome)
95 onEnter: ({ vim, storage, args: [ filter, callback ] }) ->
96 [ markers, container ] = hints.injectHints(
97 vim.rootWindow, vim.window, filter, vim.parent.options
100 storage.markers = markers
101 storage.container = container
102 storage.callback = callback
103 storage.numEnteredChars = 0
105 vim.enterMode('normal')
107 onLeave: ({ vim, storage }) ->
108 { container } = storage
109 vim.rootWindow.setTimeout((->
111 ), vim.parent.options.hints_timeout)
115 onInput: (args, match) ->
116 { vim, storage } = args
117 { markers, callback } = storage
119 if match.type == 'full'
120 match.command.run(args)
121 else if match.keyStr in vim.parent.options.hint_chars
124 for marker in markers when marker.hintIndex == storage.numEnteredChars
125 matched = marker.matchHintChar(match.keyStr)
126 marker.hide() unless matched
127 if marker.isMatched()
128 marker.markMatched(true)
129 matchedMarkers.push(marker)
131 if matchedMarkers.length > 0
132 again = callback(matchedMarkers[0])
134 vim.rootWindow.setTimeout((->
135 marker.markMatched(false) for marker in matchedMarkers
136 ), vim.parent.options.hints_timeout)
137 marker.reset() for marker in markers
138 storage.numEnteredChars = 0
140 vim.enterMode('normal')
142 storage.numEnteredChars++
147 exit: ({ vim, storage }) ->
148 # The hints are removed automatically when leaving the mode, but after a
149 # timeout. When aborting the mode we should remove the hints immediately.
150 storage.container?.remove()
151 vim.enterMode('normal')
153 rotate_markers_forward: ({ storage }) ->
154 rotateOverlappingMarkers(storage.markers, true)
156 rotate_markers_backward: ({ storage }) ->
157 rotateOverlappingMarkers(storage.markers, false)
159 delete_hint_char: ({ storage }) ->
160 for marker in storage.markers
161 switch marker.hintIndex - storage.numEnteredChars
162 when 0 then marker.deleteHintChar()
163 when -1 then marker.show()
164 storage.numEnteredChars-- unless storage.numEnteredChars == 0
170 onEnter: ({ vim, storage, args: [ count ] }) ->
171 storage.count = count ? null
173 onLeave: ({ vim }) ->
174 utils.blurActiveElement(vim.window)
176 onInput: (args, match) ->
177 { vim, storage } = args
180 if match.type == 'full'
181 match.command.run(args)
184 vim.enterMode('normal')
190 exit: ({ vim }) -> vim.enterMode('normal')
196 onEnter: ({ vim, storage, args: [ inputs ] }) ->
197 storage.inputs = inputs
199 onLeave: ({ vim, storage }) ->
200 storage.inputs = null
202 onInput: (args, match) ->
203 { vim, storage: { inputs } } = args
204 index = inputs.indexOf(vim.window.document.activeElement)
206 vim.enterMode('normal')
208 return false unless match.type == 'full'
209 diff = match.command.run(args)
210 inputs[(index + diff) %% inputs.length].select() unless diff == 0
215 utils.blurActiveElement(vim.window)
216 vim.enterMode('normal')
218 input_previous: -> -1
227 onLeave: ({ vim }) ->
228 findBar = vim.rootWindow.gBrowser.getFindBar()
229 findStorage.lastSearchString = findBar._findField.value
231 onInput: (args, match) ->
232 args.findBar = args.vim.rootWindow.gBrowser.getFindBar()
233 if match.type == 'full'
234 match.command.run(args)
239 exit: ({ findBar }) -> findBar.close()