Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP][RFC] Hyperlink Refactor #790

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 18 additions & 3 deletions lua/orgmode/api/file.lua
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
---@diagnostic disable: invisible
local OrgHeadline = require('orgmode.api.headline')
local Hyperlinks = require('orgmode.org.hyperlinks')
local org = require('orgmode')

---@class OrgApiFile
Expand Down Expand Up @@ -94,6 +93,22 @@ function OrgFile:get_closest_headline(cursor)
return nil
end

---@param file OrgFile
---@param path? string
local function get_link_to_file(file, path)
local title = file:get_title()

if config.org_id_link_to_org_use_id then
local id = file:id_get_or_create()
if id then
return ('id:%s::*%s'):format(id, title)
end
end

path = path or file.filename
return ('file:%s::*%s'):format(path, title)
end

--- Get a link destination as string
---
--- Depending if org_id_link_to_org_use_id is set the format is
Expand All @@ -112,12 +127,12 @@ function OrgFile:get_link()
-- do remote edit
return org.files
:update_file(filename, function(file)
return Hyperlinks.get_link_to_file(file)
return get_link_to_file(file)
end)
:wait()
end

return Hyperlinks.get_link_to_file(self._file)
return get_link_to_file(self._file)
end

return OrgFile
22 changes: 19 additions & 3 deletions lua/orgmode/api/headline.lua
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
local utils = require('orgmode.utils')
local OrgPosition = require('orgmode.api.position')
local config = require('orgmode.config')
local PriorityState = require('orgmode.objects.priority_state')
local Date = require('orgmode.objects.date')
local Calendar = require('orgmode.objects.calendar')
local Promise = require('orgmode.utils.promise')
local Hyperlinks = require('orgmode.org.hyperlinks')
local org = require('orgmode')

---@class OrgApiHeadline
Expand Down Expand Up @@ -263,6 +263,22 @@ function OrgHeadline:_do_action(action)
end)
end

---@param headline OrgHeadline
---@param path? string
local function get_link_to_headline(headline, path)
local title = headline:get_title()

if config.org_id_link_to_org_use_id then
local id = headline:id_get_or_create()
if id then
return ('id:%s::*%s'):format(id, title)
end
end

path = path or utils.current_file_path()
return ('file:%s::*%s'):format(path, title)
end

--- Get a link destination as string
---
--- Depending if org_id_link_to_org_use_id is set the format is
Expand All @@ -281,12 +297,12 @@ function OrgHeadline:get_link()
-- do remote edit
return org.files
:update_file(filename, function(_)
return Hyperlinks.get_link_to_headline(self._section)
return get_link_to_headline(self._section)
end)
:wait()
end

return Hyperlinks.get_link_to_headline(self._section)
return get_link_to_headline(self._section)
end

return OrgHeadline
17 changes: 15 additions & 2 deletions lua/orgmode/api/init.lua
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
---@diagnostic disable: invisible
local OrgFile = require('orgmode.api.file')
local OrgHeadline = require('orgmode.api.headline')
local Hyperlinks = require('orgmode.org.hyperlinks')
local Link = require('orgmode.org.hyperlinks.link')
local HyperLink = require('orgmode.org.hyperlinks')
local orgmode = require('orgmode')

---@class OrgApiRefileOpts
Expand Down Expand Up @@ -110,7 +111,19 @@ end
--- @param link_location string
--- @return boolean
function OrgApi.insert_link(link_location)
Hyperlinks.insert_link(link_location)
local link = Link.parse(link_location)
if not link then
return false
end

local desc = nil
if link.target and link.target.headline then
desc = link.target.headline
end

HyperLink.insert_link(HyperLink:new(link, desc))

return true
end

return OrgApi
3 changes: 3 additions & 0 deletions lua/orgmode/config/defaults.lua
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
local default_hyperlinks = require('orgmode.org.hyperlinks.builtin')

---@class OrgDefaultConfig
---@field org_id_method 'uuid' | 'ts' | 'org'
---@field org_agenda_span 'day' | 'week' | 'month' | 'year' | number
Expand Down Expand Up @@ -208,6 +210,7 @@ local DefaultConfig = {
handler = nil,
},
},
hyperlinks = default_hyperlinks,
}

return DefaultConfig
78 changes: 77 additions & 1 deletion lua/orgmode/config/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ local defaults = require('orgmode.config.defaults')
local mappings = require('orgmode.config.mappings')
local TodoKeywords = require('orgmode.objects.todo_keywords')
local PriorityState = require('orgmode.objects.priority_state')
local Alias = require('orgmode.org.hyperlinks.builtin.alias')
local Link = require('orgmode.org.hyperlinks.link')

---@class OrgConfig:OrgDefaultConfig
---@field opts table
Expand Down Expand Up @@ -53,13 +55,87 @@ function Config:extend(opts)
opts.org_priority_lowest = self.opts.org_priority_lowest
opts.org_priority_default = self.opts.org_priority_default
end
opts.hyperlinks = self:_process_links(opts.hyperlinks)
self.opts = vim.tbl_deep_extend('force', self.opts, opts)
if self.org_startup_indented then
self.org_adapt_indentation = not self.org_indent_mode_turns_off_org_adapt_indentation
end
return self
end

function Config:_process_links(links)
if not (links or type(links) == table) then
return nil
end

local processed = {}

for protocol, hyperlink in pairs(links) do
if type(hyperlink) == 'string' then
if not protocol then
utils.echo_warning(('A link alias must have a protocol key. Skipped %s'):format(hyperlink))
else
hyperlink = self:_process_link_alias(protocol, hyperlink)
processed[hyperlink.protocol] = hyperlink
end
goto continue
end

if type(hyperlink) == 'table' then
hyperlink = self:_process_link_table(protocol, hyperlink)
if hyperlink then
processed[hyperlink.protocol] = hyperlink
end
end

::continue::
end

return processed
end

function Config:_process_link_table(protocol, link)
if not link.parse or not (type(link.parse) == 'function') then
utils.echo_warning("A link must have a 'parse' function.")
return
end

if not link.protocol then
if not protocol then
utils.echo_warning('A link must have a protocol.')
return
end
link.protocol = protocol
end

-- Inherit basics from Link class
for k, v in pairs(Link) do
if not link[k] then
link[k] = v
end
end

return link
end

function Config:_process_link_alias(protocol, alias)
local components = {}
local expression = vim.regex([[%s\|%h\|%(.-)]])
repeat
local start_special, end_special = expression:match_str(alias)
if not start_special then
table.insert(components, alias)
break
end

table.insert(components, alias:sub(0, start_special))
table.insert(components, alias:sub(start_special + 1, end_special))
alias = alias:sub(end_special + 1)
until #alias <= 0

return Alias(protocol, components)
end

function Config:_are_priorities_valid(opts)
local high = opts.org_priority_highest
local low = opts.org_priority_lowest
Expand Down Expand Up @@ -97,7 +173,7 @@ function Config:_are_priorities_valid(opts)
)
return false
end
-- one-char strings
-- one-char strings
elseif
(type(high) == 'string' and #high == 1)
and (type(low) == 'string' and #low == 1)
Expand Down
6 changes: 3 additions & 3 deletions lua/orgmode/files/file.lua
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ local Headline = require('orgmode.files.headline')
local ts = vim.treesitter
local config = require('orgmode.config')
local Block = require('orgmode.files.elements.block')
local Link = require('orgmode.org.hyperlinks.link')
local HyperLink = require('orgmode.org.hyperlinks')
local Range = require('orgmode.files.elements.range')
local Memoize = require('orgmode.utils.memoize')

Expand Down Expand Up @@ -715,7 +715,7 @@ function OrgFile:get_archive_file_location()
end

memoize('get_links')
---@return OrgLink[]
---@return OrgHyperLink[]
function OrgFile:get_links()
self:parse(true)
local ts_query = ts_utils.get_query([[
Expand All @@ -729,7 +729,7 @@ function OrgFile:get_links()
for _, match in ts_query:iter_captures(self.root, self:_get_source()) do
local line = match:start()
if not processed_lines[line] then
vim.list_extend(links, Link.all_from_line(self.lines[line + 1], line + 1))
vim.list_extend(links, HyperLink.all_from_line(self.lines[line + 1], line + 1))
processed_lines[line] = true
end
end
Expand Down
10 changes: 10 additions & 0 deletions lua/orgmode/files/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,16 @@ function OrgFiles:find_files_with_property(property_name, term)
return files
end

---@param property_name string
---@param term string
---@return OrgHeadline[]
function OrgFiles:find_files_with_property_matching(property_name, term)
return vim.tbl_filter(function(item)
local property = item:get_property(property_name)
return property and property:lower():match('^' .. vim.pesc(term:lower()))
end, self:all())
end

---@param term string
---@param no_escape boolean
---@param search_extra_files boolean
Expand Down
15 changes: 9 additions & 6 deletions lua/orgmode/org/autocompletion/sources/hyperlinks.lua
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
local Hyperlinks = require('orgmode.org.hyperlinks')
local Link = require('orgmode.org.hyperlinks.link')
local HyperLink = require('orgmode.org.hyperlinks')

---@class OrgCompletionHyperlinks:OrgCompletionSource
---@field completion OrgCompletion
---@field private pattern vim.regex
local OrgCompletionHyperlinks = {}
local OrgCompletionHyperlinks = {
stored_links = {},
}
OrgCompletionHyperlinks.__index = OrgCompletionHyperlinks

---@param opts { completion: OrgCompletion }
Expand All @@ -26,9 +28,10 @@ end

---@return string[]
function OrgCompletionHyperlinks:get_results(context)
local link = Link:new(context.base)
local result, mapper = Hyperlinks.find_matching_links(link.url)
return mapper(result)
local hyperlinks = HyperLink:autocompletions(context.base)
return vim.tbl_map(function(hyperlink)
return hyperlink.link:__tostring()
end, hyperlinks)
end

return OrgCompletionHyperlinks
40 changes: 40 additions & 0 deletions lua/orgmode/org/hyperlinks/builtin/alias.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
local Link = require('orgmode.org.hyperlinks.link')

return function(protocol, components)
for _, component in pairs(components) do
if not (type(component) == 'string') then
return nil
end
end

---@class OrgLinkAlias:OrgLink
local Alias = Link:new(protocol)
Alias.components = components

---@param input string
function Alias.parse(input)
local processed_components = {}
for _, component in pairs(Alias.components) do
---@cast component string
if component == '%s' then
table.insert(processed_components, input)
goto continue
end
if component == '%h' then
table.insert(processed_components, vim.uri_encode(input))
goto continue
end
if component:find('^%%%b()$') then
local func = component:sub(3, -2)
table.insert(processed_components, vim.fn.luaeval(('%s(_A)'):format(func), input))
goto continue
end
table.insert(processed_components, component)
::continue::
end

return Link.parse(table.concat(processed_components))
end

return Alias
end
Loading
Loading