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/>.
22 utils = require('./utils')
23 { injectHints } = require('./hints')
24 { rotateOverlappingMarkers } = require('./marker')
25 Command = require('./command')
28 , findStorage } = require('./commands')
30 { interfaces: Ci } = Components
32 XULDocument = Ci.nsIDOMXULDocument
35 onEnter: (vim, storage) ->
37 storage.commands ?= {}
39 onLeave: (vim, storage) ->
40 storage.keys.length = 0
42 onInput: (vim, storage, keyStr, event) ->
43 target = event.originalTarget
44 document = target.ownerDocument
47 utils.isTextInputElement(target) or
48 utils.isContentEditable(target) or
49 (utils.isActivatable(target) and keyStr == '<enter>') or
50 (utils.isAdjustable(target) and keyStr in [
51 '<arrowup>', '<arrowdown>', '<arrowleft>', '<arrowright>'
54 vim.rootWindow.TabView.isVisible() or
55 document.fullscreenElement or document.mozFullScreenElement
57 storage.keys.push(keyStr)
59 { match, exact, command, count } =
60 Command.searchForMatchingCommand(@commands, storage.keys)
62 if vim.state.blacklistedKeys and
63 storage.keys.join('') in vim.state.blacklistedKeys
68 if autoInsertMode and command != escapeCommand
73 command.func(vim, event, count)
74 storage.keys.length = 0
76 # Esc key is not suppressed, and passed to the browser in normal mode.
78 # - It allows for stopping the loading of the page.
79 # - It allows for closing many custom dialogs (and perhaps other things
80 # -- Esc is a very commonly used key).
81 # - It is not passed if Esc is used for `command_Esc` and we’re blurring
82 # an element. That allows for blurring an input in a custom dialog
83 # without closing the dialog too.
84 # - There are two reasons we might suppress it in other modes. If some
85 # custom dialog of a website is open, we should be able to cancel hint
86 # markers on it without closing it. Secondly, otherwise cancelling hint
87 # markers on Google causes its search bar to be focused.
88 # - It may only be suppressed in web pages, not in browser chrome. That
89 # allows for reseting the location bar when blurring it, and closing
90 # dialogs such as the “bookmark this page” dialog (<c-d>).
91 inBrowserChrome = (document instanceof XULDocument)
92 if keyStr == '<escape>' and (not autoInsertMode or inBrowserChrome)
98 storage.keys.length = 0 unless /^\d$/.test(keyStr)
105 onEnter: (vim, storage, count = null) ->
106 storage.count = count
108 utils.blurActiveElement(vim.window)
109 onInput: (vim, storage, keyStr) ->
112 if @commands['exit'].match(keyStr)
113 vim.enterMode('normal')
116 vim.enterMode('normal')
124 exports['text-input'] =
125 onEnter: (vim, storage, inputs = []) ->
126 storage.inputs = inputs
127 onLeave: (vim, storage) ->
128 storage.inputs = null
129 onInput: (vim, storage, keyStr) ->
131 index = inputs.indexOf(vim.window.document.activeElement)
134 vim.enterMode('normal')
136 when escapeCommand.match(keyStr)
137 utils.blurActiveElement(vim.window)
138 vim.enterMode('normal')
140 # Override the built-in shortcuts <tab> and <s-tab> to switch between
142 when keyStr == '<tab>'
144 when keyStr == '<s-tab>'
148 inputs[index %% inputs.length].select()
155 findBar = vim.rootWindow.gBrowser.getFindBar()
156 findStorage.lastSearchString = findBar._findField.value
158 onInput: (vim, storage, keyStr) ->
159 findBar = vim.rootWindow.gBrowser.getFindBar()
160 if @commands['exit'].match(keyStr)
170 onEnter: (vim, storage, filter, callback) ->
171 [ markers, container ] = injectHints(vim.rootWindow, vim.window, filter)
172 if markers.length > 0
173 storage.markers = markers
174 storage.container = container
175 storage.callback = callback
176 storage.numEnteredChars = 0
178 vim.enterMode('normal')
180 onLeave: (vim, storage) ->
181 { container } = storage
182 vim.rootWindow.setTimeout((->
188 onInput: (vim, storage, keyStr, event) ->
189 { markers, callback } = storage
192 when @commands['exit'].match(keyStr)
193 # Remove the hints immediately.
194 storage.container?.remove()
195 vim.enterMode('normal')
198 when @commands['rotate_markers_forward'].match(keyStr)
199 rotateOverlappingMarkers(markers, true)
200 when @commands['rotate_markers_backward'].match(keyStr)
201 rotateOverlappingMarkers(markers, false)
203 when @commands['delete_hint_char'].match(keyStr)
204 for marker in markers
205 switch marker.hintIndex - storage.numEnteredChars
206 when 0 then marker.deleteHintChar()
207 when -1 then marker.show()
208 storage.numEnteredChars-- unless storage.numEnteredChars == 0
211 if keyStr not in utils.getHintChars()
214 for marker in markers when marker.hintIndex == storage.numEnteredChars
215 match = marker.matchHintChar(keyStr)
216 marker.hide() unless match
217 if marker.isMatched()
218 marker.markMatched(true)
219 matchedMarkers.push(marker)
220 if matchedMarkers.length > 0
221 again = callback(matchedMarkers[0])
223 vim.rootWindow.setTimeout((->
224 marker.markMatched(false) for marker in matchedMarkers
226 marker.reset() for marker in markers
227 storage.numEnteredChars = 0
229 vim.enterMode('normal')
231 storage.numEnteredChars++
239 'rotate_markers_forward'
240 'rotate_markers_backward'
244 for modeName, mode of exports
245 commandNames = mode.commands
246 continue if not commandNames or commandNames == commands
248 for commandName in commandNames
249 name = "mode.#{ modeName }.#{ commandName }"
250 mode.commands[commandName] = new Command(null, name, null)