]> git.gir.st - VimFx.git/blob - extension/lib/modes.coffee
Enter automatic insert mode when an element is fullscreen
[VimFx.git] / extension / lib / modes.coffee
1 ###
2 # Copyright Anton Khodakivskiy 2013, 2014.
3 # Copyright Simon Lydell 2013, 2014.
4 # Copyright Wang Zhuochun 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 utils = require('./utils')
23 { injectHints } = require('./hints')
24 { rotateOverlappingMarkers } = require('./marker')
25 { updateToolbarButton } = require('./button')
26 { commands
27 , searchForMatchingCommand
28 , escapeCommand
29 , Command
30 , findStorage } = require('./commands')
31
32 { interfaces: Ci } = Components
33
34 XULDocument = Ci.nsIDOMXULDocument
35
36 exports['normal'] =
37 onEnter: (vim, storage) ->
38 storage.keys ?= []
39 storage.commands ?= {}
40
41 onLeave: (vim, storage) ->
42 storage.keys.length = 0
43
44 onInput: (vim, storage, keyStr, event) ->
45 target = event.originalTarget
46 document = target.ownerDocument
47
48 autoInsertMode = \
49 utils.isTextInputElement(target) or
50 utils.isContentEditable(target) or
51 (utils.isActivatable(target) and keyStr == '<enter>') or
52 (utils.isAdjustable(target) and keyStr in [
53 '<arrowup>', '<arrowdown>', '<arrowleft>', '<arrowright>'
54 '<space>', '<enter>'
55 ]) or
56 vim.rootWindow.TabView.isVisible() or
57 document.fullscreenElement or document.mozFullScreenElement
58
59 storage.keys.push(keyStr)
60
61 { match, exact, command, count } = searchForMatchingCommand(storage.keys)
62
63 if vim.state.blacklistedKeys and
64 storage.keys.join('') in vim.state.blacklistedKeys
65 match = false
66
67 if match
68
69 if autoInsertMode and command != escapeCommand
70 storage.keys.pop()
71 return false
72
73 if exact
74 command.func(vim, event, count)
75 storage.keys.length = 0
76
77 # Esc key is not suppressed, and passed to the browser in normal mode.
78 #
79 # - It allows for stopping the loading of the page.
80 # - It allows for closing many custom dialogs (and perhaps other things
81 # -- Esc is a very commonly used key).
82 # - It is not passed if Esc is used for `command_Esc` and we’re blurring
83 # an element. That allows for blurring an input in a custom dialog
84 # without closing the dialog too.
85 # - There are two reasons we might suppress it in other modes. If some
86 # custom dialog of a website is open, we should be able to cancel hint
87 # markers on it without closing it. Secondly, otherwise cancelling hint
88 # markers on Google causes its search bar to be focused.
89 # - It may only be suppressed in web pages, not in browser chrome. That
90 # allows for reseting the location bar when blurring it, and closing
91 # dialogs such as the “bookmark this page” dialog (<c-d>).
92 inBrowserChrome = (document instanceof XULDocument)
93 if keyStr == '<escape>' and (not autoInsertMode or inBrowserChrome)
94 return false
95
96 return true
97
98 else
99 storage.keys.length = 0 unless /\d/.test(keyStr)
100
101 return false
102
103 commands: commands
104
105 exports['insert'] =
106 onEnter: (vim, storage, count = null) ->
107 storage.count = count
108 updateToolbarButton(vim.rootWindow, {insertMode: true})
109 onLeave: (vim) ->
110 updateToolbarButton(vim.rootWindow, {insertMode: false})
111 utils.blurActiveElement(vim.window)
112 onInput: (vim, storage, keyStr) ->
113 switch storage.count
114 when null
115 if @commands['exit'].match(keyStr)
116 vim.enterMode('normal')
117 return true
118 when 1
119 vim.enterMode('normal')
120 else
121 storage.count--
122 return false
123 commands:
124 exit: ['<s-escape>']
125
126 exports['find'] =
127 onEnter: ->
128
129 onLeave: (vim) ->
130 findBar = vim.rootWindow.gBrowser.getFindBar()
131 findStorage.lastSearchString = findBar._findField.value
132
133 onInput: (vim, storage, keyStr) ->
134 findBar = vim.rootWindow.gBrowser.getFindBar()
135 if @commands['exit'].match(keyStr)
136 findBar.close()
137 return true
138 return false
139
140 commands:
141 exit: ['<escape>', '<enter>']
142
143 exports['hints'] =
144 onEnter: (vim, storage, filter, callback) ->
145 [ markers, container ] = injectHints(vim.rootWindow, vim.window, filter)
146 if markers.length > 0
147 storage.markers = markers
148 storage.container = container
149 storage.callback = callback
150 storage.numEnteredChars = 0
151 else
152 vim.enterMode('normal')
153
154 onLeave: (vim, storage) ->
155 { container } = storage
156 vim.rootWindow.setTimeout((->
157 container?.remove()
158 ), @timeout)
159 for key of storage
160 storage[key] = null
161
162 onInput: (vim, storage, keyStr, event) ->
163 { markers, callback } = storage
164
165 switch
166 when @commands['exit'].match(keyStr)
167 # Remove the hints immediately.
168 storage.container?.remove()
169 vim.enterMode('normal')
170 return true
171
172 when @commands['rotate_markers_forward'].match(keyStr)
173 rotateOverlappingMarkers(markers, true)
174 when @commands['rotate_markers_backward'].match(keyStr)
175 rotateOverlappingMarkers(markers, false)
176
177 when @commands['delete_hint_char'].match(keyStr)
178 for marker in markers
179 switch marker.hintIndex - storage.numEnteredChars
180 when 0 then marker.deleteHintChar()
181 when -1 then marker.show()
182 storage.numEnteredChars-- unless storage.numEnteredChars == 0
183
184 else
185 if keyStr not in utils.getHintChars()
186 return true
187 matchedMarkers = []
188 for marker in markers when marker.hintIndex == storage.numEnteredChars
189 match = marker.matchHintChar(keyStr)
190 marker.hide() unless match
191 if marker.isMatched()
192 marker.markMatched(true)
193 matchedMarkers.push(marker)
194 if matchedMarkers.length > 0
195 again = callback(matchedMarkers[0])
196 if again
197 vim.rootWindow.setTimeout((->
198 marker.markMatched(false) for marker in matchedMarkers
199 ), @timeout)
200 marker.reset() for marker in markers
201 storage.numEnteredChars = 0
202 else
203 vim.enterMode('normal')
204 return true
205 storage.numEnteredChars++
206
207 return true
208
209 timeout: 200
210
211 commands:
212 exit: ['<escape>']
213 rotate_markers_forward: ['<space>']
214 rotate_markers_backward: ['<s-space>']
215 delete_hint_char: ['<backspace>']
216
217 for modeName of exports
218 mode = exports[modeName]
219 continue if Array.isArray(mode.commands)
220 for commandName of mode.commands
221 name = "mode_#{ modeName }_#{ commandName }"
222 keys = mode.commands[commandName].map((key) -> [key])
223 mode.commands[commandName] = new Command(null, name, null, keys)
Imprint / Impressum