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