├── README.md ├── after └── plugin │ └── cmp-pandoc-references.lua └── lua └── cmp-pandoc-references ├── init.lua └── references.lua /README.md: -------------------------------------------------------------------------------- 1 | # cmp-pandoc-references 2 | 3 | A source for [nvim-cmp](https://github.com/hrsh7th/nvim-cmp), providing completion for bibliography, reference and cross-ref items. 4 | 5 | ## Demo 6 | ![cmp-pandoc-references](https://user-images.githubusercontent.com/59124867/134782887-33872ae0-a23e-4f5b-99cd-74c3b0e6f497.gif) 7 | 8 | Note I have overridden the `ItemKinds`, they are set to `cmp.lsp.CompletionItemKind.Reference` by default. 9 | 10 | ## Installation & Usage 11 | 12 | Assuming Packer: 13 | 14 | ``` lua 15 | use({ 16 | "hrsh7th/nvim-cmp", 17 | requires = { 18 | { "jc-doyle/cmp-pandoc-references" } 19 | } 20 | }) 21 | ``` 22 | 23 | Add the source: 24 | 25 | ``` lua 26 | require('cmp').setup { 27 | sources = { 28 | { name = 'pandoc_references' } 29 | } 30 | } 31 | ``` 32 | 33 | ## Explanation & Limitations 34 | 35 | This source parses and validates the `bibliography: [your/bib/location.bib]` YAML metadata field, to determine the destination of the file (see [Pandoc](https://pandoc.org/MANUAL.html#specifying-bibliographic-data)). If it is not included (or you specify it through a command-line argument), no bibliography completion items will be found. 36 | 37 | (I use the metadata block to reference bibliographies, if you'd like automatic scanning of directories/sub-directories, feel free to submit a PR) 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /after/plugin/cmp-pandoc-references.lua: -------------------------------------------------------------------------------- 1 | require('cmp').register_source('pandoc_references', require('cmp-pandoc-references').new()) 2 | -------------------------------------------------------------------------------- /lua/cmp-pandoc-references/init.lua: -------------------------------------------------------------------------------- 1 | local source = {} 2 | local refs = require 'cmp-pandoc-references.references' 3 | 4 | source.new = function() 5 | return setmetatable({}, {__index = source}) 6 | end 7 | 8 | -- Add another filetype if needed 9 | source.is_available = function() 10 | return vim.o.filetype == 'pandoc' or vim.o.filetype == 'markdown' or vim.o.filetype == 'rmd' 11 | end 12 | 13 | source.get_keyword_pattern = function() 14 | return '[@][^[:blank:]]*' 15 | end 16 | 17 | source.complete = function(self, request, callback) 18 | local lines = vim.api.nvim_buf_get_lines(self.bufnr or 0, 0, -1, false) 19 | local entries = refs.get_entries(lines) 20 | 21 | if entries then 22 | self.items = entries 23 | callback(self.items) 24 | end 25 | end 26 | 27 | return source 28 | -------------------------------------------------------------------------------- /lua/cmp-pandoc-references/references.lua: -------------------------------------------------------------------------------- 1 | local cmp = require 'cmp' 2 | 3 | local entries = {} 4 | local M = {} 5 | 6 | -- (Crudely) Locates the bibliography 7 | local function locate_bib(lines) 8 | for _, line in ipairs(lines) do 9 | location = string.match(line, 'bibliography: (%g+)') 10 | if location then 11 | return location 12 | end 13 | end 14 | end 15 | 16 | -- Remove newline & excessive whitespace 17 | local function clean(text) 18 | if text then 19 | text = text:gsub('\n', ' ') 20 | return text:gsub('%s%s+', ' ') 21 | else 22 | return text 23 | end 24 | end 25 | 26 | -- Parses the .bib file, formatting the completion item 27 | -- Adapted from http://rgieseke.github.io/ta-bibtex/ 28 | local function parse_bib(filename) 29 | local file = io.open(filename, 'rb') 30 | local bibentries = file:read('*all') 31 | file:close() 32 | for bibentry in bibentries:gmatch('@.-\n}\n') do 33 | local entry = {} 34 | 35 | local title = clean(bibentry:match('title%s*=%s*["{]*(.-)["}],?')) or '' 36 | local author = clean(bibentry:match('author%s*=%s*["{]*(.-)["}],?')) or '' 37 | local year = bibentry:match('year%s*=%s*["{]?(%d+)["}]?,?') or '' 38 | 39 | local doc = {'**' .. title .. '**', '', '*' .. author .. '*', year} 40 | 41 | entry.documentation = { 42 | kind = cmp.lsp.MarkupKind.Markdown, 43 | value = table.concat(doc, '\n') 44 | } 45 | entry.label = '@' .. bibentry:match('@%w+{(.-),') 46 | entry.kind = cmp.lsp.CompletionItemKind.Reference 47 | 48 | table.insert(entries, entry) 49 | end 50 | end 51 | 52 | -- Parses the references in the current file, formatting for completion 53 | local function parse_ref(lines) 54 | local words = table.concat(lines) 55 | for ref in words:gmatch('{#(%a+:[%w_-]+)') do 56 | local entry = {} 57 | entry.label = '@' .. ref 58 | entry.kind = cmp.lsp.CompletionItemKind.Reference 59 | table.insert(entries, entry) 60 | end 61 | end 62 | 63 | -- Returns the entries as a table, clearing entries beforehand 64 | function M.get_entries(lines) 65 | local location = locate_bib(lines) 66 | entries = {} 67 | 68 | if location and vim.fn.filereadable(location) == 1 then 69 | parse_bib(location) 70 | end 71 | parse_ref(lines) 72 | 73 | return entries 74 | end 75 | 76 | return M 77 | --------------------------------------------------------------------------------