@@ -2,7 +2,6 @@ local Files = require('orgmode.parser.files')
2
2
local config = require (' orgmode.config' )
3
3
local Hyperlinks = require (' orgmode.org.hyperlinks' )
4
4
local Url = require (' orgmode.objects.url' )
5
- local Link = require (' orgmode.objects.link' )
6
5
7
6
local data = {
8
7
directives = { ' #+title' , ' #+author' , ' #+email' , ' #+name' , ' #+filetags' , ' #+archive' , ' #+options' , ' #+category' },
@@ -33,8 +32,8 @@ local properties = {
33
32
}
34
33
35
34
local links = {
36
- line_rgx = vim .regex ([[ \(\(^\|\s\+\)\[\[\)\@<=\(\*\|\ #\|file:\)\?\(\(\w\|\/\|\.\|\\\|-\|_\|\d \)\+\)\?]] ),
37
- rgx = vim .regex ([[ \(\*\|\ #\|file:\)\?\(\(\w\|\/\|\.\|\\\|-\|_\|\d \)\+\)\?$]] ),
35
+ line_rgx = vim .regex ([[ \(\(^\|\s\+\)\[\[\)\@<=\(\*\|#\|file:\)\?\(\(\w\|\/\|\.\|\\\|-\)\+\)\?]] ),
36
+ rgx = vim .regex ([[ \(\*\|#\|file:\)\?\(\(\w\|\/\|\.\|\\\|-\)\+\)\?$]] ),
38
37
fetcher = function (url )
39
38
local hyperlinks , mapper = Hyperlinks .find_matching_links (url )
40
39
return mapper (hyperlinks )
@@ -91,54 +90,84 @@ local headline_contexts = {
91
90
todo_keywords ,
92
91
}
93
92
93
+ local Omni = {}
94
+
95
+ --- @return string : the line before the current cursor position
96
+ function Omni .get_line_content_before_cursor ()
97
+ return vim .api .nvim_get_current_line ():sub (1 , vim .api .nvim_call_function (' col' , { ' .' }) - 1 )
98
+ end
99
+
100
+ function Omni .is_headline ()
101
+ return Omni .get_line_content_before_cursor ():match (' ^%*+%s+' )
102
+ end
103
+
104
+ --- @return Table
105
+ function Omni .get_all_contexts ()
106
+ return Omni .is_headline () and headline_contexts or contexts
107
+ end
108
+
94
109
--- Determines an URL for link handling. Handles a couple of corner-cases
95
110
--- @param base string The string to complete
96
111
--- @return string
97
- local function get_url_str (line , base )
112
+ function Omni . get_url_str (line , base )
98
113
local line_base = line :match (' %[%[(.-)$' ) or line
99
114
line_base = line_base :gsub (base .. ' $' , ' ' )
100
115
return (line_base or ' ' ) .. (base or ' ' )
101
116
end
102
117
103
- --- This function is registered to omnicompletion in ftplugin/org.vim.
104
- ---
105
- --- If the user want to use it in his completion plugin (like cmp) he has to do
106
- --- that in the configuration of that plugin.
107
- --- @return table
108
- local function omni (findstart , base )
109
- local line = vim .api .nvim_get_current_line ():sub (1 , vim .api .nvim_call_function (' col' , { ' .' }) - 1 )
110
- local is_headline = line :match (' ^%*+%s+' )
111
- local ctx = is_headline and headline_contexts or contexts
112
- if findstart == 1 then
113
- for _ , context in ipairs (ctx ) do
114
- local word = context .rgx :match_str (line )
115
- if word and (not context .extra_cond or context .extra_cond (line , base )) then
116
- return word
117
- end
118
+ --- Is true and only true, if all given regex in the context match appropriatly
119
+ --- line_rgx and extra_cond are optional, but if the context defines them, they must match.
120
+ --- The basic rgx must always match the base, because it is used to determine the start position for
121
+ --- the completion.
122
+ --- @param context table : the context candidate
123
+ --- @param line string : characters left to the cursor
124
+ --- @param base string : characters after the trigger (filter )
125
+ function Omni .all_ctx_conditions_apply (context , line , base )
126
+ return (not context .line_rgx or context .line_rgx :match_str (line ))
127
+ and context .rgx :match_str (base )
128
+ and (not context .extra_cond or context .extra_cond (line , base ))
129
+ end
130
+
131
+ --- @param base ? string
132
+ --- @return number
133
+ function Omni .find_start (base )
134
+ local line = Omni .get_line_content_before_cursor ()
135
+ for _ , context in ipairs (Omni .get_all_contexts ()) do
136
+ local word = context .rgx :match_str (line )
137
+ if word and (not context .extra_cond or context .extra_cond (line , base )) then
138
+ return word
118
139
end
119
- return - 1
120
140
end
141
+ return - 1
142
+ end
121
143
122
- local url = Url .new (get_url_str (line , base ))
123
- local results = {}
144
+ --- @param base string
145
+ --- @return table
146
+ function Omni .get_completions (base )
147
+ -- Workaround for the corner case of matching custom_ids to file paths without file: prefix
148
+ -- Bug is probably in the regex, but hard to fix, because the regex is so hard to read
149
+ base = base :match (' ^:#' ) and base :gsub (' ^:' , ' ' ) or base
124
150
125
- for _ , context in ipairs (ctx ) do
126
- if
127
- (not context .line_rgx or context .line_rgx :match_str (line ))
128
- and context .rgx :match_str (base )
129
- and (not context .extra_cond or context .extra_cond (line , base ))
130
- then
151
+ local line = Omni .get_line_content_before_cursor ()
152
+ local url = Url .new (Omni .get_url_str (line , base ))
153
+ local results = {}
154
+ for _ , context in ipairs (Omni .get_all_contexts ()) do
155
+ if Omni .all_ctx_conditions_apply (context , line , base ) then
131
156
local items = {}
157
+
158
+ -- fetch or just take context specific completion candidates
132
159
if context .fetcher then
133
160
items = context .fetcher (url )
134
161
else
135
162
items = { unpack (context .list ) }
136
163
end
137
164
165
+ -- incrementally limit candidates to what the user has already been typed
138
166
items = vim .tbl_filter (function (i )
139
167
return i :find (' ^' .. vim .pesc (base ))
140
168
end , items )
141
169
170
+ -- craft the actual completion entries and append them to the overall results
142
171
for _ , item in ipairs (items ) do
143
172
table.insert (results , { word = item , menu = ' [Org]' })
144
173
end
@@ -148,4 +177,8 @@ local function omni(findstart, base)
148
177
return results
149
178
end
150
179
151
- return omni
180
+ function Omni .omnifunc (findstart , base )
181
+ return findstart == 1 and Omni .find_start (base ) or Omni .get_completions (base )
182
+ end
183
+
184
+ return Omni
0 commit comments