fish completions with Raku Grammar

in #coding3 years ago

I have to use Perforce in my company and it has a lot of flags, subcommands as on would expect with any Version control Systems. I recently discovered fish shell (my favourite one) and started using it. This converged with me exploring Grammar concept in Raku. For two days I was writing p4 command completions for fish shell by parsing the help outputs of the commands like p4 help and p4 help help, and crafting p4 completions in a Raku script. But at the end, I discovered that there was already an official fish completions were being shipped with fish.(Since i was using the fish binary without installing it, i did not discover it earlier)

Raku Grammar

As any grammar, Raku Grammar is built upon regex components which can be matched against strings(generally). regex come in two more flavours in Raku: token and rule. For example:

# resembles a line containing non `newline` char \N
# with newline at the end \n
my regex line { \N* \n }

# resembles a paragraph consisting of multiple lines
my token para { <line>+ }

# resembles an essay with multiple paragraphs
my rule essay { <para>+ }

Using the above basic components one could write Raku Grammar, which is an Object oriented class structure built into Raku. I started working on perforce help scripts on can obtain by running p4 help and p4 help help. To show a sample output:

    Perforce client commands:

        add          Open a new file to add it to the depot
        aliases      Display the content of the P4ALIASES file
        ---
        ---
        workspaces   Display list of known clients

    See 'p4 help administration' for more information about additional
    commands and issues of interest to those who are administering and
    operating the server.

    See 'p4 help dvcs' for more information about additional commands and
    topics of interest to those who use using Perforce with decentralized
    workflows.

For the above was the result of p4 help help command, which I parsed with the following Rake Grammar.

grammar Cmd-Des {
  token TOP {
    <inf>+ <cd>+ <inf>+ \n
    { make %($/<cd>.map: { .made unless .made.key eq 'help' }) }
  }
  token cd { <id>**2 <cmd> \s+ <des> $$ { make $/<cmd>.made => $/<des>.made } }
  token inf { <id> <des> [\s**2 <des>]? }
  token des {  \S+ [ \s \S+ ]* { make ~$/ } }
  token cmd { 'p4 help '? (\w+) { make ~$/[0] } }
  token id { \n* \s**4 }
}

Grammar can be actualized with make and made concepts. Once you make a use of the captured strings, you can do anything with what you made. And that is how i made p4 fish completions.

my $p4fc = open 'p4-completion.fish', :w;
with Cmd-Des.parsefile('p4-help-cmds.txt').made {
 # `p4 help commands`
 .keys.sort.say;
 $p4fc.say: 'set -l p4_client_commands ' ~ .keys.sort.join(' ') ~ '
   complete -c p4 -n "__fish_use_subcommand" -f -a help -d "Print the requested help message"
   complete -c p4 -n "__fish_seen_subcommand_from help" -a "$p4_client_commands"
   complete -c p4 -n "not __fish_contains_opt help; and __fish_seen_subcommand_from $p4_client_commands" -l explain -d "display additional information about the flags"'.indent(*);
 for .kv -> $c, $d {
   $p4fc.say: 'complete -f -c p4 -n "__fish_use_subcommand" -a ' ~ "$c -d \"$d\"";
 }

I've parsed p4 help and p4 <subcommand> --explain outputs with similar Grammar's. But at that point, I discovered the official completion file. I was stunned for a while, evaluating my choice of work over the past two days. For the past two days, all i could think of was about writing the above thing. I slept a bit late at nights (only getting 7 hrs of sleep, lol..). Even dreamt about it, though I don't have the files I've written in my dreams.

Nonetheless, it was fun to code I will abandon this endeavour for now. One can find all my code at my github repo p4-completions and also check my Replit invite