mirror of https://github.com/mickael-menu/zk-nvim
Kick start the plugin and Telescope extension (#5)
parent
88a327ef1c
commit
89179f1949
@ -1,2 +1,157 @@
|
|||||||
# zk-nvim
|
# zk-nvim
|
||||||
Neovim extension for zk
|
Neovim extension for [zk](https://github.com/mickael-menu/zk).
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
Using [packer.nvim](https://github.com/wbthomason/packer.nvim)
|
||||||
|
```lua
|
||||||
|
use {
|
||||||
|
"mickael-menu/zk-nvim",
|
||||||
|
requires = { "neovim/nvim-lspconfig" }
|
||||||
|
}
|
||||||
|
-- Telescope is optional
|
||||||
|
use {
|
||||||
|
'nvim-telescope/telescope.nvim',
|
||||||
|
requires = { {'nvim-lua/plenary.nvim'} }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Using [vim-plug](https://github.com/junegunn/vim-plug)
|
||||||
|
```viml
|
||||||
|
Plug "mickael-menu/zk-nvim"
|
||||||
|
Plug "neovim/nvim-lspconfig"
|
||||||
|
Plug 'nvim-telescope/telescope.nvim' -- optional
|
||||||
|
Plug 'nvim-lua/plenary.nvim' -- optional, dependency for Telescope
|
||||||
|
```
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
```lua
|
||||||
|
require("zk").setup()
|
||||||
|
require("telescope").load_extension("zk")
|
||||||
|
```
|
||||||
|
> :warning: This plugin will setup and start the LSP server for you, do *not* call `require("lspconfig").zk.setup()`.
|
||||||
|
|
||||||
|
#### Default configuration
|
||||||
|
```lua
|
||||||
|
require("zk").setup({
|
||||||
|
lsp = {
|
||||||
|
-- automatically attach buffers in a zk notebook that match the given filetypes
|
||||||
|
auto_attach = {
|
||||||
|
enabled = true,
|
||||||
|
filetypes = { "markdown" },
|
||||||
|
},
|
||||||
|
|
||||||
|
-- `config` is passed to `vim.lsp.start_client(config)`
|
||||||
|
config = {
|
||||||
|
cmd = { "zk", "lsp" },
|
||||||
|
name = "zk",
|
||||||
|
-- init_options = ...
|
||||||
|
-- on_attach = ...
|
||||||
|
-- etc, see `:h vim.lsp.start_client()`
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
```vim
|
||||||
|
:ZkIndex
|
||||||
|
:ZkNew [<directory>]
|
||||||
|
```
|
||||||
|
or via Lua
|
||||||
|
```lua
|
||||||
|
require("zk").index(path, args) -- path and args are optional
|
||||||
|
require("zk").new(path, args) -- path and args are optional
|
||||||
|
```
|
||||||
|
|
||||||
|
### Telescope
|
||||||
|
|
||||||
|
```vim
|
||||||
|
:Telescope zk notes
|
||||||
|
:Telescope zk backlinks
|
||||||
|
:Telescope zk links
|
||||||
|
:Telescope zk related
|
||||||
|
:Telescope zk tags
|
||||||
|
```
|
||||||
|
or via Lua
|
||||||
|
```lua
|
||||||
|
require('telescope').extensions.zk.notes()
|
||||||
|
require('telescope').extensions.zk.backlinks()
|
||||||
|
require('telescope').extensions.zk.links()
|
||||||
|
require('telescope').extensions.zk.related()
|
||||||
|
require('telescope').extensions.zk.tags()
|
||||||
|
```
|
||||||
|
By default, this plugin will use the path of the current buffer to determine the location of your notebook.
|
||||||
|
Note that if the current buffer does not belong to a notebook, `$ZK_NOTEBOOK_DIR` will be used to locate your notebook.
|
||||||
|
|
||||||
|
If you want, you can also explicitly specify a notebook by providing the path to any file or folder within the notebook like so `:Telescope zk notes path=/foo/bar` or so `require('telescope').extensions.zk.notes({ path = '/foo/bar'})`.
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
The difference between e.g. `require("zk").api.new` and `require("zk").new` is that the former lets you handle the API results yourself for more flexibility.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- https://github.com/mickael-menu/zk/blob/main/docs/editors-integration.md#zkindex
|
||||||
|
-- path and args are optional
|
||||||
|
require("zk").api.index(path, args, function(stats)
|
||||||
|
-- do something with the stats
|
||||||
|
end)
|
||||||
|
```
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- https://github.com/mickael-menu/zk/blob/main/docs/editors-integration.md#zknew
|
||||||
|
-- path and args are optional
|
||||||
|
require("zk").api.new(path, args, function(res)
|
||||||
|
file_path = res.path
|
||||||
|
-- do something with the new file path
|
||||||
|
end)
|
||||||
|
```
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- https://github.com/mickael-menu/zk/blob/main/docs/editors-integration.md#zklist
|
||||||
|
-- path is optional, args.select is required
|
||||||
|
-- args = { select = { "title", "absPath", "rawContent" }, sort = { "created" } }
|
||||||
|
require("zk").api.list(path, args, function(notes)
|
||||||
|
-- do something with the notes
|
||||||
|
end)
|
||||||
|
```
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- https://github.com/mickael-menu/zk/blob/main/docs/editors-integration.md#zktaglist
|
||||||
|
-- path and args are optional
|
||||||
|
require("zk").api.tag.list(path, args, function(tags)
|
||||||
|
-- do something with the tags
|
||||||
|
end)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example Mappings
|
||||||
|
```lua
|
||||||
|
vim.api.nvim_set_keymap(
|
||||||
|
"n",
|
||||||
|
"<Leader>zn",
|
||||||
|
"<cmd>lua require('telescope').extensions.zk.notes()<CR>",
|
||||||
|
{ noremap = true }
|
||||||
|
)
|
||||||
|
|
||||||
|
vim.api.nvim_set_keymap(
|
||||||
|
"n",
|
||||||
|
"<Leader>zb",
|
||||||
|
"<cmd>lua require('telescope').extensions.zk.backlinks()<CR>",
|
||||||
|
{ noremap = true }
|
||||||
|
)
|
||||||
|
|
||||||
|
vim.api.nvim_set_keymap(
|
||||||
|
"n",
|
||||||
|
"<Leader>zl",
|
||||||
|
"<cmd>lua require('telescope').extensions.zk.links()<CR>",
|
||||||
|
{ noremap = true }
|
||||||
|
)
|
||||||
|
|
||||||
|
vim.api.nvim_set_keymap(
|
||||||
|
"n",
|
||||||
|
"<Leader>zt",
|
||||||
|
"<cmd>lua require('telescope').extensions.zk.tags()<CR>",
|
||||||
|
{ noremap = true }
|
||||||
|
)
|
||||||
|
```
|
||||||
|
@ -0,0 +1,52 @@
|
|||||||
|
local util = require("telescope.zk.util")
|
||||||
|
local zk = require("zk")
|
||||||
|
|
||||||
|
local function show_notes(opts)
|
||||||
|
opts = vim.tbl_extend("keep", opts or {}, { prompt_title = "Zk Notes" })
|
||||||
|
zk.api.list(opts.path, util.wrap_note_args({}), function(notes)
|
||||||
|
util.show_note_picker(opts, notes)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function show_backlinks(opts)
|
||||||
|
opts = vim.tbl_extend("keep", opts or {}, { prompt_title = "Zk Backlinks" })
|
||||||
|
zk.api.list(opts.path, util.wrap_note_args({ linkTo = { vim.api.nvim_buf_get_name(0) } }), function(notes)
|
||||||
|
util.show_note_picker(opts, notes)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function show_links(opts)
|
||||||
|
opts = vim.tbl_extend("keep", opts or {}, { prompt_title = "Zk Links" })
|
||||||
|
zk.api.list(opts.path, util.wrap_note_args({ linkedBy = { vim.api.nvim_buf_get_name(0) } }), function(notes)
|
||||||
|
util.show_note_picker(opts, notes)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function show_related(opts)
|
||||||
|
opts = vim.tbl_extend("keep", opts or {}, { prompt_title = "Zk Related" })
|
||||||
|
zk.api.list(opts.path, util.wrap_note_args({ related = { vim.api.nvim_buf_get_name(0) } }), function(notes)
|
||||||
|
util.show_note_picker(opts, notes)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function show_tags(opts)
|
||||||
|
opts = vim.tbl_extend("keep", opts or {}, { prompt_title = "Zk Tags" })
|
||||||
|
zk.api.tag.list(opts.path, util.wrap_tag_args({}), function(tags)
|
||||||
|
util.show_tag_picker(opts, tags, function(selected_tags)
|
||||||
|
zk.api.list(opts.path, util.wrap_note_args({ tags = selected_tags }), function(notes)
|
||||||
|
opts.prompt_title = "Zk Notes for tag(s) " .. vim.inspect(selected_tags)
|
||||||
|
util.show_note_picker(opts, notes)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
return require("telescope").register_extension({
|
||||||
|
exports = {
|
||||||
|
notes = show_notes,
|
||||||
|
backlinks = show_backlinks,
|
||||||
|
links = show_links,
|
||||||
|
related = show_related,
|
||||||
|
tags = show_tags,
|
||||||
|
},
|
||||||
|
})
|
@ -0,0 +1,113 @@
|
|||||||
|
local pickers = require("telescope.pickers")
|
||||||
|
local finders = require("telescope.finders")
|
||||||
|
local conf = require("telescope.config").values
|
||||||
|
local actions = require("telescope.actions")
|
||||||
|
local action_state = require("telescope.actions.state")
|
||||||
|
local action_utils = require("telescope.actions.utils")
|
||||||
|
local putils = require("telescope.previewers.utils")
|
||||||
|
local entry_display = require("telescope.pickers.entry_display")
|
||||||
|
local previewers = require("telescope.previewers")
|
||||||
|
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
function M.wrap_note_args(opts)
|
||||||
|
return vim.tbl_deep_extend(
|
||||||
|
"force",
|
||||||
|
{ select = { "title", "absPath", "rawContent" }, sort = { "created" } },
|
||||||
|
opts or {}
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.wrap_tag_args(opts)
|
||||||
|
return vim.tbl_deep_extend("force", { sort = { "note-count" } }, opts or {})
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.create_note_entry_maker(_)
|
||||||
|
return function(note)
|
||||||
|
return {
|
||||||
|
value = note,
|
||||||
|
path = note.absPath,
|
||||||
|
display = note.title,
|
||||||
|
ordinal = note.title,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.create_tag_entry_maker(opts)
|
||||||
|
return function(tag)
|
||||||
|
local displayer = entry_display.create({
|
||||||
|
separator = " ",
|
||||||
|
items = {
|
||||||
|
{ width = opts.note_count_width or 4 },
|
||||||
|
{ remaining = true },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
local make_display = function(e)
|
||||||
|
return displayer({
|
||||||
|
{ e.value.note_count, "TelescopeResultsNumber" },
|
||||||
|
e.value.name,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
return {
|
||||||
|
value = tag,
|
||||||
|
display = make_display,
|
||||||
|
ordinal = tag.name,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.make_note_previewer()
|
||||||
|
return previewers.new_buffer_previewer({
|
||||||
|
define_preview = function(self, entry)
|
||||||
|
vim.api.nvim_buf_set_lines(self.state.bufnr, 0, -1, false, vim.split(entry.value.rawContent, "\n"))
|
||||||
|
putils.highlighter(self.state.bufnr, "markdown")
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.show_note_picker(opts, notes)
|
||||||
|
-- zk.api.list(opts.path, zk_args, function(notes)
|
||||||
|
opts = opts or {}
|
||||||
|
pickers.new(opts, {
|
||||||
|
finder = finders.new_table({
|
||||||
|
results = notes,
|
||||||
|
entry_maker = M.create_note_entry_maker(opts),
|
||||||
|
}),
|
||||||
|
sorter = conf.file_sorter(opts),
|
||||||
|
previewer = M.make_note_previewer(),
|
||||||
|
}):find()
|
||||||
|
-- end)
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.show_tag_picker(opts, tags, cb)
|
||||||
|
-- zk.api.tag.list(opts.path, make_tag_args(), function(tags)
|
||||||
|
opts = opts or {}
|
||||||
|
pickers.new(opts, {
|
||||||
|
finder = finders.new_table({
|
||||||
|
results = tags,
|
||||||
|
entry_maker = M.create_tag_entry_maker(opts),
|
||||||
|
}),
|
||||||
|
sorter = conf.generic_sorter(opts),
|
||||||
|
attach_mappings = function(prompt_bufnr, _)
|
||||||
|
actions.select_default:replace(function()
|
||||||
|
local selection = {}
|
||||||
|
action_utils.map_selections(prompt_bufnr, function(entry, _)
|
||||||
|
table.insert(selection, entry.value.name)
|
||||||
|
end)
|
||||||
|
|
||||||
|
if vim.tbl_isempty(selection) then
|
||||||
|
selection = { action_state.get_selected_entry().value.name }
|
||||||
|
end
|
||||||
|
|
||||||
|
actions.close(prompt_bufnr)
|
||||||
|
|
||||||
|
-- _list_notes(opts, { prompt_title = "Zk Notes for tag(s) " .. vim.inspect(selection) }, { tags = selection })
|
||||||
|
cb(selection)
|
||||||
|
end)
|
||||||
|
return true
|
||||||
|
end,
|
||||||
|
}):find()
|
||||||
|
-- end)
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
@ -0,0 +1,34 @@
|
|||||||
|
local util = require("zk.util")
|
||||||
|
local config = require("zk.config")
|
||||||
|
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
M.api = require("zk.api")
|
||||||
|
|
||||||
|
M.lsp = require("zk.lsp")
|
||||||
|
|
||||||
|
function M.setup(options)
|
||||||
|
config.options = vim.tbl_deep_extend("force", config.defaults, options or {})
|
||||||
|
if config.options.lsp.auto_attach.enabled then
|
||||||
|
util.setup_lsp_auto_attach()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Commands
|
||||||
|
|
||||||
|
function M.index(path, args)
|
||||||
|
M.api.index(path, args, function(stats)
|
||||||
|
vim.notify(vim.inspect(stats))
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.new(path, args)
|
||||||
|
M.api.new(path, args, function(res)
|
||||||
|
vim.cmd("edit " .. res.path)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
vim.cmd("command! ZkIndex lua require('zk').index()")
|
||||||
|
vim.cmd("command! -nargs=? ZkNew lua require('zk').new(nil, { dir = [=[<args>]=]})")
|
||||||
|
|
||||||
|
return M
|
@ -0,0 +1,58 @@
|
|||||||
|
local lsp = require("zk.lsp")
|
||||||
|
local util = require("zk.util")
|
||||||
|
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
local function resolve_notebook_dir(bufnr)
|
||||||
|
local path = vim.api.nvim_buf_get_name(bufnr)
|
||||||
|
-- if the buffer has no name, use the current working directory
|
||||||
|
if path == "" then
|
||||||
|
path = vim.fn.getcwd(0)
|
||||||
|
end
|
||||||
|
-- if the buffer doesn't belong to a notebook, use $ZK_NOTEBOOK_DIR as fallback if available
|
||||||
|
if not util.is_notebook_path(path) and vim.env.ZK_NOTEBOOK_DIR then
|
||||||
|
path = vim.env.ZK_NOTEBOOK_DIR
|
||||||
|
end
|
||||||
|
return path
|
||||||
|
end
|
||||||
|
|
||||||
|
local function execute_command(path, cmd, args, cb)
|
||||||
|
local bufnr = 0
|
||||||
|
lsp.start()
|
||||||
|
lsp.client().request("workspace/executeCommand", {
|
||||||
|
command = "zk." .. cmd,
|
||||||
|
arguments = {
|
||||||
|
path or resolve_notebook_dir(bufnr),
|
||||||
|
args,
|
||||||
|
},
|
||||||
|
}, function(err, res)
|
||||||
|
assert(not err, tostring(err))
|
||||||
|
if res and cb then
|
||||||
|
cb(res)
|
||||||
|
end
|
||||||
|
end, bufnr)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- https://github.com/mickael-menu/zk/blob/main/docs/editors-integration.md#zkindex
|
||||||
|
function M.index(path, args, cb)
|
||||||
|
execute_command(path, "index", args, cb)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- https://github.com/mickael-menu/zk/blob/main/docs/editors-integration.md#zknew
|
||||||
|
function M.new(path, args, cb)
|
||||||
|
execute_command(path, "new", args, cb)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- https://github.com/mickael-menu/zk/blob/main/docs/editors-integration.md#zklist
|
||||||
|
function M.list(path, args, cb)
|
||||||
|
execute_command(path, "list", args, cb)
|
||||||
|
end
|
||||||
|
|
||||||
|
M.tag = {}
|
||||||
|
|
||||||
|
--- https://github.com/mickael-menu/zk/blob/main/docs/editors-integration.md#zktaglist
|
||||||
|
function M.tag.list(path, args, cb)
|
||||||
|
execute_command(path, "tag.list", args, cb)
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
@ -0,0 +1,18 @@
|
|||||||
|
local M = {}
|
||||||
|
|
||||||
|
M.defaults = {
|
||||||
|
lsp = {
|
||||||
|
auto_attach = {
|
||||||
|
enabled = true,
|
||||||
|
filetypes = { "markdown" },
|
||||||
|
},
|
||||||
|
config = {
|
||||||
|
cmd = { "zk", "lsp" },
|
||||||
|
name = "zk",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
M.options = M.defaults
|
||||||
|
|
||||||
|
return M
|
@ -0,0 +1,35 @@
|
|||||||
|
local config = require("zk.config")
|
||||||
|
|
||||||
|
local client_id = nil
|
||||||
|
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
--- Starts an LSP client if necessary
|
||||||
|
function M.start()
|
||||||
|
if not client_id then
|
||||||
|
client_id = vim.lsp.start_client(config.options.lsp.config)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Starts an LSP client if necessary, and attaches the given buffer.
|
||||||
|
function M.buf_add(bufnr)
|
||||||
|
bufnr = bufnr or 0
|
||||||
|
M.start()
|
||||||
|
vim.lsp.buf_attach_client(bufnr, client_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Stops the LSP client managed by this plugin
|
||||||
|
function M.stop()
|
||||||
|
local client = M.client()
|
||||||
|
if client then
|
||||||
|
client.stop()
|
||||||
|
end
|
||||||
|
client_id = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Gets the LSP client managed by this plugin, might be nil
|
||||||
|
function M.client()
|
||||||
|
return vim.lsp.get_client_by_id(client_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
@ -0,0 +1,36 @@
|
|||||||
|
local lsp = require("zk.lsp")
|
||||||
|
local config = require("zk.config")
|
||||||
|
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
function M.setup_lsp_auto_attach()
|
||||||
|
--- NOTE: modified version of code in nvim-lspconfig
|
||||||
|
local trigger
|
||||||
|
local filetypes = config.options.lsp.auto_attach.filetypes
|
||||||
|
if filetypes then
|
||||||
|
trigger = "FileType " .. table.concat(filetypes, ",")
|
||||||
|
else
|
||||||
|
trigger = "BufReadPost *"
|
||||||
|
end
|
||||||
|
vim.api.nvim_command(string.format("autocmd %s lua require'zk.util'.lsp_buf_auto_add(0)", trigger))
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.is_notebook_path(path)
|
||||||
|
-- check that we got a match on the .zk root directory
|
||||||
|
return require("lspconfig.util").root_pattern(".zk")(path) ~= nil
|
||||||
|
end
|
||||||
|
|
||||||
|
--- NOTE: No need to manually call this. Automatically called via an |autocmd| if lsp.auto_attach is enabled.
|
||||||
|
function M.lsp_buf_auto_add(bufnr)
|
||||||
|
if vim.api.nvim_buf_get_option(bufnr, "buftype") == "nofile" then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if not M.is_notebook_path(vim.api.nvim_buf_get_name(bufnr)) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
lsp.buf_add(bufnr)
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
@ -0,0 +1 @@
|
|||||||
|
std="vim"
|
@ -0,0 +1,2 @@
|
|||||||
|
indent_type = "Spaces"
|
||||||
|
indent_width = 2
|
Loading…
Reference in New Issue