Skip to content

Commit b409b4d

Browse files
feat(autocomplete): Fuzzy match autocompletion
1 parent 2b91d9a commit b409b4d

File tree

11 files changed

+81
-40
lines changed

11 files changed

+81
-40
lines changed

lua/orgmode/org/autocompletion/_meta.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---@meta
22

3-
---@alias OrgCompletionContext { line: string, base?: string }
3+
---@alias OrgCompletionContext { line: string, base?: string, fuzzy?: boolean, matcher?: fun(value?: string, pattern?: string): boolean }
44
---@alias OrgCompletionItem { word: string, menu: string }
55

66
---@class OrgCompletionSource

lua/orgmode/org/autocompletion/blink.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ function Source:get_completions(ctx, callback)
2121
local results = org.completion:complete({
2222
line = line,
2323
base = base,
24+
fuzzy = true
2425
})
2526

2627
local cb = function(items)

lua/orgmode/org/autocompletion/cmp.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ function Source:complete(params, callback)
3030
local results = org.completion:complete({
3131
line = params.context.cursor_before_line,
3232
base = base,
33+
fuzzy = true
3334
})
3435
local items = {}
3536
for _, item in ipairs(results) do

lua/orgmode/org/autocompletion/init.lua

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,19 @@ end
4444
---@return OrgCompletionItem
4545
function OrgCompletion:complete(context)
4646
local results = {}
47+
context.base = context.base or ''
48+
if not context.matcher then
49+
context.matcher = function(value, pattern)
50+
pattern = pattern or ''
51+
if pattern == '' then
52+
return true
53+
end
54+
if context.fuzzy then
55+
return #vim.fn.matchfuzzy({ value }, pattern) > 0
56+
end
57+
return value:find('^' .. vim.pesc(pattern)) ~= nil
58+
end
59+
end
4760
for _, source in ipairs(self.sources) do
4861
if source:get_start(context) then
4962
vim.list_extend(results, self:_get_valid_results(source:get_results(context), context))
@@ -53,12 +66,13 @@ function OrgCompletion:complete(context)
5366
return results
5467
end
5568

69+
---@param results string[]
70+
---@param context OrgCompletionContext
71+
---@return OrgCompletionItem[]
5672
function OrgCompletion:_get_valid_results(results, context)
57-
local base = context.base or ''
58-
5973
local valid_results = {}
6074
for _, item in ipairs(results) do
61-
if base == '' or item:find('^' .. vim.pesc(base)) then
75+
if context.matcher(item, context.base) then
6276
table.insert(valid_results, {
6377
word = item,
6478
menu = self.menu,
@@ -89,6 +103,9 @@ function OrgCompletion:omnifunc(findstart, base)
89103

90104
self._context = self._context or { line = self:get_line() }
91105
self._context.base = base
106+
if vim.tbl_contains(vim.opt_local.completeopt:get(), 'fuzzy') then
107+
self._context.fuzzy = true
108+
end
92109
return self:complete(self._context)
93110
end
94111

lua/orgmode/org/autocompletion/sources/hyperlinks.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ end
2525
---@param context OrgCompletionContext
2626
---@return string[]
2727
function OrgCompletionHyperlinks:get_results(context)
28-
return self.completion.links:autocomplete(context.base)
28+
return self.completion.links:autocomplete(context)
2929
end
3030

3131
return OrgCompletionHyperlinks

lua/orgmode/org/links/_meta.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@
33
---@class OrgLinkType
44
---@field get_name fun(self: OrgLinkType): string
55
---@field follow fun(self: OrgLinkType, link: string): boolean
6-
---@field autocomplete fun(self: OrgLinkType, link: string): string[]
6+
---@field autocomplete fun(self: OrgLinkType, context: OrgCompletionContext): string[]

lua/orgmode/org/links/init.lua

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -56,22 +56,20 @@ function OrgLinks:follow(link)
5656
return self.headline_search:follow(link)
5757
end
5858

59-
---@param link string
59+
---@param context OrgCompletionContext
6060
---@return string[]
61-
function OrgLinks:autocomplete(link)
62-
local pattern = '^' .. vim.pesc(link:lower())
63-
61+
function OrgLinks:autocomplete(context)
6462
local items = vim.tbl_filter(function(stored_link)
65-
return stored_link:lower():match(pattern)
63+
return context.matcher(stored_link, context.base)
6664
end, vim.tbl_keys(self.stored_links))
6765

6866
for _, source in ipairs(self.types) do
6967
if source.autocomplete then
70-
utils.concat(items, source:autocomplete(link))
68+
utils.concat(items, source:autocomplete(context))
7169
end
7270
end
7371

74-
utils.concat(items, self.headline_search:autocomplete(link))
72+
utils.concat(items, self.headline_search:autocomplete(context))
7573
return items
7674
end
7775

lua/orgmode/org/links/types/custom_id.lua

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,10 @@ function OrgLinkCustomId:follow(link)
4242
return link_utils.open_file_and_search(opts.file_path, opts.custom_id)
4343
end
4444

45-
---@param link string
45+
---@param context OrgCompletionContext
4646
---@return string[]
47-
function OrgLinkCustomId:autocomplete(link)
48-
local opts = self:_parse(link)
47+
function OrgLinkCustomId:autocomplete(context)
48+
local opts = self:_parse(context.base)
4949
if not opts then
5050
return {}
5151
end

lua/orgmode/org/links/types/headline.lua

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,10 @@ function OrgLinkHeadline:follow(link)
3838
return link_utils.open_file_and_search(opts.file_path, opts.headline)
3939
end
4040

41-
---@param link string
41+
---@param context OrgCompletionContext
4242
---@return string[]
43-
function OrgLinkHeadline:autocomplete(link)
44-
local opts = self:_parse(link)
43+
function OrgLinkHeadline:autocomplete(context)
44+
local opts = self:_parse(context.base)
4545
if not opts then
4646
return {}
4747
end
@@ -52,7 +52,9 @@ function OrgLinkHeadline:autocomplete(link)
5252
return {}
5353
end
5454

55-
local headlines = file:find_headlines_by_title(opts.headline)
55+
local headlines = vim.tbl_filter(function(headline)
56+
return context.matcher(headline:get_title(), opts.headline)
57+
end, file:get_headlines())
5658
local prefix = opts.type == 'internal' and '' or opts.link_url:get_path_with_protocol() .. '::'
5759

5860
return vim.tbl_map(function(headline)

lua/orgmode/org/links/types/headline_search.lua

Lines changed: 11 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,10 @@ function OrgLinkHeadlineSearch:follow(link)
5959
return link_utils.open_file_and_search(opts.file_path, search_text)
6060
end
6161

62-
---@param link string
62+
---@param context OrgCompletionContext
6363
---@return string[]
64-
function OrgLinkHeadlineSearch:autocomplete(link)
65-
local opts = self:_parse(link)
64+
function OrgLinkHeadlineSearch:autocomplete(context)
65+
local opts = self:_parse(context.base)
6666
if not opts then
6767
return {}
6868
end
@@ -71,22 +71,9 @@ function OrgLinkHeadlineSearch:autocomplete(link)
7171
local filenames = self.files:filenames()
7272
local valid_filenames = {}
7373
for _, f in ipairs(filenames) do
74-
if f:find('^' .. opts.file_path) then
75-
f = f:gsub('^' .. opts.file_path, opts.link_url.path)
76-
table.insert(valid_filenames, f)
77-
end
78-
79-
local real_path = opts.link_url:get_real_path()
80-
81-
if not real_path then
82-
local substitute_path = fs.substitute_path(opts.file_path)
83-
if substitute_path then
84-
local full_path = vim.fn.fnamemodify(substitute_path, ':p')
85-
if f:find('^' .. full_path) then
86-
f = f:gsub('^' .. full_path, opts.link_url.path)
87-
table.insert(valid_filenames, f)
88-
end
89-
end
74+
local converted_path = fs.convert_path(opts.link_url.path, f)
75+
if context.matcher(converted_path, opts.link_url.path) then
76+
table.insert(valid_filenames, converted_path)
9077
end
9178
end
9279

@@ -108,11 +95,15 @@ function OrgLinkHeadlineSearch:autocomplete(link)
10895
return headline:get_title()
10996
end, file:find_headlines_matching_search_term(pattern, true))
11097

98+
local matching_headlines = vim.tbl_filter(function(headline)
99+
return context.matcher(headline:get_title(), opts.headline_text)
100+
end, file:get_headlines())
101+
111102
utils.concat(
112103
headlines,
113104
vim.tbl_map(function(headline)
114105
return headline:get_title()
115-
end, file:find_headlines_by_title(opts.headline_text)),
106+
end, matching_headlines),
116107
true
117108
)
118109
local prefix = opts.type == 'internal' and '' or opts.link_url:get_path_with_protocol() .. '::'

0 commit comments

Comments
 (0)