From cac776ba053c15cee5b423102475812aa72bea35 Mon Sep 17 00:00:00 2001 From: Kristijan Husak Date: Mon, 20 Jan 2025 15:11:58 +0100 Subject: [PATCH] feat: Add global `:Org` command and global `Org` lua variable These allow interacting with orgmode in a more user friendly way --- docs/index.org | 22 +++++++++ lua/orgmode/agenda/init.lua | 1 + lua/orgmode/init.lua | 2 + lua/orgmode/org/global.lua | 91 +++++++++++++++++++++++++++++++++++++ lua/orgmode/ui/menu.lua | 9 +++- 5 files changed, 123 insertions(+), 2 deletions(-) create mode 100644 lua/orgmode/org/global.lua diff --git a/docs/index.org b/docs/index.org index e8a3a9f2f..a4033477d 100644 --- a/docs/index.org +++ b/docs/index.org @@ -3,6 +3,9 @@ Nvim orgmode is a clone of Emacs Orgmode for Neovim 0.10.0+. It aims to be a feature-complete implementation of Orgmode features in Neovim. +💡 TIP: To view this documentation offline in Neovim, run =:Org help=. More info in [[#globals-commands][Globals and commands]] section. + + ** Quick start :PROPERTIES: :CUSTOM_ID: quick-start @@ -45,3 +48,22 @@ that demonstrates how the similar Orgmode clone [[https://github.com/dhruvasagar :END: Nvim-orgmode exoses a Lua API that can be used to interact with the orgmode. To view it, check [[file:../docs/orgmode-api.txt][orgmode-api.txt]] or do =:h OrgApi= in Neovim. + +** Globals and commands +:PROPERTIES: +:CUSTOM_ID: globals-commands +:END: +There are 2 additional ways to interact with Orgmode: +1. Through the =:Org= command +2. Through =Org= Lua global variable + +List of available actions: +- =:Org help= - Open this documentation in new tab, set working directory to the docs folder for the tab to allow browsing +- =:Org helpgrep= - Open search agenda view that allows searching through the documentation +- =:Org agenda {type?}= - Open agenda view by the shortcut, for example =:Org agenda M= will open =tags_todo= view. When =type= is omitted, it opens up Agenda view. + +All of the commands above can be executed through the global Lua =Org= variable. Examples: +- =Org.help()= +- =Org.helpgrep()= +- =Org.open()= - Opens =agenda= view +- =Org.open.m()= - Opens =tags= view diff --git a/lua/orgmode/agenda/init.lua b/lua/orgmode/agenda/init.lua index 186300b28..c4e6b8e79 100644 --- a/lua/orgmode/agenda/init.lua +++ b/lua/orgmode/agenda/init.lua @@ -17,6 +17,7 @@ local AgendaTypes = require('orgmode.agenda.types') local Agenda = {} ---@param opts? { highlighter: OrgHighlighter, files: OrgFiles } +---@return OrgAgenda function Agenda:new(opts) opts = opts or {} local data = { diff --git a/lua/orgmode/init.lua b/lua/orgmode/init.lua index e97ef675b..e7bd120f1 100644 --- a/lua/orgmode/init.lua +++ b/lua/orgmode/init.lua @@ -1,4 +1,5 @@ _G.orgmode = _G.orgmode or {} +_G.Org = _G.Org or {} ---@type Org | nil local instance = nil @@ -35,6 +36,7 @@ setmetatable(Org, { }) function Org:new() + require('orgmode.org.global')(self) self.initialized = false self:setup_autocmds() require('orgmode.config'):setup_ts_predicates() diff --git a/lua/orgmode/org/global.lua b/lua/orgmode/org/global.lua new file mode 100644 index 000000000..b876dc937 --- /dev/null +++ b/lua/orgmode/org/global.lua @@ -0,0 +1,91 @@ +local current_file_path = string.sub(debug.getinfo(1, 'S').source, 2) +local docs_dir = vim.fn.fnamemodify(current_file_path, ':p:h:h:h:h') .. '/docs' + +---@param orgmode Org +local build = function(orgmode) + local Open = setmetatable({}, { + __call = function(t, ...) + t.a(...) + end, + __index = function(t, k) + local existing = rawget(t, k) + if existing then + return existing + end + + ---@diagnostic disable-next-line: invisible + local keys = orgmode.agenda:_build_menu():get_valid_keys() + + for key, item in pairs(keys) do + t[key] = item.action + end + + return rawget(t, k) + end, + }) + + for _, shortcut in ipairs({ 'a', 't', 'm', 'M', 's' }) do + Open[shortcut] = function() + return orgmode.agenda:open_by_key(shortcut) + end + end + + local OrgGlobal = { + help = function() + vim.cmd(('tabnew %s'):format(('%s/%s'):format(docs_dir, 'index.org'))) + vim.cmd(('tcd %s'):format(docs_dir)) + end, + + helpgrep = function() + orgmode.agenda:open_view('search', { + agenda_files = ('%s/**/*'):format(docs_dir), + }) + end, + + open = Open, + } + + _G.Org = OrgGlobal +end + +---@param opts string[] +---@return table +local function resolve_item(opts) + ---@type table + local obj = _G.Org + for _, opt in ipairs(opts) do + if type(obj) ~= 'table' then + return obj + end + if obj[opt] then + obj = obj[opt] + end + end + + return obj +end + +vim.api.nvim_create_user_command('Org', function(opts) + local item = resolve_item(opts.fargs) + if item and type(item) == 'function' then + return item() + end + require('orgmode.utils').echo_error(('Invalid command "Org %s"'):format(opts.args)) +end, { + nargs = '*', + complete = function(arg_lead, cmd_line) + local opts = vim.split(cmd_line:sub(5), '%s+') + local item = resolve_item(opts) + if type(item) ~= 'table' then + return {} + end + local list = vim.tbl_keys(item) + + if arg_lead == '' then + return list + end + return vim.fn.matchfuzzy(list, arg_lead) + end, +}) + +return build diff --git a/lua/orgmode/ui/menu.lua b/lua/orgmode/ui/menu.lua index a34d24961..f5744c861 100644 --- a/lua/orgmode/ui/menu.lua +++ b/lua/orgmode/ui/menu.lua @@ -135,14 +135,19 @@ function Menu._default_menu(data) return entry.action() end -function Menu:get_entry_by_key(key) +---@return table +function Menu:get_valid_keys() local valid_keys = {} for _, item in ipairs(self.items) do if item.key then valid_keys[item.key] = item end end - return valid_keys[key] + return valid_keys +end + +function Menu:get_entry_by_key(key) + return self:get_valid_keys()[key] end function Menu:open()