From 7485ca0f041eeaf3f478a78ba2a5f6e161bef23a Mon Sep 17 00:00:00 2001 From: Jan Holthuis Date: Fri, 7 Dec 2018 14:58:01 +0100 Subject: [PATCH] Add fzf support A quick and dirty implementation to select a query result (#37). --- bin/dasht | 60 +++++++++++++++++-------- bin/dasht-query-fzf | 107 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 149 insertions(+), 18 deletions(-) create mode 100755 bin/dasht-query-fzf diff --git a/bin/dasht b/bin/dasht index 5173e30..5804392 100755 --- a/bin/dasht +++ b/bin/dasht @@ -80,23 +80,47 @@ if test $count -eq 0; then fi trap 'exit 44' USR1 # exit with a nonzero status when no results are found -if ! dasht-query-html "$pattern" "$@"; then - # notify user when no results are found so they can refine their search - echo "dasht: '$pattern' not found in $count docsets matching '${*:-.*}'" >&2 +if [ "$DASHT_USE_FZF" -ne 0 ]; then + url="$(if ! ./dasht-query-fzf "$pattern" "$@"; then + # notify user when no results are found so they can refine their search + echo "dasht: '$pattern' not found in $count docsets matching '${*:-.*}'" >&2 - # emulate pipefail (which POSIX lacks) by killing off the w3m(1) process - # below (which starts up simultaneously alongside this pipeline segment) - kill $(ps -e -o comm,ppid,pid | sed -n "s/^w3m *$$ //p") + # emulate pipefail (which POSIX lacks) by killing off the w3m(1) process + # below (which starts up simultaneously alongside this pipeline segment) + kill $(ps -e -o comm,ppid,pid | sed -n "s/^w3m *$$ //p") - # and then proceed to tell this script to generate a nonzero exit status - kill -s USR1 $$ -fi | -w3m -T text/html \ - -o confirm_qq=false \ - -o color=true \ - -o active_style=true \ - -o visited_anchor=true \ - -o label_topline=true \ - -o meta_refresh=true \ - -o wrap_search=true \ - -o ignorecase_search=true + # and then proceed to tell this script to generate a nonzero exit status + kill -s USR1 $$ + fi)" + w3m -T text/html \ + -o confirm_qq=false \ + -o color=true \ + -o active_style=true \ + -o visited_anchor=true \ + -o label_topline=true \ + -o meta_refresh=true \ + -o wrap_search=true \ + -o ignorecase_search=true \ + "$url" +else + if ! dasht-query-html "$pattern" "$@"; then + # notify user when no results are found so they can refine their search + echo "dasht: '$pattern' not found in $count docsets matching '${*:-.*}'" >&2 + + # emulate pipefail (which POSIX lacks) by killing off the w3m(1) process + # below (which starts up simultaneously alongside this pipeline segment) + kill $(ps -e -o comm,ppid,pid | sed -n "s/^w3m *$$ //p") + + # and then proceed to tell this script to generate a nonzero exit status + kill -s USR1 $$ + fi | + w3m -T text/html \ + -o confirm_qq=false \ + -o color=true \ + -o active_style=true \ + -o visited_anchor=true \ + -o label_topline=true \ + -o meta_refresh=true \ + -o wrap_search=true \ + -o ignorecase_search=true +fi diff --git a/bin/dasht-query-fzf b/bin/dasht-query-fzf new file mode 100755 index 0000000..8d5cc09 --- /dev/null +++ b/bin/dasht-query-fzf @@ -0,0 +1,107 @@ +#!/bin/sh -e +# +# # DASHT-QUERY-HTML 1 2018-10-09 2.3.0 +# +# ## NAME +# dasht-query-html - searches [Dash] docsets and emits HTML table rows +# +# ## SYNOPSIS +# +# `dasht-query-html` [*PATTERN*] [*DOCSET*]... +# +# ### Examples +# +# `dasht-query-html` +# Topics (A-Z) from each installed docset. +# +# `dasht-query-html` 'c - x' +# Search for "c - x" in all installed docsets. +# +# `dasht-query-html` 'c - x' bash +# Search for "c - x" only in the "bash" docset. +# +# `dasht-query-html` 'c - x' bash css +# Search for "c - x" only in the "bash" and "css" docsets. +# +# ## DESCRIPTION +# +# Searches for *PATTERN* in all installed [Dash] docsets, optionally searching +# only in those whose names match *DOCSET*s, by calling dasht-query-line(1). +# The results are then printed, one per line, to stdout as HTML table rows. +# However, if no results were found, this program exits with a nonzero status. +# +# ### Searching +# +# Whitespace characters in *PATTERN* are treated as wildcards, whereas the +# SQL LIKE wildcard characters `%` and `_` are not: they are taken literally. +# +# Before searching, *PATTERN* is surrounded by whitespace wildcards so that it +# can match anywhere: beginning, middle, or end. As a result, if *PATTERN* is +# undefined, it becomes a whitespace wildcard and thereby matches everything. +# +# ## ENVIRONMENT +# +# `DASHT_DOCSETS_DIR` +# Defines the filesystem location where your [Dash] docsets are installed. +# If undefined, its value is assumed to be `$XDG_DATA_HOME/dasht/docsets/` +# or, if `XDG_DATA_HOME` is undefined, `$HOME/.local/share/dasht/docsets/`. +# +# ## EXIT STATUS +# +# 44 +# No results were found. +# +# ## SEE ALSO +# +# dasht-query-line(1), dasht-docsets(1), dasht(1), [Dash] +# +# [Dash]: https://kapeli.com/dash +# +# ## AUTHOR +# +# Written in 2016 by Suraj N. Kurapati +# Distributed under the terms of the ISC license (see the LICENSE file). + +trap 'exit 44' USR1 # exit with a nonzero status when no results found +{ dasht-query-line "$@" || kill -s USR1 $$ ;} | awk \ + -v style_reset="$(tput sgr 0)" \ + -v style_name="$(tput bold)$(tput setaf 2)" \ + -v style_from="$(tput setaf 4)" \ + -v style_type="$(tput setaf 3)" \ + -v pattern="$1" ' + # Transforms alphabetical characters into bracketed regular expressions + # that match either lowercase or uppercase versions of those characters. + # This basically emulates the IGNORECASE feature in a POSIX environment. + function ignorecase(regex) { + buf = "" + tmp = regex + while (pos = match(tmp, "[[:alpha:]]")) { + chr = substr(tmp, pos, 1) + buf = buf substr(tmp, 1, pos - 1) "[" tolower(chr) toupper(chr) "]" + tmp = substr(tmp, pos + 1) + } + return buf tmp + } + BEGIN { + gsub("[\\^.[$()|*+?{]", "\\\\&", pattern) # escape normal regex(7) syntax + sub("^[[:space:]]+", "", pattern) # strip leading whitespace + sub("[[:space:]]+$", "", pattern) # strip trailing whitespace + gsub("[[:space:]]+", ".*", pattern) # treat whitespace as wildcards + pattern = ignorecase(pattern) # emulate IGNORECASE=1 for POSIX + if (pattern == "") pattern = "^." # grouped by leading character + } + NR == 1 {} + $2 == "=" { result[$1] = substr($0, index($0, $2) + length($2) + 1) } + $1 == "name" { + # mark search terms with STX and ETX bytes which are ignored by escape() + if (pattern) { + gsub(pattern, "\002&\003", result["name"]) + } + } + $1 == "url" { print \ + result["url"] "\t" \ + style_name result["name"] style_reset " " \ + style_from "[" result["from"] "]" style_reset " " \ + style_type "[" result["type"] "]" style_reset + } +' | { fzf --with-nth=2,3,4 --ansi || kill -s USR1 $$ ; } | cut -f 1