<html>
  <head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8">
  </head>
  <body bgcolor="#FFFFFF" text="#000000">
    <div style="" class="markdown-here-wrapper"
      data-md-url="Thunderbird">
      <p style="margin: 1.2em 0px ! important;">Hello all,</p>
      <p style="margin: 1.2em 0px ! important;">I’ve recently written a
        few Bash functions to help me define completions for some
        personal scripts and commands, and I would appreciate your
        opinion and suggestions for improvements. As no explanation
        beats a good example, here is a small example of a completion
        description with these functions (they all start with ‘C.’ to
        facilitate reading and to avoid name clashes) :</p>
      <pre style="font-size: 0.85em; font-family: Consolas,Inconsolata,Courier,monospace;font-size: 1em; line-height: 1.2em;margin: 1.2em 0px;"><code style="font-size: 0.85em; font-family: Consolas,Inconsolata,Courier,monospace;margin: 0px 0.15em; padding: 0px 0.3em; white-space: pre-wrap; border: 1px solid rgb(234, 234, 234); background-color: rgb(248, 248, 248); border-radius: 3px; display: inline;white-space: pre; overflow: auto; border-radius: 3px; border: 1px solid rgb(204, 204, 204); padding: 0.5em 0.7em; display: block ! important;">source /usr/share/bash/bashcomps.shl # I had some qualms about putting a shell script in /usr/lib

function C.example() {
    C.alt C.describing "a color" C.wordOf 5 red green blue black white \
            C.describing "a file" C.fileIn -f . \
            C.normal "$@"
    C.alt C.describing "invert colors" C.flagOf 2 -I --invert \
            C.normal "$@"
 }
C.defcomp 'C.repeat C.example' example
</code></pre>
      <p style="margin: 1.2em 0px ! important;">These few lines define a
        completion function that completes a color then a file, or a
        single flag, repeated ad infinitum. This is of course a
        contrived example, but the approach can trivially be extended to
        more useful completions.</p>
      <p style="margin: 1.2em 0px ! important;">The library predefines a
        small number of combinators, that can then be used to construct
        all sorts of complex structures within the arguments. For
        example, you can complete a URL by simply combining a protocol,
        a <code style="font-size: 0.85em; font-family:
          Consolas,Inconsolata,Courier,monospace;margin: 0px 0.15em;
          padding: 0px 0.3em; white-space: pre-wrap; border: 1px solid
          rgb(234, 234, 234); background-color: rgb(248, 248, 248);
          border-radius: 3px; display: inline;">://</code>, a hostname,
        a <code style="font-size: 0.85em; font-family:
          Consolas,Inconsolata,Courier,monospace;margin: 0px 0.15em;
          padding: 0px 0.3em; white-space: pre-wrap; border: 1px solid
          rgb(234, 234, 234); background-color: rgb(248, 248, 248);
          border-radius: 3px; display: inline;">/</code> and a path. In
        other words :</p>
      <pre style="font-size: 0.85em; font-family: Consolas,Inconsolata,Courier,monospace;font-size: 1em; line-height: 1.2em;margin: 1.2em 0px;"><code style="font-size: 0.85em; font-family: Consolas,Inconsolata,Courier,monospace;margin: 0px 0.15em; padding: 0px 0.3em; white-space: pre-wrap; border: 1px solid rgb(234, 234, 234); background-color: rgb(248, 248, 248); border-radius: 3px; display: inline;white-space: pre; overflow: auto; border-radius: 3px; border: 1px solid rgb(204, 204, 204); padding: 0.5em 0.7em; display: block ! important;">function C.url() {
    local prevSuf="$COMP_SUFFIX"
    C.suffixed '://' C.wordOf 3 http https ftp \
       C.suffixed '/' C.hostname \
       C.suffixed "$prevSuf" C.any "$@"
}
</code></pre>
      <p style="margin: 1.2em 0px ! important;">After that, completing a
        URL as part of an option becomes a piece of cake : </p>
      <pre style="font-size: 0.85em; font-family: Consolas,Inconsolata,Courier,monospace;font-size: 1em; line-height: 1.2em;margin: 1.2em 0px;"><code style="font-size: 0.85em; font-family: Consolas,Inconsolata,Courier,monospace;margin: 0px 0.15em; padding: 0px 0.3em; white-space: pre-wrap; border: 1px solid rgb(234, 234, 234); background-color: rgb(248, 248, 248); border-radius: 3px; display: inline;white-space: pre; overflow: auto; border-radius: 3px; border: 1px solid rgb(204, 204, 204); padding: 0.5em 0.7em; display: block ! important;">function C.urlOpt() {
    C.optOf 2 --url -U C.url "$@"
}
</code></pre>
      <p style="margin: 1.2em 0px ! important;">Of course, it is still
        possible to write your own generation functions and plug them
        into a parsing chain using a function called <code
          style="font-size: 0.85em; font-family:
          Consolas,Inconsolata,Courier,monospace;margin: 0px 0.15em;
          padding: 0px 0.3em; white-space: pre-wrap; border: 1px solid
          rgb(234, 234, 234); background-color: rgb(248, 248, 248);
          border-radius: 3px; display: inline;">C.argument</code> (in
        fact, all other helpers are defined in terms of <code
          style="font-size: 0.85em; font-family:
          Consolas,Inconsolata,Courier,monospace;margin: 0px 0.15em;
          padding: 0px 0.3em; white-space: pre-wrap; border: 1px solid
          rgb(234, 234, 234); background-color: rgb(248, 248, 248);
          border-radius: 3px; display: inline;">C.argument</code>) but I
        won’t bore you with the details here, that’s what the man page
        is for ;-)</p>
      <p style="margin: 1.2em 0px ! important;">If you want to test it
        for yourself, you can download it <a
          href="https://pool.coiffier.net/packages/bashcomps.tgz">here</a>
        (for a distribution-independent .tar.gz version), or directly as
        a <a href="https://pool.coiffier.net/packages/bashcomps.deb">.deb</a>
        or <a
          href="https://pool.coiffier.net/packages/bashcomps.tar.xz">.tar.xz</a>
        if your distro supports it.</p>
      <p style="margin: 1.2em 0px ! important;">Anyway, thank you for
        reading this far, and for any feedback or opinion you might have
        :-)</p>
      <p style="margin: 1.2em 0px ! important;">Yours indubitably,<br>
        Marc Coiffier</p>
      <div
        title="MDH:SGVsbG8gYWxsLDxicj48YnI+SSd2ZSByZWNlbnRseSB3cml0dGVuIGEgZmV3IEJhc2ggZnVuY3Rpb25zIHRvIGhlbHAgbWUgZGVmaW5lIGNvbXBsZXRpb25zIGZvciBzb21lIHBlcnNvbmFsIHNjcmlw
dHMgYW5kIGNvbW1hbmRzLCBhbmQgSSB3b3VsZCBhcHByZWNpYXRlIHlvdXIgb3BpbmlvbiBhbmQg
c3VnZ2VzdGlvbnMgZm9yIGltcHJvdmVtZW50cy4gQXMgbm8gZXhwbGFuYXRpb24gYmVhdHMgYSBn
b29kIGV4YW1wbGUsIGhlcmUgaXMgYSBzbWFsbCBleGFtcGxlIG9mIGEgY29tcGxldGlvbiBkZXNj
cmlwdGlvbiB3aXRoIHRoZXNlIGZ1bmN0aW9ucyAodGhleSBhbGwgc3RhcnQgd2l0aCAnQy4nIHRv
IGZhY2lsaXRhdGUgcmVhZGluZyBhbmQgdG8gYXZvaWQgbmFtZSBjbGFzaGVzKSA6PGJyPjxicj7C
oMKgwqAgc291cmNlIC91c3Ivc2hhcmUvYmFzaC9iYXNoY29tcHMuc2hsICMgSSBoYWQgc29tZSBx
dWFsbXMgYWJvdXQgcHV0dGluZyBhIHNoZWxsIHNjcmlwdCBpbiAvdXNyL2xpYjxicj7CoMKgwqAg
PGJyPsKgwqDCoCBmdW5jdGlvbiBDLmV4YW1wbGUoKSB7PGJyPsKgwqDCoMKgwqDCoMKgIEMuYWx0
IEMuZGVzY3JpYmluZyAiYSBjb2xvciIgQy53b3JkT2YgNSByZWQgZ3JlZW4gYmx1ZSBibGFjayB3
aGl0ZSBcPGJyPsKgwqDCoMKgwqDCoMKgIMKgIMKgIMKgIMKgIEMuZGVzY3JpYmluZyAiYSBmaWxl
IiBDLmZpbGVJbiAtZiAuIFw8YnI+wqDCoMKgwqDCoMKgwqAgwqAgwqAgwqDCoMKgIEMubm9ybWFs
ICIkQCI8YnI+wqAgwqAgwqAgwqAgQy5hbHQgQy5kZXNjcmliaW5nICJpbnZlcnQgY29sb3JzIiBD
LmZsYWdPZiAyIC1JIC0taW52ZXJ0IFw8YnI+wqDCoMKgwqDCoMKgIMKgIMKgIMKgIMKgwqAgQy5u
b3JtYWwgIiRAIjxicj7CoMKgwqDCoCB9PGJyPsKgwqDCoCBDLmRlZmNvbXAgJ0MucmVwZWF0IEMu
ZXhhbXBsZScgZXhhbXBsZTxicj48YnI+VGhlc2UgZmV3IGxpbmVzIGRlZmluZSBhIGNvbXBsZXRp
b24gZnVuY3Rpb24gdGhhdCBjb21wbGV0ZXMgYSBjb2xvciB0aGVuIGEgZmlsZSwgb3IgYSBzaW5n
bGUgZmxhZywgcmVwZWF0ZWQgYWQgaW5maW5pdHVtLiBUaGlzIGlzIG9mIGNvdXJzZSBhIGNvbnRy
aXZlZCBleGFtcGxlLCBidXQgdGhlIGFwcHJvYWNoIGNhbiB0cml2aWFsbHkgYmUgZXh0ZW5kZWQg
dG8gbW9yZSB1c2VmdWwgY29tcGxldGlvbnMuPGJyPjxicj5UaGUgbGlicmFyeSBwcmVkZWZpbmVz
IGEgc21hbGwgbnVtYmVyIG9mIGNvbWJpbmF0b3JzLCB0aGF0IGNhbiB0aGVuIGJlIHVzZWQgdG8g
Y29uc3RydWN0IGFsbCBzb3J0cyBvZiBjb21wbGV4IHN0cnVjdHVyZXMgd2l0aGluIHRoZSBhcmd1
bWVudHMuIEZvciBleGFtcGxlLCB5b3UgY2FuIGNvbXBsZXRlIGEgVVJMIGJ5IHNpbXBseSBjb21i
aW5pbmcgYSBwcm90b2NvbCwgYSBgOi8vYCwgYSBob3N0bmFtZSwgYSBgL2AgYW5kIGEgcGF0aC4g
SW4gb3RoZXIgd29yZHMgOjxicj48YnI+wqDCoMKgIGZ1bmN0aW9uIEMudXJsKCkgezxicj7CoMKg
wqDCoMKgwqDCoCBsb2NhbCBwcmV2U3VmPSIkQ09NUF9TVUZGSVgiPGJyPsKgwqDCoMKgwqDCoMKg
IEMuc3VmZml4ZWQgJzovLycgQy53b3JkT2YgMyBodHRwIGh0dHBzIGZ0cCBcPGJyPsKgwqDCoMKg
wqDCoMKgwqDCoMKgIEMuc3VmZml4ZWQgJy8nIEMuaG9zdG5hbWUgXDxicj7CoMKgwqDCoMKgwqDC
oMKgwqDCoCBDLnN1ZmZpeGVkICIkcHJldlN1ZiIgQy5hbnkgIiRAIjxicj7CoMKgwqAgfTxicj48
YnI+QWZ0ZXIgdGhhdCwgY29tcGxldGluZyBhIFVSTCBhcyBwYXJ0IG9mIGFuIG9wdGlvbiBiZWNv
bWVzIGEgcGllY2Ugb2YgY2FrZSA6IDxicj48YnI+wqDCoMKgIGZ1bmN0aW9uIEMudXJsT3B0KCkg
ezxicj7CoMKgwqAgwqAgwqAgQy5vcHRPZiAyIC0tdXJsIC1VIEMudXJsICIkQCI8YnI+wqDCoMKg
IH08YnI+PGJyPk9mIGNvdXJzZSwgaXQgaXMgc3RpbGwgcG9zc2libGUgdG8gd3JpdGUgeW91ciBv
d24gZ2VuZXJhdGlvbiBmdW5jdGlvbnMgYW5kIHBsdWcgdGhlbSBpbnRvIGEgcGFyc2luZyBjaGFp
biB1c2luZyBhIGZ1bmN0aW9uIGNhbGxlZCBgQy5hcmd1bWVudGAgKGluIGZhY3QsIGFsbCBvdGhl
ciBoZWxwZXJzIGFyZSBkZWZpbmVkIGluIHRlcm1zIG9mIGBDLmFyZ3VtZW50YCkgYnV0IEkgd29u
J3QgYm9yZSB5b3Ugd2l0aCB0aGUgZGV0YWlscyBoZXJlLCB0aGF0J3Mgd2hhdCB0aGUgbWFuIHBh
Z2UgaXMgZm9yIDstKTxicj48YnI+SWYgeW91IHdhbnQgdG8gdGVzdCBpdCBmb3IgeW91cnNlbGYs
IHlvdSBjYW4gZG93bmxvYWQgaXQgW2hlcmVdKGh0dHBzOi8vcG9vbC5jb2lmZmllci5uZXQvcGFj
a2FnZXMvYmFzaGNvbXBzLnRneikgKGZvciBhIGRpc3RyaWJ1dGlvbi1pbmRlcGVuZGVudCAudGFy
Lmd6IHZlcnNpb24pLCBvciBkaXJlY3RseSBhcyBhIFsuZGViXShodHRwczovL3Bvb2wuY29pZmZp
ZXIubmV0L3BhY2thZ2VzL2Jhc2hjb21wcy5kZWIpIG9yIFsudGFyLnh6XShodHRwczovL3Bvb2wu
Y29pZmZpZXIubmV0L3BhY2thZ2VzL2Jhc2hjb21wcy50YXIueHopIGlmIHlvdXIgZGlzdHJvIHN1
cHBvcnRzIGl0Ljxicj48YnI+QW55d2F5LCB0aGFuayB5b3UgZm9yIHJlYWRpbmcgdGhpcyBmYXIs
IGFuZCBmb3IgYW55IGZlZWRiYWNrIG9yIG9waW5pb24geW91IG1pZ2h0IGhhdmUgOi0pPGJyPjxi

        cj5Zb3VycyBpbmR1Yml0YWJseSw8YnI+TWFyYyBDb2lmZmllcjxicj48YnI+"
style="height:0;width:0;max-height:0;max-width:0;overflow:hidden;font-size:0em;padding:0;margin:0;">​</div>
    </div>
  </body>
</html>