aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDefaultGen <gravel.justness270@slmail.me>2024-06-20 03:06:14 -0400
committerDefaultGen <gravel.justness270@slmail.me>2024-06-20 03:06:14 -0400
commitb8b17b0402a5996144dc099748ea4c18f576ac1b (patch)
treef481fededc59985df6b8f3e4103221adfd142df6
parent23a89932e57b50a8e9064b9c7ba329e62c88bc44 (diff)
downloaddenote-fzf-lua-b8b17b0402a5996144dc099748ea4c18f576ac1b.tar.gz
Working file and rg searches
-rw-r--r--README.md28
-rw-r--r--lua/denote-fzf-lua.lua99
-rw-r--r--lua/denote-fzf-lua/config.lua65
-rwxr-xr-xlua/denote-fzf-lua/scripts/fallback_search_files.sh49
-rwxr-xr-xlua/denote-fzf-lua/scripts/rg.sh4
-rwxr-xr-xlua/denote-fzf-lua/scripts/search_files.sh36
6 files changed, 174 insertions, 107 deletions
diff --git a/README.md b/README.md
index ecb49b1..e9ff351 100644
--- a/README.md
+++ b/README.md
@@ -4,5 +4,31 @@ Neovim plugin that uses `fzf-lua` to search a directory of notes formatted with
`DATE==SIGNATURE--TITLE__KEYWORDS.EXTENSION`
-The nice custom table for Denote files is the only unique 'feature' of this plugin. If you don't care about that you should use your existing search plugin.
+The nice custom table for Denote files is the only unique 'feature' of this plugin. If you don't care about that you should just use `fzf-lua` on its own.
+## Dependencies
+
+* `fzf` - Fuzzy finder
+* `ripgrep` - Fast `grep` replacement
+* `fd` (OPTIONAL) - Fast `find` replacement
+* `sd` (OPTIONAL) - Fast `sed` replacement
+* `qsv` (OPTIONAL) - Fast `column` replacement
+
+```
+Arch Linux: sudo pacman -S fzf ripgrep fd sd
+ yay -S qsv-bin
+
+Debian: sudo apt install fzf ripgrep fd sd
+
+qsv is available here: https://github.com/jqnatividad/qsv
+```
+
+If the optional dependencies are missing `denote-fzf-lua` falls back to standard Unix tools.
+
+As a rough benchmark, the Rust tools can perform a search through 10k notes in 0.1s vs. 0.2s for standard tools on my PC. For 100k notes it's 0.6s vs. 2.1s.
+
+`qsv` is used over the smaller, more ubiquitous `xsv` because it can table format very large inputs (e.g. 100k notes) without errors.
+
+# License
+
+GNU AGPL (`fzf-lua` license)
diff --git a/lua/denote-fzf-lua.lua b/lua/denote-fzf-lua.lua
index 1013afb..3e68c40 100644
--- a/lua/denote-fzf-lua.lua
+++ b/lua/denote-fzf-lua.lua
@@ -3,29 +3,30 @@ local M = {}
local config = require("denote-fzf-lua.config")
local fzflua = require('fzf-lua')
-
+---@param options table of user options
--- Check for dependencies and optional dependencies
-function M.has_prereqs()
+function M.has_prereqs(options)
if vim.fn.executable('fzf') ~= 1 then return "no" end
- if vim.fn.executable('rg') ~= 1 then return "no" end
+ if options.force_slow_mode then return "partial" end
if vim.fn.executable('fd') ~= 1 then return "partial" end
if vim.fn.executable('sd') ~= 1 then return "partial" end
- if vim.fn.executable('xsv') ~= 1 then return "partial" end
+ if vim.fn.executable('qsv') ~= 1 then return "partial" end
return "yes"
end
----Returns the full path of the appropriate script
-function M.get_script_path()
+---@param options table of user options
+--- Sets the full path of the appropriate search script (regular or fallback)
+function M.set_script_path(options)
local lua_file_path = debug.getinfo(1, "S").source:sub(2)
local lua_file_dir = vim.fn.fnamemodify(lua_file_path, ":h")
local script_path = lua_file_dir .. "/denote-fzf-lua/scripts/"
- local dependencies = M.has_prereqs()
+ local dependencies = M.has_prereqs(options)
if dependencies == "yes" then
script_path = script_path .. "search_files.sh"
elseif dependencies == "partial" then
script_path = script_path .. "fallback_search_files.sh"
else
- error("Missing dependencies: fzf and rg")
+ error("Missing dependency: fzf")
return false
end
return script_path
@@ -44,8 +45,8 @@ function M.format_fzf_with_nth(options)
return fzf_with_nth:sub(1, -2)
end
--- TODO: This is absolutely wrecked
-function M.open_file(filename)
+---@param filename chopped up string with fields separated by |
+function M.recombine_filename(filename)
local t = {}
filename = filename:gsub("%s%s+", "|")
t.path, t.year, t.month, t.day, t.hour, t.min, t.sec, t.sig, t.title, t.tags, t.ext =
@@ -65,33 +66,86 @@ function M.open_file(filename)
else
t.tags = "__" .. t.tags:gsub("%s", "_")
end
- local file = t.path .. t.year .. t.month .. t.day .. "T" .. t.hour .. t.min .. t.sec .. t.sig .. t.title .. t.tags .. t.ext
- vim.api.nvim_command('edit ' .. file)
+ return t.path .. t.year .. t.month .. t.day .. "T" .. t.hour .. t.min .. t.sec .. t.sig .. t.title .. t.tags .. t.ext
+end
+
+--- Sets fzf preview to bat or cat depending on program availability
+function M.set_preview_program()
+ if vim.fn.executable('bat') == 1 then
+ return "xargs bat -p --color=always"
+ else
+ return "xargs cat"
+ end
+end
+
+-- Sets fzf preview for contents search. $file is the filename, $line is the rg match line.
+function M.set_rg_preview()
+ if vim.fn.executable('bat') == 1 then
+ return "bat $file -p --color=always --highlight-line=$line"
+ else
+ return "cat $file"
+ end
end
---@param options table of user options
---Opens a window to search filenames
-function M.files(options)
- local script_path = M.get_script_path()
+function M.search_files(options)
+ script_path = M.set_script_path(options)
if not script_path then return end
options.fzf.opts["--with-nth"] = M.format_fzf_with_nth(options)
+ options.fzf.opts['--header-lines'] = 1
fzflua.fzf_exec(script_path .. " " .. options.dir,
{
- preview = options.fzf.preview,
- actions = options.fzf.actions,
+ preview = options.fzf.preview .. M.set_preview_program(),
+ -- selected[1] is the chopped up filename with 2+ spaces between fields
+ actions = { ['default'] = function(selected, opts)
+ local filename = M.recombine_filename(selected[1])
+ vim.api.nvim_command('edit ' .. filename)
+ end},
fzf_opts = options.fzf.opts,
})
- -- local fzf_args = M.format_fzf_with_nth(options) .. " " .. options.fzf.args
- -- vim.api.nvim_command('edit ' .. stdout)
+end
+
+---@param f string returned from rg. "/path/filename:line..."
+function M.open_file_at_line(f)
+ local filename, line = f:match("^(.-):(%d+)")
+ vim.api.nvim_command('edit +' .. line .. " " .. filename)
+end
+
+---@param options table
+function M.search_contents(options)
+ local r = fzflua.fzf_live("rg " .. options.rg.opts .. " -- <query> " .. options.dir .. " 2>/dev/null",
+ {
+ preview = options.rg.fzf.preview .. M.set_rg_preview(),
+ actions = { ['default'] = function(selected, opts)
+ local filename_line = selected[1]
+ filename_line = filename_line .. ":1" --Default :1 in case no line returned
+ M.open_file_at_line(filename_line)
+ end},
+ fzf_opts = options.rg.fzf.opts,
+ })
+end
+
+function M.search2(options)
+ opts = {}
+ opts.prompt = "> "
+ opts.actions = fzflua.defaults.actions.files
+ opts.previewer = "builtin"
+ opts.cwd = options.dir
+ opts.fzf_opts = options.rg.fzf.opts
+ return fzflua.fzf_live(function(q)
+ return "rg --column --color=always -- " .. vim.fn.shellescape(q or '')
+ end, opts)
end
---@param options table of user options
+---Creates Neovim command :DenoteSearch
function M.load_cmd(options)
vim.api.nvim_create_user_command("DenoteSearch", function(opts)
if opts.fargs[1] == "files" then
- M.files(options)
+ M.search_files(options)
elseif opts.fargs[1] == "contents" then
- M.conents(options)
+ M.search2(options)
else
error("Unsupported operation " .. opts.fargs[1])
end
@@ -105,8 +159,9 @@ end
---@param options? table user configuration
function M.setup(options)
- config.options = vim.tbl_deep_extend("force", config.defaults, options or {})
- M.load_cmd(config.options)
+ options = vim.tbl_deep_extend("force", config.defaults, options or {})
+ fzflua.setup({ winopts = options.winopts })
+ M.load_cmd(options)
end
return M
diff --git a/lua/denote-fzf-lua/config.lua b/lua/denote-fzf-lua/config.lua
index 6520ef8..8d466ba 100644
--- a/lua/denote-fzf-lua/config.lua
+++ b/lua/denote-fzf-lua/config.lua
@@ -1,13 +1,18 @@
local M = {}
M.defaults = {
- dir = "~/notes",
- window = {
- width_percent = 80,
- height_percent = 80,
- x_offset = 0,
- y_offset = 0,
+ dir = "~/notes",
+ force_slow_mode = false, -- Disables optional dependencies
+
+ -- Set any fzf-lua winopts here (See fzf-lua documentation)
+ winopts = {
+ height = 0.85,
+ width = 0.80,
+ row = 0.35,
+ col = 0.50,
},
+
+ -- Options sent to fzf (only relevant for filename search)
fzf = {
opts = {
['--reverse'] = true,
@@ -16,9 +21,9 @@ M.defaults = {
['--no-hscroll'] = true,
['--preview-window'] = "bottom:50%",
['-i'] = true,
- ['--delimiter'] = "\\s{2,}",
- ['--header-lines'] = 1
+ ['--delimiter'] = "\\s{2,}", -- Don't change this, change search_fields.
},
+ -- Which Denote fields are displayed in the search results
search_fields = {
path = false,
date = true,
@@ -28,23 +33,33 @@ M.defaults = {
tags = true,
ext = false,
},
- -- This recombines the chopped up filename from ./scripts/search_files.sh and outputs it to cat
- -- TODO: Rewrite this with a lua function
- preview = [[\
-sig={4}; sig=$(echo $sig | sd "\W" "" | sd " " "="); if [ ! -z ${sig} ] ; then sig="==${sig}"; fi; \
-title={5}; title=$(echo $title | tr -d '.' | tr ' ' '-'); if [ ! -z ${title} ] ; then title="--${title}"; fi; \
-tags={6}; tags=$(echo $tags | tr -d '.' | tr ' ' '_'); if [ ! -z ${tags} ] ; then tags="__${tags}"; fi; \
-ext={7}; \
-echo {1}\|{2}\|{3}\|{4}\|{5}\|{6}\|{7} | \
-sd "(?P<path>\/.*\/)?\|(?P<d1>\d\d\d\d)-(?P<d2>\d\d)-(?P<d3>\d\d)\|(?P<t1>\d\d):(?P<t2>\d\d):(?P<t3>\d\d)\|.*" "\$path\$d1\$d2\${d3}T\$t1\$t2\$t3$sig$title$tags$ext" |\
-xargs cat]],
- -- selected[1] is the chopped up filename with 2+ spaces between fields
- actions = {
- ['default'] = function(selected, opts)
- require'denote-fzf-lua'.open_file(selected[1])
- end
- }
- }
+ -- This recombines the chopped up filename from ./scripts/search_files.sh. The output needs to be piped to xargs bat or xargs cat.
+ preview = [[date={2};time={3}; datetime=$(echo ${date}T${time} | tr -d '\-:'); \
+ sig={4}; sig=$(echo $sig | tr -d '.' | tr " " "="); if [ ! -z ${sig} ] ; then sig="==${sig}"; fi; \
+ title={5}; title=$(echo $title | tr -d '.' | tr ' ' '-'); if [ ! -z ${title} ] ; then title="--${title}"; fi; \
+ tags={6}; tags=$(echo $tags | tr -d '.' | tr ' ' '_'); if [ ! -z ${tags} ] ; then tags="__${tags}"; fi; \
+ ext={7}; \
+ echo {1}${datetime}${sig}${title}${tags}${ext} | ]],
+ },
+ -- Options for ripgrep (Only relevant for contents search)
+ rg = {
+ opts = "--column --color=always",
+ fzf = {
+ opts = {
+ ['--reverse'] = true,
+ ['--no-info'] = true,
+ ['--no-separator'] = true,
+ ['--no-hscroll'] = true,
+ ['--preview-window'] = 'bottom:50%',
+ ['-i'] = true,
+ },
+ -- Checks if $line=$file in case rg --files is used and doesn't return a line number
+ -- This currently does nothing until I figure out if there's a good way to chop up filenames in the rg search
+ preview = "file=$(echo {} | cut -d ':' -f1); \
+ line=$(echo {} | cut -d ':' -f2); \
+ if [ \"$line\" = \"$file\" ] ; then line=0; fi;",
+ },
+ },
}
M.options = M.defaults
diff --git a/lua/denote-fzf-lua/scripts/fallback_search_files.sh b/lua/denote-fzf-lua/scripts/fallback_search_files.sh
index a617b64..9bc8135 100755
--- a/lua/denote-fzf-lua/scripts/fallback_search_files.sh
+++ b/lua/denote-fzf-lua/scripts/fallback_search_files.sh
@@ -1,36 +1,31 @@
#!/usr/bin/sh
find $1 |\
-sed -E "/^(.*\/)?([0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]T[0-9][0-9][0-9][0-9][0-9][0-9])(==[^=\-_\.]+)?(--[^\-_\.]+)?(__[^\.]+)?(\.[a-z0-9]+)$/!d" |\
-sed -E "s/^(.*\/)?([0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]T[0-9][0-9][0-9][0-9][0-9][0-9])(==([^=\-_\.]+))?(--([^\-_\.]+))?(__([^\.]+))?(\.[a-z0-9]+)$/\1|\2|\4|\6|\8|\9/" |\
+sed -E "/^(.*\/)?([0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]T[0-9][0-9][0-9][0-9][0-9][0-9])(==[^=_\.\-]+)?(--[^_\.\-]+)?(__[^\.]+)?(\.[a-z0-9]+)$/!d" |\
+sed -E "s/^(.*\/)?([0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]T[0-9][0-9][0-9][0-9][0-9][0-9])(==([^=_\.\-]+))?(--([^_\.\-]+))?(__([^\.]+))?(\.[a-z0-9]+)$/\1,\2,\4,\6,\8,\9/" |\
tr '\=\-_' ' ' |\
-sed -E 's/([0-9][0-9][0-9][0-9])([0-9][0-9])([0-9][0-9])T([0-9][0-9])([0-9][0-9])([0-9][0-9])/\1-\2-\3|\4:\5:\6/' |\
-sed 's/||/|.|/g' |\
-column -t -s "|" -N Path,Date,Time,Sig,Title,Tags,Ext |\
-fzf --delimiter='\s{2,}' --header-lines=1 -i $2 --preview='\
-sig={4}; sig=$(echo $sig | tr -d . | tr " " =); if [ ! -z ${sig} ] ; then sig="==${sig}"; fi; \
-title={5}; title=$(echo $title | tr -d . | tr " " -); if [ ! -z ${title} ] ; then title="--${title}"; fi; \
-tags={6}; tags=$(echo $tags | tr -d . | tr " " _); if [ ! -z ${tags} ] ; then tags="__${tags}"; fi; \
-ext={7}; \
-echo {1}\|{2}\|{3}\|{4}\|{5}\|{6}\|{7} |\
-sed -E "s/^(.*\/)?\|([0-9][0-9][0-9][0-9])-([0-9][0-9])-([0-9][0-9])\|([0-9][0-9]):([0-9][0-9]):([0-9][0-9])\|.*/\1\2\3\4T\5\6\7$sig$title$tags$ext/" |\
-xargs cat' \
---bind='enter:become(\
-sig={4}; sig=$(echo $sig | tr -d . | tr " " =); if [ ! -z ${sig} ] ; then sig="==${sig}"; fi; \
-title={5}; title=$(echo $title | tr -d . | tr " " -); if [ ! -z ${title} ] ; then title="--${title}"; fi; \
-tags={6}; tags=$(echo $tags | tr -d . | tr " " _); if [ ! -z ${tags} ] ; then tags="__${tags}"; fi; \
-ext={7}; \
-echo {1}\|{2}\|{3}\|{4}\|{5}\|{6}\|{7} | \
-sed -E "s/^(.*\/)?\|([0-9][0-9][0-9][0-9])-([0-9][0-9])-([0-9][0-9])\|([0-9][0-9]):([0-9][0-9]):([0-9][0-9])\|.*/\1\2\3\4T\5\6\7$sig$title$tags$ext/")+abort'
+sed -E 's/([0-9][0-9][0-9][0-9])([0-9][0-9])([0-9][0-9])T([0-9][0-9])([0-9][0-9])([0-9][0-9])/\1-\2-\3,\4:\5:\6/' |\
+sed 's/,,/,.,/g' |\
+column -t -s "," -N Path,Date,Time,Sig,Title,Tags,Ext
+# fzf --delimiter='\s{2,}' --header-lines=1 -i $2 --preview='\
+# sig={4}; sig=$(echo $sig | tr -d . | tr " " =); if [ ! -z ${sig} ] ; then sig="==${sig}"; fi; \
+# title={5}; title=$(echo $title | tr -d . | tr " " -); if [ ! -z ${title} ] ; then title="--${title}"; fi; \
+# tags={6}; tags=$(echo $tags | tr -d . | tr " " _); if [ ! -z ${tags} ] ; then tags="__${tags}"; fi; \
+# ext={7}; \
+# echo {1}\|{2}\|{3}\|{4}\|{5}\|{6}\|{7} |\
+# sed -E "s/^(.*\/)?\|([0-9][0-9][0-9][0-9])-([0-9][0-9])-([0-9][0-9])\|([0-9][0-9]):([0-9][0-9]):([0-9][0-9])\|.*/\1\2\3\4T\5\6\7$sig$title$tags$ext/" |\
+# xargs cat' \
+# --bind='enter:become(\
+# sig={4}; sig=$(echo $sig | tr -d . | tr " " =); if [ ! -z ${sig} ] ; then sig="==${sig}"; fi; \
+# title={5}; title=$(echo $title | tr -d . | tr " " -); if [ ! -z ${title} ] ; then title="--${title}"; fi; \
+# tags={6}; tags=$(echo $tags | tr -d . | tr " " _); if [ ! -z ${tags} ] ; then tags="__${tags}"; fi; \
+# ext={7}; \
+# echo {1}\|{2}\|{3}\|{4}\|{5}\|{6}\|{7} | \
+# sed -E "s/^(.*\/)?\|([0-9][0-9][0-9][0-9])-([0-9][0-9])-([0-9][0-9])\|([0-9][0-9]):([0-9][0-9]):([0-9][0-9])\|.*/\1\2\3\4T\5\6\7$sig$title$tags$ext/")+abort'
-# I am sorry for this. If I learn an equally fast, more readable way to implement this in Lua I will.
# 1. find everything in directory
# 2. sed deletes every line that isn't a Denote note (can't do this with find alone because -regex can't match optional capture groups)
-# 3. sed splits the Denote filename into | delimited fields
+# 3. sed splits the Denote filename into , delimited fields (pa
# 4. tr replaces sig, title, tags special characters with spaces
# 5. sed splits the date and time nicely
-# 6. sed replaces blank fields with a period (column doesn't work with null fields)
+# 6. sed replaces blank fields with a period
# 7. column pretty prints the filenames in a table
-# 8. fzf --delimiter breaks the table up into fields on 2+ space
-# fzf --nth and --with-nth (args passed in $2) determine which fields are shown
-# fzf --preview takes all the fzf fields {1}..{7} and recombines them into a filename, passed to cat
-# fzf --bind recombines the filename and just outputs it
diff --git a/lua/denote-fzf-lua/scripts/rg.sh b/lua/denote-fzf-lua/scripts/rg.sh
deleted file mode 100755
index 2cfc400..0000000
--- a/lua/denote-fzf-lua/scripts/rg.sh
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/usr/bin/sh
-rg --color=always --line-number --no-heading --smart-case "${@:1:$#-1}" ${*: -1}
-
-
diff --git a/lua/denote-fzf-lua/scripts/search_files.sh b/lua/denote-fzf-lua/scripts/search_files.sh
index 67367af..3b0379c 100755
--- a/lua/denote-fzf-lua/scripts/search_files.sh
+++ b/lua/denote-fzf-lua/scripts/search_files.sh
@@ -1,36 +1,16 @@
#/usr/bin/sh
+(echo "Path,Date,Time,Sig,Title,Tags,Ext";
fd '^\d{8}T\d{6}(==[a-z0-9=]+)?(\-\-[a-z0-9-]+)?(__[a-z0-9_]+)?\.\w+$' $1 |\
sd '(?P<path>\/.*\/)?(?P<datetime>\d{8}T\d{6})(==(?P<sig>[a-z0-9=]+))?(--(?P<title>[a-z0-9-]+))(__(?P<tags>[a-z0-9_]+))?(?P<ext>\.\w+)' '$path,$datetime,$sig,$title,$tags,$ext' |\
tr '\=\-_' ' ' |\
-sd '(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})' '$1-$2-$3,$4:$5:$6' |\
+sd '(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})' '$1-$2-$3,$4:$5:$6') |\
sd ',,' ',.,' |\
-column -t -s "," -N Path,Date,Time,Sig,Title,Tags,Ext
-# fzf --delimiter='\s{2,}' --header-lines=1 -i $2 --preview='\
-# sig={4}; sig=$(echo $sig | sd "\W" "" | sd " " "="); if [ ! -z ${sig} ] ; then sig="==${sig}"; fi; \
-# title={5}; title=$(echo $title | sd " " "-"); if [ ! -z ${title} ] ; then title="--${title}"; fi; \
-# tags={6}; tags=$(echo $tags | sd "\." "" | sd " " "_"); if [ ! -z ${tags} ] ; then tags="__${tags}"; fi; \
-# ext={7}; \
-# echo {1}\|{2}\|{3}\|{4}\|{5}\|{6}\|{7} | \
-# sd "(?P<path>\/.*\/)?\|(?P<d1>\d\d\d\d)-(?P<d2>\d\d)-(?P<d3>\d\d)\|(?P<t1>\d\d):(?P<t2>\d\d):(?P<t3>\d\d)\|.*" "\$path\$d1\$d2\${d3}T\$t1\$t2\$t3$sig$title$tags$ext" |\
-# xargs cat' \
-# --bind='enter:become(\
-# sig={4}; sig=$(echo $sig | sd "\W" "" | sd " " "="); if [ ! -z ${sig} ] ; then sig="==${sig}"; fi; \
-# title={5}; title=$(echo $title | sd " " "-"); if [ ! -z ${title} ] ; then title="--${title}"; fi; \
-# tags={6}; tags=$(echo $tags | sd "\." "" | sd " " "_"); if [ ! -z ${tags} ] ; then tags="__${tags}"; fi; \
-# ext={7}; \
-# echo {1}\|{2}\|{3}\|{4}\|{5}\|{6}\|{7} | \
-# sd "(?P<prefix>\/.*\/)?\|(?P<d1>\d\d\d\d)-(?P<d2>\d\d)-(?P<d3>\d\d)\|(?P<t1>\d\d):(?P<t2>\d\d):(?P<t3>\d\d)\|.*" "\$prefix\$d1\$d2\${d3}T\$t1\$t2\$t3$sig$title$tags$ext")+abort'
-#
-# I am sorry for this. If I learn an equally fast, more readable way to implement this in Lua I will.
-# 1. echo table headers (for xsv)
-# 2. fd all Denote files in $1 directory
+qsv table
+
+# 1. echo the table headers
+# 2. fd only Denote files in $1 directory (and subdirectories)
# 3. sd chops the Denote filename fields into a CSV
# 4. tr replaces special characters in sig, title, and tags with spaces
# 5. sd formats the date and time as YYYY-MM-DD HH:MM:SS
-# 6. sd replaces blank fields with . (ensures fzf can count fields correctly)
-# 7. xsv to print the fields in tabular format
-# 8. fzf the resulting table including $2 args list
-# 9 fzf --delimiter breaks the table up into fields on 2+ space
-# fzf --nth and --with-nth (args passed in $2) determine which fields are shown
-# fzf --preview takes all the fzf fields {1}..{7} and recombines them into a filename, passed to cat
-# fzf --bind recombines the filename and just outputs it
+# 6. sd replaces blank fields with . (fzf can't handle blank fields)
+# 7. qsv to print the fields in tabular format