Skip to content

Commit 6ad25db

Browse files
Add exclusive_layout option
When enabled, opening a terminal closes any other terminal currently shown in the same layout.
1 parent 0fd6870 commit 6ad25db

11 files changed

Lines changed: 62 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ All notable changes to this project will be documented in this file.
44

55
## [Unreleased]
66

7+
- Add `exclusive_layout` option (default `false`). When enabled, opening the terminal closes any other terminal currently shown in the same layout.
8+
79
## 1.1.0 - 2026-02-05
810

911
- Fix: Correctly center floating terminal when no borders are used.

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ require("ergoterm").setup({
7070
| `clear_env` | `boolean` | `false` | Clear environment variables before starting |
7171
| `cleanup_on_failure` | `boolean` | `false` | Cleanup terminal when process exits with non-zero code |
7272
| `cleanup_on_success` | `boolean` | `true` | Cleanup terminal when process exits with code 0 |
73+
| `exclusive_layout` | `boolean` | `false` | When this terminal opens, close any other terminal currently shown in the same layout (jobs are kept alive) |
7374
| `default_action` | `function` | `function(term) term:focus() end` | Action performed when selecting terminal with default picker action |
7475
| `fixed_height` | boolean | `true` | Keep the terminal height when new horizontal splits are opened (it depends on vim's `equalalways` being set) |
7576
| `fixed_width` | boolean | `true` | Keep the terminal width when new vertical splits are opened (it depends on vim's `equalalways` being set) |

doc/ergoterm.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,12 @@ default_action *ergoterm-default_action*
416416
Default: `function(term) term:focus() end`
417417
Action performed when selecting terminal with default picker action
418418

419+
exclusive_layout *ergoterm-exclusive_layout*
420+
Type: `boolean`
421+
Default: `false`
422+
When this terminal opens, close any other terminal currently shown in the
423+
same layout.
424+
419425
fixed_height *ergoterm-fixed_height*
420426
Type: `boolean`
421427
Default: `true`

lua/ergoterm/commandline.lua

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ end
5656
---@field auto_list boolean?
5757
---@field start_in_insert boolean?
5858
---@field sticky boolean?
59+
---@field exclusive_layout boolean?
5960
---@field cleanup_on_success boolean?
6061
---@field cleanup_on_failure boolean?
6162
---@field scrollback number?
@@ -109,6 +110,7 @@ function M.parse(args)
109110
auto_list = true,
110111
start_in_insert = true,
111112
sticky = true,
113+
exclusive_layout = true,
112114
cleanup_on_success = true,
113115
cleanup_on_failure = true,
114116
show_on_success = true,
@@ -309,6 +311,8 @@ M._all_options = {
309311

310312
sticky = M._boolean_options,
311313

314+
exclusive_layout = M._boolean_options,
315+
312316
cleanup_on_success = M._boolean_options,
313317

314318
cleanup_on_failure = M._boolean_options,
@@ -389,6 +393,7 @@ M._term_new_options = {
389393
["env."] = M._all_options["env."],
390394
start_in_insert = M._all_options.start_in_insert,
391395
sticky = M._all_options.sticky,
396+
exclusive_layout = M._all_options.exclusive_layout,
392397
cleanup_on_success = M._all_options.cleanup_on_success,
393398
cleanup_on_failure = M._all_options.cleanup_on_failure,
394399
scrollback = M._all_options.scrollback,
@@ -425,6 +430,7 @@ M._term_update_options = {
425430
auto_list = M._all_options.auto_list,
426431
start_in_insert = M._all_options.start_in_insert,
427432
sticky = M._all_options.sticky,
433+
exclusive_layout = M._all_options.exclusive_layout,
428434
cleanup_on_success = M._all_options.cleanup_on_success,
429435
cleanup_on_failure = M._all_options.cleanup_on_failure,
430436
show_on_success = M._all_options.show_on_success,

lua/ergoterm/commands.lua

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ function M.new(args)
3737
vim.validate("cleanup_on_failure", parsed.cleanup_on_failure, "boolean", true)
3838
vim.validate("show_on_success", parsed.show_on_success, "boolean", true)
3939
vim.validate("show_on_failure", parsed.show_on_failure, "boolean", true)
40+
vim.validate("exclusive_layout", parsed.exclusive_layout, "boolean", true)
4041
vim.validate("size", parsed.size, "table", true)
4142
vim.validate("float_opts", parsed.float_opts, "table", true)
4243
vim.validate("tags", parsed.tags, "table", true)
@@ -61,6 +62,7 @@ function M.new(args)
6162
cleanup_on_failure = parsed.cleanup_on_failure,
6263
show_on_success = parsed.show_on_success,
6364
show_on_failure = parsed.show_on_failure,
65+
exclusive_layout = parsed.exclusive_layout,
6466
size = parsed.size,
6567
float_opts = parsed.float_opts,
6668
tags = parsed.tags,
@@ -188,6 +190,7 @@ function M.update(args, bang, picker)
188190
vim.validate("cleanup_on_failure", parsed.cleanup_on_failure, "boolean", true)
189191
vim.validate("show_on_success", parsed.show_on_success, "boolean", true)
190192
vim.validate("show_on_failure", parsed.show_on_failure, "boolean", true)
193+
vim.validate("exclusive_layout", parsed.exclusive_layout, "boolean", true)
191194
vim.validate("size", parsed.size, "table", true)
192195
vim.validate("float_opts", parsed.float_opts, "table", true)
193196
vim.validate("tags", parsed.tags, "table", true)

lua/ergoterm/config.lua

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ M.NULL_CALLBACK = function(...) end
7777
---@field size Size?
7878
---@field start_in_insert boolean?
7979
---@field sticky boolean?
80+
---@field exclusive_layout boolean?
8081
---@field tags string[]?
8182

8283
---@class TerminalDefaults : TerminalDefaultsFromConfig
@@ -120,6 +121,7 @@ local config = {
120121
fixed_height = true,
121122
auto_list = true,
122123
sticky = false,
124+
exclusive_layout = false,
123125
size = {
124126
below = "50%",
125127
above = "50%",

lua/ergoterm/instance.lua

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ local utils = require("ergoterm.utils")
8585
---@field size Size
8686
---@field start_in_insert boolean
8787
---@field sticky boolean
88+
---@field exclusive_layout boolean
8889
---@field name string
8990
---@field on_close on_close
9091
---@field on_focus on_focus
@@ -148,6 +149,7 @@ function Terminal:new(args)
148149
term.size = vim.tbl_deep_extend("keep", term.size or {}, config.get("terminal_defaults.size"))
149150
term.start_in_insert = vim.F.if_nil(term.start_in_insert, config.get("terminal_defaults.start_in_insert"))
150151
term.sticky = vim.F.if_nil(term.sticky, config.get("terminal_defaults.sticky"))
152+
term.exclusive_layout = vim.F.if_nil(term.exclusive_layout, config.get("terminal_defaults.exclusive_layout"))
151153
term.on_close = vim.F.if_nil(term.on_close, config.get("terminal_defaults.on_close"))
152154
term.on_focus = vim.F.if_nil(term.on_focus, config.get("terminal_defaults.on_focus"))
153155
term.on_unfocus = vim.F.if_nil(term.on_unfocus, config.get("terminal_defaults.on_unfocus"))

lua/ergoterm/instance/open.lua

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
---@diagnostic disable: invisible
22

3+
---@module "ergoterm.collection"
4+
local collection = require("ergoterm.collection")
35
---@module "ergoterm.utils"
46
local utils = require("ergoterm.utils")
57

@@ -36,6 +38,9 @@ end
3638
function M.show(term, layout)
3739
if not M.is_open(term) and term._state.bufnr and vim.api.nvim_buf_is_valid(term._state.bufnr) then
3840
layout = layout or term._state.layout
41+
if term.exclusive_layout then
42+
M._close_siblings_with_same_layout(term, layout)
43+
end
3944
local window = nil
4045
if vim.tbl_contains(NEW_WINDOW_LAYOUTS, layout) then
4146
window = M._open_in_new_window(term, layout)
@@ -117,6 +122,17 @@ function M._set_float_options(term)
117122
vim.api.nvim_set_option_value("winblend", term.float_winblend, { scope = "local", win = window })
118123
end
119124

125+
---@private
126+
function M._close_siblings_with_same_layout(term, layout)
127+
for _, other in ipairs(collection.get_all()) do
128+
if other ~= term
129+
and other:is_open()
130+
and other._state.layout == layout then
131+
other:close()
132+
end
133+
end
134+
end
135+
120136
return setmetatable(M, {
121137
__call = function(_, ...)
122138
return M.open(...)

lua/ergoterm/instance/update.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ local ALLOWED_SETTINGS = {
2626
"size",
2727
"start_in_insert",
2828
"sticky",
29+
"exclusive_layout",
2930
"on_close",
3031
"on_create",
3132
"on_focus",

spec/ergoterm/commandline_spec.lua

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ describe("commandline.term_new_complete", function()
194194
assert.is_true(vim.tbl_contains(result, "auto_list="))
195195
assert.is_true(vim.tbl_contains(result, "start_in_insert="))
196196
assert.is_true(vim.tbl_contains(result, "sticky="))
197+
assert.is_true(vim.tbl_contains(result, "exclusive_layout="))
197198
assert.is_true(vim.tbl_contains(result, "cleanup_on_success="))
198199
assert.is_true(vim.tbl_contains(result, "cleanup_on_failure="))
199200
assert.is_true(vim.tbl_contains(result, "scrollback="))
@@ -263,6 +264,13 @@ describe("commandline.term_new_complete", function()
263264
assert.is_true(vim.tbl_contains(result, "sticky=false"))
264265
end)
265266

267+
it("completes boolean values for exclusive_layout", function()
268+
local result = commandline.term_new_complete("exclusive_layout=", "exclusive_layout=", 16)
269+
270+
assert.is_true(vim.tbl_contains(result, "exclusive_layout=true"))
271+
assert.is_true(vim.tbl_contains(result, "exclusive_layout=false"))
272+
end)
273+
266274
it("completes boolean values for cleanup_on_success", function()
267275
local result = commandline.term_new_complete("cleanup_on_success=", "cleanup_on_success=", 18)
268276

@@ -322,6 +330,7 @@ describe("commandline.term_update_complete", function()
322330
assert.is_true(vim.tbl_contains(result, "auto_list="))
323331
assert.is_true(vim.tbl_contains(result, "start_in_insert="))
324332
assert.is_true(vim.tbl_contains(result, "sticky="))
333+
assert.is_true(vim.tbl_contains(result, "exclusive_layout="))
325334
assert.is_true(vim.tbl_contains(result, "cleanup_on_success="))
326335
assert.is_true(vim.tbl_contains(result, "cleanup_on_failure="))
327336
assert.is_true(vim.tbl_contains(result, "show_on_success="))
@@ -339,6 +348,13 @@ describe("commandline.term_update_complete", function()
339348
assert.is_true(vim.tbl_contains(result, "tags="))
340349
assert.is_true(vim.tbl_contains(result, "meta.="))
341350
end)
351+
352+
it("completes boolean values for exclusive_layout", function()
353+
local result = commandline.term_update_complete("exclusive_layout=", "exclusive_layout=", 16)
354+
355+
assert.is_true(vim.tbl_contains(result, "exclusive_layout=true"))
356+
assert.is_true(vim.tbl_contains(result, "exclusive_layout=false"))
357+
end)
342358
end)
343359

344360
describe("commandline.term_inspect_complete", function()

0 commit comments

Comments
 (0)