Greatly improve terminal features! Persistent terminals (#275)
* remove toggleTerm plugin * Adding term binds, term hider & Telescope terms to bring them back * Adding many term features!
This commit is contained in:
parent
51760c21f5
commit
575dc10ddc
8 changed files with 303 additions and 83 deletions
|
@ -13,7 +13,6 @@ M.ui = {
|
||||||
hidden_statusline = {
|
hidden_statusline = {
|
||||||
-- these are filetypes, not pattern matched
|
-- these are filetypes, not pattern matched
|
||||||
"NvimTree",
|
"NvimTree",
|
||||||
"toggleterm",
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,6 +106,7 @@ M.mappings = {
|
||||||
bufferline = {
|
bufferline = {
|
||||||
new_buffer = "<S-t>",
|
new_buffer = "<S-t>",
|
||||||
newtab = "<C-t>b",
|
newtab = "<C-t>b",
|
||||||
|
close = "<S-x>", -- close a buffer with custom func in utils.lua
|
||||||
cycleNext = "<TAB>", -- next buffer
|
cycleNext = "<TAB>", -- next buffer
|
||||||
cyclePrev = "<S-Tab>", -- previous buffer
|
cyclePrev = "<S-Tab>", -- previous buffer
|
||||||
},
|
},
|
||||||
|
@ -116,11 +116,13 @@ M.mappings = {
|
||||||
diffget_3 = "<leader>gl",
|
diffget_3 = "<leader>gl",
|
||||||
git_blame = "<leader>gb",
|
git_blame = "<leader>gb",
|
||||||
},
|
},
|
||||||
toggleterm = {
|
terms = { -- below are NvChad mappings, not plugin mappings
|
||||||
toggle_window = "<leader>w",
|
esc_termmode = "jk",
|
||||||
toggle_vert = "<leader>v",
|
esc_hide_termmode = "JK",
|
||||||
toggle_hori = "<leader>h",
|
pick_term = "<leader>W", -- note: this is a telescope extension
|
||||||
hide_term = "JK",
|
new_wind = "<leader>w",
|
||||||
|
new_vert = "<leader>v",
|
||||||
|
new_hori = "<leader>h",
|
||||||
},
|
},
|
||||||
-- navigation in insert mode
|
-- navigation in insert mode
|
||||||
insert_nav = {
|
insert_nav = {
|
||||||
|
@ -131,10 +133,7 @@ M.mappings = {
|
||||||
prev_line = "<C-j>",
|
prev_line = "<C-j>",
|
||||||
next_line = "<C-k>",
|
next_line = "<C-k>",
|
||||||
},
|
},
|
||||||
-- non plugin
|
|
||||||
misc = {
|
misc = {
|
||||||
esc_Termmode = "jk", -- get out of terminal mode
|
|
||||||
close_buffer = "<S-x>", -- close current focused buffer
|
|
||||||
copywhole_file = "<C-a>",
|
copywhole_file = "<C-a>",
|
||||||
toggle_linenr = "<leader>n", -- show or hide line number
|
toggle_linenr = "<leader>n", -- show or hide line number
|
||||||
theme_toggle = "<leader>x",
|
theme_toggle = "<leader>x",
|
||||||
|
|
|
@ -41,28 +41,27 @@ map("n", miscMap.copywhole_file, ":%y+<CR>", opt)
|
||||||
-- toggle numbers
|
-- toggle numbers
|
||||||
map("n", miscMap.toggle_linenr, ":set nu!<CR>", opt)
|
map("n", miscMap.toggle_linenr, ":set nu!<CR>", opt)
|
||||||
|
|
||||||
-- open a new buffer as a Terminal
|
|
||||||
-- get out of terminal with jk
|
|
||||||
map("t", miscMap.esc_Termmode, "<C-\\><C-n>", opt)
|
|
||||||
|
|
||||||
-- close current focused buffer, terminal or normal
|
-- terminals
|
||||||
-- todo: don't close if non-terminal buffer is saved
|
local function terms()
|
||||||
map("n", miscMap.close_buffer, ":bd!<CR>", opt)
|
local m = user_map.terms
|
||||||
|
|
||||||
M.toggleterm = function()
|
-- get out of terminal mode
|
||||||
local m = user_map.toggleterm
|
map("t", m.esc_termmode, "<C-\\><C-n>", opt)
|
||||||
|
-- hide a term from within terminal mode
|
||||||
|
map("t", m.esc_hide_termmode, "<C-\\><C-n> :lua require('utils').close_buffer() <CR>", opt)
|
||||||
|
-- pick a hidden term
|
||||||
|
map("n", m.pick_term, ":Telescope terms <CR>", opt)
|
||||||
|
|
||||||
-- Open terminals
|
-- Open terminals
|
||||||
map("n", m.toggle_window, ":lua termW:toggle() <CR>", opt)
|
-- TODO this opens on top of an existing vert/hori term, fixme
|
||||||
map("n", m.toggle_vert, ":lua termV:toggle() <CR>", opt)
|
map("n", m.new_wind, ":execute 'terminal' | let b:term_type = 'wind' | startinsert <CR>", opt)
|
||||||
map("n", m.toggle_hori, ":lua termH:toggle() <CR>", opt)
|
map("n", m.new_vert, ":execute 'vnew +terminal' | let b:term_type = 'vert' | startinsert <CR>", opt)
|
||||||
|
map("n", m.new_hori, ":execute 15 .. 'new +terminal' | let b:term_type = 'hori' | startinsert <CR>", opt)
|
||||||
-- toggle(HIDE) a term from within terminal edit mode
|
|
||||||
map("t", m.hide_term, "<C-\\><C-n> :ToggleTerm <CR>", opt)
|
|
||||||
map("t", m.hide_term, "<C-\\><C-n> :ToggleTerm <CR>", opt)
|
|
||||||
map("t", m.hide_term, "<C-\\><C-n> :ToggleTerm <CR>", opt)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
terms()
|
||||||
|
|
||||||
M.truezen = function()
|
M.truezen = function()
|
||||||
local m = user_map.truezen
|
local m = user_map.truezen
|
||||||
|
|
||||||
|
@ -135,6 +134,7 @@ M.bufferline = function()
|
||||||
|
|
||||||
map("n", m.new_buffer, ":enew<CR>", opt) -- new buffer
|
map("n", m.new_buffer, ":enew<CR>", opt) -- new buffer
|
||||||
map("n", m.newtab, ":tabnew<CR>", opt) -- new tab
|
map("n", m.newtab, ":tabnew<CR>", opt) -- new tab
|
||||||
|
map("n", m.close, ":lua require('utils').close_buffer() <CR>", opt) -- close buffer
|
||||||
|
|
||||||
-- move between tabs
|
-- move between tabs
|
||||||
|
|
||||||
|
|
|
@ -212,16 +212,6 @@ return packer.startup(function()
|
||||||
}
|
}
|
||||||
|
|
||||||
-- misc plugins
|
-- misc plugins
|
||||||
use {
|
|
||||||
"akinsho/nvim-toggleterm.lua",
|
|
||||||
event = "BufWinEnter",
|
|
||||||
config = function()
|
|
||||||
require "plugins.toggleterm"
|
|
||||||
end,
|
|
||||||
setup = function()
|
|
||||||
require("mappings").toggleterm()
|
|
||||||
end,
|
|
||||||
}
|
|
||||||
use {
|
use {
|
||||||
"windwp/nvim-autopairs",
|
"windwp/nvim-autopairs",
|
||||||
after = "nvim-compe",
|
after = "nvim-compe",
|
||||||
|
|
|
@ -24,6 +24,24 @@ bufferline.setup {
|
||||||
separator_style = "thin",
|
separator_style = "thin",
|
||||||
mappings = true,
|
mappings = true,
|
||||||
always_show_bufferline = true,
|
always_show_bufferline = true,
|
||||||
|
custom_filter = function(buf_number)
|
||||||
|
-- Func to filter out our managed/persistent split terms
|
||||||
|
local present_type, type = pcall(function()
|
||||||
|
return vim.api.nvim_buf_get_var(buf_number, "term_type")
|
||||||
|
end)
|
||||||
|
|
||||||
|
if present_type then
|
||||||
|
if type == "vert" then
|
||||||
|
return false
|
||||||
|
elseif type == "hori" then
|
||||||
|
return false
|
||||||
|
else
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
else
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end,
|
||||||
},
|
},
|
||||||
highlights = {
|
highlights = {
|
||||||
fill = {
|
fill = {
|
||||||
|
|
|
@ -65,8 +65,11 @@ telescope.setup {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
-- NvChad pickers
|
||||||
-- load the theme_switcher extension
|
-- load the theme_switcher extension
|
||||||
require("telescope").load_extension "themes"
|
require("telescope").load_extension "themes"
|
||||||
|
-- load the term_picker extension
|
||||||
|
require("telescope").load_extension "terms"
|
||||||
|
|
||||||
if not pcall(function()
|
if not pcall(function()
|
||||||
telescope.load_extension "fzf"
|
telescope.load_extension "fzf"
|
||||||
|
|
|
@ -1,47 +0,0 @@
|
||||||
local present, toggleterm = pcall(require, "toggleterm")
|
|
||||||
if not present then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
toggleterm.setup {
|
|
||||||
-- size can be a number or function which is passed the current terminal
|
|
||||||
size = function(term)
|
|
||||||
if term.direction == "horizontal" then
|
|
||||||
return 15
|
|
||||||
elseif term.direction == "vertical" then
|
|
||||||
return vim.o.columns * 0.4
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
-- open_mapping = [[<C-\>]], -- mapping set in mappings.lua
|
|
||||||
hide_numbers = true, -- hide the number column in toggleterm buffers
|
|
||||||
shade_terminals = false,
|
|
||||||
start_in_insert = true,
|
|
||||||
-- insert_mappings = true, -- see 'open_mapping', not set on purpose
|
|
||||||
-- whether or not the open mapping applies in insert mode
|
|
||||||
persist_size = true,
|
|
||||||
direction = "vertical",
|
|
||||||
close_on_exit = true, -- close the terminal window when the process exits
|
|
||||||
-- This field is only relevant if direction is set to 'float'
|
|
||||||
float_opts = {
|
|
||||||
border = "single",
|
|
||||||
winblend = 0,
|
|
||||||
highlights = {
|
|
||||||
border = "Normal",
|
|
||||||
background = "Normal",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
local Terminal = require("toggleterm.terminal").Terminal
|
|
||||||
|
|
||||||
_G.termW = Terminal:new {
|
|
||||||
direction = "window",
|
|
||||||
}
|
|
||||||
|
|
||||||
_G.termV = Terminal:new {
|
|
||||||
direction = "vertical",
|
|
||||||
}
|
|
||||||
|
|
||||||
_G.termH = Terminal:new {
|
|
||||||
direction = "horizontal",
|
|
||||||
}
|
|
144
lua/telescope/_extensions/terms.lua
Normal file
144
lua/telescope/_extensions/terms.lua
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
-- This file can be loaded as a telescope extension
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
-- Custom theme picker
|
||||||
|
-- Most of the code is copied from telescope buffer builtin
|
||||||
|
-- Src: https://github.com/nvim-telescope/telescope.nvim/blob/master/lua/telescope/builtin/internal.lua
|
||||||
|
M.term_picker = function(opts)
|
||||||
|
local pickers, finders, previewers, make_entry, actions, action_state, utils, conf
|
||||||
|
if pcall(require, "telescope") then
|
||||||
|
pickers = require "telescope.pickers"
|
||||||
|
finders = require "telescope.finders"
|
||||||
|
previewers = require "telescope.previewers"
|
||||||
|
|
||||||
|
make_entry = require "telescope.make_entry"
|
||||||
|
actions = require "telescope.actions"
|
||||||
|
action_state = require "telescope.actions.state"
|
||||||
|
utils = require "telescope.utils"
|
||||||
|
conf = require("telescope.config").values
|
||||||
|
else
|
||||||
|
error "Cannot find telescope!"
|
||||||
|
end
|
||||||
|
|
||||||
|
local filter = vim.tbl_filter
|
||||||
|
|
||||||
|
local local_utils = require "utils"
|
||||||
|
|
||||||
|
-- buffer number and name
|
||||||
|
local bufnr = vim.api.nvim_get_current_buf()
|
||||||
|
local bufname = vim.api.nvim_buf_get_name(bufnr)
|
||||||
|
|
||||||
|
local bufnrs = filter(function(b)
|
||||||
|
local present_type, type = pcall(function()
|
||||||
|
return vim.api.nvim_buf_get_var(b, "term_type")
|
||||||
|
end)
|
||||||
|
|
||||||
|
if not present_type then
|
||||||
|
-- let's only terms that we created
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- if 1 ~= vim.fn.buflisted(b) then
|
||||||
|
-- return false
|
||||||
|
-- end
|
||||||
|
-- only hide unloaded buffers if opts.show_all_buffers is false, keep them listed if true or nil
|
||||||
|
if opts.show_all_buffers == false and not vim.api.nvim_buf_is_loaded(b) then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
if opts.ignore_current_buffer and b == vim.api.nvim_get_current_buf() then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end, vim.api.nvim_list_bufs())
|
||||||
|
if not next(bufnrs) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if opts.sort_mru then
|
||||||
|
table.sort(bufnrs, function(a, b)
|
||||||
|
return vim.fn.getbufinfo(a)[1].lastused > vim.fn.getbufinfo(b)[1].lastused
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
local buffers = {}
|
||||||
|
local default_selection_idx = 1
|
||||||
|
for _, bufnr in ipairs(bufnrs) do
|
||||||
|
local flag = bufnr == vim.fn.bufnr "" and "%" or (bufnr == vim.fn.bufnr "#" and "#" or " ")
|
||||||
|
|
||||||
|
if opts.sort_lastused and not opts.ignore_current_buffer and flag == "#" then
|
||||||
|
default_selection_idx = 2
|
||||||
|
end
|
||||||
|
|
||||||
|
local element = {
|
||||||
|
bufnr = bufnr,
|
||||||
|
flag = flag,
|
||||||
|
info = vim.fn.getbufinfo(bufnr)[1],
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.sort_lastused and (flag == "#" or flag == "%") then
|
||||||
|
local idx = ((buffers[1] ~= nil and buffers[1].flag == "%") and 2 or 1)
|
||||||
|
table.insert(buffers, idx, element)
|
||||||
|
else
|
||||||
|
table.insert(buffers, element)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not opts.bufnr_width then
|
||||||
|
local max_bufnr = math.max(unpack(bufnrs))
|
||||||
|
opts.bufnr_width = #tostring(max_bufnr)
|
||||||
|
end
|
||||||
|
|
||||||
|
pickers.new(opts, {
|
||||||
|
prompt_title = "Terminal buffers",
|
||||||
|
finder = finders.new_table {
|
||||||
|
results = buffers,
|
||||||
|
entry_maker = opts.entry_maker or make_entry.gen_from_buffer(opts),
|
||||||
|
},
|
||||||
|
previewer = conf.grep_previewer(opts),
|
||||||
|
sorter = conf.generic_sorter(opts),
|
||||||
|
default_selection_index = default_selection_idx,
|
||||||
|
attach_mappings = function(prompt_bufnr)
|
||||||
|
actions.select_default:replace(function()
|
||||||
|
local entry = action_state.get_selected_entry()
|
||||||
|
actions.close(prompt_bufnr)
|
||||||
|
|
||||||
|
local buf = entry.bufnr
|
||||||
|
|
||||||
|
local chad_term, type = pcall(function()
|
||||||
|
return vim.api.nvim_buf_get_var(buf, "term_type")
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- TODO buffer checks/error detection (make sure we do get a buf)
|
||||||
|
|
||||||
|
if chad_term then
|
||||||
|
if type == "wind" then
|
||||||
|
-- swtich to term buff & show in bufferline
|
||||||
|
vim.cmd(string.format('b %d | setlocal bl', buf))
|
||||||
|
-- vim.cmd('startinsert') TODO fix this
|
||||||
|
elseif type == "vert" then
|
||||||
|
vim.cmd(string.format('vsp #%d', buf))
|
||||||
|
-- vim.cmd('startinsert') TODO fix this
|
||||||
|
elseif type == "hori" then
|
||||||
|
-- TODO change 15 to a chad config var number
|
||||||
|
vim.cmd(string.format('15 sp #%d ', buf))
|
||||||
|
-- vim.cmd('startinsert') TODO fix this
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
return true
|
||||||
|
end,
|
||||||
|
}):find()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- register term picker as terms to telescope
|
||||||
|
local present, telescope = pcall(require, "telescope")
|
||||||
|
if present then
|
||||||
|
return telescope.register_extension {
|
||||||
|
exports = {
|
||||||
|
terms = M.term_picker,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
else
|
||||||
|
error "Cannot find telescope!"
|
||||||
|
end
|
113
lua/utils.lua
113
lua/utils.lua
|
@ -34,6 +34,119 @@ M.clear_cmdline = function()
|
||||||
end, 0)
|
end, 0)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
M.close_buffer = function(bufexpr, force)
|
||||||
|
-- This is a modification of a NeoVim plugin from
|
||||||
|
-- Author: ojroques - Olivier Roques
|
||||||
|
-- Src: https://github.com/ojroques/nvim-bufdel
|
||||||
|
-- (Author has okayed copy-paste)
|
||||||
|
|
||||||
|
-- Options
|
||||||
|
local opts = {
|
||||||
|
next = 'cycle', -- how to retrieve the next buffer
|
||||||
|
quit = false, -- exit when last buffer is deleted
|
||||||
|
--TODO make this a chadrc flag/option
|
||||||
|
}
|
||||||
|
|
||||||
|
-- ----------------
|
||||||
|
-- Helper functions
|
||||||
|
-- ----------------
|
||||||
|
|
||||||
|
-- Switch to buffer 'buf' on each window from list 'windows'
|
||||||
|
local function switch_buffer(windows, buf)
|
||||||
|
local cur_win = vim.fn.winnr()
|
||||||
|
for _, winid in ipairs(windows) do
|
||||||
|
vim.cmd(string.format('%d wincmd w', vim.fn.win_id2win(winid)))
|
||||||
|
vim.cmd(string.format('buffer %d', buf))
|
||||||
|
end
|
||||||
|
vim.cmd(string.format('%d wincmd w', cur_win)) -- return to original window
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Select the first buffer with a number greater than given buffer
|
||||||
|
local function get_next_buf(buf)
|
||||||
|
local next = vim.fn.bufnr('#')
|
||||||
|
if opts.next == 'alternate' and vim.fn.buflisted(next) == 1 then
|
||||||
|
return next
|
||||||
|
end
|
||||||
|
for i = 0, vim.fn.bufnr('$') - 1 do
|
||||||
|
next = (buf + i) % vim.fn.bufnr('$') + 1 -- will loop back to 1
|
||||||
|
if vim.fn.buflisted(next) == 1 then
|
||||||
|
return next
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ----------------
|
||||||
|
-- End helper functions
|
||||||
|
-- ----------------
|
||||||
|
|
||||||
|
local buf = vim.fn.bufnr()
|
||||||
|
if vim.fn.buflisted(buf) == 0 then -- exit if buffer number is invalid
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if #vim.fn.getbufinfo({buflisted = 1}) < 2 then
|
||||||
|
if opts.quit then
|
||||||
|
-- exit when there is only one buffer left
|
||||||
|
if force then
|
||||||
|
vim.cmd('qall!')
|
||||||
|
else
|
||||||
|
vim.cmd('confirm qall')
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local chad_term, type = pcall(function()
|
||||||
|
return vim.api.nvim_buf_get_var(buf, "term_type")
|
||||||
|
end)
|
||||||
|
|
||||||
|
if chad_term then
|
||||||
|
-- Must be a window type
|
||||||
|
vim.cmd(string.format('setlocal nobl', buf))
|
||||||
|
vim.cmd('enew')
|
||||||
|
return
|
||||||
|
end
|
||||||
|
-- don't exit and create a new empty buffer
|
||||||
|
vim.cmd('enew')
|
||||||
|
vim.cmd('bp')
|
||||||
|
end
|
||||||
|
|
||||||
|
local next_buf = get_next_buf(buf)
|
||||||
|
local windows = vim.fn.getbufinfo(buf)[1].windows
|
||||||
|
|
||||||
|
-- force deletion of terminal buffers to avoid the prompt
|
||||||
|
if force or vim.fn.getbufvar(buf, '&buftype') == 'terminal' then
|
||||||
|
local chad_term, type = pcall(function()
|
||||||
|
return vim.api.nvim_buf_get_var(buf, "term_type")
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- TODO this scope is error prone, make resilient
|
||||||
|
if chad_term then
|
||||||
|
if type == "wind" then
|
||||||
|
-- hide from bufferline
|
||||||
|
vim.cmd(string.format('%d bufdo setlocal nobl', buf))
|
||||||
|
-- swtich to another buff
|
||||||
|
-- TODO switch to next bufffer, this works too
|
||||||
|
vim.cmd('BufferLineCycleNext')
|
||||||
|
else
|
||||||
|
local cur_win = vim.fn.winnr()
|
||||||
|
-- we can close this window
|
||||||
|
vim.cmd(string.format('%d wincmd c', cur_win))
|
||||||
|
return
|
||||||
|
end
|
||||||
|
else
|
||||||
|
switch_buffer(windows, next_buf)
|
||||||
|
vim.cmd(string.format('bd! %d', buf))
|
||||||
|
end
|
||||||
|
else
|
||||||
|
switch_buffer(windows, next_buf)
|
||||||
|
vim.cmd(string.format('silent! confirm bd %d', buf))
|
||||||
|
end
|
||||||
|
-- revert buffer switches if user has canceled deletion
|
||||||
|
if vim.fn.buflisted(buf) == 1 then
|
||||||
|
switch_buffer(windows, buf)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- 1st arg - r or w
|
-- 1st arg - r or w
|
||||||
-- 2nd arg - file path
|
-- 2nd arg - file path
|
||||||
-- 3rd arg - content if 1st arg is w
|
-- 3rd arg - content if 1st arg is w
|
||||||
|
|
Loading…
Reference in a new issue