Advanced usage

Elvi with search options

mkelvis allows you to create elvi with options like -sort= and -results=NUM seen in many existing elvi.

We’ll create an elvis for Reddit–starting simple and building up functionality. The end-result is based on commit a8ec5a7833e44df9cc3d9f56dc6cf7aa69d13e8c from my elvi repo.

Name your file reddit.in and create a Makefile with these contents:

reddit: reddit.in
    grep -v '^[[:space:]]*\#' < $< | xargs mkelvis $@

Including the elvis name in the command means that we don’t have to include them in the file. This makes expanding this to more elvi easier. The grep call also allows line comments with #.

Bookmark-like elvis

www.reddit.com
www.reddit.com/search?q=
--description='Search Reddit'

This doesn’t give us much more than the bookmarks that surfraw allows: a line of

reddit  https://www.reddit.com/search?q=%s

in your bookmarks file would be enough to reimplement this.

The --description= isn’t strictly needed since it defaults to Search $elvisname.

Sorting and date-restricting

www.reddit.com
www.reddit.com/search?
--description='Search Reddit'

--enum=sort:relevance:relevance,hot,top,new,comments
--map=sort:sort

--enum=time:all-time:hour,day,week,year,all-time
--collapse=time:all-time,all
--map=time:t

--query-parameter=q

This provides -sort= and -time= options to define how the search is sorted and how far back you want your results to be. Run ./reddit -lh and you’ll see it nicely formatted for you with your options:

Usage: reddit [options] [search words]...
Description:
  Search Reddit (www.reddit.com)
Local options:
  -sort=SORT         An enum option for 'sort'
        relevance  | 
        hot        | 
        top        | 
        new        | 
        comments   | 
                     Default: relevance
                     Environment: SURFRAW_reddit_sort
  -time=TIME         An enum option for 'time'
        hour       | 
        day        | 
        week       | 
        year       | 
        all-time   | 
                     Default: all-time
                     Environment: SURFRAW_reddit_time

We can override the descriptions and metavars of options with --describe= and --metavar= respectively. We’ll do that later.

-sort=

--enum=sort:relevance:relevance,hot,top,new,comments: this defines the -sort= option, says the default value is relevance, and then defines all of the valid values: relevance, hot, top, new, and comments. The default (relevance) must be included in that list of valid values. mkelvis will exit with an error otherwise.

--map=sort:sort: this says that it’s mapping the sort variable (defined above, but order doesn’t matter) to the sort URL query string parameter. In the URL, it will appear like sort=$SURFRAW_reddit_sort.

-time=

--enum=time:all-time:hour,day,week,year,all-time: this defines the date-restricting option you can see on Reddit. Like with sort, we can see that it defaults to all-time–out of hour, day, week, year, all-time.

--map=time:t: it’s mapped to the t parameter.

--collapse=time:all-time,all: this says that if the time variable has the exact value of all-time, it should be replaced with all. This allows us to define a nicer interface for our elvis–not being bound to what the search engine’s parameter values actually are.

In fact, each colon-delimited (:) argument after the first one can be comma-delimited (,) lists of patterns describing these changes. See the reference for more.

Now, a problem with our previous version is that it would be hard for mkelvis to decide what order to append the mappings and the search terms in. The --query-parameter/-Q option solves this for us. The q= suffix can now be removed from the search URL because this will be appended to it later.

www.reddit.com
www.reddit.com/search?
--description='Search Reddit'

--enum=sort:relevance:relevance,hot,top,new,comments
--map=sort:sort

--enum=time:all-time:hour,day,week,year,all-time
--collapse=time:all-time,all
--map=time:t

--yes-no=nsfw:no
--inline=nsfw:nsfw
--describe=nsfw:'Whether to include NSFW posts'

--anything=flair:
--inline=flair:flair

--enum=self:any:any,only,none
--collapse=self:any,:only,yes:none,no
--inline=self:self
--describe=self:'Whether to include text posts'

--query-parameter=q

This gives us an elvis that unifies the syntax of searches with URL query parameters and searches with keywords such as site:github.com to limit results to github.com.

-nsfw=

--yes-no=nsfw:no: this defines a boolean option, corresponding to the values that have the surfraw functions ok, yesno, ifyes, and ifno, called on it. It’s called nsfw, and has a default value of no. In the elvis, you’d be able to use -nsfw=yes, -nsfw=no, -nsfw=on, -nsfw=off, -nsfw=1, -nsfw=0. They’re treated the same.

--inline=nsfw:nsfw: this places the value of nsfw inside the search terms in the form nsfw:$SURFRAW_reddit_nsfw. This is common search syntax.

--describe=nsfw:'Whether to include NSFW posts': this overrides the description for the -nsfw= option. Run ./reddit -lh to see it.

-flair=

--anything=flair:: this defines an option that has a value that the elvis doesn’t check. This is a useful catch-all option type that works very well for this elvis because Reddit flairs can really be anything.

--inline=flair:flair: like -nsfw=, this inlines the value of flair into the search terms. If flair has an empty value, it doesn’t get inlined.

-self=

--collapse=self:any,:only,yes:none,no: expanding on what was said above, --collapse= can have an unlimited number of comma-delimited arguments after the first argument (which defines the variable it applies to).

--inline=self:self: like the others, this is also inlined.

--describe=self:'Whether to include text posts': this overrides the description for the -self= option. Run ./reddit -lh to see it.

www.reddit.com/r/${SURFRAW_reddit_subreddit:-all}
www.reddit.com/r/${SURFRAW_reddit_subreddit:-all}/search?restrict_sr=1&
--description='Search posts in a subreddit'

--anything=subreddit:all
--alias=search:subreddit:anything
--describe=subreddit:'Set subreddit to search in; "all" is a global search'

--flag=global:subreddit:all

--enum=sort:relevance:relevance,hot,top,new,comments
--map=sort:sort

--enum=time:all-time:hour,day,week,year,all-time
--collapse=time:all-time,all
--map=time:t

--yes-no=nsfw:no
--inline=nsfw:nsfw
--describe=nsfw:'Whether to include NSFW posts'

--anything=flair:
--inline=flair:flair

--enum=self:any:any,only,none
--collapse=self:any,:only,yes:none,no
--inline=self:self
--describe=self:'Whether to include text posts'

--query-parameter=q

This expands our elvis from only searching globally to searching globally or in one subreddit, defaulting to all (i.e., r/all)–using a -subreddit= option.

--alias=search:subreddit:anything

This defines an alias, -search= (a common option for elvi), to the -subreddit= option. The anything as the third argument is needed because aliases can target options like -foo= and -foo, which mkelvis distinguishes–one takes a value, and the other is an alias to an option that does take a value. This anything argument is called a “typename”. See the reference for the details. These value- and non-value-taking versions can co-exist with the same names.

--flag=global:subreddit:all

This defines an alias to -subreddit=all for quick access to r/all (if it’s needed for some reason). You could replace this with your favourite subreddit and you won’t have to type the -subreddit= prefix all the time.

www.reddit.com/r/${SURFRAW_reddit_subreddit:-all} (base url)

At this point, any variables defined by the elvis will have their final values after validating and collapsing. The base URL text is placed in the elvis inside double quotes, so parameter expansions and command substitutions are available. It’s placed like so (without escaping or validating–so be careful!):

w3_browse_url "YOUR_BASE_URL"

Note that this parameter expansion is visible in the surfraw -elvi output.

(For your elvi, please stick to POSIX shell since surfraw is a POSIX shell program. We want elvi to be usable everywhere.)

www.reddit.com/r/${SURFRAW_reddit_subreddit:-all}/search?restrict_sr=1& (search url)

Like in the base URL, any variables will have their final values at this point and the search URL text is available for interaction by the shell:

w3_browse_url "YOUR_SEARCH_URL"

By the time w3_browse_url is called, the search URL will have any mappings and inlines, and the search terms placed within it.

Note that this search URL has an & at the end. This is because every search is restricted to the subreddit using the restrict_sr query parameter. mkelvis doesn’t care if there are already query parameters in the search URL–it just places its own mappings after it.

Swappable domain

# you can place these in quotes if you want
# it's only needed if the urls contain whitespace--since we use `xargs`
${SURFRAW_reddit_site}/r/${SURFRAW_reddit_subreddit:-all}
${SURFRAW_reddit_site}/r/${SURFRAW_reddit_subreddit:-all}/search?restrict_sr=1&
--description='Search posts in a subreddit'

--enum=site:reddit:reddit,oldreddit
--collapse=site:reddit,www.reddit.com:oldreddit,old.reddit.com

--anything=subreddit:all
--alias=search:subreddit:anything
--describe=subreddit:'Set subreddit to search in; "all" is a global search'

--flag=global:subreddit:all

--enum=sort:relevance:relevance,hot,top,new,comments
--map=sort:sort

--enum=time:all-time:hour,day,week,year,all-time
--collapse=time:all-time,all
--map=time:t

--yes-no=nsfw:no
--inline=nsfw:nsfw
--describe=nsfw:'Whether to include NSFW posts'

--anything=flair:
--inline=flair:flair

--enum=self:any:any,only,none
--collapse=self:any,:only,yes:none,no
--inline=self:self
--describe=self:'Whether to include text posts'

--num-tabs=2

--query-parameter=q

Finally, this allows us to switch between the default Reddit interface at www.reddit.com and the classic interface at old.reddit.com–using a -site= option.

Both URLs now replace their www.reddit.com prefix with a parameter expansion.

We also use --num-tabs=2 to align the description of the elvis with that of other elvi in surfraw -elvi.

Done! You can play around with it and its options, and read the elvis code (it has lots of nice comments). Now run ./reddit -lh and see the output of a nice elvis:

Usage: reddit [options] [search words]...
Description:
  Search posts in a subreddit (${SURFRAW_reddit_site}/r/${SURFRAW_reddit_subreddit:-all})
Local options:
  -nsfw=NSFW                                 Whether to include NSFW posts
                                             Default: no
                                             Environment: SURFRAW_reddit_nsfw
  -site=SITE                                 An enum option for 'site'
        reddit                             | 
        oldreddit                          | 
                                             Default: reddit
                                             Environment: SURFRAW_reddit_site
  -sort=SORT                                 An enum option for 'sort'
        relevance                          | 
        hot                                | 
        top                                | 
        new                                | 
        comments                           | 
                                             Default: relevance
                                             Environment: SURFRAW_reddit_sort
  -time=TIME                                 An enum option for 'time'
        hour                               | 
        day                                | 
        week                               | 
        year                               | 
        all-time                           | 
                                             Default: all-time
                                             Environment: SURFRAW_reddit_time
  -self=SELF                                 Whether to include text posts
        any                                | 
        only                               | 
        none                               | 
                                             Default: any
                                             Environment: SURFRAW_reddit_self
  -search=SUBREDDIT, -subreddit=SUBREDDIT    Set subreddit to search in; "all" is a global search
                                             Default: all
                                             Environment: SURFRAW_reddit_subreddit
  -flair=FLAIR                               An unchecked option for 'flair'
                                             Default: 
                                             Environment: SURFRAW_reddit_flair
  -global                                    An alias for -subreddit=all

See the reference for the details on individual options to mkelvis.

Making many elvi at once

mkelvis only generates one elvis per invocation (KISS!), leaving the user free to decide how they want to create many of them. An example follows.

If there are lots of simple elvi, a single text file is enough. It will be called elvi.in here.

First, specify the elvi you want by putting the arguments to mkelvis for each elvis on separate lines. The name should have no whitespace.

Example elvi.in file:

reddit www.reddit.com www.reddit.com/search?q=
subreddit www.reddit.com www.reddit.com/subreddits/search?q= --description='Visit a subreddit'

A possible Makefile:

.PHONY: all elvi
all: elvi
elvi: elvi.in
    xargs -L 1 mkelvis < $<

Note that after running make, the current directory will be filled with elvi. You could modify this so that each elvis has a suffix like .elvis to manage them easier.

See my elvi repo for more examples.