Skip to content

feat(files): add method OrgHeadline:toggle_tag() #894

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

Merged
merged 1 commit into from
Feb 7, 2025
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
43 changes: 43 additions & 0 deletions lua/orgmode/files/headline.lua
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,49 @@ function Headline:set_tags(tags)
vim.api.nvim_buf_set_text(bufnr, pred_end_row, pred_end_col, pred_end_row, end_col, { text })
end

---@param tag string
---@return boolean newly_added
function Headline:add_tag(tag)
local current_tags = self:get_own_tags()
local present = vim.tbl_contains(current_tags, tag)
if not present then
table.insert(current_tags, tag)
end
self:set_tags(utils.tags_to_string(current_tags))
return not present
end

---@param tag string
---@return boolean newly_removed
function Headline:remove_tag(tag)
local current_tags = self:get_own_tags()
---@type string[]
local new_tags = vim.tbl_filter(function(i)
return i ~= tag
end, current_tags)
local present = #new_tags ~= #current_tags
if present then
self:set_tags(utils.tags_to_string(new_tags))
end
return present
end

---@param tag string
---@return boolean newly_added
function Headline:toggle_tag(tag)
local current_tags = self:get_own_tags()
local present = vim.tbl_contains(current_tags, tag)
if present then
current_tags = vim.tbl_filter(function(i)
return i ~= tag
end, current_tags)
else
table.insert(current_tags, tag)
end
self:set_tags(utils.tags_to_string(current_tags))
return not present
end

function Headline:align_tags()
local own_tags, node = self:get_own_tags()
if node then
Expand Down
13 changes: 2 additions & 11 deletions lua/orgmode/org/mappings.lua
Original file line number Diff line number Diff line change
Expand Up @@ -71,19 +71,10 @@ function OrgMappings:set_tags(tags)
end)
end

---@return nil
function OrgMappings:toggle_archive_tag()
local headline = self.files:get_closest_headline()
local current_tags = headline:get_own_tags()

if vim.tbl_contains(current_tags, 'ARCHIVE') then
current_tags = vim.tbl_filter(function(tag)
return tag ~= 'ARCHIVE'
end, current_tags)
else
table.insert(current_tags, 'ARCHIVE')
end

return headline:set_tags(utils.tags_to_string(current_tags))
headline:toggle_tag('ARCHIVE')
end

function OrgMappings:cycle()
Expand Down
135 changes: 135 additions & 0 deletions tests/plenary/files/headline_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -152,4 +152,139 @@ describe('Headline', function()
assert_range(second_headline_dates[3], { 5, 3, 5, 18 })
end)
end)

describe('tags', function()
---@type OrgFile
local file
local orig_tags_column

before_each(function()
-- Put tags flush to headlines for shorter tests.
if not orig_tags_column then
orig_tags_column = config.org_tags_column
end
config:extend({ org_tags_column = 0 })
-- Reinitialize test file to same state.
if not file then
file = helpers.load_file(vim.fn.tempname() .. '.org')
end
local bufnr = file:get_valid_bufnr()
vim.api.nvim_buf_set_lines(bufnr, 0, -1, true, {
'* Headline 1',
'* Headline 2 :other:',
'* Headline 3 :other:more:ARCHIVE:',
})
file:reload_sync()
end)

after_each(function()
config:extend({ org_tags_column = orig_tags_column })
end)

describe('toggling', function()
it('adds a tag where there is none', function()
file:get_headlines()[1]:toggle_tag('ARCHIVE')
local expected = {
'* Headline 1 :ARCHIVE:',
'* Headline 2 :other:',
'* Headline 3 :other:more:ARCHIVE:',
}
assert.are.same(expected, file:reload_sync().lines)
end)

it('adds a tag if another already exists', function()
file:get_headlines()[2]:toggle_tag('ARCHIVE')
local expected = {
'* Headline 1',
'* Headline 2 :other:ARCHIVE:',
'* Headline 3 :other:more:ARCHIVE:',
}
assert.are.same(expected, file:reload_sync().lines)
end)

it('removes an existing tag', function()
file:get_headlines()[2]:toggle_tag('other')
local expected = {
'* Headline 1',
'* Headline 2',
'* Headline 3 :other:more:ARCHIVE:',
}
assert.are.same(expected, file:reload_sync().lines)
end)

it('keeps other tags when removing one', function()
file:get_headlines()[3]:toggle_tag('more')
local expected = {
'* Headline 1',
'* Headline 2 :other:',
'* Headline 3 :other:ARCHIVE:',
}
assert.are.same(expected, file:reload_sync().lines)
end)
end)

describe('addition', function()
it('adds a tag where there is none', function()
file:get_headlines()[1]:add_tag('ARCHIVE')
local expected = {
'* Headline 1 :ARCHIVE:',
'* Headline 2 :other:',
'* Headline 3 :other:more:ARCHIVE:',
}
assert.are.same(expected, file:reload_sync().lines)
end)

it('adds a tag if another already exists', function()
file:get_headlines()[2]:add_tag('ARCHIVE')
local expected = {
'* Headline 1',
'* Headline 2 :other:ARCHIVE:',
'* Headline 3 :other:more:ARCHIVE:',
}
assert.are.same(expected, file:reload_sync().lines)
end)

it('does not add the same tag twice', function()
file:get_headlines()[2]:add_tag('other')
local expected = {
'* Headline 1',
'* Headline 2 :other:',
'* Headline 3 :other:more:ARCHIVE:',
}
assert.are.same(expected, file:reload_sync().lines)
end)
end)

describe('removal', function()
it('removes an existing tag', function()
file:get_headlines()[2]:remove_tag('other')
local expected = {
'* Headline 1',
'* Headline 2',
'* Headline 3 :other:more:ARCHIVE:',
}
assert.are.same(expected, file:reload_sync().lines)
end)

it('keeps other tags when removing one', function()
file:get_headlines()[3]:remove_tag('more')
local expected = {
'* Headline 1',
'* Headline 2 :other:',
'* Headline 3 :other:ARCHIVE:',
}
assert.are.same(expected, file:reload_sync().lines)
end)

it('does nothing when removing a non-existent tag', function()
file:get_headlines()[1]:remove_tag('other')
local expected = {
'* Headline 1',
'* Headline 2 :other:',
'* Headline 3 :other:more:ARCHIVE:',
}
assert.are.same(expected, file:reload_sync().lines)
end)
end)
end)
end)
Loading