summaryrefslogtreecommitdiffstats
path: root/.config/nvim/plugin/50-goto.lua
blob: 8c72c15842d77cd4ab1869d31432e84a1a0c3ce0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
--
-- 50-goto.lua
--

local follow = require("dotfiles.goto")

-- Add `'` to the list of characters included in `<cfile>` because `'` is a valid URI character
vim.opt.isfname:append("'")

vim.keymap.set({ "n", "x" }, "<Leader>gg", function()
	follow.follow("edit")
end, { desc = "Edit URL/file in current window" })
vim.keymap.set({ "n", "x" }, "<Leader>gs", function()
	follow.follow("split")
end, { desc = "Edit URL/file in split window" })
vim.keymap.set({ "n", "x" }, "<Leader>gv", function()
	follow.follow("vsplit")
end, { desc = "Edit URL/file in vertically split window" })

------------------------------------------------------------------------------------------------------------------------
-- Following notes (`notes://` scheme and `[[wiki-links]]`), wired into the follow engine
------------------------------------------------------------------------------------------------------------------------

-- `notes://<path>` resolves to `<path>` relative to the notes dir.
follow.register_scheme("notes", {
	resolve = function(uri)
		return notes_dir .. "/" .. uri
	end,
})

-- Returns the inner text of a `[[wiki-link]]` under the cursor, or `nil`.
local function get_wikilink_target()
	local line = vim.api.nvim_get_current_line()
	local col = vim.api.nvim_win_get_cursor(0)[2] + 1
	local init = 1
	while true do
		local s, e, inner = line:find("%[%[(.-)%]%]", init)
		if s == nil then
			return nil
		end
		if col >= s and col <= e then
			return inner
		end
		init = e + 1
	end
end

-- Follow a `[[note]]` / `[[note#Heading]]` / `[[dir/]]` wiki-link relative to the
-- notes dir, creating parent directories (and the note itself on save) as needed.
local function follow_wikilink(inner, edit_cmd)
	local name, heading = inner:match("^(.-)#(.*)$")
	if name == nil then
		name = inner
	end
	local target = notes_dir .. "/" .. name
	if name:sub(-1) == "/" then -- directory link: create and open it
		vim.fn.mkdir(target, "p")
		vim.cmd(edit_cmd .. " " .. vim.fn.fnameescape(target))
		return
	end
	if not name:match("%.%w+$") then -- default to a Markdown note
		target = target .. ".md"
	end
	vim.fn.mkdir(vim.fs.dirname(target), "p")
	vim.cmd(edit_cmd .. " " .. vim.fn.fnameescape(target))
	if heading ~= nil and heading ~= "" then
		vim.fn.cursor(1, 1)
		-- `\V` matches the heading literally; `\v` brackets the heading markers.
		local pat = [[\v^#+\s+\V]] .. vim.fn.escape(heading, [[\]]) .. [[\v\s*$]]
		if vim.fn.search(pat, "cW") == 0 then
			vim.notify("No heading '" .. heading .. "' in " .. name, vim.log.levels.WARN)
		end
	end
end

-- Wiki-links take precedence over generic URL/file following, but only on a
-- `[[...]]` under the cursor in a markdown buffer in normal mode.
follow.register_handler(function(edit_cmd)
	if vim.fn.mode() ~= "n" or vim.bo.filetype ~= "markdown" then
		return false
	end
	local inner = get_wikilink_target()
	if inner == nil then
		return false
	end
	follow_wikilink(inner, edit_cmd)
	return true
end)

-- `nvim-help://<tag>` — open Neovim help for <tag> in a scratch, read-only
-- buffer. Registered with the follow engine (see 50-follow.lua).

require("dotfiles.goto").register_scheme("nvim-help", {
	resolve = function(uri)
		local tagfiles = {}
		for _, path in pairs(vim.opt.runtimepath:get()) do
			tagfiles[#tagfiles + 1] = path .. "/doc/tags"
		end
		vim.opt_local.tags = tagfiles
		local matches = vim.fn.taglist(uri)
		if #matches == 0 then
			return nil, "No help page found for nvim-help://" .. uri
		end
		return matches[1].filename
	end,
	after = function()
		vim.opt_local.bufhidden = "wipe"
		vim.opt_local.buftype = "nofile"
		vim.opt_local.swapfile = false
		vim.opt_local.readonly = true
	end,
})