Skip to content

Commit 760beef

Browse files
Add initial version of api. (#331)
1 parent 8a71ef3 commit 760beef

File tree

14 files changed

+611
-128
lines changed

14 files changed

+611
-128
lines changed

.github/workflows/docgen.yml

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,37 @@ jobs:
2121
env:
2222
COMMIT_MSG: |
2323
[docgen] Update doc/orgmode.txt
24-
skip-checks: true
2524
run: |
2625
git config user.name github-actions
2726
git config user.email [email protected]
2827
git add doc/orgmode.txt
2928
# Only commit and push if we have changes
3029
git diff --quiet && git diff --staged --quiet || (git commit -m "${COMMIT_MSG}"; git push)
30+
31+
api_docgen:
32+
runs-on: ubuntu-latest
33+
steps:
34+
- uses: actions/checkout@v2
35+
- name: Install Neovim
36+
uses: rhysd/action-setup-vim@v1
37+
id: neovim
38+
with:
39+
neovim: true
40+
version: v0.7.0
41+
- name: Install mini.nvim
42+
uses: actions/checkout@v2
43+
with:
44+
repository: echasnovski/mini.nvim
45+
path: mini.nvim
46+
- name: Generate api docs
47+
run: make api_docs
48+
- name: Commit changes
49+
env:
50+
COMMIT_MSG: |
51+
[docgen] Update doc/orgmode_api.txt
52+
run: |
53+
git config user.name github-actions
54+
git config user.email [email protected]
55+
git add doc/orgmode_api.txt
56+
# Only commit and push if we have changes
57+
git diff --quiet && git diff --staged --quiet || (git commit -m "${COMMIT_MSG}"; git push)

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,4 @@ luac.out
4141

4242
plenary.nvim/
4343
nvim-treesitter/
44+
mini.nvim/

Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,7 @@ ci:
66
nvim --noplugin --clean -u tests/minimal_init.vim -c "TSUpdateSync org" -c "qa!" && make test
77
docs:
88
md2vim -desc "*orgmode* *orgmode.nvim*\n* NOTE: This file is autogenerated from DOCS.md file" DOCS.md doc/orgmode.txt
9+
api_docs:
10+
nvim --headless --clean -u ./scripts/gendoc.lua
911
format:
1012
stylua lua/ tests/

doc/orgmode_api.txt

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
==============================================================================
2+
------------------------------------------------------------------------------
3+
*OrgApi.load()*
4+
`OrgApi.load`({name})
5+
Parameters~
6+
{name} `(optional)` `(string|string[])` specific file names to return (absolute path). If ommitted, returns all loaded files
7+
Return~
8+
OrgFile|OrgFile[]
9+
10+
------------------------------------------------------------------------------
11+
*OrgApi.current()*
12+
`OrgApi.current`()
13+
Get current org buffer file
14+
Return~
15+
OrgFile
16+
17+
18+
==============================================================================
19+
------------------------------------------------------------------------------
20+
*OrgFile*
21+
`OrgFile`
22+
Class~
23+
{OrgFile}
24+
Fields~
25+
{category} `(string)` current file category name. By default only filename without extension unless defined via #+CATEGORY directive
26+
{filename} `(string)` absolute path of the current file
27+
{headlines} OrgHeadline[]
28+
{is_archive_file} `(boolean)`
29+
30+
------------------------------------------------------------------------------
31+
*OrgFile:reload()*
32+
`OrgFile:reload`()
33+
Return refreshed instance of the file
34+
Return~
35+
OrgFile
36+
37+
38+
==============================================================================
39+
------------------------------------------------------------------------------
40+
*OrgHeadline*
41+
`OrgHeadline`
42+
Class~
43+
{OrgHeadline}
44+
Fields~
45+
{title} `(string)` headline title without todo keyword, tags and priority. Ex. `* TODO I am a headline :SOMETAG:` returns `I am a headline`
46+
{line} `(string)` full headline line
47+
{todo_value} `(optional)` `(string)` todo keyword of the headline (Example: TODO, DONE)
48+
{todo_type} `(optional)` `(string)` | "'TODO'" | "'DONE'" | "''"
49+
{tags} `(string[])` List of own tags
50+
{deadline} `(Date|nil)`
51+
{scheduled} `(Date|nil)`
52+
{closed} `(Date|nil)`
53+
{dates} Date[] List of all dates that are not "plan" dates
54+
{position} Range
55+
{all_tags} `(string[])` List of all tags (own + inherited)
56+
{file} OrgFile
57+
{parent} `(OrgHeadline|nil)`
58+
{priority} `(string|nil)`
59+
{headlines} OrgHeadline[]
60+
61+
------------------------------------------------------------------------------
62+
*OrgHeadline:reload()*
63+
`OrgHeadline:reload`()
64+
Return updated version of headline
65+
Return~
66+
OrgHeadline
67+
68+
------------------------------------------------------------------------------
69+
*OrgHeadline:set_tags()*
70+
`OrgHeadline:set_tags`({tags})
71+
Set tags on the headline. This replaces all current tags with provided ones
72+
Parameters~
73+
{tags} `(string[])`
74+
Return~
75+
Promise
76+
77+
------------------------------------------------------------------------------
78+
*OrgHeadline:priority_up()*
79+
`OrgHeadline:priority_up`()
80+
Increase priority on a headline
81+
Return~
82+
Promise
83+
84+
------------------------------------------------------------------------------
85+
*OrgHeadline:priority_down()*
86+
`OrgHeadline:priority_down`()
87+
Decrease priority on a headline
88+
Return~
89+
Promise
90+
91+
------------------------------------------------------------------------------
92+
*OrgHeadline:set_priority()*
93+
`OrgHeadline:set_priority`({priority})
94+
Set specific priority on a headline. Empty string clears the priority
95+
Parameters~
96+
{priority} `(string)`
97+
Return~
98+
Promise
99+
100+
101+
==============================================================================
102+
------------------------------------------------------------------------------
103+
*OrgPosition*
104+
`OrgPosition`
105+
Class~
106+
{OrgPosition}
107+
Fields~
108+
{start_line} `(number)` start line number
109+
{end_line} `(number)` end line number
110+
{start_col} `(number)` start column number
111+
{end_col} `(number)` end column number
112+
113+
114+
vim:tw=78:ts=8:noet:ft=help:norl:

lua/orgmode/api/file.lua

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
local OrgHeadline = require('orgmode.api.headline')
2+
3+
---@class OrgFile
4+
---@field category string current file category name. By default only filename without extension unless defined via #+CATEGORY directive
5+
---@field filename string absolute path of the current file
6+
---@field headlines OrgHeadline[]
7+
---@field is_archive_file boolean
8+
local OrgFile = {}
9+
10+
---@param headline OrgHeadline
11+
---@param headlines_by_id table<string, OrgHeadline>
12+
---@private
13+
local function map_child_headlines(headline, headlines_by_id)
14+
if #headline._section.sections == 0 then
15+
return headline
16+
end
17+
18+
local child_headlines = {}
19+
for _, child_section in ipairs(headline._section.sections) do
20+
local child_headline = headlines_by_id[child_section.id]
21+
child_headline.parent = headline
22+
table.insert(child_headlines, child_headline)
23+
map_child_headlines(child_headline, headlines_by_id)
24+
end
25+
headline.headlines = child_headlines
26+
return headline
27+
end
28+
29+
---@private
30+
function OrgFile:_new(opts)
31+
local data = {}
32+
data.category = opts.category
33+
data.filename = opts.filename
34+
data.headlines = opts.headlines
35+
data.is_archive_file = opts.is_archive_file or false
36+
data._file = opts._file
37+
setmetatable(data, self)
38+
self.__index = self
39+
return data
40+
end
41+
42+
---@param file File
43+
---@private
44+
function OrgFile._build_from_internal_file(file)
45+
local headlines = {}
46+
local headlines_by_id = {}
47+
for i, section in ipairs(file.sections) do
48+
local headline = OrgHeadline._build_from_internal_section(section, i)
49+
table.insert(headlines, headline)
50+
headlines_by_id[section.id] = headline
51+
end
52+
53+
local instance = OrgFile:_new({
54+
_file = file,
55+
category = file.category,
56+
filename = file.filename,
57+
headlines = headlines,
58+
is_archive_file = file.is_archive_file,
59+
})
60+
61+
for _, headline in ipairs(instance.headlines) do
62+
map_child_headlines(headline, headlines_by_id)
63+
headline.file = instance
64+
end
65+
66+
return instance
67+
end
68+
69+
--- Return refreshed instance of the file
70+
---@return OrgFile
71+
function OrgFile:reload()
72+
return OrgFile._build_from_internal_file(self._file:refresh())
73+
end
74+
75+
return OrgFile

lua/orgmode/api/headline.lua

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
local Files = require('orgmode.parser.files')
2+
local utils = require('orgmode.utils')
3+
local ts_org = require('orgmode.treesitter')
4+
local OrgPosition = require('orgmode.api.position')
5+
local PriorityState = require('orgmode.objects.priority_state')
6+
7+
---@class OrgHeadline
8+
---@field title string headline title without todo keyword, tags and priority. Ex. `* TODO I am a headline :SOMETAG:` returns `I am a headline`
9+
---@field line string full headline line
10+
---@field todo_value? string todo keyword of the headline (Example: TODO, DONE)
11+
---@field todo_type? string | "'TODO'" | "'DONE'" | "''"
12+
---@field tags string[] List of own tags
13+
---@field deadline Date|nil
14+
---@field scheduled Date|nil
15+
---@field closed Date|nil
16+
---@field dates Date[] List of all dates that are not "plan" dates
17+
---@field position Range
18+
---@field all_tags string[] List of all tags (own + inherited)
19+
---@field file OrgFile
20+
---@field parent OrgHeadline|nil
21+
---@field priority string|nil
22+
---@field headlines OrgHeadline[]
23+
local OrgHeadline = {}
24+
25+
---@private
26+
function OrgHeadline:_new(opts)
27+
local data = {}
28+
data.file = opts.file
29+
data.todo_type = opts.todo_type
30+
data.todo_value = opts.todo_value
31+
data.title = opts.title
32+
data.line = opts.line
33+
data.category = opts.category
34+
data.position = opts.position
35+
data.tags = opts.tags
36+
data.all_tags = opts.all_tags
37+
data.priority = opts.priority
38+
data.deadline = opts.deadline
39+
data.scheduled = opts.scheduled
40+
data.closed = opts.closed
41+
data.dates = opts.dates
42+
data.parent = opts.parent
43+
data.headlines = opts.headlines or {}
44+
data._section = opts._section
45+
data._index = opts._index
46+
47+
setmetatable(data, self)
48+
self.__index = self
49+
return data
50+
end
51+
52+
---@param section Section
53+
---@param index number
54+
---@private
55+
function OrgHeadline._build_from_internal_section(section, index)
56+
return OrgHeadline:_new({
57+
title = section.title,
58+
line = section.line,
59+
todo_type = section.todo_keyword.type,
60+
todo_value = section.todo_keyword.value,
61+
all_tags = { unpack(section.tags) },
62+
tags = section:get_own_tags(),
63+
position = OrgPosition:_build_from_internal_range(section.range),
64+
deadline = section:get_deadline_date(),
65+
scheduled = section:get_scheduled_date(),
66+
closed = section:get_closed_date(),
67+
dates = vim.tbl_filter(function(date)
68+
return date:is_none()
69+
end, section.dates),
70+
priority = section.priority,
71+
_section = section,
72+
_index = index,
73+
})
74+
end
75+
76+
--- Return updated version of headline
77+
---@return OrgHeadline
78+
function OrgHeadline:reload()
79+
local file = self.file:reload()
80+
return file.headlines[self._index]
81+
end
82+
83+
--- Set tags on the headline. This replaces all current tags with provided ones
84+
---@param tags string[]
85+
---@return Promise
86+
function OrgHeadline:set_tags(tags)
87+
return self:_do_action(function()
88+
local headline = ts_org.closest_headline()
89+
return headline:set_tags(string.format(':%s:', table.concat(tags, ':')))
90+
end)
91+
end
92+
93+
--- Increase priority on a headline
94+
---@return Promise
95+
function OrgHeadline:priority_up()
96+
return self:_do_action(function()
97+
local headline = ts_org.closest_headline()
98+
local _, current_priority = headline:priority()
99+
local priority_state = PriorityState:new(current_priority)
100+
return headline:set_priority(priority_state:increase())
101+
end)
102+
end
103+
104+
--- Decrease priority on a headline
105+
---@return Promise
106+
function OrgHeadline:priority_down()
107+
return self:_do_action(function()
108+
local headline = ts_org.closest_headline()
109+
local _, current_priority = headline:priority()
110+
local priority_state = PriorityState:new(current_priority)
111+
return headline:set_priority(priority_state:decrease())
112+
end)
113+
end
114+
115+
--- Set specific priority on a headline. Empty string clears the priority
116+
---@param priority string
117+
---@return Promise
118+
function OrgHeadline:set_priority(priority)
119+
return self:_do_action(function()
120+
local headline = ts_org.closest_headline()
121+
return headline:set_priority(priority)
122+
end)
123+
end
124+
125+
---@param action function
126+
---@private
127+
function OrgHeadline:_do_action(action)
128+
return Files.update_file(self.file.filename, function()
129+
local view = vim.fn.winsaveview()
130+
vim.fn.cursor({ self.position.start_line, 0 })
131+
return utils.promisify(action()):next(function()
132+
vim.fn.winrestview(view)
133+
return self:reload()
134+
end)
135+
end)
136+
end
137+
138+
return OrgHeadline

0 commit comments

Comments
 (0)