Skip to content

Commit

Permalink
Extend API to get links to headline and org file (#768)
Browse files Browse the repository at this point in the history
* feat(api): Extend API to get links

Considers id links, if org-id-link-to-org-use-id is set.
Creates ids on file and headline level if needed.

* chore: Remove stale require

* chore: tiny refactoring to align similar methods

Adapt the style of get_link_to_headline to new get_link_to_file
- avoid empty declaration of `id`
- move initialization of `path` close to it's usage

* refactor: make get_link member functions

Simplify the code by providing member functions on org.api.file and
org.api.headline.

* fix: handle unloaded buffers

---------

Co-authored-by: Sebastian Flügge <[email protected]>
  • Loading branch information
seflue and seflue committed Jul 1, 2024
1 parent 3fc5c6b commit fb50c10
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 6 deletions.
28 changes: 28 additions & 0 deletions lua/orgmode/api/file.lua
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
---@diagnostic disable: invisible
local OrgHeadline = require('orgmode.api.headline')
local Hyperlinks = require('orgmode.org.hyperlinks')
local org = require('orgmode')

---@class OrgApiFile
---@field category string current file category name. By default it's only filename without extension unless defined differently via #+CATEGORY directive
Expand Down Expand Up @@ -92,4 +94,30 @@ function OrgFile:get_closest_headline(cursor)
return nil
end

--- Get a link destination as string
---
--- Depending if org_id_link_to_org_use_id is set the format is
---
--- id:<uuid>::*title and the id is created if not existing
--- or
--- file:<filepath>::*title
---
--- The result is meant to be used as link_location for OrgApi.insert_link.
--- @return string
function OrgFile:get_link()
local filename = self.filename
local bufnr = vim.fn.bufnr(filename)

if bufnr == -1 or not vim.api.nvim_buf_is_loaded(bufnr) then
-- do remote edit
return org.files
:update_file(filename, function(file)
return Hyperlinks.get_link_to_file(file)
end)
:wait()
end

return Hyperlinks.get_link_to_file(self._file)
end

return OrgFile
27 changes: 27 additions & 0 deletions lua/orgmode/api/headline.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ 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 @@ -259,4 +260,30 @@ function OrgHeadline:_do_action(action)
end)
end

--- Get a link destination as string
---
--- Depending if org_id_link_to_org_use_id is set the format is
---
--- id:<uuid>::*title and the id is created if not existing
--- or
--- file:<filepath>::*title
---
--- The result is meant to be used as link_location for OrgApi.insert_link.
--- @return string
function OrgHeadline:get_link()
local filename = self.file.filename
local bufnr = vim.fn.bufnr(filename)

if bufnr == -1 or not vim.api.nvim_buf_is_loaded(bufnr) then
-- do remote edit
return org.files
:update_file(filename, function(_)
return Hyperlinks.get_link_to_headline(self._section)
end)
:wait()
end

return Hyperlinks.get_link_to_headline(self._section)
end

return OrgHeadline
7 changes: 6 additions & 1 deletion lua/orgmode/api/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
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 orgmode = require('orgmode')

---@class OrgApiRefileOpts
Expand Down Expand Up @@ -102,6 +101,12 @@ function OrgApi.refile(opts)
end

--- Insert a link to a given location at the current cursor position
---
--- The expected format is
--- <protocol>:<location>::<in_file_location>
---
--- If <in_file_location> is *<headline>, <headline> is used as prefilled description for the link.
--- If <protocol> is id, this format can also be used to pass a prefilled description.
--- @param link_location string
--- @return boolean
function OrgApi.insert_link(link_location)
Expand Down
26 changes: 26 additions & 0 deletions lua/orgmode/files/file.lua
Original file line number Diff line number Diff line change
Expand Up @@ -669,6 +669,7 @@ end

memoize('get_category')
--- Get the category name for this file
--- If no category is set, the filename without extension is returned
--- @return string
function OrgFile:get_category()
local category = self:_get_directive('category')
Expand All @@ -679,6 +680,19 @@ function OrgFile:get_category()
return vim.fn.fnamemodify(self.filename, ':t:r') or ''
end

memoize('get_title')
--- Get the title for this file
--- If no title is set, the filename without extension is returned
--- @return string
function OrgFile:get_title()
local title = self:_get_directive('title')
if title then
return title
end

return vim.fn.fnamemodify(self.filename, ':t:r') or ''
end

memoize('get_opened_unfinished_headlines')
---@return OrgHeadline[]
function OrgFile:get_opened_unfinished_headlines()
Expand Down Expand Up @@ -729,6 +743,18 @@ function OrgFile:get_directive(directive_name)
return self:_get_directive(directive_name)
end

--- Get headline id or create a new one if it doesn't exist
--- @return string
function OrgFile:id_get_or_create()
local id = self:get_property('id')
if id then
return id
end
local org_id = require('orgmode.org.id').new()
self:set_property('ID', org_id)
return org_id
end

---@private
---@return string | nil
function OrgFile:_get_directive(directive_name)
Expand Down
26 changes: 21 additions & 5 deletions lua/orgmode/org/hyperlinks/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -174,16 +174,32 @@ end
---@param headline OrgHeadline
---@param path? string
function Hyperlinks.get_link_to_headline(headline, path)
path = path or utils.current_file_path()
local title = headline:get_title()
local id

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

if config.org_id_link_to_org_use_id and id then
return ('id:%s::*%s'):format(id, title)
path = path or utils.current_file_path()
return ('file:%s::*%s'):format(path, title)
end

---@param file OrgFile
---@param path? string
function Hyperlinks.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

Expand Down

0 comments on commit fb50c10

Please sign in to comment.