Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
aa45809
add ZkGrep command
shfattig Jun 22, 2024
6e786f4
Add conditional on 'grep' argument in pick_notes()
shfattig Jun 22, 2024
4034cd5
Condition pick_notes() on 'grep' option
shfattig Jun 22, 2024
4c66abd
add new grep picker
shfattig Jun 22, 2024
4499af1
replace hard coded notebook path with util.notebook_root
tjex Jun 22, 2024
81bb6b8
improve logic checks
tjex Sep 25, 2024
20a3058
temporary notice, instead of returning an error
tjex Sep 28, 2024
8357420
fix placement of temporary notice conditional check
tjex Sep 28, 2024
405e4e0
Merge branch 'main' into zk_grep
tjex Dec 12, 2024
c1f1e1f
feat: add ZkGrep command
riodelphino May 15, 2025
ba0c42b
fix: Mod README.md, Change hl group to TelescopeResults*
riodelphino May 16, 2025
26fcbcd
feat: show lnum:col in results
riodelphino May 16, 2025
da7bbeb
fix: Change lnum:col's hl group
riodelphino May 16, 2025
a3ac44b
fix: Get zk root path correctly
riodelphino May 16, 2025
4047c27
fix: Restrict highlight to body text
riodelphino May 16, 2025
b6ffd07
refactor: refine code to match original function style
riodelphino May 17, 2025
5ff0b3d
feat: Support custom buffer names from YAML
riodelphino May 27, 2025
d066611
docs: Refine the example codes
riodelphino May 28, 2025
b381d0e
docs: Fix lyaml's url and Mod example code
riodelphino May 28, 2025
1413886
docs: Refine the example codes / mod: Use zk.config.options instead o…
riodelphino Jun 25, 2025
ec0efee
fix: Remove buf.name.formatter option and configure purely in bufferl…
riodelphino Jun 25, 2025
4600878
docs: Add example codes for bufferline integration
riodelphino Jun 25, 2025
a0425a7
fix: Except .zk dir from fetching yaml
riodelphino Jul 18, 2025
e8ef974
mod: func table_has_value() to table_contains()
riodelphino Jul 18, 2025
9077c31
mod: yaml functions / docs: refine integration sample code
riodelphino Jul 19, 2025
d430f5d
fixup: Before remove junk code
riodelphino Jun 25, 2025
ccaa9f9
fixup: Before give up excluding yaml.title highlight
riodelphino Jun 28, 2025
c4f0886
add: Grep with snacks.nvim / refactor: refine telescope code
riodelphino Jun 28, 2025
e6b825a
del: Removed table_has_value() and fetch_yaml() for merging bufname_f…
riodelphino Jul 19, 2025
fca3be1
Merge branch 'bufname_from_yaml' into zk_grep
riodelphino Jul 19, 2025
9f7e221
mod: Adjust to merged bufname_from_yaml branch
riodelphino Jul 19, 2025
2c144d8
fix: the slow display on neo-tree with too many files
riodelphino Aug 31, 2025
2458a18
fix: Enable grepping another notebook / Some optimizations
riodelphino Aug 31, 2025
1dc4b5d
docs: Add sample codes for grep
riodelphino Sep 1, 2025
64e39f0
docs: zk.txt
riodelphino Sep 1, 2025
40189a3
Merge remote-tracking branch 'upstream/main' into zk_grep
riodelphino Sep 5, 2025
52c8dc9
docs: Remove trash config (came from my earlier commit)
riodelphino Sep 6, 2025
6df95bb
docs: Remove neo-tree & bufferline integration sample codes
riodelphino Sep 14, 2025
6117bc6
del: Remove lyaml code
riodelphino Sep 20, 2025
324f4ff
docs: Add ripgrep in docs for grep feature
riodelphino Sep 21, 2025
a4c7f5c
fix: Replaced lyaml with zk.api.list in snacks_picker
riodelphino Sep 21, 2025
ca17552
fix: Replaced lyaml with zk.api.list in Telescope
riodelphino Sep 21, 2025
9bbb429
del: Remove debug code
riodelphino Sep 21, 2025
3fc67a3
fix: :ZkNotes error on select option
riodelphino Sep 21, 2025
1ee1bb0
fix: 'no file' 'absPath' errors (Restore 'absPath' into zk_api_select…
riodelphino Sep 22, 2025
a7a8132
Merge branch 'upstream-main' into zk_grep
riodelphino Sep 25, 2025
3a082b1
fix: Remove unused variable
riodelphino Sep 28, 2025
bd79991
fix: Add sort scoring in telescope grep / Refactor telescope grep
riodelphino Sep 28, 2025
636a334
fix: root detections / del: debug comments
riodelphino Sep 29, 2025
98df902
fix: Rename M.zk_api_select as M.note_picker_list_api_selection (Revert)
riodelphino Sep 29, 2025
5ae8be7
fix: Aboid truncpath() arg type error
riodelphino Nov 15, 2025
07561f8
sync: snacks.nvim upstream
riodelphino Nov 15, 2025
2eb5dbb
feat: Support displaying directories in snacks_picker (not yet suppor…
riodelphino Nov 15, 2025
4887518
Merge branch 'upstream-main' into zk_grep
riodelphino Nov 15, 2025
2f495d5
feat: Support displaying directories in telescope too
riodelphino Nov 15, 2025
672233b
doc: Add ripgrep version
riodelphino Nov 18, 2025
a46dec4
fix: Highlight matched text in snacks_picker item list
riodelphino Nov 20, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ to see it in action.
| 0.1.1 | 0.13.0 - 0.14.1 | 0.9.5 |
| 0.1.0 | 0.13.0 - 0.14.1 | 0.8.0 - 0.9.5 |

* `ripgrep` >= 14.1.0 (for grep feature)

## Installation

Via [packer.nvim](https://github.com/wbthomason/packer.nvim)
Expand Down Expand Up @@ -179,6 +181,9 @@ see what they can do, and learn as you go.
- `:ZkNotes [{options}]`
Opens a notes picker.

- `:ZkGrep [{options}]`
Opens a grep notes picker

- `:ZkBuffers [{options}]`
Opens a notes picker for active buffers (notebook files only).

Expand Down Expand Up @@ -333,6 +338,16 @@ require("zk").index(options)
require("zk").pick_notes(options, picker_options, cb)
```

```lua
---Opens a notes grep picker, and calls the callback with the selection
--
---@param options? table additional options
---@param picker_options? table options for the picker
---@param cb function
---@see zk.ui.grep_notes
require("zk").grep_notes(options, picker_options, cb)
```

```lua
---Opens a tags picker, and calls the callback with the selection
--
Expand Down Expand Up @@ -415,6 +430,16 @@ Used by the [high-level API](#high-level-api) to display the results of the
require("zk.ui").pick_notes(notes, options, cb)
```

```lua
---Opens a grep picker
--
---@param options? table containing {notebook_path}, {picker}, {multi_select} keys
---@param picker_options? table
---@param cb function

require("zk.ui").grep_notes(options, picker_options, cb)
```

```lua
---Opens a tags picker
--
Expand Down Expand Up @@ -444,6 +469,8 @@ vim.api.nvim_set_keymap("n", "<leader>zn", "<Cmd>ZkNew { title = vim.fn.input('T

-- Open notes.
vim.api.nvim_set_keymap("n", "<leader>zo", "<Cmd>ZkNotes { sort = { 'modified' } }<CR>", opts)
-- Grep notes.
vim.api.nvim_set_keymap("n", "<leader>zg", "<Cmd>ZkGrep<CR>", opts)
-- Open notes associated with the selected tags.
vim.api.nvim_set_keymap("n", "<leader>zt", "<Cmd>ZkTags<CR>", opts)

Expand All @@ -453,6 +480,17 @@ vim.api.nvim_set_keymap("n", "<leader>zf", "<Cmd>ZkNotes { sort = { 'modified' }
vim.api.nvim_set_keymap("v", "<leader>zf", ":'<,'>ZkMatch<CR>", opts)
```

Open another notebook:

```lua
-- Open notes in another notebook.
vim.api.nvim_set_keymap("n", "<leader>zO", "<Cmd>ZkNotes { notebook_path = '/path/to/another/notebook' }<CR>", opts)
-- Open notes associated with a specific tag, in another notebook.
vim.api.nvim_set_keymap("n", "<leader>zT", "<Cmd>ZkNotes { notebook_path = '/path/to/another/notebook', tags = { 'todo' } }<CR>", opts)
-- Grep notes in another notebook.
vim.api.nvim_set_keymap("n", "<leader>zG", "<Cmd>ZkGrep { notebook_path = '/path/to/another/notebook' }<CR>", opts)
```

You can add additional key mappings for Markdown buffers located in a `zk`
notebook, using `ftplugin`. First, make sure it is enabled in your Neovim
config:
Expand Down
30 changes: 28 additions & 2 deletions doc/zk.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ REQUIREMENTS *zk-requirement

* Neovim >= 0.10.0
* `zk` >= 0.14.1
* `ripgrep` >= 14.1.0 (for grep feature)

--------------------------------------------------------------------------------
INSTALLATION *zk-installation*
Expand Down Expand Up @@ -164,6 +165,11 @@ BUILT-IN COMMANDS *zk-built-in_command
" params
" (optional) additional options, see https://github.com/zk-org/zk/blob/main/docs/tips/editors-integration.md#zklist
:ZkNotes [{options}]

" Opens a notes grep picker
" params
" (optional) additional options, see https://github.com/zk-org/zk/blob/main/docs/tips/editors-integration.md#zklist
:ZkGrep [{options}]
<
" Opens a notes picker for active buffers (showing notebook files only).
" params
Expand Down Expand Up @@ -298,7 +304,14 @@ It's mainly used for the implementation of built-in and custom commands.
---@see https://github.com/zk-org/zk/blob/main/docs/tips/editors-integration.md#zklist
---@see zk.ui.pick_notes
require("zk").pick_notes(options, picker_options, cb)
<

---Opens a notes grep picker, and calls the callback with the selection
--
---@param options? table additional options
---@param picker_options? table options for the picker
---@param cb function
---@see zk.ui.grep_notes
require("zk").grep_notes(options, picker_options, cb)
>
---Opens a tags picker, and calls the callback with the selection
--
Expand Down Expand Up @@ -400,13 +413,25 @@ Add these global mappings in your main Neovim config:
vim.api.nvim_set_keymap("n", "<leader>zn", "<Cmd>ZkNew { title = vim.fn.input('Title: ') }<CR>", opts)
-- Open notes.
vim.api.nvim_set_keymap("n", "<leader>zo", "<Cmd>ZkNotes { sort = { 'modified' } }<CR>", opts)
-- Grep notes.
vim.api.nvim_set_keymap("n", "<leader>zg", "<Cmd>ZkGrep<CR>", opts)
-- Open notes associated with the selected tags.
vim.api.nvim_set_keymap("n", "<leader>zt", "<Cmd>ZkTags<CR>", opts)
-- Search for the notes matching a given query.
vim.api.nvim_set_keymap("n", "<leader>zf", "<Cmd>ZkNotes { sort = { 'modified' }, match = { vim.fn.input('Search: ') } }<CR>", opts)
-- Search for the notes matching the current visual selection.
vim.api.nvim_set_keymap("v", "<leader>zf", ":'<,'>ZkMatch<CR>", opts)
<


Open another notebook:

-- Open notes in another notebook.
vim.api.nvim_set_keymap("n", "<leader>zO", "<Cmd>ZkNotes { notebook_path = '/path/to/another/notebook' }<CR>", opts)
-- Open notes associated with a specific tag, in another notebook.
vim.api.nvim_set_keymap("n", "<leader>zT", "<Cmd>ZkNotes { notebook_path = '/path/to/another/notebook', tags = { 'todo' } }<CR>", opts)
-- Grep notes in another notebook.
vim.api.nvim_set_keymap("n", "<leader>zG", "<Cmd>ZkGrep { notebook_path = '/path/to/another/notebook' }<CR>", opts)


You can add additional key mappings for Markdown buffers located in a `zk` notebook, using `ftplugin`. First, make sure it is enabled in your Neovim config:
>
Expand Down Expand Up @@ -518,5 +543,6 @@ It's possible (but unnecessary) to also load the notes and tags pickers as a tel
:Telescope zk tags created=today
<


==============================================================================
vim:tw=78:ts=8:ft=help:norl:fdm=marker:cole=2:
23 changes: 21 additions & 2 deletions lua/zk.lua
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,16 @@ function M.pick_notes(options, picker_options, cb)
end)
end

---Opens a grep picker, and calls the callback with the selection
--
---@param options? table additional options
---@param picker_options? table options for the picker
---@param cb function
---@see zk.ui.grep_notes
function M.grep_notes(options, picker_options, cb)
ui.grep_notes(options, picker_options, cb)
end

---Opens a tags picker, and calls the callback with the selection
--
---@param options? table additional options
Expand All @@ -131,14 +141,23 @@ end
---@see https://github.com/zk-org/zk/blob/main/docs/tips/editors-integration.md#zklist
---@see zk.ui.pick_notes
function M.edit(options, picker_options)
M.pick_notes(options, picker_options, function(notes)
function cb(notes)
if picker_options and picker_options.multi_select == false then
notes = { notes }
end
for _, note in ipairs(notes) do
vim.cmd("e " .. note.absPath)
if note.lnum and note.col then
vim.api.nvim_win_set_cursor(0, { note.lnum, math.max(note.col - 1, 0) })
end
end
end)
end

if options and options.grep and options.grep == true then
M.grep_notes(options, picker_options, cb)
else
M.pick_notes(options, picker_options, cb)
end
end

return M
5 changes: 5 additions & 0 deletions lua/zk/commands/builtin.lua
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,11 @@ commands.add("ZkBuffers", function(options)
zk.edit(options, { title = "Zk Buffers" })
end)

commands.add("ZkGrep", function(options)
options = vim.tbl_extend("force", { grep = true }, options or {})
zk.edit(options, { title = "Zk Grep" })
end)

commands.add("ZkBacklinks", function(options)
options = vim.tbl_extend("force", { linkTo = { vim.api.nvim_buf_get_name(0) } }, options or {})
zk.edit(options, { title = "Zk Backlinks" })
Expand Down
167 changes: 166 additions & 1 deletion lua/zk/pickers/snacks_picker.lua
Original file line number Diff line number Diff line change
@@ -1,9 +1,25 @@
local Snacks = require("snacks")
local snacks_picker = require("snacks.picker")
local snacks_format = require("snacks.picker.format")
local api = require("zk.api")
local util = require("zk.util")
local uv = vim.uv or vim.loop
local notes_cache = {}

local M = {}
local H = {}
local snacks_picker = require("snacks.picker")

-- See https://zk-org.github.io/zk/tips/editors-integration.html#zk-list --> Expand section `2`
M.note_picker_list_api_selection = { "title", "path", "absPath" }

local function index_notes_by_path(notes)
local tbl = {}
for _, note in ipairs(notes) do
tbl[note.path] = note
end
return tbl
end

M.show_note_picker = function(notes, opts, cb)
notes = vim.tbl_map(function(note)
local title = note.title or note.path
Expand All @@ -12,6 +28,28 @@ M.show_note_picker = function(notes, opts, cb)
H.item_picker(notes, opts, cb)
end

M.show_grep_picker = function(opts, cb)
local root = opts.notebook_path or nil
if not root then
local path = util.resolve_notebook_path(0)
root = util.notebook_root(path or vim.fn.getcwd())
end

local picker_opts = vim.tbl_deep_extend("force", {
format = "zk_grep",
cwd = root,
title = opts.title or "Zk Grep",
sort = { fields = { "score:desc", "idx" } }, -- TODO: Add custom sorting fields (e.g. "title:desc", "pos")
}, opts.snacks_picker or {})

api.list(picker_opts.cwd, { select = M.note_picker_list_api_selection }, function(err, notes)
if not err then
notes_cache = index_notes_by_path(notes)
Snacks.picker.grep(picker_opts, cb)
end
end)
end

M.show_tag_picker = function(tags, opts, cb)
opts.snacks_picker = opts.snacks_picker or {}
opts.snacks_picker =
Expand All @@ -28,6 +66,133 @@ M.show_tag_picker = function(tags, opts, cb)
H.item_picker(tags, opts, cb)
end

-- Create a new function that extends builtin `F.filename` with YAML frontmatter title.
-- https://github.com/folke/snacks.nvim/blob/main/lua/snacks/picker/format.lua
---@param item snacks.picker.Item
function snacks_format.zk_filename(item, picker)
---@type snacks.picker.Highlight[]
local ret = {}
if not item.file then
return ret
end
local path = Snacks.picker.util.path(item) or item.file
path =
Snacks.picker.util.truncpath(path, picker.opts.formatters.file.min_width or 40, { cwd = picker:cwd() })

if picker.opts.icons.files.enabled ~= false then
local name, cat = path, (item.dir and "directory" or "file")
if item.buf and vim.api.nvim_buf_is_loaded(item.buf) and vim.bo[item.buf].buftype ~= "" then
name = vim.bo[item.buf].filetype
cat = "filetype"
end
local icon, hl = Snacks.util.icon(name, cat, {
fallback = picker.opts.icons.files,
})
if item.buftype == "terminal" then
icon, hl = " ", "Special"
end
if item.dir and item.open then
icon = picker.opts.icons.files.dir_open
end
icon = Snacks.picker.util.align(icon, picker.opts.formatters.file.icon_width or 2)
ret[#ret + 1] = { icon, hl, virtual = true }
end

local base_hl = item.dir and "SnacksPickerDirectory" or "SnacksPickerFile"
local function is(prop)
local it = item
while it do
if it[prop] then
return true
end
it = it.parent
end
end

if is("ignored") then
base_hl = "SnacksPickerPathIgnored"
elseif is("hidden") then
base_hl = "SnacksPickerPathHidden"
elseif item.filename_hl then
base_hl = item.filename_hl
end
local dir_hl = "SnacksPickerDir"

local note = notes_cache[path]
if picker.opts.formatters.file.filename_only then
path = vim.fn.fnamemodify(item.file, ":t")
path = path == "" and item.file or path
ret[#ret + 1] = { note and note.title or path, base_hl, field = "file" }
else
ret[#ret + 1] = {
"",
resolve = function(max_width)
local truncpath = Snacks.picker.util.truncpath(
path,
math.max(max_width, picker.opts.formatters.file.min_width or 20),
{ cwd = picker:cwd(), kind = picker.opts.formatters.file.truncate }
)
local dir, base = truncpath:match("^(.*)/(.+)$")
base = note and note.title or base
local resolved = {} ---@type snacks.picker.Highlight[]
if base and dir then
if picker.opts.formatters.file.filename_first then
resolved[#resolved + 1] = { base, base_hl, field = "file" }
resolved[#resolved + 1] = { " " }
resolved[#resolved + 1] = { dir, dir_hl, field = "file" }
else
resolved[#resolved + 1] = { dir .. "/", dir_hl, field = "file" }
resolved[#resolved + 1] = { base, base_hl, field = "file" }
end
else
resolved[#resolved + 1] = { truncpath, base_hl, field = "file" }
end
return resolved
end,
}
end

if item.pos and item.pos[1] > 0 then
ret[#ret + 1] = { ":", "SnacksPickerDelim" }
ret[#ret + 1] = { tostring(item.pos[1]), "SnacksPickerRow" }
if item.pos[2] > 0 then
ret[#ret + 1] = { ":", "SnacksPickerDelim" }
ret[#ret + 1] = { tostring(item.pos[2]), "SnacksPickerCol" }
end
end
ret[#ret + 1] = { " " }
if item.type == "link" then
local real = uv.fs_realpath(item.file)
local broken = not real
real = real or uv.fs_readlink(item.file)
if real then
ret[#ret + 1] = { "-> ", "SnacksPickerDelim" }
ret[#ret + 1] =
{ Snacks.picker.util.truncpath(real, 20), broken and "SnacksPickerLinkBroken" or "SnacksPickerLink" }
ret[#ret + 1] = { " " }
end
end
return ret
end

-- Add 'zk_grep' format
Snacks.picker.format["zk_grep"] = function(item, picker)
local ret = {}
if not item.file then
return ret
end
vim.list_extend(ret, snacks_format.zk_filename(item, picker))
if item.line then
if item.positions then
local offset = Snacks.picker.highlight.offset(ret)
Snacks.picker.highlight.matches(ret, item.positions, offset)
end
Snacks.picker.highlight.format(item, item.line, ret)
table.insert(ret, { " " })
end
return ret
end

H.item_picker = function(items, opts, cb)
opts = opts or {}
local picker_opts = vim.tbl_deep_extend("force", {
Expand Down
Loading