diff --git a/lua/nvimux/bindings.lua b/lua/nvimux/bindings.lua index 978b25c..d35b5c9 100644 --- a/lua/nvimux/bindings.lua +++ b/lua/nvimux/bindings.lua @@ -4,53 +4,8 @@ local fns = {} local consts = { terminal_quit = '', - esc = '', } -fns.nvim_do_bind = function(options) - local escape_prefix = options.escape_prefix or '' - local mode = options.mode - return function(cfg) - local suffix = cfg.suffix - local prefix = vars.local_prefix[mode] or vars.prefix - if suffix == nil then - suffix = string.sub(cfg.mapping, 1, 1) == ':' and '' or '' - end - vim.cmd(mode .. 'noremap ' .. prefix .. cfg.key .. ' ' .. escape_prefix .. cfg.mapping .. suffix) - end -end - -fns.bind = { - t = fns.nvim_do_bind{mode = 't', escape_prefix = consts.terminal_quit}, - i = fns.nvim_do_bind{mode = 'i', escape_prefix = consts.esc}, - n = fns.nvim_do_bind{mode = 'n'}, - v = fns.nvim_do_bind{mode = 'v'} -} - -fns.bind_all_modes = function(options) - for _, mode in ipairs(options.modes) do - fns.bind[mode](options) - end -end - -bindings.bind = function(options) - fns.bind_all_modes(options) -end - -bindings.create_binding = function(modes, command) - local tbl = {} - tbl[table.concat(modes, "")] = { command } - return tbl - -end - -bindings.bind_all = function(options) - for _, bind in ipairs(options) do - local key, cmd, modes = unpack(bind) - bindings.mappings[key] = bindings.create_binding(modes, cmd) - end -end - if vim.keymap ~= nil then bindings.set_keymap = vim.keymap.set else @@ -82,26 +37,29 @@ bindings.keymap = function(binding, context) bindings.set_keymap(binding[1], context.prefix .. binding[2], binding[3], options) elseif (type(binding[3]) == "string") then local suffix = '' + local starts_with_colon = string.sub(binding[3], 1, 1) == ':' if binding.suffix == nil then -- TODO revisit - suffix = string.sub(binding[3], 1, 1) == ':' and '' or '' + suffix = starts_with_colon and '' or '' else suffix = binding.suffix end - if vim.tbl_contains(binding[1], 't') then - binding[1] = vim.tbl_filter(function(mode) return mode ~= 't' end, binding[1]) - bindings.set_keymap('t', - context.prefix .. binding[2], - consts.terminal_quit .. binding[3] .. suffix, - options) - elseif vim.tbl_contains(binding[1], 'i') then - binding[1] = vim.tbl_filter(function(mode) return mode ~= 'i' end, binding[1]) - bindings.set_keymap('i', - context.prefix .. binding[2], - consts.esc .. binding[3] .. suffix, - options) + if starts_with_colon then + if vim.tbl_contains(binding[1], 't') then + binding[1] = vim.tbl_filter(function(mode) return mode ~= 't' end, binding[1]) + bindings.set_keymap('t', + context.prefix .. binding[2], + consts.terminal_quit .. binding[3] .. suffix, + options) + elseif vim.tbl_contains(binding[1], 'i') then + binding[1] = vim.tbl_filter(function(mode) return mode ~= 'i' end, binding[1]) + bindings.set_keymap('i', + context.prefix .. binding[2], + consts.esc .. binding[3] .. suffix, + options) + end end bindings.set_keymap(binding[1], context.prefix .. binding[2], binding[3] .. suffix, options) diff --git a/lua/nvimux/fns.lua b/lua/nvimux/fns.lua index ed878e7..1997dc0 100644 --- a/lua/nvimux/fns.lua +++ b/lua/nvimux/fns.lua @@ -1,25 +1,23 @@ local fns = {} -fns.prompt = function(message) - vim.fn.inputsave() - local ret = vim.fn.input(message) - vim.fn.inputrestore() - return ret +local function capitalize_first(s) + return string.upper(string.sub(s,1,1))..string.sub(s,2) end -fns.split = function(str) - local p = {} - for i=1, #str do - table.insert(p, str:sub(i, i)) - end - return p +fns.snake_to_pascal = function(s) + return (string.gsub(capitalize_first(s),"_(%w+)",capitalize_first)) end -fns.build_cmd = function(options) - local nargs = options.nargs or 0 - local cmd = options.cmd or options.lazy_cmd() - - vim.cmd('command! -nargs=' .. nargs .. ' ' .. options.name .. ' ' .. cmd) +fns.fn_or_command = function(cmd) + local tp = type(cmd) + if tp == "function" then + cmd() + elseif tp == "string" then + vim.cmd(cmd) + else + print("nvimux: Cannot run command of type " .. tp) + end end + return fns diff --git a/lua/nvimux/init.lua b/lua/nvimux/init.lua index e54d44a..2000d94 100644 --- a/lua/nvimux/init.lua +++ b/lua/nvimux/init.lua @@ -5,168 +5,59 @@ -- @module nvimux -- luacheck: globals unpack -local __dep_warn = true -local deprecated = function(msg) - if __dep_warn then - print(msg) - __dep_warn = false - end -end - local nvimux = {} local bindings = require('nvimux.bindings') -local vars = require('nvimux.vars') local fns = require('nvimux.fns') -nvimux.debug = {} -nvimux.bindings = bindings nvimux.config = {} nvimux.term = {} -nvimux.term.prompt = {} -nvimux.commands = {} - --- [ Private variables and tables -local nvim_proxy = { - __index = function(_, key) - deprecated("Don't use the proxy to vim vars. It will be removed in the next version") - local key_ = 'nvimux_' .. key - local val = nil - if vim.fn.exists(key_) == 1 then - val = vim.api.nvim_get_var(key_) - end - return val +nvimux.commands = setmetatable({},{ + +__newindex = function(tbl, key, value) + vim.api.nvim_add_user_command("Nvimux"..fns.snake_to_pascal(key), + function(opts) + value(opts.args, opts) + end, {}) + rawset(tbl, key, value) end -} - --- [[ Table of default bindings --- Deprecated -bindings.mappings = { - [''] = { nvi = {':so $MYVIMRC'}}, - ['!'] = { nvit = {':wincmd T'}}, - ['%'] = { nvit = {function() return vars.vertical_split end}}, - ['\"'] = { nvit = {function() return vars.horizontal_split end}}, - ['-'] = { nvit = {':NvimuxPreviousTab'}}, - ['q'] = { nvit = {':NvimuxToggleTerm'}}, - ['w'] = { nvit = {':tabs'}}, - ['o'] = { nvit = {'w'}}, - ['n'] = { nvit = {'gt'}}, - ['p'] = { nvit = {'gT'}}, - ['x'] = { nvi = {':bd %'}, - t = {function() return vars.close_term end}}, - ['X'] = { nvi = {':enew \\| bd #'}}, - ['h'] = { nvit = {''}}, - ['j'] = { nvit = {''}}, - ['k'] = { nvit = {''}}, - ['l'] = { nvit = {''}}, - [':'] = { t = {':', suffix = ''}}, - ['['] = { t = {''}}, - [']'] = { t = {':NvimuxTermPaste'}}, - [','] = { t = {'', nvimux.term.prompt.rename}}, - ['c'] = { nvit = {':NvimuxNewTab'}}, -} +}) bindings.map_table = {} --- Deprecated local win_cmd = function(create_window) - local select_buffer - vim.cmd(create_window) - - if type(vars.new_window) == "function" then - select_buffer = vars.new_window - else - select_buffer = function() - vim.api.nvim_command(vars.new_window) - end - end - - select_buffer() - + vim.cmd(create_window) + fns.fn_or_command(nvimux.context.new_window) end --- Deprecated local tab_cmd = function(create_window) - local select_buffer vim.cmd(create_window) - - local selector = nvimux.context.new_tab or nvimux.context.new_window - - if type(selector) == "function" then - select_buffer = selector - else - select_buffer = function() - vim.api.nvim_command(selector) - end - end - - select_buffer() + fns.fn_or_command(nvimux.context.new_tab) end +-- [[ Commands +-- Commands defined in `nvimux.commands` will be automatically converted to nvim's command nvimux.commands.horizontal_split = function() return win_cmd[[spl|wincmd j]] end nvimux.commands.vertical_split = function() return win_cmd[[vspl|wincmd l]] end nvimux.commands.new_tab = function() return tab_cmd[[tabe]] end - --- Deprecated -local nvimux_commands = { - {name = 'NvimuxPreviousTab', cmd = [[lua require('nvimux').go_to_last_tab()]]}, - {name = 'NvimuxSet', cmd = [[lua require('nvimux').config.set_fargs()]], nargs='+'}, -} - -local autocmds = { - {event = "TabLeave", target="*", cmd = [[lua require('nvimux').set_last_tab()]]}, -} - -local mappings = { - -- Reload global configs - {{'n', 'v', 'i'}, '', ':so $MYVIMRC'}, - - -- Window management - {{'n', 'v', 'i', 't'}, '!', ':wincmd T'}, - {{'n', 'v', 'i', 't'}, '%', nvimux.commands.vertical_split}, - {{'n', 'v', 'i', 't'}, '\"', nvimux.commands.horizontal_split}, - {{'n', 'v', 'i', 't'}, '-', nvimux.go_to_last_tab}, - {{'n', 'v', 'i', 't'}, 'q', nvimux.term.toggle }, - {{'n', 'v', 'i', 't'}, 'w', ':tabs'}, - {{'n', 'v', 'i', 't'}, 'o', 'w'}, - {{'n', 'v', 'i', 't'}, 'n', 'gt'}, - {{'n', 'v', 'i', 't'}, 'p', 'gT'}, - {{'n', 'v', 'i'}, 'x', ':bd %'}, - {{'t'}, 'x', function() vim.api.nvim_buf_delete(0, {force = true}) end}, - {{'n', 'v', 'i'}, 'X', ':enew \\| bd #'}, - - -- Moving around - {{'n', 'v', 'i', 't'}, 'h', ''}, - {{'n', 'v', 'i', 't'}, 'j', ''}, - {{'n', 'v', 'i', 't'}, 'k', ''}, - {{'n', 'v', 'i', 't'}, 'l', ''}, - - -- Term facilities - {{'t'}, ':', ':', suffix = ''}, - {{'t'}, '[', ''}, - {{'t'}, ']', function() nvimux.term_only{cmd = 'normal pa'} end}, - {{'t'}, ',', nvimux.term.prompt.rename}, - - -- Tab management - {{'n', 'v', 'i', 't'}, 'c', nvimux.commands.new_tab}, - {{'n', 'v', 'i', 't'}, '0', '0gt'}, - {{'n', 'v', 'i', 't'}, '1', '1gt'}, - {{'n', 'v', 'i', 't'}, '2', '2gt'}, - {{'n', 'v', 'i', 't'}, '3', '3gt'}, - {{'n', 'v', 'i', 't'}, '4', '4gt'}, - {{'n', 'v', 'i', 't'}, '5', '5gt'}, - {{'n', 'v', 'i', 't'}, '6', '6gt'}, - {{'n', 'v', 'i', 't'}, '7', '7gt'}, - {{'n', 'v', 'i', 't'}, '8', '8gt'}, - {{'n', 'v', 'i', 't'}, '9', '9gt'}, -} - +nvimux.commands.previous_tab = nvimux.go_to_last_tab +nvimux.commands.term_paste = function(reg) vim.paste(vim.fn.getreg(reg or '"', 1, true), -1) end +nvimux.commands.toggle_term = nvimux.term.toggle +nvimux.commands.term_rename = nvimux.term.rename -- ]] -setmetatable(vars, nvim_proxy) +-- [[ Top-level helper functions +nvimux.set_last_tab = function(tabn) + if tabn == nil then + tabn = vim.fn.tabpagenr() + end + + nvimux.context.state.last_tab = tabn +end --- ] +nvimux.go_to_last_tab = function() + vim.cmd((nvimux.context.state.last_tab or 1) .. 'tabn') +end --- ] nvimux.do_autocmd = function(commands) local au = {"augroup nvimux"} @@ -176,84 +67,52 @@ nvimux.do_autocmd = function(commands) table.insert(au, "augroup END") vim.fn.execute(au) end - --- [ Public API --- [[ Config-handling commands -nvimux.config.set = function(options) - deprecated("nvimux.config.set is deprecated. Use nvimux.setup") - vars[options.key] = options.value -end - -nvimux.config.set_fargs = function(key, value) - deprecated("nvimux.config.set_fargs is deprecated. Use nvimux.setup") - nvimux.config.set{key=key, value=value} -end - -nvimux.config.set_all = function(options) - deprecated("nvimux.config.set_all is deprecated. Use nvimux.setup") - for key, value in pairs(options) do - nvimux.config.set{['key'] = key, ['value'] = value} - end -end -- ]] + -- [[ Quickterm --- TODO port nvimux.term.new_toggle = function() local split_type = nvimux.context.quickterm:split_type() - vim.cmd(split_type .. ' | enew | ' .. nvimux.context.quickterm.command) - local buf_nr = vim.fn.bufnr('%') + vim.cmd(split_type) + fns.fn_or_command(nvimux.context.quickterm.command) + local buf_nr = vim.api.nvim_get_current_buf() vim.wo.wfw = true vim.b[buf_nr].nvimux_buf_orientation = split_type vim[nvimux.context.quickterm.scope].nvimux_last_buffer_id = buf_nr end --- TODO port nvimux.term.toggle = function() -- TODO Allow external commands - local buf_nr = vim.g.nvimux_last_buffer_id + local buf_nr = vim[nvimux.context.quickterm.scope].nvimux_last_buffer_id if not buf_nr then nvimux.term.new_toggle() else - local id = math.floor(buf_nr) - local window = vim.fn.bufwinnr(id) + local window = vim.fn.bufwinid(buf_nr) if window == -1 then - if vim.fn.bufname(id) == '' then - nvimux.term.new_toggle() - else + if vim.api.nvim_buf_is_loaded(buf_nr) then local split_type = vim.b[buf_nr].nvimux_buf_orientation - vim.cmd(split_type .. ' | b' .. id) + vim.cmd(split_type) + vim.api.nvim_win_set_buf(0, buf_nr) + else + nvimux.term.new_toggle() end else - vim.cmd(window .. ' wincmd w | q | stopinsert') + vim.api.nvim_win_hide(window) end end end -nvimux.term.prompt.rename = function() - nvimux.term_only{ - cmd = fns.prompt('nvimux > New term name: '), - action = function(k) vim.api.nvim_command('file term://' .. k) end - } -end --- ]] - --- [[ Bindings --- ]] - --- [[ Top-level commands -nvimux.debug.context = function() - print(vim.inspect(nvimux.context)) -end - -nvimux.debug.bindings = function() - print(vim.inspect(nvimux.context.bindings)) -end - -nvimux.debug.state = function() - print(vim.inspect(nvimux.context.state)) +nvimux.term.rename = function() + vim.ui.input( + {prompt = 'nvimux > New term name: '}, + function(name) + nvimux.term_only{ + cmd = name, + action = function(k) vim.api.nvim_command('file term://' .. k) end + } + end) end nvimux.term_only = function(options) @@ -265,63 +124,57 @@ nvimux.term_only = function(options) end end --- deprecated -nvimux.mapped = function(options) - local mapping = bindings.map_table[options.key] - local ret = mapping.arg() - if ret ~= '' and ret ~= nil then - vim.cmd(ret) - end -end - -nvimux.set_last_tab = function(tabn) - if tabn == nil then - tabn = vim.fn.tabpagenr() - end +-- ]] - nvimux.context.state.last_tab = tabn -end +local autocmds = { + {event = "TabLeave", target="*", cmd = [[lua require('nvimux').set_last_tab()]]}, +} -nvimux.go_to_last_tab = function() - vim.cmd((nvimux.context.state.last_tab or 1) .. 'tabn') -end +local mappings = { + -- Reload global configs + {{'n', 'v', 'i'}, '', 'source $MYVIMRC'}, - -- ]] --- ] + -- Window management + {{'n', 'v', 'i', 't'}, '!', 'wincmd T'}, + {{'n', 'v', 'i', 't'}, '%', nvimux.commands.vertical_split}, + {{'n', 'v', 'i', 't'}, '\"', nvimux.commands.horizontal_split}, + {{'n', 'v', 'i', 't'}, '-', nvimux.go_to_last_tab}, + {{'n', 'v', 'i', 't'}, 'q', nvimux.term.toggle }, + {{'n', 'v', 'i', 't'}, 'w', 'tabs'}, + {{'n', 'v', 'i', 't'}, 'o', 'w'}, + {{'n', 'v', 'i', 't'}, 'n', 'gt'}, + {{'n', 'v', 'i', 't'}, 'p', 'gT'}, + {{'n', 'v', 'i'}, 'x', 'bdelete %'}, + {{'t'}, 'x', function() vim.api.nvim_buf_delete(0, {force = true}) end}, + {{'n', 'v', 'i'}, 'X', 'enew \\| bd #'}, + -- Moving around + {{'n', 'v', 'i', 't'}, 'h', ''}, + {{'n', 'v', 'i', 't'}, 'j', ''}, + {{'n', 'v', 'i', 't'}, 'k', ''}, + {{'n', 'v', 'i', 't'}, 'l', ''}, -nvimux.bootstrap = function(force) - deprecated("nvimux.bootstrap is deprecated. Use nvimux.setup") - if force or nvimux.loaded == nil then - for i=1, 9 do - bindings.mappings[i] = bindings.create_binding({"n", "v", "i", "t"} , i .. 'gt') - end + -- Term facilities + {{'t'}, ':', ':', suffix = ''}, + {{'t'}, '[', ''}, + {{'t'}, ']', nvimux.commands.term_paste }, + {{'t', 'n'}, ',', nvimux.term.rename}, - for _, cmd in ipairs(nvimux_commands) do - fns.build_cmd(cmd) - end + -- Tab management + {{'n', 'v', 'i', 't'}, 'c', nvimux.commands.new_tab}, + {{'n', 'v', 'i', 't'}, '0', '0gt'}, + {{'n', 'v', 'i', 't'}, '1', '1gt'}, + {{'n', 'v', 'i', 't'}, '2', '2gt'}, + {{'n', 'v', 'i', 't'}, '3', '3gt'}, + {{'n', 'v', 'i', 't'}, '4', '4gt'}, + {{'n', 'v', 'i', 't'}, '5', '5gt'}, + {{'n', 'v', 'i', 't'}, '6', '6gt'}, + {{'n', 'v', 'i', 't'}, '7', '7gt'}, + {{'n', 'v', 'i', 't'}, '8', '8gt'}, + {{'n', 'v', 'i', 't'}, '9', '9gt'}, +} - for key, cmd in pairs(bindings.mappings) do - for modes, binds in pairs(cmd) do - modes = fns.split(modes) - local arg = table.remove(binds, 1) - binds.key = key - binds.modes = modes - if type(arg) == 'function' then - bindings.map_table[key] = {['arg'] = arg, ['action'] = nil} - binds.mapping = ":lua require('nvimux').mapped{key = '" .. key .. "'}" - else - binds.mapping = arg - end - bindings.bind(binds) - end - end - fns.build_cmd{name = 'NvimuxReload', cmd = 'lua require("nvimux").bootstrap(true)'} - nvimux.do_autocmd(autocmds) - nvimux.loaded = true - end -end --- ] +-- ]] --- Configure nvimux to start with the supplied arguments -- It can be configured to use the defaults by only supplying an empty table. @@ -333,10 +186,8 @@ end -- @tparam opts.autocmds table autocmds that belong to the same logical group than nvimux -- @see nvimux.vars for the defaults nvimux.setup = function(opts) - -- TODO Remove global vars, make it local to context only - vars = vim.tbl_deep_extend("force", vars or {}, opts.config or {}) + local context = vim.tbl_deep_extend("force", require('nvimux.vars'), opts.config or {}) - local context = vars context.bindings = mappings for _, b in ipairs(opts.bindings) do table.insert(context.bindings, b) diff --git a/lua/nvimux/vars.lua b/lua/nvimux/vars.lua index a9704b1..2f41434 100644 --- a/lua/nvimux/vars.lua +++ b/lua/nvimux/vars.lua @@ -22,13 +22,13 @@ local singleton_buf = function() end -vars.local_prefix = { - n = nil, - v = nil, - i = nil, - t = nil - } - +--- Quickterm configuration +-- @table quickterm +-- @field scope Scope of quickterm. Can be one of: 'b', 'w', 't', 'g' +-- @field direction nvim's window direction +-- @field orientation nvim's window orientation +-- @field size size in columns/rows depending on orientation +-- @field command nvim command or lua function for the quickterm vars.quickterm = { scope = 'g', direction = 'botright', @@ -38,30 +38,34 @@ vars.quickterm = { } vars.prefix = '' --- Deprecated -vars.vertical_split = ':NvimuxVerticalSplit' -vars.horizontal_split = ':NvimuxHorizontalSplit' - -vars.close_term = function() - vim.api.nvim_buf_delete(0, {force = true}) -end +--- Defines what is going to be displayed when a buffer is +-- required for a new window/tab. +-- By default, that content will be created once on a +-- non-listed scratch buffer. vars.scratch_buf_content = { "" } -vars.new_buffer = function() - return vim.api.nvim_create_buf(false, true) -end - +--- Prepares a new window +-- Can be used to display dashboards, TODO lists or as a hook to invoke +-- other functions (like telescope). +-- Defaults to setting a scratch buffer whose contents are defined by +-- @{\\vars.scratch_buf_content}. vars.new_window = function() vim.api.nvim_set_current_buf(singleton_buf()) end +--- Prepares a new tab +-- Can be used to display dashboards, TODO lists or as a hook to invoke +-- other functions (like telescope). +-- Defaults to setting a scratch buffer whose contents are defined by +-- @{\\vars.scratch_buf_content}. vars.new_tab = function() vim.api.nvim_set_current_buf(singleton_buf()) end +--- Prepares the command that will be executed to create a new quickterm vars.quickterm.split_type = function(t) return t.direction .. ' ' .. t.orientation .. ' ' .. t.size .. 'split' end diff --git a/plugin/nvimux.vim b/plugin/nvimux.vim deleted file mode 100644 index 3f4c36a..0000000 --- a/plugin/nvimux.vim +++ /dev/null @@ -1,8 +0,0 @@ -" Commands -command! -nargs=0 NvimuxTermPaste lua require('nvimux').term_only{cmd = 'normal pa'} -command! -nargs=0 NvimuxToggleTerm lua require('nvimux').term.toggle() -command! -nargs=1 NvimuxTermRename lua require('nvimux').term_only{cmd = 'file term://'} - -command! -nargs=0 NvimuxHorizontalSplit lua require('nvimux').commands.horizontal_split() -command! -nargs=0 NvimuxVerticalSplit lua require('nvimux').commands.vertical_split() -command! -nargs=0 NvimuxNewTab lua require('nvimux').commands.new_tab()