Skip to content

Commit

Permalink
Add support for keyword faces.
Browse files Browse the repository at this point in the history
  • Loading branch information
kristijanhusak committed Jul 3, 2021
1 parent 1d786fc commit 6e2174e
Show file tree
Hide file tree
Showing 8 changed files with 251 additions and 57 deletions.
29 changes: 25 additions & 4 deletions DOCS.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,29 @@ Examples:
* `{'TODO', 'NEXT', '|', 'DONE'}`
* `{'TODO', 'WAITING', '|', 'DONE', 'DELEGATED'}`

#### **org_todo_keyword_faces**
*type*: `table<string, string>`<br />
*default value*: `{}`<br />
Custom colors for todo keywords.<br />
Available options:
* foreground - `:foreground hex/colorname`. Examples: `:foreground #FF0000`, `:foreground blue`
* background - `:background hex/colorname`. Examples: `:background #FF0000`, `:background blue`
* weight - `:weight bold`.
* underline - `:underline on`
* italic - `:slant italic`

Full configuration example with additional todo keywords and their colors:
```lua
require('orgmode').setup({
org_todo_keywords = {'TODO', 'WAITING', '|', 'DONE', 'DELEGATED'},
org_todo_keyword_faces = {
WAITING = ':foreground blue :weight bold',
DELEGATED = ':background #FFFFFF :slant italic :underline on',
TODO - ':background #000000 :foreground red', -- overrides builtin color for `TODO` keyword
}
})
```

#### **org_archive_location**
*type*: `string`<br />
*default value*: `'%s_archive::'`<br />
Expand Down Expand Up @@ -467,8 +490,6 @@ If those colors are not suitable you can override them like this:
autocmd ColorScheme * call s:setup_org_colors()
function! s:setup_org_colors() abort
hi OrgTODO guifg=#FF0000
hi OrgDONE guifg=#00FF00
hi OrgAgendaDeadline guifg=#FFAAAA
hi OrgAgendaScheduled guifg=#AAFFAA
hi OrgAgendaScheduledPast guifg=Orange
Expand All @@ -479,10 +500,10 @@ or you can link it to another highlight group:

```vim
function! s:setup_org_colors() abort
hi link OrgTODO ErrorMsg
hi link OrgDONE String
hi link OrgAgendaDeadline Error
hi link OrgAgendaScheduled DiffAdd
hi link OrgAgendaScheduledPast Statement
endfunction
```

For adding/changing todo keyword colors see [org-todo-keyword-faces](#org_todo_keyword_faces)
33 changes: 28 additions & 5 deletions doc/orgmode.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ CONTENTS *orgmode-content
1.1.1.1. org_agenda_files...................|orgmode-org_agenda_files|
1.1.1.2. org_default_notes_file.......|orgmode-org_default_notes_file|
1.1.1.3. org_todo_keywords.................|orgmode-org_todo_keywords|
1.1.1.4. org_archive_location...........|orgmode-org_archive_location|
1.1.1.4. org_todo_keyword_faces.......|orgmode-org_todo_keyword_faces|
1.1.1.5. org_archive_location...........|orgmode-org_archive_location|
1.1.2. Agenda settings...........................|orgmode-agenda_settings|
1.1.2.1. org_deadline_warning_days.|orgmode-org_deadline_warning_days|
1.1.2.2. org_agenda_span.....................|orgmode-org_agenda_span|
Expand Down Expand Up @@ -129,6 +130,30 @@ Examples:
* `{'TODO', 'NEXT', '|', 'DONE'}`
* `{'TODO', 'WAITING', '|', 'DONE', 'DELEGATED'}`

ORG_TODO_KEYWORD_FACES *orgmode-org_todo_keyword_faces*

type: `table<string, string>`
default value: `{}`
Custom colors for todo keywords.
Available options:
* foreground - `:foreground hex/colorname`. Examples: `:foreground #FF0000`, `:foreground blue`
* background - `:background hex/colorname`. Examples: `:background #FF0000`, `:background blue`
* weight - `:weight bold`.
* underline - `:underline on`
* italic - `:slant italic`

Full configuration example with additional todo keywords and their colors:
>
require('orgmode').setup({
org_todo_keywords = {'TODO', 'WAITING', '|', 'DONE', 'DELEGATED'},
org_todo_keyword_faces = {
WAITING = ':foreground blue :weight bold',
DELEGATED = ':background #FFFFFF :slant italic :underline on',
TODO - ':background #000000 :foreground red', -- overrides builtin color for `TODO` keyword
}
})
<

ORG_ARCHIVE_LOCATION *orgmode-org_archive_location*

type: `string`
Expand Down Expand Up @@ -644,8 +669,6 @@ If those colors are not suitable you can override them like this:
>
autocmd ColorScheme * call s:setup_org_colors()
function! s:setup_org_colors() abort
hi OrgTODO guifg=#FF0000
hi OrgDONE guifg=#00FF00
hi OrgAgendaDeadline guifg=#FFAAAA
hi OrgAgendaScheduled guifg=#AAFFAA
hi OrgAgendaScheduledPast guifg=Orange
Expand All @@ -655,11 +678,11 @@ If those colors are not suitable you can override them like this:
or you can link it to another highlight group:
>
function! s:setup_org_colors() abort
hi link OrgTODO ErrorMsg
hi link OrgDONE String
hi link OrgAgendaDeadline Error
hi link OrgAgendaScheduled DiffAdd
hi link OrgAgendaScheduledPast Statement
endfunction
<

For adding/changing todo keyword colors see org-todo-keyword-faces (#org_todo_keyword_faces)

2 changes: 1 addition & 1 deletion lua/orgmode/agenda/agenda_item.lua
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ end

function AgendaItem:_add_keyword_highlight()
if self.headline.todo_keyword.value == '' then return end
local hlgroup = hl_map[self.headline.todo_keyword.type]
local hlgroup = hl_map[self.headline.todo_keyword.value]
if hlgroup then
table.insert(self.highlights, {
hlgroup = hlgroup,
Expand Down
38 changes: 18 additions & 20 deletions lua/orgmode/agenda/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -132,13 +132,15 @@ function Agenda:render()
date = string.format(' %-'..date_len..'s', agenda_item.label)
end
local todo_keyword = agenda_item.headline.todo_keyword.value
local todo_padding = ''
if todo_keyword ~= '' and vim.trim(agenda_item.label):find(':$') then
todo_keyword = ' '..todo_keyword
todo_padding = ' '
end
todo_keyword = todo_padding..todo_keyword
local line = string.format(
'%s%s%s %s', category, date, todo_keyword, headline.title
)
local todo_keyword_pos = string.format('%s%s ', category, date):len()
local todo_keyword_pos = string.format('%s%s%s', category, date, todo_padding):len()
if #headline.tags > 0 then
line = string.format('%-99s %s', line, headline:tags_to_string())
end
Expand All @@ -152,7 +154,7 @@ function Agenda:render()
end_col = 0,
})
if hl.todo_keyword then
hl.range.start_col = todo_keyword_pos
hl.range.start_col = todo_keyword_pos + 1
hl.range.end_col = todo_keyword_pos + hl.todo_keyword:len() + 1
end
return hl
Expand Down Expand Up @@ -220,7 +222,7 @@ function Agenda:todos()
if #todo.tags > 0 then
line = string.format('%-99s %s', line, todo:tags_to_string())
end
local todo_keyword_pos = category:len() + 3
local todo_keyword_pos = category:len() + 4
table.insert(content, {
line_content = line,
line = i + 1,
Expand All @@ -230,12 +232,12 @@ function Agenda:todos()
})

table.insert(highlights, {
hlgroup = hl_map[todo.todo_keyword.type],
hlgroup = hl_map[todo.todo_keyword.value],
range = Range:new({
start_line = i + 1,
end_line = i + 1,
start_col = todo_keyword_pos,
end_col = todo_keyword_pos + todo_keyword:len() + 1
end_col = todo_keyword_pos + todo_keyword:len()
})
})
end
Expand Down Expand Up @@ -270,10 +272,8 @@ function Agenda:search(clear_search)
for i, headline in ipairs(headlines) do
local category = string.format(' %-'..(longest_category + 1)..'s', headline:get_category()..':')
local todo_keyword = headline.todo_keyword.value
if todo_keyword ~= '' then
todo_keyword = ' '..todo_keyword
end
local line = string.format(' %s%s %s', category, todo_keyword, headline.title)
local todo_keyword_padding = todo_keyword ~= '' and ' ' or ''
local line = string.format(' %s%s%s %s', category, todo_keyword_padding, todo_keyword, headline.title)
if #headline.tags > 0 then
line = string.format('%-99s %s', line, headline:tags_to_string())
end
Expand All @@ -286,14 +286,14 @@ function Agenda:search(clear_search)
})

if headline.todo_keyword.value ~= '' then
local todo_keyword_pos = category:len() + 3
local todo_keyword_pos = category:len() + 4
table.insert(highlights, {
hlgroup = hl_map[headline.todo_keyword.type],
hlgroup = hl_map[headline.todo_keyword.value],
range = Range:new({
start_line = i + 2,
end_line = i + 2,
start_col = todo_keyword_pos,
end_col = todo_keyword_pos + todo_keyword:len() + 1
end_col = todo_keyword_pos + todo_keyword:len()
})
})
end
Expand Down Expand Up @@ -338,10 +338,8 @@ function Agenda:tags(clear_search)
for i, headline in ipairs(headlines) do
local category = string.format(' %-'..(longest_category + 1)..'s', headline:get_category()..':')
local todo_keyword = headline.todo_keyword.value
if todo_keyword ~= '' then
todo_keyword = ' '..todo_keyword
end
local line = string.format(' %s%s %s', category, todo_keyword, headline.title)
local todo_keyword_padding = todo_keyword ~= '' and ' ' or ''
local line = string.format(' %s%s%s %s', category, todo_keyword_padding, todo_keyword, headline.title)
if #headline.tags > 0 then
line = string.format('%-99s %s', line, headline:tags_to_string())
end
Expand All @@ -354,14 +352,14 @@ function Agenda:tags(clear_search)
})

if headline.todo_keyword.value ~= '' then
local todo_keyword_pos = category:len() + 3
local todo_keyword_pos = category:len() + 4
table.insert(highlights, {
hlgroup = hl_map[headline.todo_keyword.type],
hlgroup = hl_map[headline.todo_keyword.value],
range = Range:new({
start_line = i + 2,
end_line = i + 2,
start_col = todo_keyword_pos,
end_col = todo_keyword_pos + todo_keyword:len() + 1
end_col = todo_keyword_pos + todo_keyword:len()
})
})
end
Expand Down
124 changes: 99 additions & 25 deletions lua/orgmode/colors/highlights.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3,53 +3,127 @@ local colors = require('orgmode.colors')
local M = {}

function M.define_agenda_colors()
local hl_map = M.get_agenda_hl_map()
local keyword_colors = colors.get_todo_keywords_colors()
local todo_keywords = config:get_todo_keywords()
for type, hlname in pairs(hl_map) do
local bold = ''
if vim.tbl_contains(todo_keywords.ALL, type) then
bold = ' gui=bold'
end
vim.cmd(string.format('hi %s_builtin guifg=%s ctermfg=%s%s', hlname, keyword_colors[type].gui, keyword_colors[type].cterm, bold))
local c = {
deadline = 'OrgAgendaDeadline',
ok = 'OrgAgendaScheduled',
warning = 'OrgAgendaScheduledPast'
}
for type, hlname in pairs(c) do
vim.cmd(string.format('hi %s_builtin guifg=%s ctermfg=%s', hlname, keyword_colors[type].gui, keyword_colors[type].cterm))
vim.cmd(string.format('hi default link %s %s_builtin', hlname, hlname))
end
M.define_org_todo_keyword_colors()
end

function M.define_org_todo_keyword_colors()
function M.define_org_todo_keyword_colors(do_syn_match)
local keyword_colors = colors.get_todo_keywords_colors()
local todo_keywords = config:get_todo_keywords()
vim.cmd(string.format([[syn match OrgTODO "\<\(%s\)\>" contained]], table.concat(todo_keywords.TODO, [[\|]])))
vim.cmd(string.format([[syn match OrgDONE "\<\(%s\)\>" contained]], table.concat(todo_keywords.DONE, [[\|]])))
vim.cmd(string.format('hi OrgTODO_builtin guifg=%s ctermfg=%s', keyword_colors.TODO.gui, keyword_colors.TODO.cterm))
if do_syn_match then
vim.cmd(string.format([[syn match OrgTODO "\<\(%s\)\>" contained]], table.concat(todo_keywords.TODO, [[\|]])))
vim.cmd(string.format([[syn match OrgDONE "\<\(%s\)\>" contained]], table.concat(todo_keywords.DONE, [[\|]])))
end
vim.cmd(string.format('hi OrgTODO_builtin guifg=%s ctermfg=%s gui=bold cterm=bold', keyword_colors.TODO.gui, keyword_colors.TODO.cterm))
vim.cmd('hi default link OrgTODO OrgTODO_builtin')
vim.cmd(string.format('hi OrgDONE_builtin guifg=%s ctermfg=%s', keyword_colors.DONE.gui, keyword_colors.DONE.cterm))
vim.cmd(string.format('hi OrgDONE_builtin guifg=%s ctermfg=%s gui=bold cterm=bold', keyword_colors.DONE.gui, keyword_colors.DONE.cterm))
vim.cmd('hi default link OrgDONE OrgDONE_builtin')
return M.parse_todo_keyword_faces(do_syn_match)
end

function M.define_org_headline_colors()
local headline_colors = {'Title', 'Constant', 'Identifier', 'Statement', 'PreProc', 'Type', 'Special', 'String'}
local todo_keywords = config:get_todo_keywords()
local all_keywords = table.concat(todo_keywords.ALL, [[\|]])
for i, color in ipairs(headline_colors) do
local j = i
while j < 40 do
vim.cmd(string.format([[syn match OrgHeadlineLevel%d "^\*\{%d}\s\+\(\<\(%s\)\>\)\?.*$" contains=OrgTODO,OrgDONE]], j, j, all_keywords))
vim.cmd(string.format('hi default link OrgHeadlineLevel%d %s', j, color))
j = j + 8
function M.define_org_headline_colors(faces)
local headline_colors = {'Title', 'Constant', 'Identifier', 'Statement', 'PreProc', 'Type', 'Special', 'String'}
local todo_keywords = config:get_todo_keywords()
local all_keywords = table.concat(todo_keywords.ALL, [[\|]])
local contains = {'OrgTODO', 'OrgDONE'}
for _, face in pairs(faces) do
table.insert(contains, face)
end
contains = table.concat(contains, ',')
for i, color in ipairs(headline_colors) do
local j = i
while j < 40 do
vim.cmd(string.format([[syn match OrgHeadlineLevel%d "^\*\{%d}\s\+\(\<\(%s\)\>\)\?.*$" contains=%s]], j, j, all_keywords, contains))
vim.cmd(string.format('hi default link OrgHeadlineLevel%d %s', j, color))
j = j + 8
end
end
end

function M.define_highlights()
local faces = M.define_org_todo_keyword_colors(true)
return M.define_org_headline_colors(faces)
end

function M.parse_todo_keyword_faces(do_syn_match)
local opts = {
underline = {
type = vim.o.termguicolors and 'gui' or 'cterm',
valid = 'on',
result = 'underline'
},
weight = {
type = vim.o.termguicolors and 'gui' or 'cterm',
valid = 'bold'
},
foreground = {
type = vim.o.termguicolors and 'guifg' or 'ctermfg',
},
background = {
type = vim.o.termguicolors and 'guibg' or 'ctermbg',
},
slant = {
type = vim.o.termguicolors and 'gui' or 'cterm',
valid = 'italic'
}
}

local result = {}

for name, values in pairs(config.org_todo_keyword_faces) do
local parts = vim.split(values, ':', true)
local hl_opts = {}
for _, part in ipairs(parts) do
local faces = vim.split(vim.trim(part), ' ')
if #faces == 2 then
local opt_name = vim.trim(faces[1])
local opt_value = vim.trim(faces[2])
opt_value = opt_value:gsub('^"*', ''):gsub('"*$', '')
local opt = opts[opt_name]
if opt and (not opt.valid or opt.valid == opt_value) then
if not hl_opts[opt.type] then
hl_opts[opt.type] = {}
end
table.insert(hl_opts[opt.type], opt.result or opt_value)
end
end
end
if not vim.tbl_isempty(hl_opts) then
local hl_name = 'OrgKeywordFace'..name
local hl = ''
for hl_item, hl_values in pairs(hl_opts) do
hl = hl..' '..hl_item..'='..table.concat(hl_values, ',')
end
if do_syn_match then
vim.cmd(string.format([[syn match %s "\<%s\>" contained]], hl_name, name))
end
vim.cmd(string.format('hi %s %s', hl_name, hl))
result[name] = hl_name
end
end

return result
end

---@return table<string, string>
function M.get_agenda_hl_map()
return {
local faces = M.parse_todo_keyword_faces()
return vim.tbl_extend('force', {
TODO = 'OrgTODO',
DONE = 'OrgDONE',
deadline = 'OrgAgendaDeadline',
ok = 'OrgAgendaScheduled',
warning = 'OrgAgendaScheduledPast'
}
}, faces)
end

return M
1 change: 1 addition & 0 deletions lua/orgmode/config/defaults.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ return {
org_agenda_files = '',
org_default_notes_file = '',
org_todo_keywords = {'TODO', '|', 'DONE'},
org_todo_keyword_faces = {},
org_deadline_warning_days = 14,
org_agenda_span = 'week', -- day/week/month/year/number of days
org_agenda_start_on_weekday = 1,
Expand Down
Loading

0 comments on commit 6e2174e

Please sign in to comment.