Skip to content

Commit c1c4ebb

Browse files
committed
feat: initial try to do anchor links
1 parent 86e6a06 commit c1c4ebb

File tree

1 file changed

+120
-19
lines changed

1 file changed

+120
-19
lines changed

lua/obsidian/lsp/handlers/completion.lua

Lines changed: 120 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -46,31 +46,113 @@ local function calc_ref_item(note, insert_text, insert_start, insert_end, line_n
4646
}
4747
end
4848

49+
local state = {
50+
---@type obsidian.Note
51+
current_note = nil,
52+
}
53+
54+
---Collect matching anchor links.
55+
---@param note obsidian.Note
56+
---@param anchor_link string?
57+
---@return obsidian.note.HeaderAnchor[]?
58+
local function collect_matching_anchors(note, anchor_link)
59+
---@type obsidian.note.HeaderAnchor[]|?
60+
local matching_anchors
61+
if anchor_link then
62+
assert(note.anchor_links)
63+
matching_anchors = {}
64+
for anchor, anchor_data in pairs(note.anchor_links) do
65+
if vim.startswith(anchor, anchor_link) then
66+
table.insert(matching_anchors, anchor_data)
67+
end
68+
end
69+
70+
if #matching_anchors == 0 then
71+
-- Unmatched, create a mock one.
72+
table.insert(matching_anchors, { anchor = anchor_link, header = string.sub(anchor_link, 2), level = 1, line = 1 })
73+
end
74+
end
75+
76+
return matching_anchors
77+
end
78+
79+
---@client obsidian.Client
4980
local function handle_ref(client, partial, ref_start, cursor_col, line_num, handler)
5081
---@type string|?
5182
-- local block_link
5283
-- cc.search, block_link = util.strip_block_links(cc.search)
5384
--
54-
-- ---@type string|?
55-
-- local anchor_link
56-
-- cc.search, anchor_link = util.strip_anchor_links(cc.search)
85+
---@type string|?
86+
local anchor_link
87+
partial, anchor_link = util.strip_anchor_links(partial)
88+
print(partial)
5789

5890
local items = {}
59-
client:find_notes_async(
60-
partial,
61-
vim.schedule_wrap(function(notes)
62-
for _, note in ipairs(notes) do
63-
local title = note.title
64-
local pattern = vim.pesc(lower(partial))
65-
if title and find(lower(title), pattern) then
66-
local link_text = client:format_link(note)
67-
local style = client.opts.preferred_link_style
68-
items[#items + 1] = calc_ref_item(note, link_text, ref_start, cursor_col, line_num, style)
91+
if not anchor_link then
92+
client:find_notes_async(
93+
partial,
94+
vim.schedule_wrap(function(notes)
95+
for _, note in ipairs(notes) do
96+
local title = note.title
97+
local pattern = vim.pesc(lower(partial))
98+
if title and find(lower(title), pattern) then
99+
local link_text = client:format_link(note)
100+
local style = client.opts.preferred_link_style
101+
items[#items + 1] = calc_ref_item(note, link_text, ref_start, cursor_col, line_num, style)
102+
end
103+
handler(nil, { items = items })
69104
end
70-
end
71-
handler(nil, { items = items })
72-
end)
73-
)
105+
end)
106+
)
107+
else
108+
local Note = require "obsidian.note"
109+
-- state.current_note = state.current_note or client:find_notes(partial)[2]
110+
-- TODO: calc current_note once
111+
-- TODO: handle two cases:
112+
-- 1. typing partial note name, no completeed text after cursor, insert the full link
113+
-- 2. jumped to heading, only insert anchor
114+
-- TODO: need to do more textEdit to insert additional #title to path so that app supports?
115+
client:find_notes_async(
116+
partial,
117+
vim.schedule_wrap(function(notes)
118+
for _, note in ipairs(notes) do
119+
local title = note.title
120+
local pattern = vim.pesc(lower(partial))
121+
if title and find(lower(title), pattern) then
122+
local note2 = Note.from_file(note.path.filename, { collect_anchor_links = true })
123+
124+
local note_anchors = collect_matching_anchors(note2, anchor_link)
125+
if not note_anchors then
126+
return
127+
end
128+
for _, anchor in ipairs(note_anchors) do
129+
items[#items + 1] = {
130+
kind = 17,
131+
label = anchor.header,
132+
filterText = anchor.header,
133+
insertText = anchor.header,
134+
-- insertTextFormat = 2, -- is snippet
135+
-- textEdit = {
136+
-- range = {
137+
-- start = { line = line_num, character = insert_start },
138+
-- ["end"] = { line = line_num, character = insert_end },
139+
-- },
140+
-- newText = insert_snippet_marker(insert_text, style),
141+
-- },
142+
labelDetails = { description = "ObsidianAnchor" },
143+
data = {
144+
file = note.path.filename,
145+
kind = "anchor",
146+
},
147+
}
148+
end
149+
end
150+
handler(nil, { items = items })
151+
end
152+
end)
153+
)
154+
vim.print(state.current_note)
155+
end
74156
end
75157

76158
local function calc_tag_item(tag)
@@ -99,6 +181,18 @@ local function handle_tag(client, partial, handler)
99181
)
100182
end
101183

184+
local function handle_heading(client)
185+
-- TODO: client:find_headings_async
186+
-- client:find_
187+
end
188+
189+
-- util.BLOCK_PATTERN = "%^[%w%d][%w%d-]*"
190+
local anchor_trigger_pattern = {
191+
markdown = "%[%S+#(%w*)",
192+
}
193+
194+
local heading_trigger_pattern = "[##"
195+
102196
---@param client obsidian.Client
103197
---@param params table
104198
---@param handler function
@@ -118,10 +212,16 @@ return function(client, params, handler, _)
118212
--
119213
local text_before_cursor = sub(line_text, 1, char_num)
120214

121-
local tag_start = find(text_before_cursor, "#", 1, true)
122215
local ref_start = find(text_before_cursor, ref_trigger_pattern[link_style], 1, true)
216+
local tag_start = find(text_before_cursor, "#", 1, true)
217+
local heading_start = find(text_before_cursor, heading_trigger_pattern, 1, true)
123218

124-
if ref_start then
219+
if heading_start then
220+
local partial = sub(text_before_cursor, heading_start + #heading_trigger_pattern)
221+
-- if #partial >= min_chars then
222+
-- handle_heading(client, partial, ref_start - 1, char_num, line_num, handler)
223+
-- end
224+
elseif ref_start then
125225
local partial = sub(text_before_cursor, ref_start + #ref_trigger_pattern[link_style])
126226
if #partial >= min_chars then
127227
handle_ref(client, partial, ref_start - 1, char_num, line_num, handler)
@@ -131,5 +231,6 @@ return function(client, params, handler, _)
131231
if #partial >= min_chars then
132232
handle_tag(client, partial, handler)
133233
end
234+
else
134235
end
135236
end

0 commit comments

Comments
 (0)