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
					
				| @ -13,7 +13,6 @@ M.ui = { | ||||
|    hidden_statusline = { | ||||
|       -- these are filetypes, not pattern matched | ||||
|       "NvimTree", | ||||
|       "toggleterm", | ||||
|    }, | ||||
| } | ||||
| 
 | ||||
| @ -107,6 +106,7 @@ M.mappings = { | ||||
|    bufferline = { | ||||
|       new_buffer = "<S-t>", | ||||
|       newtab = "<C-t>b", | ||||
|       close = "<S-x>", -- close a buffer with custom func in utils.lua | ||||
|       cycleNext = "<TAB>", -- next buffer | ||||
|       cyclePrev = "<S-Tab>", -- previous buffer | ||||
|    }, | ||||
| @ -116,11 +116,13 @@ M.mappings = { | ||||
|       diffget_3 = "<leader>gl", | ||||
|       git_blame = "<leader>gb", | ||||
|    }, | ||||
|    toggleterm = { | ||||
|       toggle_window = "<leader>w", | ||||
|       toggle_vert = "<leader>v", | ||||
|       toggle_hori = "<leader>h", | ||||
|       hide_term = "JK", | ||||
|    terms = { -- below are NvChad mappings, not plugin mappings | ||||
|       esc_termmode = "jk", | ||||
|       esc_hide_termmode = "JK", | ||||
|       pick_term = "<leader>W", -- note: this is a telescope extension | ||||
|       new_wind = "<leader>w", | ||||
|       new_vert = "<leader>v", | ||||
|       new_hori = "<leader>h", | ||||
|    }, | ||||
|    -- navigation in insert mode | ||||
|    insert_nav = { | ||||
| @ -131,10 +133,7 @@ M.mappings = { | ||||
|       prev_line = "<C-j>", | ||||
|       next_line = "<C-k>", | ||||
|    }, | ||||
|    -- non plugin | ||||
|    misc = { | ||||
|       esc_Termmode = "jk", -- get out of terminal mode | ||||
|       close_buffer = "<S-x>", -- close current focused buffer | ||||
|       copywhole_file = "<C-a>", | ||||
|       toggle_linenr = "<leader>n", -- show or hide line number | ||||
|       theme_toggle = "<leader>x", | ||||
|  | ||||
| @ -41,28 +41,27 @@ map("n", miscMap.copywhole_file, ":%y+<CR>", opt) | ||||
| -- toggle numbers | ||||
| 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 | ||||
| -- todo: don't close if non-terminal buffer is saved | ||||
| map("n", miscMap.close_buffer, ":bd!<CR>", opt) | ||||
| -- terminals | ||||
| local function terms() | ||||
|    local m = user_map.terms | ||||
| 
 | ||||
| M.toggleterm = function() | ||||
|    local m = user_map.toggleterm | ||||
|    -- get out of terminal mode | ||||
|    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 | ||||
|    map("n", m.toggle_window, ":lua termW:toggle() <CR>", opt) | ||||
|    map("n", m.toggle_vert, ":lua termV:toggle() <CR>", opt) | ||||
|    map("n", m.toggle_hori, ":lua termH:toggle() <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) | ||||
|    -- TODO this opens on top of an existing vert/hori term, fixme | ||||
|    map("n", m.new_wind, ":execute 'terminal' | let b:term_type = 'wind' | startinsert <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) | ||||
| end | ||||
| 
 | ||||
| terms() | ||||
| 
 | ||||
| M.truezen = function() | ||||
|    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.newtab, ":tabnew<CR>", opt) -- new tab | ||||
|    map("n", m.close, ":lua require('utils').close_buffer() <CR>", opt) -- close  buffer | ||||
| 
 | ||||
|    -- move between tabs | ||||
| 
 | ||||
|  | ||||
| @ -212,16 +212,6 @@ return packer.startup(function() | ||||
|    } | ||||
| 
 | ||||
|    -- misc plugins | ||||
|    use { | ||||
|       "akinsho/nvim-toggleterm.lua", | ||||
|       event = "BufWinEnter", | ||||
|       config = function() | ||||
|          require "plugins.toggleterm" | ||||
|       end, | ||||
|       setup = function() | ||||
|          require("mappings").toggleterm() | ||||
|       end, | ||||
|    } | ||||
|    use { | ||||
|       "windwp/nvim-autopairs", | ||||
|       after = "nvim-compe", | ||||
|  | ||||
| @ -5,7 +5,7 @@ local present, bufferline = pcall(require, "bufferline") | ||||
| if not present then | ||||
|    return | ||||
| end | ||||
| 
 | ||||
|        | ||||
| bufferline.setup { | ||||
|    options = { | ||||
|       offsets = { { filetype = "NvimTree", text = "", padding = 1 } }, | ||||
| @ -24,6 +24,24 @@ bufferline.setup { | ||||
|       separator_style = "thin", | ||||
|       mappings = 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 = { | ||||
|       fill = { | ||||
|  | ||||
| @ -65,8 +65,11 @@ telescope.setup { | ||||
|    }, | ||||
| } | ||||
| 
 | ||||
| -- NvChad pickers | ||||
| -- load the theme_switcher extension | ||||
| require("telescope").load_extension "themes" | ||||
| -- load the term_picker extension | ||||
| require("telescope").load_extension "terms" | ||||
| 
 | ||||
| if not pcall(function() | ||||
|    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 | ||||
| 
 | ||||
| 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 | ||||
| -- 2nd arg - file path | ||||
| -- 3rd arg - content if 1st arg is w | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user