Skip to content

Commit b445c09

Browse files
committed
refactor: big restructuring of the plugin
- decouple org file and headline search - clearer dependency structure - access orgmode only through one wrapper module - use telescope concepts in module names - unify initialization of a picker
1 parent 78d453d commit b445c09

16 files changed

+457
-382
lines changed

lua/telescope-orgmode/actions.lua

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
local finders = require('telescope-orgmode.finders')
2+
local org = require('telescope-orgmode.org')
3+
4+
local actions = require('telescope.actions')
5+
local action_state = require('telescope.actions.state')
6+
local state = require('telescope.state')
7+
8+
local M = {}
9+
10+
function M.toggle_headlines_orgfiles(opts)
11+
return function(prompt_bufnr)
12+
local status = state.get_status(prompt_bufnr)
13+
14+
-- _ot_ is used as our plugin-specific namespace in the action status
15+
-- (ot - orgmode telescope)
16+
--
17+
-- FIXME: the state get's sometimes nil when the initalization has already been run
18+
-- In this case, a toggle is "dropped" (keypress does not change the state).
19+
-- Can we avoid that by initializing the state in the higher order function?
20+
-- Idea: We can try to do it as before, but pass the prompt_bufnr with the opts.
21+
if status._ot_state == nil then
22+
-- uninitialized state - initialize with orgfiles
23+
-- Because when this function is called the first time, it is triggered
24+
-- by the users and we search over headlines by default, we set the state
25+
-- for the first toggle already here.
26+
status._ot_state = { current = opts.states[2], next = opts.states[1] }
27+
else
28+
status._ot_state.current, status._ot_state.next = status._ot_state.next, status._ot_state.current
29+
end
30+
31+
if status._ot_state.current == 'headlines' then
32+
M._find_headlines(opts, prompt_bufnr)
33+
elseif status._ot_state.current == 'orgfiles' then
34+
M._find_orgfiles(opts, prompt_bufnr)
35+
else
36+
-- this should not happen
37+
error(string.format('Invalid state %s', status._ot_state.current))
38+
end
39+
end
40+
end
41+
42+
function M.search_headlines(opts)
43+
return function(prompt_bufnr)
44+
M._find_headlines(opts, prompt_bufnr)
45+
end
46+
end
47+
48+
function M.search_orgfiles(opts)
49+
return function(prompt_bufnr)
50+
M._find_orgfiles(opts, prompt_bufnr)
51+
end
52+
end
53+
54+
function M.refile(closest_headline)
55+
return function(prompt_bufnr)
56+
local entry = action_state.get_selected_entry()
57+
actions.close(prompt_bufnr)
58+
59+
-- Refile to the file by default
60+
local destination = entry.value.file
61+
62+
-- Refile to a specific heading if is set
63+
if entry.value.headline then
64+
destination = entry.value.headline
65+
end
66+
67+
return org.refile({
68+
source = closest_headline,
69+
destination = destination,
70+
})
71+
end
72+
end
73+
74+
function M.insert(_)
75+
return function(prompt_bufnr)
76+
actions.close(prompt_bufnr)
77+
78+
---@type MatchEntry
79+
local entry = action_state.get_selected_entry()
80+
81+
-- Link to the filename by default
82+
local destination = entry.value.file.filename
83+
84+
-- Link to a specific heading if is set
85+
if entry.value.headline then
86+
destination = 'file:' .. entry.value.file.filename .. '::*' .. entry.value.headline.title
87+
end
88+
89+
org.insert_link(destination)
90+
return true
91+
end
92+
end
93+
94+
function M._find_headlines(opts, prompt_bufnr)
95+
local headlines = finders.headlines(opts)
96+
M._update_picker(headlines, opts.prompt_titles.headlines, prompt_bufnr)
97+
end
98+
99+
function M._find_orgfiles(opts, prompt_bufnr)
100+
local orgfiles = finders.orgfiles(opts)
101+
M._update_picker(orgfiles, opts.prompt_titles.orgfiles, prompt_bufnr)
102+
end
103+
104+
function M._update_picker(results, title, prompt_bufnr)
105+
local current_picker = action_state.get_current_picker(prompt_bufnr)
106+
107+
current_picker.layout.prompt.border:change_title(title)
108+
current_picker:refresh(results)
109+
end
110+
111+
return M

lua/telescope-orgmode/config.lua

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,14 @@ function M.setup(ext_opts)
88
M.opts = vim.tbl_extend('force', M.opts, ext_opts or {})
99
end
1010

11+
function M.init_opts(opts, prompt_titles)
12+
opts = vim.tbl_extend('force', M.opts, opts or {})
13+
opts.prompt_titles = prompt_titles
14+
opts.states = {}
15+
for state, _ in pairs(prompt_titles) do
16+
table.insert(opts.states, state)
17+
end
18+
return opts
19+
end
20+
1121
return M
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
require('telescope-orgmode.typehints')
2+
local org = require('telescope-orgmode.org')
3+
local entry_display = require('telescope.pickers.entry_display')
4+
5+
---@class OrgHeadlineEntry
6+
---@field file OrgApiFile
7+
---@field filename string
8+
---@field headline OrgApiHeadline
9+
10+
---@param file_results { file: OrgApiFile, filename: string }[]
11+
---@return OrgHeadlineEntry[]
12+
local function index_headlines(file_results, opts)
13+
local results = {}
14+
for _, file_entry in ipairs(file_results) do
15+
for _, headline in ipairs(file_entry.file.headlines) do
16+
local allowed_depth = opts.max_depth == nil or headline.level <= opts.max_depth
17+
local allowed_archive = opts.archived or not headline.is_archived
18+
if allowed_depth and allowed_archive then
19+
local entry = {
20+
file = file_entry.file,
21+
filename = file_entry.filename,
22+
headline = headline,
23+
title = nil,
24+
}
25+
table.insert(results, entry)
26+
end
27+
end
28+
end
29+
30+
return results
31+
end
32+
33+
local M = {}
34+
---Fetches entrys from OrgApi and extracts the relevant information
35+
---@param opts any
36+
---@return OrgHeadlineEntry[]
37+
M.get_entries = function(opts)
38+
return index_headlines(org.load_files(opts), opts)
39+
end
40+
41+
---Entry-Maker for Telescope
42+
---@param opts any
43+
---@return fun(entry: OrgHeadlineEntry):MatchEntry
44+
M.make_entry = function(opts)
45+
local displayer = entry_display.create({
46+
separator = ' ',
47+
items = {
48+
{ width = vim.F.if_nil(opts.location_width, 20) },
49+
{ remaining = true },
50+
},
51+
})
52+
53+
---@param entry MatchEntry
54+
local function make_display(entry)
55+
return displayer({ entry.location, entry.tags .. ' ' .. entry.line })
56+
end
57+
58+
return function(entry)
59+
local headline = entry.headline
60+
local lnum = headline.position.start_line
61+
local location = string.format('%s:%i', vim.fn.fnamemodify(entry.filename, ':t'), lnum)
62+
local line = string.format('%s %s', string.rep('*', headline.level), headline.title)
63+
local tags = table.concat(headline.all_tags, ':')
64+
local ordinal = tags .. ' ' .. line .. ' ' .. location
65+
66+
return {
67+
value = entry,
68+
ordinal = ordinal,
69+
filename = entry.filename,
70+
lnum = lnum,
71+
display = make_display,
72+
location = location,
73+
line = line,
74+
tags = tags,
75+
}
76+
end
77+
end
78+
79+
return M
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
require('telescope-orgmode.typehints')
2+
local org = require('telescope-orgmode.org')
3+
local entry_display = require('telescope.pickers.entry_display')
4+
5+
local M = {}
6+
7+
---@class OrgFileEntry
8+
---@field file OrgApiFile
9+
---@field filename string
10+
---@field title string?
11+
12+
---@param file_results { file: OrgApiFile, filename: string }[]
13+
---@return OrgFileEntry[]
14+
local function index_orgfiles(file_results)
15+
local results = {}
16+
for _, file_entry in ipairs(file_results) do
17+
local entry = {
18+
file = file_entry.file,
19+
filename = file_entry.filename,
20+
-- not beautiful to access a private property, but this is the only way to get the title
21+
---@diagnostic disable-next-line: invisible, undefined-field
22+
title = file_entry.file._file:get_directive('TITLE') or nil,
23+
headline = nil,
24+
}
25+
table.insert(results, entry)
26+
end
27+
return results
28+
end
29+
30+
---Fetches entrys from OrgApi and extracts the relevant information
31+
---@param opts any
32+
---@return OrgFileEntry[]
33+
M.get_entries = function(opts)
34+
return index_orgfiles(org.load_files(opts))
35+
end
36+
37+
---Entry-Maker for Telescope
38+
---@return fun(entry: OrgFileEntry):MatchEntry
39+
M.make_entry = function()
40+
local orgfile_displayer = entry_display.create({
41+
separator = ' ',
42+
items = {
43+
{ remaining = true },
44+
},
45+
})
46+
47+
---@param entry MatchEntry
48+
local function make_display(entry)
49+
return orgfile_displayer({ entry.line })
50+
end
51+
52+
return function(entry)
53+
local lnum = nil
54+
local location = vim.fn.fnamemodify(entry.filename, ':t')
55+
local line = entry.title or location
56+
local tags = ''
57+
local ordinal = line
58+
59+
return {
60+
value = entry,
61+
ordinal = ordinal,
62+
filename = entry.filename,
63+
lnum = lnum,
64+
display = make_display,
65+
location = location,
66+
line = line,
67+
tags = tags,
68+
}
69+
end
70+
end
71+
return M

lua/telescope-orgmode/finders.lua

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
local headlines = require('telescope-orgmode.entry_maker.headlines')
2+
local orgfiles = require('telescope-orgmode.entry_maker.orgfiles')
3+
4+
local finders = require('telescope.finders')
5+
6+
local M = {}
7+
8+
function M.headlines(opts)
9+
return finders.new_table({
10+
results = headlines.get_entries(opts),
11+
entry_maker = opts.entry_maker or headlines.make_entry(opts),
12+
})
13+
end
14+
15+
function M.orgfiles(opts)
16+
return finders.new_table({
17+
results = orgfiles.get_entries(opts),
18+
entry_maker = opts.entry_maker or orgfiles.make_entry(),
19+
})
20+
end
21+
22+
return M

lua/telescope-orgmode/init.lua

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
return {
22
setup = require('telescope-orgmode.config').setup,
3-
refile_heading = require('telescope-orgmode.refile_heading'),
4-
search_headings = require('telescope-orgmode.search_headings'),
5-
insert_link = require('telescope-orgmode.insert_link'),
3+
refile_heading = require('telescope-orgmode.picker.refile_heading'),
4+
search_headings = require('telescope-orgmode.picker.search_headings'),
5+
insert_link = require('telescope-orgmode.picker.insert_link'),
66
}

lua/telescope-orgmode/insert_link.lua

Lines changed: 0 additions & 78 deletions
This file was deleted.

0 commit comments

Comments
 (0)