aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorzbirenbaum <zacharyobirenbaum@gmail.com>2022-04-21 23:50:43 -0400
committerzbirenbaum <zacharyobirenbaum@gmail.com>2022-04-22 00:36:42 -0400
commit9cabfafc08a1cd9e83762896e0eac5299ceed29b (patch)
tree1449e8b92325d487ac441956037f12e025141db8
parentInitial commit (diff)
initial update
-rw-r--r--lua/chadterm/init.lua68
-rw-r--r--lua/chadterm/terminal.lua111
-rw-r--r--lua/chadterm/termutil.lua51
3 files changed, 230 insertions, 0 deletions
diff --git a/lua/chadterm/init.lua b/lua/chadterm/init.lua
new file mode 100644
index 0000000..2c14e9c
--- /dev/null
+++ b/lua/chadterm/init.lua
@@ -0,0 +1,68 @@
+local M = {}
+
+local terminal = require("chadterm.terminal")
+
+local defaults = {
+ terminals = {
+ list = {},
+ type_opts = {
+ float = {
+ relative = 'editor',
+ row = 0.3,
+ col = 0.25,
+ width = 0.5,
+ height = 0.4,
+ border = "single",
+ },
+ horizontal = { location = "rightbelow", split_ratio = .3, },
+ vertical = { location = "rightbelow", split_ratio = .5 },
+ }
+ },
+ behavior = {
+ close_on_exit = true,
+ auto_insert = true,
+ },
+ mappings = {
+ toggle = {
+ { '<A-i>', function () terminal.new_or_toggle('float') end },
+ { '<A-s>', function () terminal.new_or_toggle('horizontal') end },
+ { '<A-v>', function () terminal.new_or_toggle('vertical') end },
+ }
+ }
+}
+
+local set_behavior = function(behavior)
+ if behavior.close_on_exit then
+ vim.api.nvim_create_autocmd({"TermClose"},{
+ callback = function()
+ vim.schedule_wrap(vim.api.nvim_input('<CR>'))
+ end
+ })
+ end
+ if behavior.auto_insert then
+ vim.api.nvim_create_autocmd({"BufEnter"}, {
+ callback = function() vim.cmd('startinsert') end,
+ pattern = 'term://*'
+ })
+ vim.api.nvim_create_autocmd({"BufLeave"}, {
+ callback = function() vim.cmd('stopinsert') end,
+ pattern = 'term://*'
+ })
+ end
+end
+
+local create_mappings = function (mappings)
+ local opts = { noremap = true, silent = true }
+ for _, mapping in ipairs(mappings.toggle) do
+ vim.keymap.set({'n', 't'}, mapping[1], mapping[2], opts)
+ end
+end
+
+M.setup = function (config)
+ config = config and vim.tbl_deep_extend("force", config, defaults) or defaults
+ set_behavior(config.behavior)
+ create_mappings(config.mappings)
+ require('chadterm.terminal').init(config.terminals)
+end
+
+return M
diff --git a/lua/chadterm/terminal.lua b/lua/chadterm/terminal.lua
new file mode 100644
index 0000000..66ee2de
--- /dev/null
+++ b/lua/chadterm/terminal.lua
@@ -0,0 +1,111 @@
+local util = require('chadterm.termutil')
+local a = vim.api
+local nvterm = {}
+local terminals = {}
+
+
+local function get_last(list)
+ if list then return not vim.tbl_isempty(list) and list[#list] or nil end
+ return terminals[#terminals] or nil
+end
+
+local function get_type(type)
+ return vim.tbl_filter(function(t)
+ return t.type == type
+ end, terminals.list)
+end
+
+local function get_still_open()
+ return vim.tbl_filter(function(t)
+ return t.open == true
+ end, terminals.list)
+end
+
+local function get_last_still_open()
+ return get_last(get_still_open())
+end
+
+local function get_type_last(type)
+ return get_last(get_type(type))
+end
+
+local create_term_window = function (type)
+ util.execute_type_cmd(type, terminals)
+ vim.wo.relativenumber = false
+ vim.wo.number = false
+ return a.nvim_get_current_win()
+end
+
+local create_term = function (type)
+ local win = create_term_window(type)
+ local buf = a.nvim_create_buf(false, true)
+ a.nvim_buf_set_option(buf, 'filetype', 'terminal')
+ a.nvim_buf_set_option(buf, 'buflisted', false)
+ a.nvim_win_set_buf(win, buf)
+ local job_id = vim.fn.termopen(vim.o.shell)
+ local term = { win = win, buf = buf, open = true, type=type, job_id=job_id}
+ table.insert(terminals.list, term)
+ vim.cmd('startinsert')
+ return term
+end
+
+local ensure_and_send = function(cmd, type)
+ terminals = util.verify_terminals(terminals)
+ local term = type and get_type_last(type) or get_last_still_open() or nvterm.new("vertical")
+ if not term then term = nvterm.new("horizontal") end
+ a.nvim_chan_send(term.job_id, cmd..'\n')
+end
+
+local call_and_restore = function (fn, opts)
+ local current_win = a.nvim_get_current_win()
+ local mode = a.nvim_get_mode().mode == 'i' and 'startinsert' or 'stopinsert'
+ fn(unpack(opts))
+ a.nvim_set_current_win(current_win)
+ vim.cmd(mode)
+end
+
+nvterm.send = function(cmd, type)
+ if not cmd then return end
+ call_and_restore(ensure_and_send, {cmd, type})
+end
+
+nvterm.hide_term = function (term)
+ term.open = false
+ a.nvim_win_close(term.win, false)
+end
+
+nvterm.show_term = function (term)
+ term.open = true
+ term.win = create_term_window(term.type)
+ a.nvim_win_set_buf(term.win, term.buf)
+ vim.cmd('startinsert')
+end
+
+nvterm.hide = function (type)
+ local term = type and get_type_last(type) or get_last()
+ nvterm.hide_term(term)
+end
+
+nvterm.show = function(type)
+ local term = type and get_type_last(type) or terminals.last
+ nvterm.show_term(term)
+end
+
+nvterm.new = function (type)
+ local term = create_term(type)
+ return term
+end
+
+nvterm.new_or_toggle = function (type)
+ terminals = util.verify_terminals(terminals)
+ local term = get_type_last(type)
+ if not term then term = nvterm.new(type)
+ elseif term.open then nvterm.hide_term(term)
+ else nvterm.show_term(term) end
+end
+
+nvterm.init = function(term_config)
+ terminals = term_config
+end
+
+return nvterm
diff --git a/lua/chadterm/termutil.lua b/lua/chadterm/termutil.lua
new file mode 100644
index 0000000..92fc632
--- /dev/null
+++ b/lua/chadterm/termutil.lua
@@ -0,0 +1,51 @@
+local util = {}
+local a = vim.api
+
+util.calc_float_opts = function(opts)
+ return {
+ relative = "editor",
+ width = math.ceil(opts.width*vim.o.columns),
+ height = math.ceil(opts.height*vim.o.lines),
+ row = math.floor(opts.row*vim.o.lines),
+ col = math.floor(opts.col*vim.o.columns),
+ border = opts.border,
+ }
+end
+
+util.get_split_dims = function(type, ratio)
+ local type_switch = type == "horizontal"
+ local type_func = type_switch and a.nvim_win_get_height or a.nvim_win_get_width
+ return math.floor(type_func(0) * ratio)
+end
+
+util.execute_type_cmd = function(type, terminals)
+ local opts = terminals.type_opts[type]
+ local type_cmds = {
+ horizontal = function()
+ local dims = util.get_split_dims(type, opts.split_ratio)
+ vim.cmd(opts.location .. dims .. " split")
+ end,
+ vertical = function()
+ local dims = util.get_split_dims(type, opts.split_ratio)
+ vim.cmd(opts.location .. dims .. " vsplit")
+ end,
+ float = function()
+ a.nvim_open_win(0, true, util.calc_float_opts(opts))
+ end,
+ }
+ type_cmds[type]()
+end
+
+
+util.verify_terminals = function (terminals)
+ terminals.list = vim.tbl_filter(function(term)
+ return vim.api.nvim_buf_is_valid(term.buf)
+ end, terminals.list)
+ terminals.list = vim.tbl_map(function(term)
+ term.open = vim.api.nvim_win_is_valid(term.win)
+ return term
+ end, terminals.list)
+ return terminals
+end
+
+return util