├── .gitignore
├── LICENSE
├── README.md
├── dev
└── init.lua
├── lua
└── nvim-terminal
│ ├── config.lua
│ ├── init.lua
│ ├── terminal.lua
│ ├── util.lua
│ └── window.lua
└── resources
└── gif
└── demo.gif
/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled Lua sources
2 | luac.out
3 |
4 | # luarocks build files
5 | *.src.rock
6 | *.zip
7 | *.tar.gz
8 |
9 | # Object files
10 | *.o
11 | *.os
12 | *.ko
13 | *.obj
14 | *.elf
15 |
16 | # Precompiled Headers
17 | *.gch
18 | *.pch
19 |
20 | # Libraries
21 | *.lib
22 | *.a
23 | *.la
24 | *.lo
25 | *.def
26 | *.exp
27 |
28 | # Shared objects (inc. Windows DLLs)
29 | *.dll
30 | *.so
31 | *.so.*
32 | *.dylib
33 |
34 | # Executables
35 | *.exe
36 | *.out
37 | *.app
38 | *.i*86
39 | *.x86_64
40 | *.hex
41 |
42 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 s1n7ax
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # nvim-terminal
2 |
3 | Terminal plugin to open/toggle the terminals in Neovim
4 |
5 | https://user-images.githubusercontent.com/18459807/129582749-2e732591-cb8d-4cb8-a427-9da0c79a621d.mp4
6 |
7 | ## Features
8 |
9 | * Toggle terminal window
10 | * Quick switching between multiple terminal buffers
11 |
12 | ## Install the plugin
13 |
14 | **packer**
15 | ```lua
16 | use {
17 | 's1n7ax/nvim-terminal',
18 | config = function()
19 | vim.o.hidden = true
20 | require('nvim-terminal').setup()
21 | end,
22 | }
23 | ```
24 |
25 | ## Default Keymaps
26 |
27 | leader + ; - **Toggle open/close terminal**
28 |
29 | leader + 1 - **Open terminal 1**
30 |
31 | leader + 2 - **Open terminal 2**
32 |
33 | leader + 3 - **Open terminal 3**
34 |
35 | leader + 4 - **Open terminal 4**
36 |
37 | leader + 5 - **Open terminal 5**
38 |
39 | leader + + - **Increase window height**
40 |
41 | leader + - - **Decrease window height**
42 |
43 | leader + leader + + - **Increase window width**
44 |
45 | leader + leader + - - **Decrease window width**
46 |
47 | ## Configuration
48 |
49 | Simply pass the custom configuration to `setup` method
50 |
51 | ```lua
52 | -- following option will hide the buffer when it is closed instead of deleting
53 | -- the buffer. This is important to reuse the last terminal buffer
54 | -- IF the option is not set, plugin will open a new terminal every single time
55 | vim.o.hidden = true
56 |
57 | require('nvim-terminal').setup({
58 | window = {
59 | -- Do `:h :botright` for more information
60 | -- NOTE: width or height may not be applied in some "pos"
61 | position = 'botright',
62 |
63 | -- Do `:h split` for more information
64 | split = 'sp',
65 |
66 | -- Width of the terminal
67 | width = 50,
68 |
69 | -- Height of the terminal
70 | height = 15,
71 | },
72 |
73 | -- keymap to disable all the default keymaps
74 | disable_default_keymaps = false,
75 |
76 | -- keymap to toggle open and close terminal window
77 | toggle_keymap = ';',
78 |
79 | -- increase the window height by when you hit the keymap
80 | window_height_change_amount = 2,
81 |
82 | -- increase the window width by when you hit the keymap
83 | window_width_change_amount = 2,
84 |
85 | -- keymap to increase the window width
86 | increase_width_keymap = '+',
87 |
88 | -- keymap to decrease the window width
89 | decrease_width_keymap = '-',
90 |
91 | -- keymap to increase the window height
92 | increase_height_keymap = '+',
93 |
94 | -- keymap to decrease the window height
95 | decrease_height_keymap = '-',
96 |
97 | terminals = {
98 | -- keymaps to open nth terminal
99 | {keymap = '1'},
100 | {keymap = '2'},
101 | {keymap = '3'},
102 | {keymap = '4'},
103 | {keymap = '5'},
104 | },
105 | })
106 | ```
107 |
108 | ## Add Keymaps Manually
109 |
110 | `nvim-terminal` adds a global variable called `NTGlobal`. When you call
111 | `require('nvim-terminal').setup()` it adds `terminal` and `window` properties to
112 | `NTGlobal`
113 |
114 | ```lua
115 | vim.api.nvim_set_keymap('n', 't', ':lua NTGlobal["terminal"]:toggle()', silent)
116 | vim.api.nvim_set_keymap('n', '1', ':lua NTGlobal["terminal"]:open(1)', silent)
117 | vim.api.nvim_set_keymap('n', '+', ':lua NTGlobal["window"]:change_height(2)', silent)
118 | vim.api.nvim_set_keymap('n', '-', ':lua NTGlobal["window"]:change_height(-2)', silent)
119 | ```
120 |
121 | ## PRO MODE
122 |
123 | ### Default Terminal
124 |
125 | ```lua
126 | terminal = require('nvim-terminal').DefaultTerminal;
127 |
128 | local silent = { silent = true }
129 |
130 | vim.api.nvim_set_keymap('n', 't', ':lua terminal:toggle()', silent)
131 | vim.api.nvim_set_keymap('n', '1', ':lua terminal:open(1)', silent)
132 | vim.api.nvim_set_keymap('n', '2', ':lua terminal:open(2)', silent)
133 | vim.api.nvim_set_keymap('n', '3', ':lua terminal:open(3)', silent)
134 | ```
135 |
136 | ### Customized Window
137 |
138 | ```lua
139 | local Terminal = require('nvim-terminal.terminal')
140 | local Window = require('nvim-terminal.window')
141 |
142 | local window = Window:new({
143 | position = 'botright',
144 | split = 'sp',
145 | width = 50,
146 | height = 15
147 | })
148 |
149 | terminal = Terminal:new(window)
150 | ```
151 |
--------------------------------------------------------------------------------
/dev/init.lua:
--------------------------------------------------------------------------------
1 | -- plugin name will be used to reload the loaded modules
2 | local package_name = 'nvim-terminal'
3 |
4 | -- add the escape character to special characters
5 | local escape_pattern = function(text) return text:gsub('([^%w])', '%%%1') end
6 |
7 | -- unload loaded modules by the matching text
8 | local unload_packages = function()
9 | local esc_package_name = escape_pattern(package_name)
10 |
11 | for module_name, _ in pairs(package.loaded) do
12 | if string.find(module_name, esc_package_name) then
13 | package.loaded[module_name] = nil
14 | end
15 | end
16 | end
17 |
18 | -- executes the run method in the package
19 | local run_action = function()
20 | require('nvim-terminal').setup()
21 |
22 | end
23 |
24 | -- unload and run the function from the package
25 | function Reload_and_run()
26 | unload_packages()
27 | run_action()
28 | end
29 |
30 | local set_keymap = vim.api.nvim_set_keymap
31 |
32 | set_keymap('n', ',r', 'luafile dev/init.lua', {})
33 | set_keymap('n', ',w', 'lua Reload_and_run()', {})
34 |
--------------------------------------------------------------------------------
/lua/nvim-terminal/config.lua:
--------------------------------------------------------------------------------
1 | local config = {
2 | window = {
3 | -- Do `:h :botright` for more information
4 | position = 'botright',
5 |
6 | -- Do `:h split` for more information
7 | split = 'sp',
8 |
9 | -- Width of the terminal
10 | width = 50,
11 |
12 | -- Height of the terminal
13 | height = 15,
14 | },
15 |
16 | -- keymap to disablesb all the default keymaps
17 | disable_default_keymaps = false,
18 |
19 | -- keymap to toggle open and close terminal window
20 | toggle_keymap = ';',
21 |
22 | -- increase the window width by when you hit the keymap
23 | window_height_change_amount = 2,
24 |
25 | -- increase the window height by when you hit the keymap
26 | window_width_change_amount = 2,
27 |
28 | -- keymap to increase the window width
29 | increase_width_keymap = '+',
30 |
31 | -- keymap to decrease the window width
32 | decrease_width_keymap = '-',
33 |
34 | -- keymap to increase the window height
35 | increase_height_keymap = '+',
36 |
37 | -- keymap to decrease the window height
38 | decrease_height_keymap = '-',
39 |
40 | terminals = {
41 | -- keymaps to open nth terminal
42 | {keymap = '1'},
43 | {keymap = '2'},
44 | {keymap = '3'},
45 | {keymap = '4'},
46 | {keymap = '5'},
47 | },
48 | }
49 |
50 | return config
51 |
--------------------------------------------------------------------------------
/lua/nvim-terminal/init.lua:
--------------------------------------------------------------------------------
1 | local config = require("nvim-terminal.config")
2 | local Util = require("nvim-terminal.util")
3 | local Terminal = require("nvim-terminal.terminal")
4 | local Window = require("nvim-terminal.window")
5 | local DefaultTerminal = Terminal:new(Window:new())
6 | local S = Util.String
7 |
8 | NTGlobal = {}
9 |
10 | local setup = function(opts)
11 | config = Util.Lua.merge_tables(config, opts or {})
12 |
13 | if config.terminals == nil then
14 | return
15 | end
16 |
17 | local window = Window:new(config.window)
18 | local terminal = Terminal:new(window)
19 |
20 | NTGlobal["terminal"] = terminal
21 | NTGlobal["window"] = window
22 |
23 | if not config.disable_default_keymaps then
24 | -- setting toggle keymap
25 | if S.is_not_empty(config.toggle_keymap) then
26 | vim.keymap.set("n", config.toggle_keymap, function()
27 | NTGlobal["terminal"]:toggle()
28 | end, { silent = true })
29 | end
30 |
31 | -- setting window width keymap
32 | if S.is_not_empty(config.increase_width_keymap) then
33 | vim.keymap.set("n", config.increase_width_keymap, function()
34 | NTGlobal["window"]:change_width(config.window_width_change_amount)
35 | end, { silent = true })
36 | end
37 |
38 | -- setting window width keymap
39 | if S.is_not_empty(config.decrease_width_keymap) then
40 | vim.keymap.set("n", config.decrease_width_keymap, function()
41 | NTGlobal["window"]:change_width(-config.window_width_change_amount)
42 | end, { silent = true })
43 | end
44 |
45 | -- setting window height keymap
46 | if S.is_not_empty(config.increase_height_keymap) then
47 | vim.keymap.set("n", config.increase_height_keymap, function()
48 | NTGlobal["window"]:change_height(config.window_height_change_amount)
49 | end, { silent = true })
50 | end
51 |
52 | -- setting window height keymap
53 | if S.is_not_empty(config.decrease_height_keymap) then
54 | vim.keymap.set("n", config.decrease_height_keymap, function()
55 | NTGlobal["window"]:change_height(-config.window_height_change_amount)
56 | end, { silent = true })
57 | end
58 |
59 | for index, term_conf in ipairs(config.terminals) do
60 | -- setting terminal keymap
61 | vim.keymap.set("n", term_conf.keymap, function()
62 | NTGlobal["terminal"]:open(index)
63 | end, { silent = true })
64 | end
65 | end
66 | end
67 |
68 | return {
69 | Terminal = Terminal,
70 | Window = Window,
71 | DefaultTerminal = DefaultTerminal,
72 | setup = setup,
73 | }
74 |
--------------------------------------------------------------------------------
/lua/nvim-terminal/terminal.lua:
--------------------------------------------------------------------------------
1 | local Window = require('nvim-terminal.window')
2 |
3 | local v = vim.api
4 | local cmd = vim.cmd
5 |
6 | local Terminal = {bufs = {}, last_winid = nil, last_term = nil}
7 |
8 | function Terminal:new(window, opt)
9 | self.window = window or Window:new()
10 | return self
11 | end
12 |
13 | function Terminal:init()
14 | error('There are some breaking changes!')
15 | error(
16 | 'Please check new configuration at https://github.com/s1n7ax/nvim-terminal')
17 | end
18 |
19 | function Terminal:open(term_number)
20 | term_number = term_number or 1
21 |
22 | local create_win = not self.window:is_valid()
23 | -- create buffer if it does not exist by the given term_number or the stored
24 | -- buffer number is no longer valid
25 | local create_buf = self.bufs[term_number] == nil or
26 | not v.nvim_buf_is_valid(self.bufs[term_number])
27 |
28 | -- window and buffer does not exist
29 | if create_win and create_buf then
30 | self.last_winid = v.nvim_get_current_win()
31 | self.window:create_term()
32 | self.bufs[term_number] = self.window:get_bufno()
33 |
34 | -- window does not exist but buffer does
35 | elseif create_win then
36 | self.last_winid = v.nvim_get_current_win()
37 | self.window:create(self.bufs[term_number])
38 |
39 | -- buffer does not exist but window does
40 | elseif create_buf then
41 | self.window:focus()
42 | cmd(':terminal')
43 | self.bufs[term_number] = self.window:get_bufno()
44 |
45 | -- buffer and window exist
46 | else
47 | local curr_term_buf = self.bufs[term_number]
48 | local last_term_buf = self.bufs[self.last_term]
49 |
50 | if curr_term_buf ~= last_term_buf then
51 | self.window:set_buf(curr_term_buf)
52 | end
53 | end
54 |
55 | self.last_term = term_number
56 | end
57 |
58 | function Terminal:close()
59 | local current_winid = v.nvim_get_current_win()
60 |
61 | if self.window:is_valid() then
62 | self.window:close()
63 |
64 | if current_winid == self.window.winid then
65 | v.nvim_set_current_win(self.last_winid)
66 | end
67 | end
68 | end
69 |
70 | function Terminal:toggle()
71 | self.last_term = self.last_term and self.last_term or 1
72 |
73 | local opened = self.window:is_valid()
74 |
75 | if opened then
76 | self:close()
77 | else
78 | self:open(self.last_term)
79 | end
80 |
81 | end
82 |
83 | return Terminal
84 |
--------------------------------------------------------------------------------
/lua/nvim-terminal/util.lua:
--------------------------------------------------------------------------------
1 | local Lua = {}
2 |
3 | -- Merge content of two table and returns a new table
4 | function Lua.merge_tables(t1, t2)
5 | for k, v in pairs(t2) do
6 | if (type(v) == 'table') and (type(t1[k] or false) == 'table') then
7 | Lua.merge_tables(t1[k], t2[k])
8 | else
9 | t1[k] = v
10 | end
11 | end
12 |
13 | return t1
14 | end
15 |
16 | local String = {}
17 |
18 | function String.is_not_empty(str) return str ~= nil and str ~= '' or false end
19 |
20 | return {Lua = Lua, String = String}
21 |
--------------------------------------------------------------------------------
/lua/nvim-terminal/window.lua:
--------------------------------------------------------------------------------
1 | local v = vim.api
2 | local cmd = vim.cmd
3 | local fn = vim.fn
4 |
5 | local Window = {}
6 |
7 | function Window:new(opt)
8 | opt = opt and opt or {}
9 |
10 | self.pos = opt.pos or opt.position or 'botright'
11 | self.split = opt.split or 'sp'
12 | self.width = opt.width or nil
13 | self.height = opt.height or nil
14 |
15 | return self
16 | end
17 |
18 | -- Opens new window bottom of tab
19 | -- @return { number } window id
20 | function Window:create(bufnr)
21 | local cmd_format = '%s %s +buffer\\ %d'
22 | cmd(cmd_format:format(self.pos, self.split, bufnr))
23 |
24 | self.winid = fn.win_getid()
25 |
26 | self:update_size()
27 |
28 | return self.winid
29 | end
30 |
31 | -- Opens new terminal window bottom of tab
32 | -- @return { number } window number
33 | function Window:create_term()
34 | local cmd_format = '%s new +term'
35 | cmd(cmd_format:format(self.pos))
36 |
37 | self.winid = fn.win_getid()
38 |
39 | self:update_size()
40 |
41 | return self.winid
42 | end
43 |
44 | -- Set window width to self.width
45 | function Window:update_size()
46 | if self.width ~= nil then v.nvim_win_set_width(self.winid, self.width) end
47 |
48 | if self.height ~= nil then v.nvim_win_set_height(self.winid, self.height) end
49 | end
50 |
51 | function Window:get_size()
52 | local width = v.nvim_win_get_width(self.winid)
53 | local height = v.nvim_win_get_height(self.winid)
54 |
55 | return width, height
56 | end
57 |
58 | -- close the window
59 | function Window:close(winid)
60 | if self:is_valid() then v.nvim_win_close(self.winid, false) end
61 | end
62 |
63 | -- Returns the validity of the window
64 | -- @return { boolean } window is valid or not
65 | function Window:is_valid()
66 | if (self.winid == nil) then return false end
67 |
68 | return v.nvim_win_is_valid(self.winid)
69 | end
70 |
71 | function Window:set_buf(bufno) return v.nvim_win_set_buf(self.winid, bufno) end
72 |
73 | function Window:focus() v.nvim_set_current_win(self.winid) end
74 |
75 | -- Returns the buffer number
76 | -- @return { number } buffer number
77 | function Window:get_bufno()
78 | if self:is_valid() then return v.nvim_win_get_buf(self.winid) end
79 | end
80 |
81 | -- Increase window height
82 | function Window:change_height(by)
83 | local _, height = self:get_size()
84 | self.height = height + by
85 | self:update_size()
86 | end
87 |
88 | -- Increase window height
89 | function Window:change_width(by)
90 | local width, _ = self:get_size()
91 | self.width = width + by
92 | self:update_size()
93 | end
94 |
95 | return Window
96 |
--------------------------------------------------------------------------------
/resources/gif/demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s1n7ax/nvim-terminal/e058de4b8029d7605b17275f30f83be8f8df5f62/resources/gif/demo.gif
--------------------------------------------------------------------------------