Overview
This project provides a script (fsl) written in Tcl/Expect with the following features:
- Command aliasing and pre-processing
- Output filtering
- A number of preconfigured aliases, filters for colouring output
Though still inchoate, you should be able to use it as you would fossil on the command line.
N.B. As a supplement to the documentation here, the Cookbook supplies useful examples of fsl enhancements. There is also a tutorial available that explains things in bite-sized chunks.
Installation
Dependencies
Since the script makes use of dictionaries and anonymous functions, it requires Tcl >= 8.5. Installation of expect should be straightforward on most systems:
- Mac OSX: should be pre-installed;
- Fedora: run sudo yum install expect;
- Debian, Mint, Ubuntu: run sudo apt-get install expect.
Placing fsl on the $PATH
Simply copy the fsl script to a directory on $PATH:
$ echo $PATH /home/username/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games $ cp fsl ~/bin
Details
Out of the box, a number of filters and aliases are defined in ~/.fslrc (created on first run); see here for more information. The configuration file is a Tcl script: you can define procs (for helper functions) and employ them in your filters (definitions will end up in the config::fslrc namespace so you needn't worry about clobbering predefined functions).
To view a summary of these definitions use the fsl wrapper pseudo-command:
$ fsl wrapper => Aliases: . d , log heads => Filters: changes status timeline add rm addremove leaves d branch => Interceptors: wr wra wrap wrapp wrappe wrapper he hel help ali alia alias aliase aliases
Additional information on expansions can be listed using fsl aliases:
$ fsl aliases Currently defined expansions: . -> changes d -> diff , -> ui log -> timeline heads -> leaves
Defining aliases
Alias declarations are quite straightforward; their first argument is a command trigger, their second a bareword or quoted string, e.g.
alias log timeline alias history {timeline -n 100}
With these in place,
- fsl log → fossil timeline, and
- fsl history → fossil timeline -n 100.
Abbreviations can be declared using span syntax. For example, to introduce a more flexible fsl heads alias,
alias hea:heads leaves
Now any of the following will trigger the expansion:
$ fsl hea [...] $ fsl head [...] $ fsl heads [...]
Disjunctions may also be used as part of the command trigger, e.g.
alias heads|tips leaves
Since disjunction takes precedence over span specification, the above patterns can be combined into a single declaration:
alias hea:heads|tip:tips leaves
Writing filters
Filters are named, allowing them to be referenced elsewhere in your configuration script. Their structure is:
filter <internal name> <list of commands to filter> <body>
If you filter on a fossil command like diff, aliases of this command (by default, d) will also be filtered. Conversely, filtering on an alias (d), leaves its expansion (diff) untouched. These aliases may use span and disjunction syntax as described above. Also note that more than one filter may apply to a given command: under the default configuration, the timeline will be filtered by both status and log_entry.
A filter body can reference the current output line via the implicitly defined $line variable. By way of example, here's the log_entry filter:
filter log_entry {leaves timeline} { if {[regexp "^=== .* ===" $line]} { coloured blue $line } else { regsub -all {\[[A-Fa-f0-9]+\]} $line [coloured yellow &] } }
This filter is named log_entry (a proc in config::fslrc) and applies the defined substitutions to the output of leaves and timeline.
As listed, the log_entry filter will not be run on fsl time. Where a filter should apply to all abbreviations, simply use span syntax in the definition,
filter log_entry {leaves tim:timeline} { if {[regexp "^=== .* ===" $line]} { coloured blue $line } else { regsub -all {\[[A-Fa-f0-9]+\]} $line [coloured yellow &] } }
Command interception
Interceptors offer more fine grained control over command flow by providing a means to pre-process, override and define new commands. They come in two flavours:
- pre-processors. These are triggered before calling fossil and can modify the argument list.
- pseudo-commands. Provide new commands or override existing ones without spawning Fossil.
Three interceptors are defined out of the box:
- help (pre-processor). Wraps the builtin
help command, expanding its argument list. This makes it
possible to request help on aliased commands:
$ fsl help log (Expanded 'log' -> 'timeline -t ci') Usage: fossil timeline ?WHEN? ?BASELINE|DATETIME? ?-n N? ?-t TYPE? ?-showfiles? Print a summary of activity going backwards in date and time [...]
- wrapper (pseudo-command). Provides a summary of alias and filter definitions.
- aliases (pseudo-command). Lists currently defined expansions.
All of the above may be abbreviated.
Controlling interception
Interceptors can refer to the current parameter list via an implicit $params variable and are registered using a command trigger.
The distinction between pseudo-command and pre-processor boils down to what is returned:
- When an empty list is returned, the wrapper is expected to exit
without calling Fossil, thereby fully intercepting the query:
this defines a pseudo-command. For example, wrapper
provides a summary of currently defined aliases and filters:
interceptor wr:wrapper { puts [concat "Aliases: " [dict keys $config::aliases]] puts [concat "Filters: " [dict keys $config::filters]] }
- Pre-processors return a non-empty list. The help
interceptor leverages this functionality to expand its
arguments before the call to fossil:
interceptor he:help { concat help [expand [lrange $params 1 end] 1] }
Interceptors may also be used to call Fossil multiple times; this can be useful for scripting a repetitive workflow. To introduce additional calls, simply use the fossil command,
interceptor demo { fossil version fossil info -l return {} }
For further examples, see the Cookbook.
Utilities
Useful functions defined in fsl:
- coloured (for colouring output)
- display (for setting other display attributes)
- alias?
- filter?
- empty?, first and prefix? (simple utility functions)