]> git.gir.st - VimFx.git/blob - documentation/api.md
Fix VimFx being broken in new windows
[VimFx.git] / documentation / api.md
1 <!--
2 This is part of the VimFx documentation.
3 Copyright Simon Lydell 2015.
4 See the file README.md for copying conditions.
5 -->
6
7 # Public API
8
9 VimFx has a public API. It is intended to be used by:
10
11 - Users who prefer to configure things using text files.
12 - Users who would like to add custom commands.
13 - Users who would like to set [special options].
14 - Users who would like to make site-specific customizations.
15 - Extension authors who would like to extend VimFx.
16
17 VimFx users who use the public API should write a so-called [config file].
18
19
20 ## Getting the API
21
22 ```js
23 let {classes: Cc, interfaces: Ci, utils: Cu} = Components
24 Cu.import('resource://gre/modules/Services.jsm')
25 let apiPref = 'extensions.VimFx.api_url'
26 let apiUrl = Services.prefs.getComplexValue(apiPref, Ci.nsISupportsString).data
27 Cu.import(apiUrl, {}).getAPI(vimfx => {
28
29 // Do things with the `vimfx` object here.
30
31 })
32 ```
33
34 You might also want to take a look at the [config file bootstrap.js
35 example][bootstrap.js].
36
37
38 ## API
39
40 The following sub-sections assume that you store VimFx’s public API in a
41 variable called `vimfx`.
42
43 ### `vimfx.get(pref)` and `vimfx.set(pref, value)`
44
45 Gets or sets the value of the VimFx pref `pref`.
46
47 You can see all prefs in [defaults.coffee], or by opening [about:config] and
48 filtering by `extensions.vimfx`. Note that you can also access the [special
49 options], which may not be accessed in [about:config], using `vimfx.get()` and
50 `vimfx.set()`—in fact, this is the _only_ way of accessing those options.
51
52 #### `vimfx.get(pref)`
53
54 Gets the value of the VimFx pref `pref`.
55
56 ```js
57 // Get the value of the Hint chars option:
58 vimfx.get('hint_chars')
59 // Get all keyboard shortcuts (as a string) for the `f` command:
60 vimfx.get('modes.normal.follow')
61 ```
62
63 #### `vimfx.set(pref, value)`
64
65 Sets the value of the VimFx pref `pref` to `value`.
66
67 ```js
68 // Set the value of the Hint chars option:
69 vimfx.set('hint_chars', 'abcdefghijklmnopqrstuvwxyz')
70 // Add yet a keyboard shortcut for the `f` command:
71 vimfx.set('modes.normal.follow', vimfx.get('modes.normal.follow') + ' e')
72 ```
73
74 Note: If you produce conflicting keyboard shortcuts, the order of your code does
75 not matter. The command that comes first in VimFx’s settings page in the Add-ons
76 Manager (and in the help dialog) gets the shortcut; the other one(s) do(es) not.
77 See the notes about order in [mode object], [category object] and [command
78 object] for more information about order.
79
80 ```js
81 // Even though we set the shortcut for focusing the search bar last, the command
82 // for focusing the location bar “wins”, because it comes first in VimFx’s
83 // settings page in the Add-ons Manager.
84 vimfx.set('modes.normal.focus_location_bar', 'ö')
85 vimfx.set('modes.normal.focus_search_bar', 'ö')
86
87 // Swapping their orders also swaps the “winner”.
88 let {commands} = vimfx.modes.normal
89 ;[commands.focus_location_bar.order, commands.focus_search_bar.order] =
90 [commands.focus_search_bar.order, commands.focus_location_bar.order]
91 ```
92
93 ### `vimfx.addCommand(options, fn)`
94
95 Creates a new command.
96
97 **Note:** This should only be used by config file users, not by extension
98 authors who wish to extend VimFx. They should add commands manually to
99 [`vimfx.modes`] instead.
100
101 `options`:
102
103 - name: `String`. The name used when accessing the command via
104 `vimfx.modes[options.mode].commands[options.name]`. It is also used for the
105 pref used to store the shortcuts for the command:
106 `` `custom.mode.${options.mode}.${options.name}` ``.
107 - description: `String`. Shown in the help dialog and VimFx’s settings page in
108 the Add-ons Manager.
109 - mode: `String`. Defaults to `'normal'`. The mode to add the command to. The
110 value has to be one of the keys of [`vimfx.modes`].
111 - category: `String`. Defaults to `'misc'` for Normal mode and `''`
112 (uncategorized) otherwise. The category to add the command to. The
113 value has to be one of the keys of [`vimfx.get('categories')`][categories].
114 - order: `Number`. Defaults to putting the command at the end of the category.
115 The first of the default commands has the order `100` and then they increase
116 by `100` per command. This allows to put new commands between two already
117 existing ones.
118
119 `fn` is called when the command is activated. See the [onInput] documentation
120 below for more information.
121
122 Note that you have to give the new command a shortcut in VimFx’s settings page
123 in the Add-ons Manager or set one using `vimfx.set()` to able to use the new
124 command.
125
126 ```js
127 vimfx.addCommand({
128 name: 'hello',
129 description: 'Log Hello World',
130 }, => {
131 console.log('Hello World!')
132 })
133 // Optional:
134 vimfx.set('custom.mode.normal.hello', 'gö')
135 ```
136
137 ### `vimfx.addOptionOverrides(...rules)` and `vimfx.addKeyOverrides(...rules)`
138
139 These methods take any number of arguments. Each argument is a rule. The rules
140 are added in order. The methods may be run multiple times.
141
142 A rule is an `Array` of length 2:
143
144 1. The first item is a function that returns `true` if the rule should be
145 applied and `false` if not. This is called the matching function.
146 2. The second item is the value that should be used if the rule is applied. This
147 is called the override.
148
149 The rules are tried in the same order they were added. When a matching rule is
150 found it is applied. No more rules will be applied.
151
152 #### `vimfx.addOptionOverrides(...rules)`
153
154 The rules are matched any time the value of a VimFx pref is needed.
155
156 The matching function receives a [location object].
157
158 The override is an object whose keys are VimFx pref names and whose values
159 override the pref in question. The values should be formatted as in an [options
160 object].
161
162 ```js
163 vimfx.addOptionOverrides(
164 [ ({hostname, pathname, hash}) =>
165 `${hostname}${pathname}${hash}` === 'google.com/',
166 {prevent_autofocus: false}
167 ]
168 )
169 ```
170
171 #### `vimfx.addKeyOverrides(...rules)`
172
173 The rules are matched any time you press a key that is not part of the tail of a
174 multi-key shortcut.
175
176 The matching function receives a [location object] as well as the current
177 mode name (one of the keys of [`vimfx.modes`]).
178
179 The override is an array of keys which should not activate VimFx commands but be
180 sent to the page.
181
182 This allows to disable commands on specific sites. To _add_ commands on specific
183 sites, add them globally and then disable them on all _other_ sites.
184
185 ```js
186 vimfx.addKeyOverrides(
187 [ location => location.hostname === 'facebook.com',
188 ['j', 'k']
189 ]
190 )
191 ```
192
193 ### `vimfx.on(eventName, listener)`
194
195 Runs `listener(data)` when `eventName` is fired.
196
197 #### The `load` event
198
199 Occurs when opening a new tab or navigating to a new URL. The data passed to
200 listeners is an object with the following properties:
201
202 - vim: The current [vim object].
203 - location: A [location object].
204
205 This can be used to enter a different mode by default on some pages (which can
206 be used to replace the blacklist option).
207
208 ```js
209 vimfx.on('load', ({vim, location}) => {
210 if (location.hostname === 'example.com') {
211 vim.enterMode('ignore')
212 }
213 })
214 ```
215
216 #### The `modeChange` event
217
218 Occurs whenever the current mode in any tab changes. The initial entering of the
219 default mode in new tabs also counts as a mode change. The data passed to
220 listeners is the current [vim object].
221
222 ```js
223 vimfx.on('modeChange', vim => {
224 let mode = vimfx.modes[vim.mode].name()
225 vim.notify(`Entering mode: ${mode}`)
226 })
227 ```
228
229 #### The `TabSelect` event
230
231 Occurs whenever any tab in any window is selected. This is also fired when
232 Firefox starts for the currently selected tab. The data passed to listeners is
233 the `event` object passed to the standard Firefox [TabSelect] event.
234
235 ### `vimfx.refresh()`
236
237 If you make changes to [`vimfx.modes`] directly you need to call
238 `vimfx.refresh()` for your changes to take effect.
239
240 ### `vimfx.modes`
241
242 An object whose keys are mode names and whose values are [mode object]s.
243
244 This is a very low-level part of the API. It allows to:
245
246 - Access all commands and run them. This is the only thing that a config file
247 user needs it for.
248
249 ```js
250 let {commands} = vimfx.modes.normal
251 // Inside a custom command:
252 commands.tab_new.run(args)
253 ```
254
255 - Adding new commands. This is intended to be used by extension authors who wish
256 to extend VimFx, not config file users. They should use the
257 `vimfx.addCommand()` helper instead.
258
259 ```js
260 vimfx.modes.normal.commands.new_command = {
261 pref: 'extensions.my_extension.mode.normal.new_command',
262 category: 'misc',
263 order: 10000,
264 description: () => translate('mode.normal.new_command'),
265 run: args => console.log('New command! args:', args)
266 }
267 ```
268
269 - Adding new modes. This is intended to be used by extension authors who wish to
270 extend VimFx, not config file users.
271
272 ```js
273 vimfx.modes.new_mode = {
274 name: () => translate('mode.new_mode'),
275 order: 10000,
276 commands: {},
277 onEnter(args) {},
278 onLeave(args) {},
279 onInput(args, match) {
280 if (match.type === 'full') {
281 match.command.run(args)
282 }
283 return (match.type !== 'none')
284 },
285 }
286 ```
287
288 When you’re done modifying `vimfx.modes` directly, you need to call
289 `vimfx.refresh()`. (That’s taken care of automatically in the
290 `vimfx.addCommand()` helper.)
291
292 Have a look at [modes.coffee] and [commands.coffee] for more information.
293
294 ### `vimfx.get('categories')`
295
296 An object whose keys are category names and whose values are [category object]s.
297
298 ```js
299 let categories = vimfx.get('categories')
300
301 // Add a new category.
302 categories.custom = {
303 name: () => 'Custom commands',
304 order: 10000,
305 }
306
307 // Swap the order of the Location and Tabs categories.
308 ;[commands.focus_location_bar.order, categories.tabs.order] =
309 [categories.tabs.order, commands.focus_location_bar.order]
310 ```
311
312 ### Mode object
313
314 A mode is an object with the follwing properties:
315
316 - name(): `Function`. Returns a human readable name of the mode used in the help
317 dialog and VimFx’s settings page in the Add-ons Manager.
318 - order: `Number`. The first of the default modes has the order `100` and then
319 they increase by `100` per mode. This allows to put new modes between two
320 already existing ones.
321 - commands: `Object`. The keys are command names and the values are [command
322 object]s.
323 - onEnter(data, ...args): `Function`. Called when the mode is entered.
324 - onLeave(data): `Function`. Called when the mode is left.
325 - onInput(data, match): `Function`. Called when a key is pressed.
326
327 #### onEnter, onLeave and onInput
328
329 These methods are called with an object (called `data` above) with the following
330 properties:
331
332 - vim: The current [vim object].
333 - storage: An object unique to the current [vim object] and to the current mode.
334 Allows to share things between commands of the same mode by getting and
335 setting keys on it.
336
337 ##### onEnter
338
339 This method is called with an object as mentioned above, and after that there
340 may be any number of arguments (`args` in `vim.enterMode(modeName, ...args)`)
341 that the mode is free to do whatever it wants with.
342
343 ##### onInput
344
345 The object passed to this method (see above) also has the following properties:
346
347 - isFrameEvent: `Boolean`. `true` if the event occured in web page content,
348 `false` otherwise (if the event occured in the browser UI).
349 - count: `Number`. The count for the command. `undefined` if no count. (This is
350 simply a copy of `match.count`. `match` is defined below.)
351
352 The above object should be passed to commands when running them. The mode is
353 free to do whatever it wants with the return value (if any) of the commands it
354 runs.
355
356 It also receives a [match object] as the second argument.
357
358 `onInput` should return `true` if the current keypress should not be passed on
359 to the browser and web pages, and `false` otherwise.
360
361 ### Category object
362
363 A category is an object with the follwing properties:
364
365 - name(): `Function`. Returns a human readable name of the category used in the
366 help dialog and VimFx’s settings page in the Add-ons Manager. Config file
367 users adding custom categories could simply return a string; extension authors
368 are encouraged to look up the name from a locale file.
369 - order: `Number`. The first of the default categories is the “uncategorized”
370 category. It has the order `100` and then they increase by `100` per category.
371 This allows to put new categories between two already existing ones.
372
373 ### Command object
374
375 A command is an object with the following properties:
376
377 - pref: `String`. The pref used to store the shortcuts for the command.
378 - run(args): `Function`. Called when the command is activated.
379 - description(): `Function`. Returns a description of the command (as a string),
380 shown in the help dialog and VimFx’s settings page in the Add-ons Manager.
381 - category: `String`. The category to add the command to. The value has to be
382 one of the keys of [`vimfx.get('categories')`][categories].
383 - order: `Number`. The first of the default commands has the order `100` and
384 then they increase by `100` per command. This allows to put new commands
385 between two already existing ones.
386
387 ### Match object
388
389 A `match` object has the following properties:
390
391 - type: `String`. It has one of the following values:
392
393 - `'full'`: The current keypress, together with previous keypresses, fully
394 matches a command shortcut.
395 - `'partial'`: The current keypress, together with previous keypresses,
396 partially matches a command shortcut.
397 - `'count'`: The current keypress is not part of a command shortcut, but is a
398 digit and contributes to the count of a future matched command.
399 - `'none'`: The current keypress is not part of a command shortcut and does
400 not contribute to a count.
401
402 - focus: `String` or `null`. The type of currently focused _element_ plus
403 current pressed _key_ combo. You might not want to run commands and suppress
404 the event if this value is anything other than null. It has one of the
405 following values, depending on what kind of _element_ is focused and which
406 _key_ was pressed:
407
408 - `'editable'`: element: a text input or a `contenteditable` element.
409 key: any pressed key.
410 - `'activatable'`: element: an “activatable” element (link or button).
411 key: see the [`activatable_element_keys`] option.
412 - `'adjustable'`: element: an “adjustable” element (form control or video
413 player). key: see the [`adjustable_element_keys`] option.
414 - `'other'`: element: some other kind of element that can receive keystrokes,
415 for example an element in fullscreen mode. key: any pressed key.
416
417 If none of the above criteria is met, the value is `null`, which means that
418 the currently focused element does not appear to respond to keystrokes in any
419 special way.
420
421 - command: `null` unless `type` is `'full'`. Then it is the matched command (a
422 [command object]).
423
424 The matched command should usually be run at this point. It is suitable to
425 pass on the object passed to [onInput] to the command. Some modes might choose
426 to add extra properties to the object first. (That is favored over passing
427 several arguments, since it makes it easier for the command to in turn pass
428 the same data it got on to another command, if needed.)
429
430 Usually the return value of the command isn’t used, but that’s up to the mode.
431
432 - count: `Number`. The count for the command. `undefined` if no count.
433
434 - force: `Boolean`. Indicates if the current key sequence started with
435 `<force>`.
436
437 - keyStr: `String`. The current keypress represented as a string.
438
439 - unmodifiedKey: `String`. `keyStr` without modifiers.
440
441 - toplevel: `Boolean`. Whether or not the match was a toplevel match in the
442 shortcut key tree. This is `true` unless the match is part of the tail of a
443 multi-key shortcut.
444
445 ### Vim object
446
447 There is one `vim` object per tab.
448
449 A `vim` object has the following properties:
450
451 - window: [`Window`]. The current Firefox window object. Most commands
452 interacting with Firefox’s UI use this.
453
454 - browser: [`Browser`]. The `browser` that this vim object handles.
455
456 - options: `Object`. Provides access to all of VimFx’s options. It is an
457 [options object].
458
459 - mode: `String`. The current mode name.
460
461 - enterMode(modeName, ...args): `Function`. Enter mode `modeName`, passing
462 `...args` to the mode. It is up to every mode to do whatever it wants to with
463 `...args`.
464
465 - isBlacklisted(): `Function`. Returns `true` if the current URL is
466 [blacklisted], and `false` otherwise.
467
468 - isFrameEvent(event): `Function`. Returns `true` if `event` occurred in web
469 page content, and `false` otherwise (if it occurred in Firefox’s UI).
470
471 - isCurrent(): `Function`. Returns whether this vim object is the currently used
472 one. In other words, if the tab that this vim object manages is the currently
473 selected tab in the current Firefox window. Note, though, that the current
474 Firefox window is not necessarily the current window of your operating system.
475
476 - notify(title, options = {}): `Function`. Display a notification with the title
477 `title` (a `String`). If you need more text than a title, use `options.body`.
478 See [`Notification`] for more information.
479
480 - markPageInteraction(): `Function`. Marks that the user has interacted with the
481 page. After that [autofocus prevention] is not done anymore. Commands
482 interacting with web page content might want to do this.
483
484 **Warning:** There are also properties starting with an underscore on `vim`
485 objects. They are private, and not supposed to be used outside of VimFx’s own
486 source code. They may change at any time.
487
488 ### Options object
489
490 An `options` object provides access to all of VimFx’s options. It is an object
491 whose keys are VimFx pref names.
492
493 Note that the values are not just simply `vimfx.get(pref)` for the `pref` in
494 question; they are _parsed_ (`parse(vimfx.get(pref))`):
495
496 - Space-separated prefs are parsed into arrays of strings.
497
498 - `black_list` and `{prev,next}_patterns` are parsed into arrays of regular
499 expressions.
500
501 (See [parse-prefs.coffee] for all details.)
502
503 Any [option overrides] are automatically taken into account when getting an
504 option value.
505
506 The [special options] are also available on this object.
507
508
509 ### Location object
510
511 A location object is very similar to [`window.location`] in web pages.
512 Technically, it is a [`URL`] instance. You can experient with the current
513 location object by opening the [web console] and entering `location`.
514
515
516 ## Stability
517
518 The public API is currently **experimental** and therefore **unstable.** Things
519 might break with new VimFx versions.
520
521 As soon as VimFx 1.0.0 is released backwards compatibility will be a priority
522 and won’t be broken until VimFx 2.0.0.
523
524 [option overrides]: #vimfxaddOptionOverridesrules
525 [categories]: #vimfxgetcategories
526 [`vimfx.modes`]: #vimfxmodes
527 [onInput]: #oninput
528 [mode object]: #mode-object
529 [category object]: #category-object
530 [command object]: #command-object
531 [match object]: #match-object
532 [vim object]: #vim-object
533 [options object]: #options-object
534 [location object]: #location-object
535
536 [blacklisted]: options.md#blacklist
537 [special options]: options.md#special-options
538 [config file]: config-file.md
539 [bootstrap.js]: config-file.md#bootstrapjs
540 [autofocus prevention]: options.md#prevent-autofocus
541 [`activatable_element_keys`]: options.md#activatable_element_keys
542 [`adjustable_element_keys`]: options.md#adjustable_element_keys
543
544 [defaults.coffee]: ../extension/lib/defaults.coffee
545 [parse-prefs.coffee]: ../extension/lib/parse-prefs.coffee
546 [modes.coffee]: ../extension/lib/modes.coffee
547 [commands.coffee]: ../extension/lib/commands.coffee
548 [vim.coffee]: ../extension/lib/vim.coffee
549
550 [`Window`]: https://developer.mozilla.org/en-US/docs/Web/API/Window
551 [`Browser`]: https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XUL/browser
552 [`Notification`]: https://developer.mozilla.org/en-US/docs/Web/API/Notification
553 [`window.location`]: https://developer.mozilla.org/en-US/docs/Web/API/Location
554 [`URL`]: https://developer.mozilla.org/en-US/docs/Web/API/URL
555 [TabSelect]: https://developer.mozilla.org/en-US/docs/Web/Events/TabSelect
556 [web console]: https://developer.mozilla.org/en-US/docs/Tools/Web_Console
557 [about:config]: http://kb.mozillazine.org/About:config
Imprint / Impressum