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