Skip to content
Merged
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
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 @@ -643,6 +643,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 @@ -653,6 +654,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 @@ -703,6 +717,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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is a file path with the title valid format for a file link?

From the examples here https://orgmode.org/guide/Hyperlinks.html I don't see any of those.

I think for link to file it is enough to either be an id, or just file:/path/to/file.org if there is no id.

Copy link
Contributor Author

@seflue seflue Jul 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not, and it's also not treated such. I am piggy-backing here on the "title" format to sneak a description into insert_link to get the form prefilled.
This is something, which actually needs to get cleaned up a bit more properly:
get_link should actually return a table or even better an api equivalent of Url. And insert_link should take that. But this would need some more decent refactoring effort which touches a lot of places (think also of store_link) so I would prefer to defer this to a separate PR.

Right now I am quite happy, how Telescope-orgmode works although the API is not perfect yet.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, we can leave it for the time being, but later once we do some refactoring, I'd prefer to remove it.
It can also cause side effects in cases where #+TITLE or the file name is the same as some other headline value. In that case, the link would jump to the headline.

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