Skip to content

Commit

Permalink
feat!: rework commands and keymaps
Browse files Browse the repository at this point in the history
- Papis has now various subcommands (e.g. "Papis reload")
- Keymaps run those commands
- Commands and keymaps are set by modules (rather than centrally)
  • Loading branch information
jghauser committed Jun 18, 2024
1 parent db1ada5 commit 93f56ca
Show file tree
Hide file tree
Showing 7 changed files with 159 additions and 309 deletions.
42 changes: 19 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,50 +20,50 @@ While papis.nvim is likely buggy, it is equally likely unable to mess with your

A number of features (bundled into `modules`) are shipped with papis.nvim. These can be (de)activated as desired.

### 'search' module
### *Search* module

![search (trimmed)](https://user-images.githubusercontent.com/10319377/193468846-327988b0-de69-4484-887f-e294f1ed8ed8.gif)

Papis.nvim integrates with telescope to easily and quickly search one's bibliography. Open the picker and enter the title (or author, year, etc.) of the article you're looking for. Once you've found it, you can insert a citation, open attached files and notes, and edit the `info.yaml` file. When attempting to open a note where none exists, papis.nvim will ask to create a new one.

Commands:
- `:Telescope papis`: Opens the papis.nvim telescope picker
- `:Papis search`: Opens the papis.nvim telescope picker

With the picker open, the following (currently hardcoded) keymaps become available:
- `of` (normal) / `<c-o>f` (insert): Opens files attached to the entry
- `on` (normal) / `<c-o>n` (insert): Opens notes attached to the entry (asks for the creation of a new one if none exists)
- `e` (normal) / `c-e` (insert): Opens the `info.yaml` file
- `f` (normal) / `c-f` (insert): Insert a formatted reference

### 'completion' module
### *Completion* module

![completion (trimmed)](https://user-images.githubusercontent.com/10319377/193469045-4941bb6d-3582-4ad0-9e29-249ddc8aae46.gif)

When editing `tags` in `info.yaml` files, papis.nvim will suggest tags found in the database. This module is implemented as a [nvim-cmp](https://github.com/hrsh7th/nvim-cmp) source.

### 'cursor-actions' module
### *At-cursor* module

![cursor-actions (trimmed)](https://user-images.githubusercontent.com/10319377/193468973-3755f5b9-e2bb-4de9-900c-bf130ea09bad.gif)
![at-cursor (trimmed)](https://user-images.githubusercontent.com/10319377/193468973-3755f5b9-e2bb-4de9-900c-bf130ea09bad.gif)

When the cursor is positioned over a citation key (e.g. `Kant1781Critique`), papis.nvim allows you to interact with the bibliography item referenced by it.

Commands:
- `:PapisShowPopup`: Opens a floating window with information about the entry
- `:PapisOpenFile`: Opens files attached to the entry
- `:PapisOpenNote`: Opens notes attached to the entry (asks for the creation of a new one if none exists)
- `:PapisEditEntry`: Opens the `info.yaml` file
- `:Papis at-cursor show-popup`: Opens a floating window with information about the entry
- `:Papis at-cursor open-file`: Opens files attached to the entry
- `:Papis at-cursor open-note`: Opens notes attached to the entry (asks for the creation of a new one if none exists)
- `:Papis at-cursor edit`: Opens the `info.yaml` file

### 'formatter' module
### *Formatter* module

![formatter_trimmed](https://user-images.githubusercontent.com/10319377/193469179-35e1a3b5-bad6-4289-a9ae-586dc9b3af8a.gif)

When creating new notes (via `:Telescope papis` or `:PapisOpenNote`), papis.nvim can be set up to format the new note with a custom function. You can, for example, give the note a title that corresponds to the entry's title or provide it with a skeleton structure. Below, in the setup section, there's an example suitable for the `markdown` format.
When creating new notes (via `:Papis search` or `:Papis at-cursor open-note`), papis.nvim can be set up to format the new note with a custom function. You can, for example, give the note a title that corresponds to the entry's title or provide it with a skeleton structure. Below, in the setup section, there's an example suitable for the `markdown` format.

## The database

All of papis.nvim's features are made possible by a sqlite database that is created when the plugin is first started. This might take a while, so be patient. From then on, the database is automatically (and very quickly) updated whenever `info.yaml` files are added, changed, or deleted. The database is synchronised when papis.nvim is started and is then kept up-to-date continuously while at least one neovim instance with a running papis.nvim session exists.

Note that fiddling with the plugin's options can leave the database in a messy state. If strange errors appear, use `:PapisReInitData` to re-initialise the database.
Note that fiddling with the plugin's options can leave the database in a messy state. If strange errors appear, use `:Papis reload data` to re-initialise the database.

## Installation

Expand Down Expand Up @@ -160,7 +160,7 @@ The `flake.nix` provides an overlay that can be used to install `papis.nvim`. Wi
home-manager.url = "github:nix-community/home-manager";
papis-nvim.url = "github:jghauser/papis.nvim";
};
outputs = { self, nixpkgs, home-manager, neorg, ... }: {
outputs = { self, nixpkgs, home-manager, ... }: {
nixosConfigurations.machine = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
Expand Down Expand Up @@ -213,7 +213,7 @@ require("papis").setup({
enable_modules = {
["search"] = true, -- Enables/disables the search module
["completion"] = true, -- Enables/disables the completion module
["cursor-actions"] = true, -- Enables/disables the cursor-actions module
["at-cursor"] = true, -- Enables/disables the at-cursor module
["formatter"] = true, -- Enables/disables the formatter module
["colors"] = true, -- Enables/disables default highlight groups (you
-- probably want this)
Expand All @@ -225,7 +225,7 @@ enable_modules = {

-- Defines citation formats for various filetypes. When the value is a table, then
-- the first entry is used to insert citations, whereas the second will be used to
-- find references (e.g. by the `cursor-action` module). `%s` stands for the reference.
-- find references (e.g. by the `at-cursor` module). `%s` stands for the reference.
-- Note that the first entry is a string (where e.g. `\` needs to be excaped as `\\`)
-- and the second a lua pattern (where magic characters need to be escaped with
-- `%`; https://www.lua.org/pil/20.2.html).
Expand All @@ -244,10 +244,6 @@ cite_formats_fallback = "plain",
-- Enable default keymaps.
enable_keymaps = false,

-- Enable commands (disabling this still allows you to call the relevant lua
-- functions directly)
enable_commands = true,

-- Whether to enable the file system event watcher. When disabled, the database
-- is only updated on startup.
enable_fs_watcher = true,
Expand Down Expand Up @@ -357,10 +353,10 @@ enable_icons = true,
},
},

-- Configuration of the cursor-actions module.
["cursor-actions"] = {
-- Configuration of the at-cursor module.
["at-cursor"] = {

-- The format of the popup shown on `:PapisShowPopup` (equivalent to points 1-3
-- The format of the popup shown on `:Papis at-cursor show-popup` (equivalent to points 1-3
-- of `preview_format`)
popup_format = {
{ "author", "%s", "PapisPopupAuthor" },
Expand Down Expand Up @@ -464,7 +460,7 @@ cmp.setup({

## Usage

Papis.nvim will start automatically according to the filetypes defined in `init_filetypes` (see the [setup section](#setup)). When first starting, papis.nvim will import some configuration values from Papis and save them in the database. If you update your Papis configuration, you should re-import the configuration into papis.nvim with `:PapisReInitConfig`.
Papis.nvim will start automatically according to the filetypes defined in `init_filetypes` (see the [setup section](#setup)). When first starting, papis.nvim will import some configuration values from Papis and save them in the database. If you update your Papis configuration, you should re-import the configuration into papis.nvim with `:Papis reload config`.

## Keymaps

Expand Down
169 changes: 84 additions & 85 deletions lua/papis/commands.lua
Original file line number Diff line number Diff line change
Expand Up @@ -4,100 +4,99 @@
--
-- Sets up default commands.
--
-- Adapted from https://github.com/nvim-neorocks/nvim-best-practices

local config = require("papis.config")
local api = vim.api

local commands = {
["base"] = {
reinit_data = {
name = "PapisReInitData",
command = function()
require("papis.data"):reset_db()
end,
opts = { desc = "Papis: empty and repopulate the sqlite database from disk" },
},
reinit_papis_py_config = {
name = "PapisReInitConfig",
command = function()
---@class PapisSubcommand
---@field impl fun(args:string[], opts: table) The command implementation
---@field complete? fun(subcmd_arg_lead: string): string[] (optional) Command completions callback, taking the lead of the subcommand's arguments
---@type table<string, PapisSubcommand>
local subcommand_tbl = {
reload = {
impl = function(args, _)
-- Implementation (args is a list of strings)
if args[1] == "config" then
config:update_papis_py_conf()
end,
opts = { desc = "Papis: import configuration from Papis" },
},
},
["debug"] = {
stop_fw = {
name = "PapisDebugFWStop",
command = function()
require("papis.fs-watcher").stop()
end,
opts = { desc = "Papis: stop file watching" },
},
start_fw = {
name = "PapisDebugFWStart",
command = function()
require("papis.fs-watcher").start()
end,
opts = { desc = "Papis: start file watching" },
},
get_log_path = {
name = "PapisDebugGetLogPath",
command = function()
require("papis.log").get_path()
end,
opts = { desc = "Papis: get path to the log file" },
},
},
["cursor-actions"] = {
open_file = {
name = "PapisOpenFile",
command = function()
return require("papis.cursor-actions").open_file()
end,
opts = { desc = "Papis: open the files attached to entry under cursor" },
},
open_note = {
name = "PapisOpenNote",
command = function()
return require("papis.cursor-actions").open_note()
end,
opts = { desc = "Papis: open the note of the entry under cursor" },
},
edit_entry = {
name = "PapisEditEntry",
command = function()
return require("papis.cursor-actions").edit_entry()
end,
opts = { desc = "Papis: edit the entry under cursor" },
},
show_popup = {
name = "PapisShowPopup",
command = function()
return require("papis.cursor-actions").show_popup()
end,
opts = { desc = "Papis: show popup of the entry under cursor" },
},
elseif args[1] == "data" then
require("papis.data"):reset_db()
end
end,
complete = function(subcmd_arg_lead)
-- Simplified example
local reload_args = {
"config",
"data",
}
return vim.iter(reload_args)
:filter(function(install_arg)
-- If the user has typed `:Papis reload co`,
-- this will match 'config'
return install_arg:find(subcmd_arg_lead) ~= nil
end)
:totable()
end,
},
}

local M = {}
---Main Papis command
---@param opts table
local function papis_cmd(opts)
local fargs = opts.fargs
local subcommand_key = fargs[1]
-- Get the subcommand's arguments, if any
local args = #fargs > 1 and vim.list_slice(fargs, 2, #fargs) or {}
local subcommand = subcommand_tbl[subcommand_key]
if not subcommand then
vim.notify("Papis: Unknown command: " .. subcommand_key, vim.log.levels.ERROR)
return
end
-- Invoke the subcommand
subcommand.impl(args, opts)
end

---Sets up either the commands of all enabled modules or of a given module
---@param module? string #Name of the module to be set up
function M.setup(module)
if not module then
for module_name, module_commands in pairs(commands) do
if config["enable_modules"][module_name] then
for _, command in pairs(module_commands) do
api.nvim_buf_create_user_command(0, command["name"], command["command"], command["opts"])
end
---Creates the main Papis command
local function create_command()
-- NOTE: the options will vary, based on your use case.
vim.api.nvim_create_user_command("Papis", papis_cmd, {
nargs = "+",
desc = "Papis main command (with subcommands)",
complete = function(arg_lead, cmdline, _)
-- Get the subcommand.
local subcmd_key, subcmd_arg_lead = cmdline:match("^['<,'>]*Papis[!]*%s(%S+)%s(.*)$")
if subcmd_key
and subcmd_arg_lead
and subcommand_tbl[subcmd_key]
and subcommand_tbl[subcmd_key].complete
then
-- The subcommand has completions. Return them.
return subcommand_tbl[subcmd_key].complete(subcmd_arg_lead)
end
end
else
for _, command in pairs(commands[module]) do
api.nvim_buf_create_user_command(0, command["name"], command["command"], command["opts"])
end
end
-- Check if cmdline is a subcommand
if cmdline:match("^['<,'>]*Papis[!]*%s+%w*$") then
-- Filter subcommands that match
local subcommand_keys = vim.tbl_keys(subcommand_tbl)
return vim.iter(subcommand_keys)
:filter(function(key)
return key:find(arg_lead) ~= nil
end)
:totable()
end
end,
})
end

local M = {}

---Sets up main "Papis" command
function M.setup()
create_command()
end

--- Recursively merges the provided table with the subcommand_tbl table.
---@param module_subcommand table #A table with a module's commands
function M:add_commands(module_subcommand)
subcommand_tbl = vim.tbl_extend("force", subcommand_tbl, module_subcommand)
end

return M
5 changes: 2 additions & 3 deletions lua/papis/config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
-- default configuration values
local default_config = {
enable_modules = {
["cursor-actions"] = true,
["at-cursor"] = true,
["search"] = true,
["completion"] = true,
["formatter"] = true,
Expand All @@ -28,7 +28,6 @@ local default_config = {
cite_formats_fallback = "plain",
always_use_plain = false,
enable_keymaps = false,
enable_commands = true,
enable_fs_watcher = true,
data_tbl_schema = { -- only "text" and "luatable" are allowed
id = { "integer", pk = true },
Expand Down Expand Up @@ -104,7 +103,7 @@ local default_config = {
return table.concat(reference_data)
end,
},
["cursor-actions"] = {
["at-cursor"] = {
popup_format = {
{ "author", "%s", "PapisPopupAuthor" },
{ "year", "%s", "PapisPopupYear" },
Expand Down
Loading

0 comments on commit 93f56ca

Please sign in to comment.