diff --git a/README.md b/README.md index 3231e51..70f85bd 100644 --- a/README.md +++ b/README.md @@ -75,12 +75,19 @@ However, this is always optional, and usually not necessary. ### VimL ```vim " Indexes the notebook -:ZkIndex +" params +" (optional) additional options, see https://github.com/mickael-menu/zk/blob/main/docs/editors-integration.md#zkindex +:ZkIndex [] + +" Creates a new note +" params +" (optional) additional options, see https://github.com/mickael-menu/zk/blob/main/docs/editors-integration.md#zknew +:ZkNew [] -" Creates and opens a new note +" Creates a new note and uses the last visual selection as the title while replacing the selection with a link to the new note " params -" (optional) directory for the new note, relative to the notebook root -:ZkNew [] +" (optional) additional options, see https://github.com/mickael-menu/zk/blob/main/docs/editors-integration.md#zknew +:ZkNewLink [] " Opens a Telescope picker " params @@ -96,8 +103,9 @@ where `options` can be any valid *Lua* expression that evaluates to a table. *Examples:* ```vim -:ZkNew daily +:ZkNew { dir = "daily", date = "yesterday" } :ZkList { createdAfter = "3 days ago", tags = { "work" } } +:'<,'>ZkNewLink " this will use your last visual mode selection. Note that you *must* call this command with the '<,'> range. ``` ### Lua @@ -116,6 +124,12 @@ require("zk").index(path, options) ---@see https://github.com/mickael-menu/zk/blob/main/docs/editors-integration.md#zknew require("zk").new(path, options) +---Creates a new note and uses the last visual selection as the title while replacing the selection with a link to the new note +-- +---@param path? string path to explicitly specify the notebook +---@param options table additional options +require("zk").new_link(path, options) + ---Opens a Telescope picker -- ---@param path? string path to explicitly specify the notebook @@ -135,6 +149,7 @@ require("zk").tag.list(path, options) ```lua require("zk").new(nil, { dir = "daily" }) require("zk").list(nil, { createdAfter = "3 days ago", tags = { "work" } }) +require("zk").new_link() -- this will use your last visual mode selection ``` As you can see, the `path` is optional, and can usually be omitted; see [Notebook Directory Discovery](#notebook-directory-discovery). @@ -159,7 +174,7 @@ require('telescope').extensions.zk.tags() ``` The Telescope pickers also allow you to explicitly specify a notebook like so `:Telescope zk notes path=/foo/bar` or so `require('telescope').extensions.zk.notes({ path = '/foo/bar'})`. -However, specifing a `path` is optional, and is usually not necessary; see [Notebook Directory Discovery](#notebook-directory-discovery). +However, specifying a `path` is optional, and is usually not necessary; see [Notebook Directory Discovery](#notebook-directory-discovery). You can even pass the same additional options to the Telescope pickers as described in [list and tag list commands](#commands). @@ -217,6 +232,25 @@ end) ## Example Mappings ```lua + +-- Create notes / links + +vim.api.nvim_set_keymap( + "n", + "zc", + "lua require('zk').new()", + { noremap = true } +) + +vim.api.nvim_set_keymap( + "x", + "zc", + "lua require('zk').new_link()", + { noremap = true } +) + +-- Show Telescope pickers + vim.api.nvim_set_keymap( "n", "zn", diff --git a/lua/zk.lua b/lua/zk.lua index ae5a70c..33f52f0 100644 --- a/lua/zk.lua +++ b/lua/zk.lua @@ -17,19 +17,23 @@ function M.setup(options) end if config.options.create_user_commands then - vim.cmd("command! ZkIndex lua require('zk').index()") - vim.cmd("command! -nargs=? ZkNew lua require('zk').new(nil, { dir = })") -- the command arg (directory) is interpreted relative to the notebook root - -- vim.cmd("command! -nargs=? -complete=dir ZkNew lua require('zk').new(nil, { dir = vim.fn.fnamemodify(, ':p') })") -- this would interpret the command arg (dir) relative to the cwd instead - vim.cmd( - "command! -nargs=? -complete=lua ZkList lua require('zk').list(nil, assert(loadstring('return ' .. ))())" - ) - vim.cmd( - "command! -nargs=? -complete=lua ZkTagList lua require('zk').tag.list(nil, assert(loadstring('return ' .. ))())" - ) + vim.cmd([[ + " Core Commands + command! -nargs=? -complete=lua ZkIndex lua require('zk').index(nil, assert(loadstring('return ' .. ))()) + command! -nargs=? -complete=lua ZkNew lua require('zk').new(nil, assert(loadstring('return ' .. ))()) + command! -nargs=? -complete=lua ZkList lua require('zk').list(nil, assert(loadstring('return ' .. ))()) + command! -nargs=? -complete=lua ZkTagList lua require('zk').tag.list(nil, assert(loadstring('return ' .. ))()) + + " Convenience Commands + command! -range -nargs=? -complete=lua ZkNewLink lua assert( == 2, "ZkNewLink must be called with '<,'> range. Try :'<'>ZkNewLink"); require('zk').new_link(nil, assert(loadstring('return ' .. ))()) + ]]) + -- The definition of :ZkNewLink is kind of a hack. + -- The lua function that is called by ZkNewLink will always use the '<,'> marks to get the selection. + -- The only way that this command can be called that makes semantical sense is :'<,'>ZkNewLink. end end --- Commands +-- Core Commands ---Indexes the notebook -- @@ -42,13 +46,20 @@ function M.index(path, options) end) end ----Creates and opens a new note +---Creates a new note -- ---@param path? string path to explicitly specify the notebook ---@param options table additional options ---@see https://github.com/mickael-menu/zk/blob/main/docs/editors-integration.md#zknew function M.new(path, options) + -- neovim does not yet support window/showDocument, therefore we handle options.edit locally + if options and options.edit then + options.edit = nil -- nil means true in this context + end M.api.new(path, options, function(res) + if options and options.edit == false then + return + end vim.cmd("edit " .. res.path) end) end @@ -85,4 +96,20 @@ function M.tag.list(path, options) require("telescope._extensions.zk").exports.tags(options) end +-- Convenience Commands + +---Creates a new note and uses the last visual selection as the title while replacing the selection with a link to the new note +-- +---@param path? string path to explicitly specify the notebook +---@param options table additional options +function M.new_link(path, options) + local location = util.make_lsp_location() + local selected_text = util.get_text_in_range(location.range) + if not selected_text then + vim.notify("Selection not set", vim.log.levels.ERROR) + return + end + M.new(path, vim.tbl_extend("keep", options or {}, { insertLinkAtLocation = location, title = selected_text })) +end + return M diff --git a/lua/zk/api.lua b/lua/zk/api.lua index c5fba5e..4fbdd6f 100644 --- a/lua/zk/api.lua +++ b/lua/zk/api.lua @@ -38,6 +38,10 @@ end ---@param options table? ---@param cb function? local function execute_command(cmd, path, options, cb) + if options and vim.tbl_isempty(options) then + -- an empty table would be send as an empty list, which causes an error on the server + options = nil + end local bufnr = 0 lsp.start() lsp.client().request("workspace/executeCommand", { diff --git a/lua/zk/util.lua b/lua/zk/util.lua index aa14bce..7cfd853 100644 --- a/lua/zk/util.lua +++ b/lua/zk/util.lua @@ -36,4 +36,26 @@ function M.lsp_buf_auto_add(bufnr) lsp.buf_add(bufnr) end +function M.make_lsp_location() + local params = vim.lsp.util.make_given_range_params() + params.uri = params.textDocument.uri + params.textDocument = nil + return params +end + +--- needed until https://github.com/neovim/neovim/pull/13896 is merged +---@param range table LSP range object +function M.get_text_in_range(range) + local A = range["start"] + local B = range["end"] + + local lines = vim.api.nvim_buf_get_lines(0, A.line, B.line + 1, true) + if vim.tbl_isempty(lines) then + return nil + end + lines[#lines] = string.sub(lines[#lines], 1, B.character) + lines[1] = string.sub(lines[1], A.character + 1) + return table.concat(lines, "\n") +end + return M