a BibTeX source for fzf
fzf-bibtex
A BibTeX source for fzf.
- Blazingly fast, even with extremely large BibTeX files
- Caches results intelligently (hence the speed)
- Uses a well-understood framework to parse BibTeX (bibtool)
- vim and neovim integration (with fzf.vim or fzf-lua)
- Supports multiple BibTeX files
- Supports cross references (thanks to \@cao)
- Supports multiple citation formats
- BibLaTeX support (thanks to \@ashwinvis)
Example use
To select items using fzf from a .bib file (as in image above):
bibtex-ls references.bib | fzf --multi --ansi
To cite items (using the pandoc '@' format) from a .bib file:
bibtex-ls references.bib | fzf --multi --ansi | bibtex-cite
To pretty print items (in markdown) from a .bib file:
bibtex-ls references.bib | fzf --multi --ansi | bibtex-markdown references.bib
Installation
Requirements
On the Mac, these can be installed by homebrew:brew install fzf
brew install bib-tool
brew install go
If you want vim/neovim integration, either:
or, if you prefer lua (for neovim only): NB. You only need one or other of these (see mappings below). You can install both if you really want.Installation
go install github.com/msprev/fzf-bibtex/cmd/bibtex-ls@latest
go install github.com/msprev/fzf-bibtex/cmd/bibtex-markdown@latest
go install github.com/msprev/fzf-bibtex/cmd/bibtex-cite@latest
Why these dependencies?
Parsing BibTeX is a non-trivial task. It is best to do it in a well-understood and reliable way. fzf-bibtex uses an extremely stable, reliable, and widely used parser, bibtool. The goal of fzf-bibtex is to have no noticable delay when searching, even for extremely large BibTeX files. Writing it with Go allows the desired responsiveness to be achieved.
Command line use
bibtex-ls
bibtex-ls [-cache=...] [file1.bib file2.bib ...]
Lists to stdout the content of .bib files, one record per line.
If the following environment variables are set, then these command line arguments can be omitted.
FZFBIBTEXCACHEDIR: path to a cache directoryFZFBIBTEXSOURCES: path to bibtex file; multiple items separated by a ':'
(NB. If you are tinkering with fzf-bibtex's codebase, beware of outdated caches. Cache is only updated if the underlying BibTeX file has been changed. If you change the fzf-bibtex codebase, make sure to flush the cache by touching the BibTeX files, or deleting the cache, before you run new code on them).
bibtex-cite
bibtex-cite [-mode=pandoc|latex] [-prefix=...] [-postfix=...] [-separator=...]
Pretty print citations for selected entries passed over stdin.
Citation format may be customised with -prefix, -postfix, and -separator options.
Default values (suitable for pandoc '@' format):
-prefix="@"-postfix=""-separator="; @"
-mode option provides presets for pandoc and LaTeX style
citations. -mode options:
-mode=pandoc=-prefix="@" -postfix="" -separator="; @"-mode=latex=-prefix="\cite{" -postfix="}" -separator=", "
bibtex-markdown
bibtex-markdown [-cache=...] [file1.bib file2.bib ...]
Pretty print items (in markdown) for selected .bib entries passed over stdin.
Cache directory may be set using the same environment variable as bibtex-ls.
fzf.vim integration
Assuming the executables installed above are available to Vim in your file path, add the following code to your vimrc file (or, for neovim, your init.vim):
fzf-vim integration (normal mode)
let $FZFBIBTEXCACHEDIR = 'PATH-TO-CACHE-DIR'
let $FZFBIBTEXSOURCES = 'PATH-TO-BIBTEX-FILE'
function! s:bibtexcitesink(lines) let r=system("bibtex-cite ", a:lines) execute ':normal! a' . r endfunction
function! s:bibtexmarkdownsink(lines) let r=system("bibtex-markdown ", a:lines) execute ':normal! a' . r endfunction
nnoremap <silent> <leader>c :call fzf#run({ \ 'source': 'bibtex-ls', \ 'sink*': function('<sid>bibtexcitesink'), \ 'up': '40%', \ 'options': '--ansi --layout=reverse-list --multi --prompt "Cite> "'})<CR>
nnoremap <silent> <leader>m :call fzf#run({ \ 'source': 'bibtex-ls', \ 'sink*': function('<sid>bibtexmarkdownsink'), \ 'up': '40%', \ 'options': '--ansi --layout=reverse-list --multi --prompt "Markdown> "'})<CR>
<leader>cwill bring up fzf to cite selected items<leader>mwill bring up fzf to markdown pretty print cite selected items
fzf-vim integration (insert mode)
function! s:bibtexcitesink_insert(lines)
let r=system("bibtex-cite ", a:lines)
execute ':normal! a' . r
call feedkeys('a', 'n')
endfunction
inoremap <silent> @@ <c-g>u<c-o>:call fzf#run({ \ 'source': 'bibtex-ls', \ 'sink*': function('<sid>bibtexcitesink_insert'), \ 'up': '40%', \ 'options': '--ansi --layout=reverse-list --multi --prompt "Cite> "'})<CR>
@@will bring up fzf to cite selected items
@@) that detects .bib files in parent, current or child directories (thanks to \@ashwinvis):
fzf-vim integration (alternative insert mapping -- automatically reads from nearby .bib files)
function! Bibtex_ls()
let bibfiles = (
\ globpath('.', '*.bib', v:true, v:true) +
\ globpath('..', '*.bib', v:true, v:true) +
\ globpath('/', '.bib', v:true, v:true)
\ )
let bibfiles = join(bibfiles, ' ')
let source_cmd = 'bibtex-ls '.bibfiles
return source_cmd
endfunction
function! s:bibtexcitesink_insert(lines) let r=system("bibtex-cite ", a:lines) execute ':normal! a' . r call feedkeys('a', 'n') endfunction
inoremap <silent> @@ <c-g>u<c-o>:call fzf#run({ \ 'source': Bibtex_ls(), \ 'sink*': function('<sid>bibtexcitesink_insert'), \ 'up': '40%', \ 'options': '--ansi --layout=reverse-list --multi --prompt "Cite> "'})<CR>
fzf-lua integration
If you use fzf-lua in neovim, you can add the following code inside to your init.lua or similar config file.
fzf-lua integration
-- default list of bibfiles
-- can be overriden by changing vim.b.bibfiles inside buffer
local default_bibfiles = {
-- put your default bibfiles here
}
-- default cache directory -- uses neovim's stdpath to set up a cache - no need to fiddle with this local cachedir = vim.fn.stdpath("state") .. "/fzf-bibtex/"
-- actions local pandoc = function(selected, opts) local result = vim.fn.system('bibtex-cite', selected) vim.api.nvim_put({ result }, "c", false, true) if opts.fzf_bibtex.mode == "i" then vim.api.nvim_feedkeys("i", "n", true) end end
local citet = function(selected, opts) local result = vim.fn.system('bibtex-cite -prefix="\\citet{" -postfix="}" -separator=","', selected) vim.api.nvim_put({ result }, "c", false, true) if opts.fzf_bibtex.mode == "i" then vim.api.nvim_feedkeys("i", "n", true) end end
local citep = function(selected, opts) local result = vim.fn.system('bibtex-cite -prefix="\\citep{" -postfix="}" -separator=","', selected) vim.api.nvim_put({ result }, "c", false, true) if opts.fzf_bibtex.mode == "i" then vim.api.nvim_feedkeys("i", "n", true) end end
local markdown_print = function(selected, opts) local result = vim.fn.system("bibtex-markdown -cache=" .. cachedir .. " " .. table.concat(vim.b.bibfiles, " "), selected) local result_lines = {} for line in result:gmatch('[^\n]+') do table.insert(result_lines, line) end vim.api.nvimput(resultlines, "l", true, true) if opts.fzf_bibtex.mode == "i" then vim.api.nvim_feedkeys("i", "n", true) end end
local fzfbibtexmenu = function(mode) return function() -- check cache directory hasn't mysteriously disappeared if vim.fn.isdirectory(cachedir) == 0 then vim.fn.mkdir(cachedir, "p") end
require 'fzf-lua'.config.setactionhelpstr(pandoc, "@-pandoc") require 'fzf-lua'.config.setactionhelpstr(citet, "\\citet{}") require 'fzf-lua'.config.setactionhelpstr(citep, "\\citep{}") require 'fzf-lua'.config.setactionhelpstr(markdown_print, "markdown-pretty-print")
-- header line: the bibtex filenames local filenames = {} for i, fullpath in ipairs(vim.b.bibfiles) do filenames[i] = vim.fn.fnamemodify(fullpath, ":t") end local header = table.concat(filenames, "\\ ")
-- set default action local default_action = nil if vim.bo.ft == "markdown" then default_action = pandoc elseif vim.bo.ft == "tex" then default_action = citet end
-- run fzf return require 'fzf-lua'.fzf_exec( "bibtex-ls " .. "-cache=" .. cachedir .. " " .. table.concat(vim.b.bibfiles, " "), { actions = { ['default'] = default_action, ['alt-a'] = pandoc, ['alt-t'] = citet, ['alt-p'] = citep, ['alt-m'] = markdown_print, }, fzf_bibtex = { ['mode'] = mode }, fzf_opts = { ["--multi"] = true, ['--prompt'] = 'BibTeX> ', ['--header'] = header } }) end end
-- Only enable mapping in tex or markdown vim.api.nvimcreateautocmd("Filetype", { desc = "Set up keymaps for fzf-bibtex", group = vim.api.nvimcreateaugroup("fzf-bibtex", { clear = true }), pattern = { "markdown", "tex" }, callback = function() vim.b.bibfiles = default_bibfiles vim.keymap.set("n", "<leader>c", fzfbibtexmenu("n"), { buffer = true, desc = "FZF: BibTeX [C]itations" }) vim.keymap.set("i", "@@", fzfbibtexmenu("i"), { buffer = true, desc = "FZF: BibTeX [C]itations" }) end })
Mappings will only be active for tex or markdown filetypes:
<leader>cwill bring up fzf to cite selected items
<cr>: insert with default citation style
- <alt-a>: insert citation with pandoc @ style
- <alt-t>: insert citation with LaTeX \\citet{} style
- <alt-p>: insert citation with LaTeX \\citep{} style
- <alt-m>: pretty print selected items in markdown
@@in insert mode brings up the same fzf menu.
Errors?
fzf-bibtex uses bibtool to parse BibTeX files. If there is an error, it is likely that your BibTeX file is not being parsed correctly. You can locate the cause, and correct it, by running bibtool directly on your BibTeX file from the command line. Look at any errors reported from:
bibtool references.bib -o parsed.bib
The BibTeX fields that fzf-bibtex asks bibtool to extract from your file can be seen by running bibtool with the rsc file specified in this string.
Release notes
- 1.1 (17 February 2020)
- 1.0 (4 November 2018)
Similar
- unite-bibtex -- no longer maintained; this replaces it.