diff --git a/README.md b/README.md index e85f13f..8b7653b 100644 --- a/README.md +++ b/README.md @@ -314,8 +314,15 @@ enable_icons = true, -- What keys to search for matches. search_keys = { "author", "editor", "year", "title", "tags" }, - -- The format for the previewer. Each line in the config represents a line in - -- the preview. For each line, we define: + -- Papis.nvim uses a common configuration format for defining the formatting + -- of strings. Sometimes -- as for instance in the below `preview_format` option -- + -- we define a set of lines. At other times -- as for instance in the `results_format` + -- option -- we define a single line. Sets of lines are composed of single lines. + -- A line can be composed of either a single element or multiple elements. The below + -- `preview_format` shows an example where each line is defined by a table with just + -- one element. The `results_format` and `popup_format` are examples where (some) of + -- the lines contain multiple elements (and are represented by a table of tables). + -- Each element contains: -- 1. The key whose value is shown -- 2. How it is formatted (here, each is just given as is) -- 3. The highlight group @@ -324,10 +331,10 @@ enable_icons = true, -- formatting of the key and its highlight group. The key is shown *before* -- the value in the preview (even though it is defined after it in this -- configuration (e.g. `title = Critique of Pure Reason`)). - -- `empty_line` is used to insert an empty line + -- An element may also just contain `empty_line`. This is used to insert an empty line -- Strings that define the formatting (such as in 2. and 4. above) can optionally - -- be a table, defining, first, an icon, and second, a non-icon version. What is - -- used is defined by the `enable_icons` option. + -- be a table, defining, first, an icon, and second, a non-icon version. The + -- `enable_icons` option determines what is used. preview_format = { { "author", "%s", "PapisPreviewAuthor" }, { "year", "%s", "PapisPreviewYear" }, @@ -357,10 +364,18 @@ enable_icons = true, ["at-cursor"] = { -- The format of the popup shown on `:Papis at-cursor show-popup` (equivalent to points 1-3 - -- of `preview_format`) + -- of `preview_format`). Note that one of the lines is composed of multiple elements. Note + -- also the `{ "vspace", "vspace" },` line which is exclusive to `popup_format` and which tells + -- papis.nvim to fill the space between the previous and next element with whitespace (and + -- in effect make whatever comes after right-aligned). It can only occur once in a line. popup_format = { - { "author", "%s", "PapisPopupAuthor" }, - { "year", "%s", "PapisPopupYear" }, + { + { "author", "%s", "PapisPopupAuthor" }, + { "vspace", "vspace" }, + { "files", { " ", "F " }, "PapisResultsFiles" }, + { "notes", { " ", "N " }, "PapisResultsNotes" }, + }, + { "year", "%s", "PapisPopupYear" }, { "title", "%s", "PapisPopupTitle" }, }, }, diff --git a/lua/papis/at-cursor/init.lua b/lua/papis/at-cursor/init.lua index 76663b1..0e8c3f5 100644 --- a/lua/papis/at-cursor/init.lua +++ b/lua/papis/at-cursor/init.lua @@ -21,7 +21,6 @@ local db = require("papis.sqlite-wrapper") if not db then return nil end -local hover_required_db_keys = utils:get_required_db_keys({ popup_format }) ---Tries to identify the ref under cursor ---@return string|nil #Nil if nothing is found, otherwise is the identified ref @@ -72,9 +71,8 @@ end ---Creates a popup with information regarding the entry specified by `ref` ---@param papis_id string #The `papis_id` of the entry local function create_hover_popup(papis_id) - local entry = db.data:get({ papis_id = papis_id }, hover_required_db_keys)[1] - local clean_popup_format = utils.do_clean_format_tbl(popup_format, entry) - local popup_lines, width = utils.make_nui_lines(clean_popup_format, entry) + local entry = db.data:get({ papis_id = papis_id })[1] + local popup_lines, width = utils:make_nui_lines(popup_format, entry) local popup = NuiPopup({ position = 1, diff --git a/lua/papis/config.lua b/lua/papis/config.lua index e953d38..b4b514c 100644 --- a/lua/papis/config.lua +++ b/lua/papis/config.lua @@ -37,6 +37,7 @@ local default_config = { editor = "text", year = "text", title = "text", + shorttitle = "text", type = "text", abstract = "text", time_added = "text", @@ -105,9 +106,14 @@ local default_config = { }, ["at-cursor"] = { popup_format = { - { "author", "%s", "PapisPopupAuthor" }, - { "year", "%s", "PapisPopupYear" }, - { "title", "%s", "PapisPopupTitle" }, + { + { "author", "%s", "PapisPopupAuthor" }, + { "vspace", "vspace" }, + { "files", { " ", "F " }, "PapisResultsFiles" }, + { "notes", { " ", "N " }, "PapisResultsNotes" }, + }, + { "year", "%s", "PapisPopupYear" }, + { "title", "%s", "PapisPopupTitle" }, }, }, ["search"] = { diff --git a/lua/papis/search/data.lua b/lua/papis/search/data.lua index fcf5a9c..9e92b36 100644 --- a/lua/papis/search/data.lua +++ b/lua/papis/search/data.lua @@ -101,7 +101,7 @@ local function init_tbl() local entry = db["data"]:__get({ where = { id = id } })[1] - local display_strings = utils:format_display_strings(entry, results_format) + local display_strings = utils:format_display_strings(entry, results_format, false, true) local search_string = format_search_string(entry) local items = {} diff --git a/lua/papis/utils.lua b/lua/papis/utils.lua index 8b94351..cf794d9 100644 --- a/lua/papis/utils.lua +++ b/lua/papis/utils.lua @@ -225,107 +225,72 @@ function M:do_open_text_file(papis_id, type) vim.cmd(cmd) end ----Takes the format table and removes k = v pairs not existing in the entry + some other conditions ----@param format_table table #As defined in config.lua (e.g. "preview_format") ----@param entry table #An entry ----@param remove_editor_if_author boolean? #If true we don't add the editor if the entry has an author ----@return table #Same format as `format_table` but with k = v pairs removed -function M.do_clean_format_tbl(format_table, entry, remove_editor_if_author) - local enable_icons = require("papis.config")["enable_icons"] - local clean_format_table = {} - for _, v in ipairs(format_table) do - local f = vim.deepcopy(v) -- TODO: check if deepcopy necessary - -- add entry value if either there's an entry value corresponding to the value in the - -- format table or the value in the format table is "empty_line" - if entry[f[1]] or f[1] == "empty_line" then - clean_format_table[#clean_format_table + 1] = f - -- don't add editor if there is author and use_author_if_editor is true - elseif remove_editor_if_author and f[1] == "author" and entry["editor"] then - clean_format_table[#clean_format_table + 1] = f - -- add empty space if space is forced but the element doesn't exist for entry - elseif f[4] == "force_space" then - f[2] = " " -- TODO: this only works for icons, hardcoded because luajit doesn't support utf8.len - clean_format_table[#clean_format_table + 1] = f - end - -- use either icons or normal characters depending on settings - if type(f[2]) == "table" then - if enable_icons then - f[2] = f[2][1] - else - f[2] = f[2][2] - end - end - if type(f[5]) == "table" then - if enable_icons then - f[5] = f[5][1] - else - f[5] = f[5][2] - end - end - end - return clean_format_table -end - ---Makes nui lines ready to be displayed ----@param clean_format_tbl table #A cleaned format table as output by self.do_clean_format_tbl +---@param lines_format_tbl table #A format table defining multiple lines ---@param entry table #An entry ---@return table #A list of nui lines ---@return integer #The maximum character length of the nui lines -function M.make_nui_lines(clean_format_tbl, entry) - local nui_lines = {} +function M:make_nui_lines(lines_format_tbl, entry) + local lines = {} + local line_widths = {} local max_width = 0 - for _, v in ipairs(clean_format_tbl) do - local line = NuiLine() - local width1 = 0 - local width2 = 0 - if v[1] == "empty_line" then - line:append(" ") + -- local max_width_line_nr + local vspace = {} + for _, line_format_tbl in ipairs(lines_format_tbl) do + local line = {} + local width = 0 + if line_format_tbl[1] == "empty_line" then + -- here we add a line without hl group + line[#line + 1] = { " " } else - if v[4] == "show_key" then - local str = v[1] - str = string.format(v[5], str) - str = string.gsub(str, "\n", "") - width1 = vim.fn.strdisplaywidth(str, 1) - line:append(str, v[6]) - end - if type(entry[v[1]]) ~= "table" then - local str = tostring(entry[v[1]]) - str = string.format(v[2], str) - str = string.gsub(str, "\n", "") - width2 = vim.fn.strdisplaywidth(str, 1) - line:append(str, v[3]) - else - local str = table.concat(entry[v[1]], ", ") - str = string.format(v[2], str) - str = string.gsub(str, "\n", "") - width2 = vim.fn.strdisplaywidth(str, 1) - line:append(str, v[3]) + -- we format the strings for the line and add them to the line + local formatted_strings = self:format_display_strings(entry, line_format_tbl) + for k, v in ipairs(formatted_strings) do + line[#line + 1] = { v[1], v[2] } + if v[1] == "vspace" then + -- in case of vspace elements, we gotta keep track where they occur + vspace[#vspace + 1] = { linenr = #lines + 1, elem = k } + else + -- we get the width of the line by adding all elements' width + width = width + vim.fn.strdisplaywidth(v[1], 1) + end end end - max_width = math.max(max_width, (width1 + width2)) - nui_lines[#nui_lines + 1] = line + -- add the width of the line just processed to the table of line_widths + line_widths[#lines + 1] = width + + -- add the line just processed to the table of lines + lines[#lines + 1] = line end - return nui_lines, max_width -end + max_width = math.max(unpack(line_widths)) ----Get the list of keys required by format table ----@param tbls table #A format table(e.g. "preview_format" in config.lua) ----@return table #A list of keys -function M:get_required_db_keys(tbls) - local required_db_keys = { id = true } - for _, tbl in ipairs(tbls) do - for _, v in ipairs(tbl) do - if v[1] == nil then - required_db_keys[v] = true - else - required_db_keys[v[1]] = true - end + local vspace_len = 0 + -- sort out vertical space padding for each line that has `vspace` + for _, v in pairs(vspace) do + if line_widths[v.linenr] >= max_width then + -- if the line with the vspace is the longest line, only add 1 space + vspace_len = 1 + else + -- if it isn't the longest line, calculate required vspace + vspace_len = max_width - (line_widths[v.linenr]) end + -- replace "vspace" by the required number of " " + lines[v.linenr][v.elem] = { string.rep(" ", vspace_len) } + -- and recalculate max_width + max_width = math.max(max_width, (line_widths[v.linenr] + vspace_len)) end - required_db_keys["empty_line"] = nil - required_db_keys = vim.tbl_keys(required_db_keys) - return required_db_keys + + -- turn our lines into NuiLines + local nui_lines = {} + for _, line in ipairs(lines) do + local nui_line = NuiLine() + for _, v in ipairs(line) do + nui_line:append(v[1], v[2]) + end + nui_lines[#nui_lines + 1] = nui_line + end + return nui_lines, max_width end ---Determine whether there's a process with a given pid @@ -356,21 +321,48 @@ end ---Creates a table of formatted strings to be displayed in a line (e.g. Telescope results pane) ---@param entry table #A papis entry +---@param line_format_tbl table #A table containing format strings defining the line ---@param use_shortitle? boolean #If true, use short titles ----@return table #A list of strings -function M:format_display_strings(entry, format_table, use_shortitle) - local clean_results_format = self.do_clean_format_tbl(format_table, entry, true) - - local str_elements = {} - for _, v in ipairs(clean_results_format) do - assert(v ~= "empty_line", "Empty lines aren't allowed for the results_format") - if v[1] == "author" then +---@param remove_editor_if_author? boolean #If true, remove editor if author exists +---@return table #A list of lists like { { "formatted string", "HighlightGroup", {opts} }, ... } +function M:format_display_strings(entry, line_format_tbl, use_shortitle, remove_editor_if_author) + local enable_icons = require("papis.config")["enable_icons"] + + -- if the line has just one item, embed within a tbl so we can process like the others + if type(line_format_tbl[1]) == "string" then + log.debug("line has just one item, embed within table") + line_format_tbl = { line_format_tbl } + end + + ---Table containing tables {format string, string, highlight group} + ---@type table