Skip to content

Commit fec502c

Browse files
author
Chris Pecunies
committed
adding linkify to words, adding down find
1 parent 3079a12 commit fec502c

File tree

17 files changed

+1381
-134
lines changed

17 files changed

+1381
-134
lines changed

README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,24 @@ the _familiar_, organized future for neovim and beyond!
9191

9292
---
9393

94+
<details>
95+
<summary>
96+
Builtin
97+
</summary>
98+
99+
```lua
100+
101+
-- Add to your `init.lua`
102+
vim.pack.add({
103+
{ src = "https://github.com/clpi/down.nvim" }
104+
})
105+
106+
```
107+
108+
</details>
109+
110+
---
111+
94112
<details>
95113

96114
<summary>

lua/down/mod/find/init.lua

Lines changed: 56 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -53,31 +53,32 @@ Find.commands = {
5353
name = "find",
5454
enabled = true,
5555
callback = function(e)
56+
-- Default: find files in current workspace
5657
Find.picker("file")()
5758
end,
5859
commands = {
59-
tags = {
60+
tag = {
6061
callback = function(e)
61-
Find.picker("tag")()
62+
Find.picker("tag")({ scope = "workspace" })
6263
end,
63-
enabled = false,
64-
name = "find.links",
64+
enabled = true,
65+
name = "find.tag",
6566
args = 0,
6667
},
67-
project = {
68+
tags = {
6869
callback = function(e)
69-
Find.picker("project")()
70+
Find.picker("tag")({ scope = "workspace" })
7071
end,
71-
enabled = false,
72-
name = "find.links",
72+
enabled = true,
73+
name = "find.tags",
7374
args = 0,
7475
},
75-
notes = {
76+
link = {
7677
callback = function(e)
77-
Find.picker("note")()
78+
Find.picker("link")()
7879
end,
79-
enabled = false,
80-
name = "find.links",
80+
enabled = true,
81+
name = "find.link",
8182
args = 0,
8283
},
8384
links = {
@@ -88,12 +89,28 @@ Find.commands = {
8889
name = "find.links",
8990
args = 0,
9091
},
92+
task = {
93+
callback = function(e)
94+
Find.picker("task")({ scope = "workspace" })
95+
end,
96+
enabled = true,
97+
name = "find.task",
98+
args = 0,
99+
},
91100
tasks = {
92101
callback = function(e)
93-
Find.picker("task")()
102+
Find.picker("task")({ scope = "workspace" })
94103
end,
95104
enabled = true,
96-
name = "find.tags",
105+
name = "find.tasks",
106+
args = 0,
107+
},
108+
file = {
109+
callback = function(e)
110+
Find.picker("file")()
111+
end,
112+
enabled = true,
113+
name = "find.file",
97114
args = 0,
98115
},
99116
files = {
@@ -106,12 +123,37 @@ Find.commands = {
106123
},
107124
workspace = {
108125
callback = function(e)
126+
-- Workspace switcher
109127
Find.picker("workspace")()
110128
end,
111129
enabled = true,
112130
name = "find.workspace",
113131
args = 0,
114132
},
133+
note = {
134+
callback = function(e)
135+
Find.picker("note")()
136+
end,
137+
enabled = true,
138+
name = "find.note",
139+
args = 0,
140+
},
141+
notes = {
142+
callback = function(e)
143+
Find.picker("note")()
144+
end,
145+
enabled = true,
146+
name = "find.notes",
147+
args = 0,
148+
},
149+
template = {
150+
callback = function(e)
151+
Find.picker("template")()
152+
end,
153+
enabled = true,
154+
name = "find.template",
155+
args = 0,
156+
},
115157
},
116158
},
117159
}

lua/down/mod/find/snacks/init.lua

Lines changed: 57 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,71 @@
11
--- Snacks find module
22
---
3-
--- @class down.mod.find.fzflua.Snacks: down.Mod
4-
local Snacks = require("down.mod").new("find.mini")
3+
--- @class down.mod.find.snacks.Snacks: down.Mod
4+
local Snacks = require("down.mod").new("find.snacks")
55

6-
--- @class down.mod.find.builtin.Config: down.mod.Config
6+
--- @class down.mod.find.snacks.Config: down.mod.Config
77
Snacks.config = {}
88

99
Snacks.setup = function()
1010
return {
1111
dependencies = {},
12-
loaded = true,
12+
loaded = pcall(require, "snacks"),
1313
}
1414
end
1515

1616
Snacks.load = function() end
1717

18+
--- Picker functions for snacks.nvim
19+
Snacks.down = {
20+
link = function(opts)
21+
return require("down.mod.find.snacks.picker.link")(opts)
22+
end,
23+
tag = function(opts)
24+
return require("down.mod.find.snacks.picker.tag")(opts)
25+
end,
26+
workspace = function(opts)
27+
-- Reuse workspace picker if available
28+
local ws_mod = require("down.mod").get_mod("workspace")
29+
if ws_mod then
30+
local workspaces = ws_mod.get_workspaces()
31+
local items = {}
32+
for name, path in pairs(workspaces) do
33+
table.insert(items, {
34+
text = name .. " - " .. path,
35+
name = name,
36+
path = path,
37+
})
38+
end
39+
40+
local snacks = require("snacks")
41+
snacks.picker({
42+
source = items,
43+
prompt = "Workspaces",
44+
format = function(item)
45+
return item.text
46+
end,
47+
confirm = function(item)
48+
if item then
49+
ws_mod.set_workspace(item.name)
50+
end
51+
end,
52+
})
53+
end
54+
end,
55+
file = function(opts)
56+
local snacks = require("snacks")
57+
snacks.picker.files(opts)
58+
end,
59+
note = function(opts)
60+
local snacks = require("snacks")
61+
snacks.picker.files(opts)
62+
end,
63+
task = function(opts)
64+
-- TODO: implement task picker
65+
vim.notify("Task picker not yet implemented for snacks", vim.log.levels.WARN)
66+
end,
67+
}
68+
69+
Snacks.picker = Snacks.down
70+
1871
return Snacks
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
local function parse_links(bufnr)
2+
local links = {}
3+
local lines = vim.api.nvim_buf_get_lines(bufnr or 0, 0, -1, false)
4+
5+
for lnum, line in ipairs(lines) do
6+
-- Parse wikilinks [[link]]
7+
for link in line:gmatch("%[%[([^%]]+)%]%]") do
8+
table.insert(links, {
9+
text = link,
10+
line = lnum,
11+
col = line:find("%[%[" .. vim.pesc(link)),
12+
type = "wikilink",
13+
preview = line,
14+
})
15+
end
16+
17+
-- Parse markdown links [text](link)
18+
for text, link in line:gmatch("%[([^%]]+)%]%(([^%)]+)%)") do
19+
table.insert(links, {
20+
text = link,
21+
display_text = text,
22+
line = lnum,
23+
col = line:find("%[" .. vim.pesc(text)),
24+
type = "markdown",
25+
preview = line,
26+
})
27+
end
28+
29+
-- Parse autolinks <link>
30+
for link in line:gmatch("<([^>]+)>") do
31+
table.insert(links, {
32+
text = link,
33+
line = lnum,
34+
col = line:find("<" .. vim.pesc(link)),
35+
type = "autolink",
36+
preview = line,
37+
})
38+
end
39+
end
40+
41+
return links
42+
end
43+
44+
local function markdown_links_picker(opts)
45+
local snacks = require("snacks")
46+
opts = opts or {}
47+
local bufnr = vim.api.nvim_get_current_buf()
48+
local links = parse_links(bufnr)
49+
50+
if #links == 0 then
51+
vim.notify("No links found in current buffer", vim.log.levels.INFO)
52+
return
53+
end
54+
55+
local items = {}
56+
for _, link in ipairs(links) do
57+
table.insert(items, {
58+
text = string.format("[%s] %s:%s - %s", link.type, link.line, link.col, link.text),
59+
line = link.line,
60+
col = link.col,
61+
file = vim.api.nvim_buf_get_name(bufnr),
62+
})
63+
end
64+
65+
snacks.picker({
66+
source = items,
67+
prompt = "Links in Buffer",
68+
format = function(item)
69+
return item.text
70+
end,
71+
confirm = function(item)
72+
if item then
73+
vim.api.nvim_win_set_cursor(0, { item.line, item.col - 1 })
74+
vim.cmd("normal! zz")
75+
end
76+
end,
77+
})
78+
end
79+
80+
return markdown_links_picker
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
local function parse_tags(bufnr)
2+
local tags = {}
3+
local lines = vim.api.nvim_buf_get_lines(bufnr or 0, 0, -1, false)
4+
local filepath = vim.api.nvim_buf_get_name(bufnr or 0)
5+
6+
for lnum, line in ipairs(lines) do
7+
-- Parse tags: #tag or #tag-with-dashes
8+
for tag in line:gmatch("#([%w%-_]+)") do
9+
table.insert(tags, {
10+
tag = "#" .. tag,
11+
line = lnum,
12+
col = line:find("#" .. vim.pesc(tag)),
13+
text = line:match("^%s*(.-)%s*$"), -- trim whitespace
14+
file = filepath,
15+
})
16+
end
17+
end
18+
19+
return tags
20+
end
21+
22+
local function parse_workspace_tags()
23+
local mod = require("down.mod")
24+
local workspace_mod = mod.get_mod("workspace")
25+
26+
if not workspace_mod then
27+
return {}
28+
end
29+
30+
local ws_path = workspace_mod.get_current_workspace_path()
31+
if not ws_path then
32+
return {}
33+
end
34+
35+
-- Find all markdown files in workspace
36+
local files = vim.fn.globpath(ws_path, "**/*.md", false, true)
37+
local all_tags = {}
38+
39+
for _, file in ipairs(files) do
40+
local bufnr = vim.fn.bufadd(file)
41+
vim.fn.bufload(bufnr)
42+
local tags = parse_tags(bufnr)
43+
44+
for _, tag_info in ipairs(tags) do
45+
table.insert(all_tags, tag_info)
46+
end
47+
end
48+
49+
return all_tags
50+
end
51+
52+
local function tag_picker(opts)
53+
local snacks = require("snacks")
54+
opts = opts or {}
55+
opts.scope = opts.scope or "buffer" -- buffer, workspace, or all
56+
57+
local tags = {}
58+
59+
if opts.scope == "buffer" then
60+
tags = parse_tags(0)
61+
elseif opts.scope == "workspace" then
62+
tags = parse_workspace_tags()
63+
end
64+
65+
if #tags == 0 then
66+
vim.notify("No tags found", vim.log.levels.INFO)
67+
return
68+
end
69+
70+
local items = {}
71+
for _, tag in ipairs(tags) do
72+
local filename = vim.fn.fnamemodify(tag.file, ":t")
73+
table.insert(items, {
74+
text = string.format("%s %s:%s - %s", tag.tag, tag.line, tag.col, filename),
75+
line = tag.line,
76+
col = tag.col,
77+
file = tag.file,
78+
tag = tag.tag,
79+
})
80+
end
81+
82+
snacks.picker({
83+
source = items,
84+
prompt = "Tags (" .. opts.scope .. ")",
85+
format = function(item)
86+
return item.text
87+
end,
88+
confirm = function(item)
89+
if item then
90+
vim.cmd("edit " .. item.file)
91+
vim.api.nvim_win_set_cursor(0, { item.line, item.col - 1 })
92+
vim.cmd("normal! zz")
93+
end
94+
end,
95+
})
96+
end
97+
98+
return tag_picker

0 commit comments

Comments
 (0)