toolkit/devtools/gcli/source/docs/writing-commands.md

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1
michael@0 2 # Writing Commands
michael@0 3
michael@0 4 ## Basics
michael@0 5
michael@0 6 GCLI has opinions about how commands should be written, and it encourages you
michael@0 7 to do The Right Thing. The opinions are based on helping users convert their
michael@0 8 intentions to commands and commands to what's actually going to happen.
michael@0 9
michael@0 10 - Related commands should be sub-commands of a parent command. One of the goals
michael@0 11 of GCLI is to support a large number of commands without things becoming
michael@0 12 confusing, this will require some sort of namespacing or there will be
michael@0 13 many people wanting to implement the ``add`` command. This style of
michael@0 14 writing commands has become common place in Unix as the number of commands
michael@0 15 has gone up.
michael@0 16 The ```context``` command allows users to focus on a parent command, promoting
michael@0 17 its sub-commands above others.
michael@0 18
michael@0 19 - Each command should do exactly and only one thing. An example of a Unix
michael@0 20 command that breaks this principle is the ``tar`` command
michael@0 21
michael@0 22 $ tar -zcf foo.tar.gz .
michael@0 23 $ tar -zxf foo.tar.gz .
michael@0 24
michael@0 25 These 2 commands do exactly opposite things. Many a file has died as a result
michael@0 26 of a x/c typo. In GCLI this would be better expressed:
michael@0 27
michael@0 28 $ tar create foo.tar.gz -z .
michael@0 29 $ tar extract foo.tar.gz -z .
michael@0 30
michael@0 31 There may be commands (like tar) which have enough history behind them
michael@0 32 that we shouldn't force everyone to re-learn a new syntax. The can be achieved
michael@0 33 by having a single string parameter and parsing the input in the command)
michael@0 34
michael@0 35 - Avoid errors. We try to avoid the user having to start again with a command
michael@0 36 due to some problem. The majority of problems are simple typos which we can
michael@0 37 catch using command metadata, but there are 2 things command authors can do
michael@0 38 to prevent breakage.
michael@0 39
michael@0 40 - Where possible avoid the need to validate command line parameters in the
michael@0 41 exec function. This can be done by good parameter design (see 'do exactly
michael@0 42 and only one thing' above)
michael@0 43
michael@0 44 - If there is an obvious fix for an unpredictable problem, offer the
michael@0 45 solution in the command output. So rather than use request.error (see
michael@0 46 Request Object below) output some HTML which contains a link to a fixed
michael@0 47 command line.
michael@0 48
michael@0 49 Currently these concepts are not enforced at a code level, but they could be in
michael@0 50 the future.
michael@0 51
michael@0 52
michael@0 53 ## How commands work
michael@0 54
michael@0 55 This is how to create a basic ``greet`` command:
michael@0 56
michael@0 57 gcli.addCommand({
michael@0 58 name: 'greet',
michael@0 59 description: 'Show a greeting',
michael@0 60 params: [
michael@0 61 {
michael@0 62 name: 'name',
michael@0 63 type: 'string',
michael@0 64 description: 'The name to greet'
michael@0 65 }
michael@0 66 ],
michael@0 67 returnType: 'string',
michael@0 68 exec: function(args, context) {
michael@0 69 return 'Hello, ' + args.name;
michael@0 70 }
michael@0 71 });
michael@0 72
michael@0 73 This command is used as follows:
michael@0 74
michael@0 75 : greet Joe
michael@0 76 Hello, Joe
michael@0 77
michael@0 78 Some terminology that isn't always obvious: a function has 'parameters', and
michael@0 79 when you call a function, you pass 'arguments' to it.
michael@0 80
michael@0 81
michael@0 82 ## Internationalization (i18n)
michael@0 83
michael@0 84 There are several ways that GCLI commands can be localized. The best method
michael@0 85 depends on what context you are writing your command for.
michael@0 86
michael@0 87 ### Firefox Embedding
michael@0 88
michael@0 89 GCLI supports Mozilla style localization. To add a command that will only ever
michael@0 90 be used embedded in Firefox, this is the way to go. Your strings should be
michael@0 91 stored in ``browser/locales/en-US/chrome/browser/devtools/gclicommands.properties``,
michael@0 92 And you should access them using ``gcli.lookup(...)`` or ``gcli.lookupFormat()``
michael@0 93
michael@0 94 For examples of existing commands, take a look in
michael@0 95 ``browser/devtools/webconsole/GcliCommands.jsm``, which contains most of the
michael@0 96 current GCLI commands. If you will be adding a number of new commands, then
michael@0 97 consider starting a new JSM.
michael@0 98
michael@0 99 Your command will then look something like this:
michael@0 100
michael@0 101 gcli.addCommand({
michael@0 102 name: 'greet',
michael@0 103 description: gcli.lookup("greetDesc")
michael@0 104 ...
michael@0 105 });
michael@0 106
michael@0 107 ### Web Commands
michael@0 108
michael@0 109 There are 2 ways to provide translated strings for web use. The first is to
michael@0 110 supply the translated strings in the description:
michael@0 111
michael@0 112 gcli.addCommand({
michael@0 113 name: 'greet',
michael@0 114 description: {
michael@0 115 'root': 'Show a greeting',
michael@0 116 'fr-fr': 'Afficher un message d'accueil',
michael@0 117 'de-de': 'Zeige einen Gruß',
michael@0 118 'gk-gk': 'Εμφάνιση ένα χαιρετισμό',
michael@0 119 ...
michael@0 120 }
michael@0 121 ...
michael@0 122 });
michael@0 123
michael@0 124 Each description should contain at least a 'root' entry which is the
michael@0 125 default if no better match is found. This method has the benefit of being
michael@0 126 compact and simple, however it has the significant drawback of being wasteful
michael@0 127 of memory and bandwidth to transmit and store a significant number of strings,
michael@0 128 the majority of which will never be used.
michael@0 129
michael@0 130 More efficient is to supply a lookup key and ask GCLI to lookup the key from an
michael@0 131 appropriate localized strings file:
michael@0 132
michael@0 133 gcli.addCommand({
michael@0 134 name: 'greet',
michael@0 135 description: { 'key': 'demoGreetingDesc' }
michael@0 136 ...
michael@0 137 });
michael@0 138
michael@0 139 For web usage, the central store of localized strings is
michael@0 140 ``lib/gcli/nls/strings.js``. Other string files can be added using the
michael@0 141 ``l10n.registerStringsSource(...)`` function.
michael@0 142
michael@0 143 This method can be used both in Firefox and on the Web (see the help command
michael@0 144 for an example). However this method has the drawback that it will not work
michael@0 145 with DryIce built files until we fix bug 683844.
michael@0 146
michael@0 147
michael@0 148 ## Default argument values
michael@0 149
michael@0 150 The ``greet`` command requires the entry of the ``name`` parameter. This
michael@0 151 parameter can be made optional with the addition of a ``defaultValue`` to the
michael@0 152 parameter:
michael@0 153
michael@0 154 gcli.addCommand({
michael@0 155 name: 'greet',
michael@0 156 description: 'Show a message to someone',
michael@0 157 params: [
michael@0 158 {
michael@0 159 name: 'name',
michael@0 160 type: 'string',
michael@0 161 description: 'The name to greet',
michael@0 162 defaultValue: 'World!'
michael@0 163 }
michael@0 164 ],
michael@0 165 returnType: 'string',
michael@0 166 exec: function(args, context) {
michael@0 167 return "Hello, " + args.name;
michael@0 168 }
michael@0 169 });
michael@0 170
michael@0 171 Now we can also use the ``greet`` command as follows:
michael@0 172
michael@0 173 : greet
michael@0 174 Hello, World!
michael@0 175
michael@0 176
michael@0 177 ## Positional vs. named arguments
michael@0 178
michael@0 179 Arguments can be entered either positionally or as named arguments. Generally
michael@0 180 users will prefer to type the positional version, however the named alternative
michael@0 181 can be more self documenting.
michael@0 182
michael@0 183 For example, we can also invoke the greet command as follows:
michael@0 184
michael@0 185 : greet --name Joe
michael@0 186 Hello, Joe
michael@0 187
michael@0 188
michael@0 189 ## Short argument names
michael@0 190
michael@0 191 GCLI allows you to specify a 'short' character for any parameter:
michael@0 192
michael@0 193 gcli.addCommand({
michael@0 194 name: 'greet',
michael@0 195 params: [
michael@0 196 {
michael@0 197 name: 'name',
michael@0 198 short: 'n',
michael@0 199 type: 'string',
michael@0 200 ...
michael@0 201 }
michael@0 202 ],
michael@0 203 ...
michael@0 204 });
michael@0 205
michael@0 206 This is used as follows:
michael@0 207
michael@0 208 : greet -n Fred
michael@0 209 Hello, Fred
michael@0 210
michael@0 211 Currently GCLI does not allow short parameter merging (i.e. ```ls -la```)
michael@0 212 however this is planned.
michael@0 213
michael@0 214
michael@0 215 ## Parameter types
michael@0 216
michael@0 217 Initially the available types are:
michael@0 218
michael@0 219 - string
michael@0 220 - boolean
michael@0 221 - number
michael@0 222 - selection
michael@0 223 - delegate
michael@0 224 - date
michael@0 225 - array
michael@0 226 - file
michael@0 227 - node
michael@0 228 - nodelist
michael@0 229 - resource
michael@0 230 - command
michael@0 231 - setting
michael@0 232
michael@0 233 This list can be extended. See [Writing Types](writing-types.md) on types for
michael@0 234 more information.
michael@0 235
michael@0 236 The following examples assume the following definition of the ```greet```
michael@0 237 command:
michael@0 238
michael@0 239 gcli.addCommand({
michael@0 240 name: 'greet',
michael@0 241 params: [
michael@0 242 { name: 'name', type: 'string' },
michael@0 243 { name: 'repeat', type: 'number' }
michael@0 244 ],
michael@0 245 ...
michael@0 246 });
michael@0 247
michael@0 248 Parameters can be specified either with named arguments:
michael@0 249
michael@0 250 : greet --name Joe --repeat 2
michael@0 251
michael@0 252 And sometimes positionally:
michael@0 253
michael@0 254 : greet Joe 2
michael@0 255
michael@0 256 Parameters can be specified positionally if they are considered 'important'.
michael@0 257 Unimportant parameters must be specified with a named argument.
michael@0 258
michael@0 259 Named arguments can be specified anywhere on the command line (after the
michael@0 260 command itself) however positional arguments must be in order. So
michael@0 261 these examples are the same:
michael@0 262
michael@0 263 : greet --name Joe --repeat 2
michael@0 264 : greet --repeat 2 --name Joe
michael@0 265
michael@0 266 However (obviously) these are not the same:
michael@0 267
michael@0 268 : greet Joe 2
michael@0 269 : greet 2 Joe
michael@0 270
michael@0 271 (The second would be an error because 'Joe' is not a number).
michael@0 272
michael@0 273 Named arguments are assigned first, then the remaining arguments are assigned
michael@0 274 to the remaining parameters. So the following is valid and unambiguous:
michael@0 275
michael@0 276 : greet 2 --name Joe
michael@0 277
michael@0 278 Positional parameters quickly become unwieldy with long parameter lists so we
michael@0 279 recommend only having 2 or 3 important parameters. GCLI provides hints for
michael@0 280 important parameters more obviously than unimportant ones.
michael@0 281
michael@0 282 Parameters are 'important' if they are not in a parameter group. The easiest way
michael@0 283 to achieve this is to use the ```option: true``` property.
michael@0 284
michael@0 285 For example, using:
michael@0 286
michael@0 287 gcli.addCommand({
michael@0 288 name: 'greet',
michael@0 289 params: [
michael@0 290 { name: 'name', type: 'string' },
michael@0 291 { name: 'repeat', type: 'number', option: true, defaultValue: 1 }
michael@0 292 ],
michael@0 293 ...
michael@0 294 });
michael@0 295
michael@0 296 Would mean that this is an error
michael@0 297
michael@0 298 : greet Joe 2
michael@0 299
michael@0 300 You would instead need to do the following:
michael@0 301
michael@0 302 : greet Joe --repeat 2
michael@0 303
michael@0 304 For more on parameter groups, see below.
michael@0 305
michael@0 306 In addition to being 'important' and 'unimportant' parameters can also be
michael@0 307 optional. If is possible to be important and optional, but it is not possible
michael@0 308 to be unimportant and non-optional.
michael@0 309
michael@0 310 Parameters are optional if they either:
michael@0 311 - Have a ```defaultValue``` property
michael@0 312 - Are of ```type=boolean``` (boolean arguments automatically default to being false)
michael@0 313
michael@0 314 There is currently no way to make parameters mutually exclusive.
michael@0 315
michael@0 316
michael@0 317 ## Selection types
michael@0 318
michael@0 319 Parameters can have a type of ``selection``. For example:
michael@0 320
michael@0 321 gcli.addCommand({
michael@0 322 name: 'greet',
michael@0 323 params: [
michael@0 324 { name: 'name', ... },
michael@0 325 {
michael@0 326 name: 'lang',
michael@0 327 description: 'In which language should we greet',
michael@0 328 type: { name: 'selection', data: [ 'en', 'fr', 'de', 'es', 'gk' ] },
michael@0 329 defaultValue: 'en'
michael@0 330 }
michael@0 331 ],
michael@0 332 ...
michael@0 333 });
michael@0 334
michael@0 335 GCLI will enforce that the value of ``arg.lang`` was one of the values
michael@0 336 specified. Alternatively ``data`` can be a function which returns an array of
michael@0 337 strings.
michael@0 338
michael@0 339 The ``data`` property is useful when the underlying type is a string but it
michael@0 340 doesn't work when the underlying type is something else. For this use the
michael@0 341 ``lookup`` property as follows:
michael@0 342
michael@0 343 type: {
michael@0 344 name: 'selection',
michael@0 345 lookup: {
michael@0 346 'en': Locale.EN,
michael@0 347 'fr': Locale.FR,
michael@0 348 ...
michael@0 349 }
michael@0 350 },
michael@0 351
michael@0 352 Similarly, ``lookup`` can be a function returning the data of this type.
michael@0 353
michael@0 354
michael@0 355 ## Number types
michael@0 356
michael@0 357 Number types are mostly self explanatory, they have one special property which
michael@0 358 is the ability to specify upper and lower bounds for the number:
michael@0 359
michael@0 360 gcli.addCommand({
michael@0 361 name: 'volume',
michael@0 362 params: [
michael@0 363 {
michael@0 364 name: 'vol',
michael@0 365 description: 'How loud should we go',
michael@0 366 type: { name: 'number', min: 0, max: 11 }
michael@0 367 }
michael@0 368 ],
michael@0 369 ...
michael@0 370 });
michael@0 371
michael@0 372 You can also specify a ``step`` property which specifies by what amount we
michael@0 373 should increment and decrement the values. The ``min``, ``max``, and ``step``
michael@0 374 properties are used by the command line when up and down are pressed and in
michael@0 375 the input type of a dialog generated from this command.
michael@0 376
michael@0 377
michael@0 378 ## Delegate types
michael@0 379
michael@0 380 Delegate types are needed when the type of some parameter depends on the type
michael@0 381 of another parameter. For example:
michael@0 382
michael@0 383 : set height 100
michael@0 384 : set name "Joe Walker"
michael@0 385
michael@0 386 We can achieve this as follows:
michael@0 387
michael@0 388 gcli.addCommand({
michael@0 389 name: 'set',
michael@0 390 params: [
michael@0 391 {
michael@0 392 name: 'setting',
michael@0 393 type: { name: 'selection', values: [ 'height', 'name' ] }
michael@0 394 },
michael@0 395 {
michael@0 396 name: 'value',
michael@0 397 type: {
michael@0 398 name: 'delegate',
michael@0 399 delegateType: function() { ... }
michael@0 400 }
michael@0 401 }
michael@0 402 ],
michael@0 403 ...
michael@0 404 });
michael@0 405
michael@0 406 Several details are left out of this example, like how the delegateType()
michael@0 407 function knows what the current setting is. See the ``pref`` command for an
michael@0 408 example.
michael@0 409
michael@0 410
michael@0 411 ## Array types
michael@0 412
michael@0 413 Parameters can have a type of ``array``. For example:
michael@0 414
michael@0 415 gcli.addCommand({
michael@0 416 name: 'greet',
michael@0 417 params: [
michael@0 418 {
michael@0 419 name: 'names',
michael@0 420 type: { name: 'array', subtype: 'string' },
michael@0 421 description: 'The names to greet',
michael@0 422 defaultValue: [ 'World!' ]
michael@0 423 }
michael@0 424 ],
michael@0 425 ...
michael@0 426 exec: function(args, context) {
michael@0 427 return "Hello, " + args.names.join(', ') + '.';
michael@0 428 }
michael@0 429 });
michael@0 430
michael@0 431 This would be used as follows:
michael@0 432
michael@0 433 : greet Fred Jim Shiela
michael@0 434 Hello, Fred, Jim, Shiela.
michael@0 435
michael@0 436 Or using named arguments:
michael@0 437
michael@0 438 : greet --names Fred --names Jim --names Shiela
michael@0 439 Hello, Fred, Jim, Shiela.
michael@0 440
michael@0 441 There can only be one ungrouped parameter with an array type, and it must be
michael@0 442 at the end of the list of parameters (i.e. just before any parameter groups).
michael@0 443 This avoids confusion as to which parameter an argument should be assigned.
michael@0 444
michael@0 445
michael@0 446 ## Sub-commands
michael@0 447
michael@0 448 It is common for commands to be groups into those with similar functionality.
michael@0 449 Examples include virtually all VCS commands, ``apt-get``, etc. There are many
michael@0 450 examples of commands that should be structured as in a sub-command style -
michael@0 451 ``tar`` being the obvious example, but others include ``crontab``.
michael@0 452
michael@0 453 Groups of commands are specified with the top level command not having an
michael@0 454 exec function:
michael@0 455
michael@0 456 gcli.addCommand({
michael@0 457 name: 'tar',
michael@0 458 description: 'Commands to manipulate archives',
michael@0 459 });
michael@0 460 gcli.addCommand({
michael@0 461 name: 'tar create',
michael@0 462 description: 'Create a new archive',
michael@0 463 exec: function(args, context) { ... },
michael@0 464 ...
michael@0 465 });
michael@0 466 gcli.addCommand({
michael@0 467 name: 'tar extract',
michael@0 468 description: 'Extract from an archive',
michael@0 469 exec: function(args, context) { ... },
michael@0 470 ...
michael@0 471 });
michael@0 472
michael@0 473
michael@0 474 ## Parameter groups
michael@0 475
michael@0 476 Parameters can be grouped into sections.
michael@0 477
michael@0 478 There are 3 ways to assign a parameter to a group.
michael@0 479
michael@0 480 The simplest uses ```option: true``` to put a parameter into the default
michael@0 481 'Options' group:
michael@0 482
michael@0 483 gcli.addCommand({
michael@0 484 name: 'greet',
michael@0 485 params: [
michael@0 486 { name: 'repeat', type: 'number', option: true }
michael@0 487 ],
michael@0 488 ...
michael@0 489 });
michael@0 490
michael@0 491 The ```option``` property can also take a string to use an alternative parameter
michael@0 492 group:
michael@0 493
michael@0 494 gcli.addCommand({
michael@0 495 name: 'greet',
michael@0 496 params: [
michael@0 497 { name: 'repeat', type: 'number', option: 'Advanced' }
michael@0 498 ],
michael@0 499 ...
michael@0 500 });
michael@0 501
michael@0 502 An example of how this can be useful is 'git' which categorizes parameters into
michael@0 503 'porcelain' and 'plumbing'.
michael@0 504
michael@0 505 Finally, parameters can be grouped together as follows:
michael@0 506
michael@0 507 gcli.addCommand({
michael@0 508 name: 'greet',
michael@0 509 params: [
michael@0 510 { name: 'name', type: 'string', description: 'The name to greet' },
michael@0 511 {
michael@0 512 group: 'Advanced Options',
michael@0 513 params: [
michael@0 514 { name: 'repeat', type: 'number', defaultValue: 1 },
michael@0 515 { name: 'debug', type: 'boolean' }
michael@0 516 ]
michael@0 517 }
michael@0 518 ],
michael@0 519 ...
michael@0 520 });
michael@0 521
michael@0 522 This could be used as follows:
michael@0 523
michael@0 524 : greet Joe --repeat 2 --debug
michael@0 525 About to send greeting
michael@0 526 Hello, Joe
michael@0 527 Hello, Joe
michael@0 528 Done!
michael@0 529
michael@0 530 Parameter groups must come after non-grouped parameters because non-grouped
michael@0 531 parameters can be assigned positionally, so their index is important. We don't
michael@0 532 want 'holes' in the order caused by parameter groups.
michael@0 533
michael@0 534
michael@0 535 ## Command metadata
michael@0 536
michael@0 537 Each command should have the following properties:
michael@0 538
michael@0 539 - A string ``name``.
michael@0 540 - A short ``description`` string. Generally no more than 20 characters without
michael@0 541 a terminating period/fullstop.
michael@0 542 - A function to ``exec``ute. (Optional for the parent containing sub-commands)
michael@0 543 See below for more details.
michael@0 544
michael@0 545 And optionally the following extra properties:
michael@0 546
michael@0 547 - A declaration of the accepted ``params``.
michael@0 548 - A ``hidden`` property to stop the command showing up in requests for help.
michael@0 549 - A ``context`` property which defines the scope of the function that we're
michael@0 550 calling. Rather than simply call ``exec()``, we do ``exec.call(context)``.
michael@0 551 - A ``manual`` property which allows a fuller description of the purpose of the
michael@0 552 command.
michael@0 553 - A ``returnType`` specifying how we should handle the value returned from the
michael@0 554 exec function.
michael@0 555
michael@0 556 The ``params`` property is an array of objects, one for each parameter. Each
michael@0 557 parameter object should have the following 3 properties:
michael@0 558
michael@0 559 - A string ``name``.
michael@0 560 - A short string ``description`` as for the command.
michael@0 561 - A ``type`` which refers to an existing Type (see Writing Types).
michael@0 562
michael@0 563 Optionally each parameter can have these properties:
michael@0 564
michael@0 565 - A ``defaultValue`` (which should be in the type specified in ``type``).
michael@0 566 The defaultValue will be used when there is no argument supplied for this
michael@0 567 parameter on the command line.
michael@0 568 If the parameter has a ``defaultValue``, other than ``undefined`` then the
michael@0 569 parameter is optional, and if unspecified on the command line, the matching
michael@0 570 argument will have this value when the function is called.
michael@0 571 If ``defaultValue`` is missing, or if it is set to ``undefined``, then the
michael@0 572 system will ensure that a value is provided before anything is executed.
michael@0 573 There are 2 special cases:
michael@0 574 - If the type is ``selection``, then defaultValue must not be undefined.
michael@0 575 The defaultValue must either be ``null`` (meaning that a value must be
michael@0 576 supplied by the user) or one of the selection values.
michael@0 577 - If the type is ``boolean``, then ``defaultValue:false`` is implied and
michael@0 578 can't be changed. Boolean toggles are assumed to be off by default, and
michael@0 579 should be named to match.
michael@0 580 - A ``manual`` property for parameters is exactly analogous to the ``manual``
michael@0 581 property for commands - descriptive text that is longer than than 20
michael@0 582 characters.
michael@0 583
michael@0 584
michael@0 585 ## The Command Function (exec)
michael@0 586
michael@0 587 The parameters to the exec function are designed to be useful when you have a
michael@0 588 large number of parameters, and to give direct access to the environment (if
michael@0 589 used).
michael@0 590
michael@0 591 gcli.addCommand({
michael@0 592 name: 'echo',
michael@0 593 description: 'The message to display.',
michael@0 594 params: [
michael@0 595 {
michael@0 596 name: 'message',
michael@0 597 type: 'string',
michael@0 598 description: 'The message to display.'
michael@0 599 }
michael@0 600 ],
michael@0 601 returnType: 'string',
michael@0 602 exec: function(args, context) {
michael@0 603 return args.message;
michael@0 604 }
michael@0 605 });
michael@0 606
michael@0 607 The ``args`` object contains the values specified on the params section and
michael@0 608 provided on the command line. In this example it would contain the message for
michael@0 609 display as ``args.message``.
michael@0 610
michael@0 611 The ``context`` object has the following signature:
michael@0 612
michael@0 613 {
michael@0 614 environment: ..., // environment object passed to createTerminal()
michael@0 615 exec: ..., // function to execute a command
michael@0 616 update: ..., // function to alter the text of the input area
michael@0 617 createView: ..., // function to help creating rich output
michael@0 618 defer: ..., // function to create a deferred promise
michael@0 619 }
michael@0 620
michael@0 621 The ``environment`` object is opaque to GCLI. It can be used for providing
michael@0 622 arbitrary data to your commands about their environment. It is most useful
michael@0 623 when more than one command line exists on a page with similar commands in both
michael@0 624 which should act in their own ways.
michael@0 625 An example use for ``environment`` would be a page with several tabs, each
michael@0 626 containing an editor with a command line. Commands executed in those editors
michael@0 627 should apply to the relevant editor.
michael@0 628 The ``environment`` object is passed to GCLI at startup (probably in the
michael@0 629 ``createTerminal()`` function).
michael@0 630
michael@0 631 The ``document`` object is also passed to GCLI at startup. In some environments
michael@0 632 (e.g. embedded in Firefox) there is no global ``document``. This object
michael@0 633 provides a way to create DOM nodes.
michael@0 634
michael@0 635 ``defer()`` allows commands to execute asynchronously.
michael@0 636
michael@0 637
michael@0 638 ## Returning data
michael@0 639
michael@0 640 The command meta-data specifies the type of data returned by the command using
michael@0 641 the ``returnValue`` setting.
michael@0 642
michael@0 643 ``returnValue`` processing is currently functioning, but incomplete, and being
michael@0 644 tracked in [Bug 657595](http://bugzil.la/657595). Currently you should specify
michael@0 645 a ``returnType`` of ``string`` or ``html``. If using HTML, you can return
michael@0 646 either an HTML string or a DOM node.
michael@0 647
michael@0 648 In the future, JSON will be strongly encouraged as the return type, with some
michael@0 649 formatting functions to convert the JSON to HTML.
michael@0 650
michael@0 651 Asynchronous output is achieved using a promise created from the ``context``
michael@0 652 parameter: ``context.defer()``.
michael@0 653
michael@0 654 Some examples of this is practice:
michael@0 655
michael@0 656 { returnType: "string" }
michael@0 657 ...
michael@0 658 return "example";
michael@0 659
michael@0 660 GCLI interprets the output as a plain string. It will be escaped before display
michael@0 661 and available as input to other commands as a plain string.
michael@0 662
michael@0 663 { returnType: "html" }
michael@0 664 ...
michael@0 665 return "<p>Hello</p>";
michael@0 666
michael@0 667 GCLI will interpret this as HTML, and parse it for display.
michael@0 668
michael@0 669 { returnType: "dom" }
michael@0 670 ...
michael@0 671 return util.createElement(context.document, 'div');
michael@0 672
michael@0 673 ``util.createElement`` is a utility to ensure use of the XHTML namespace in XUL
michael@0 674 and other XML documents. In an HTML document it's functionally equivalent to
michael@0 675 ``context.document.createElement('div')``. If your command is likely to be used
michael@0 676 in Firefox or another XML environment, you should use it. You can import it
michael@0 677 with ``var util = require('util/util');``.
michael@0 678
michael@0 679 GCLI will use the returned HTML element as returned. See notes on ``context``
michael@0 680 above.
michael@0 681
michael@0 682 { returnType: "number" }
michael@0 683 ...
michael@0 684 return 42;
michael@0 685
michael@0 686 GCLI will display the element in a similar way to a string, but it the value
michael@0 687 will be available to future commands as a number.
michael@0 688
michael@0 689 { returnType: "date" }
michael@0 690 ...
michael@0 691 return new Date();
michael@0 692
michael@0 693 { returnType: "file" }
michael@0 694 ...
michael@0 695 return new File();
michael@0 696
michael@0 697 Both these examples return data as a given type, for which a converter will
michael@0 698 be required before the value can be displayed. The type system is likely to
michael@0 699 change before this is finalized. Please contact the author for more
michael@0 700 information.
michael@0 701
michael@0 702 { returnType: "string" }
michael@0 703 ...
michael@0 704 var deferred = context.defer();
michael@0 705 setTimeout(function() {
michael@0 706 deferred.resolve("hello");
michael@0 707 }, 500);
michael@0 708 return deferred.promise;
michael@0 709
michael@0 710 Errors can be signaled by throwing an exception. GCLI will display the message
michael@0 711 property (or the toString() value if there is no message property). (However
michael@0 712 see *3 principles for writing commands* above for ways to avoid doing this).
michael@0 713
michael@0 714
michael@0 715 ## Specifying Types
michael@0 716
michael@0 717 Types are generally specified by a simple string, e.g. ``'string'``. For most
michael@0 718 types this is enough detail. There are a number of exceptions:
michael@0 719
michael@0 720 * Array types. We declare a parameter to be an array of things using ``[]``,
michael@0 721 for example: ``number[]``.
michael@0 722 * Selection types. There are 3 ways to specify the options in a selection:
michael@0 723 * Using a lookup map
michael@0 724
michael@0 725 type: {
michael@0 726 name: 'selection',
michael@0 727 lookup: { one:1, two:2, three:3 }
michael@0 728 }
michael@0 729
michael@0 730 (The boolean type is effectively just a selection that uses
michael@0 731 ``lookup:{ 'true': true, 'false': false }``)
michael@0 732
michael@0 733 * Using given strings
michael@0 734
michael@0 735 type: {
michael@0 736 name: 'selection',
michael@0 737 data: [ 'left', 'center', 'right' ]
michael@0 738 }
michael@0 739
michael@0 740 * Using named objects, (objects with a ``name`` property)
michael@0 741
michael@0 742 type: {
michael@0 743 name: 'selection',
michael@0 744 data: [
michael@0 745 { name: 'Google', url: 'http://www.google.com/' },
michael@0 746 { name: 'Microsoft', url: 'http://www.microsoft.com/' },
michael@0 747 { name: 'Yahoo', url: 'http://www.yahoo.com/' }
michael@0 748 ]
michael@0 749 }
michael@0 750
michael@0 751 * Delegate type. It is generally best to inherit from Delegate in order to
michael@0 752 provide a customization of this type. See settingValue for an example.
michael@0 753
michael@0 754 See below for more information.
michael@0 755

mercurial