From 003aaadd2ac60516776bf6743876d7a1da912cda Mon Sep 17 00:00:00 2001
From: MrZeLee <mrzelee404@gmail.com>
Date: Tue, 4 Mar 2025 15:53:48 +0000
Subject: [PATCH 1/2] feat: add focused window minwidth and minheight options

Adds options to set minimum width and height for the focused window.
This allows users to customize the size of the focused window.
---
 README.md                     | 16 ++++++++++++++++
 doc/focus.txt                 | 30 ++++++++++++++++++++++++------
 extras/repro.lua              |  2 ++
 lua/focus/init.lua            | 10 ++++++++++
 lua/focus/modules/resizer.lua | 22 ++++++++++++++++++----
 5 files changed, 70 insertions(+), 10 deletions(-)

diff --git a/README.md b/README.md
index a3c418f..5d4b381 100644
--- a/README.md
+++ b/README.md
@@ -117,6 +117,8 @@ require("focus").setup({
         height = 0, -- Force height for the focused window
         minwidth = 0, -- Force minimum width for the unfocused window
         minheight = 0, -- Force minimum height for the unfocused window
+        focusedwindow_minwidth = 0, --Force minimum width for the focused window
+        focusedwindow_minheight = 0, --Force minimum height for the focused window
         height_quickfix = 10, -- Set the height of quickfix panel
     },
     split = {
@@ -187,6 +189,13 @@ require("focus").setup({ autoresize = { width = 120 } })
 require("focus").setup({ autoresize = { minwidth = 80} })
 ```
 
+**Set Focus Minimum Width for Focused Window**
+```lua
+-- Force minimum width for the focused window
+-- Default: Calculated based on golden ratio
+require("focus").setup({ autoresize = { focusedwindow_minwidth = 80} })
+```
+
 **Set Focus Height**
 ```lua
 -- Force height for the focused window
@@ -201,6 +210,13 @@ require("focus").setup({ autoresize = { height = 40 } })
 require("focus").setup({ autoresize = { minheight = 10} })
 ```
 
+**Set Focus Minimum Height for Focused Window**
+```lua
+-- Force minimum height for the focused window
+-- Default: Calculated based on golden ratio
+require("focus").setup({ autoresize = { focusedwindow_minheight = 80} })
+```
+
 **Set Focus Quickfix Height**
 ```lua
 -- Sets the height of quickfix panel, in case you pass the height to
diff --git a/doc/focus.txt b/doc/focus.txt
index 2e1e1b4..22b85de 100644
--- a/doc/focus.txt
+++ b/doc/focus.txt
@@ -80,6 +80,8 @@ default settings:
             height = 0, -- Force height for the focused window
             minwidth = 0, -- Force minimum width for the unfocused window
             minheight = 0, -- Force minimum height for the unfocused window
+            focusedwindow_minwidth = 0, -- Force minimum width for the focused window
+            focusedwindow_minheight = 0, -- Force minimum height for the focused window
             height_quickfix = 10, -- Set the height of quickfix panel
         },
         split = {
@@ -91,7 +93,7 @@ default settings:
             relativenumber = false, -- Display relative line numbers in the focussed window only
             hybridnumber = false, -- Display hybrid line numbers in the focussed window only
             absolutenumber_unfocussed = false, -- Preserve absolute numbers in the unfocussed windows
-    
+
             cursorline = true, -- Display a cursorline in the focussed window only
             cursorcolumn = false, -- Display cursorcolumn in the focussed window only
             colorcolumn = {
@@ -154,6 +156,14 @@ SETUP OPTIONS ~
     require("focus").setup({ autoresize = { minwidth = 80} })
 <
 
+**Set Focus Minimum Width for Focused Window**
+
+>lua
+    -- Force minimum width for the focused window
+    -- Default: Calculated based on golden ratio
+    require("focus").setup({ autoresize = { focusedwindow_minwidth = 80} })
+<
+
 **Set Focus Height**
 
 >lua
@@ -170,6 +180,14 @@ SETUP OPTIONS ~
     require("focus").setup({ autoresize = { minheight = 10} })
 <
 
+**Set Focus Minimum Height for Focused Window**
+
+>lua
+    -- Force minimum height for the focused window
+    -- Default: Calculated based on golden ratio
+    require("focus").setup({ autoresize = { focusedwindow_minheight = 80} })
+<
+
 **Set Focus Quickfix Height**
 
 >lua
@@ -287,7 +305,7 @@ buffer**
     -- Enable auto highlighting for focussed/unfocussed windows
     -- Default: false
     require("focus").setup({ ui = { winhighlight = true } })
-    
+
     -- By default, the highlight groups are setup as such:
     --   hi default link FocusedWindow VertSplit
     --   hi default link UnfocusedWindow Normal
@@ -312,10 +330,10 @@ Here is an example:
 >lua
     local ignore_filetypes = { 'neo-tree' }
     local ignore_buftypes = { 'nofile', 'prompt', 'popup' }
-    
+
     local augroup =
         vim.api.nvim_create_augroup('FocusDisable', { clear = true })
-    
+
     vim.api.nvim_create_autocmd('WinEnter', {
         group = augroup,
         callback = function(_)
@@ -328,7 +346,7 @@ Here is an example:
         end,
         desc = 'Disable focus autoresize for BufType',
     })
-    
+
     vim.api.nvim_create_autocmd('FileType', {
         group = augroup,
         callback = function(_)
@@ -473,7 +491,7 @@ LEVERAGE HJKL TO MOVE OR CREATE YOUR SPLITS DIRECTIONALLY ~
             require('focus').split_command(direction)
         end, { desc = string.format('Create or move to split (%s)', direction) })
     end
-    
+
     -- Use `<Leader>h` to split the screen to the left, same as command FocusSplitLeft etc
     focusmap('h')
     focusmap('j')
diff --git a/extras/repro.lua b/extras/repro.lua
index 01f5f5b..9dcf798 100644
--- a/extras/repro.lua
+++ b/extras/repro.lua
@@ -37,6 +37,8 @@ require('focus').setup({
         height = 0, -- Force height for the focused window
         minwidth = 0, -- Force minimum width for the unfocused window
         minheight = 0, -- Force minimum height for the unfocused window
+        focusedwindow_minwidth = 0, -- Force minimum width for the focused window
+        focusedwindow_minheight = 0, -- Force minimum height for the focused window
         height_quickfix = 10, -- Set the height of quickfix panel
     },
     split = {
diff --git a/lua/focus/init.lua b/lua/focus/init.lua
index 2379018..39d5da4 100644
--- a/lua/focus/init.lua
+++ b/lua/focus/init.lua
@@ -27,6 +27,8 @@ Focus.config = {
         height = 0, -- Force height for the focused window
         minwidth = 0, -- Force minimum width for the unfocused window
         minheight = 0, -- Force minimum height for the unfocused window
+        focusedwindow_minwidth = 0, -- Force minimum width for the focused window
+        focusedwindow_minheight = 0, -- Force minimum height for the focused window
         height_quickfix = 10, -- Set the height of quickfix panel
     },
     split = {
@@ -196,6 +198,14 @@ H.setup_config = function(config)
         ['autoresize.height'] = { config.autoresize.height, 'number' },
         ['autoresize.minwidth'] = { config.autoresize.minwidth, 'number' },
         ['autoresize.minheight'] = { config.autoresize.minheight, 'number' },
+        ['autoresize.focusedwindow_minwidth'] = {
+            config.autoresize.focusedwindow_minwidth,
+            'number',
+        },
+        ['autoresize.focusedwindow_minheight'] = {
+            config.autoresize.focusedwindow_minheight,
+            'number',
+        },
         ['autoresize.height_quickfix'] = {
             config.autoresize.height_quickfix,
             'number',
diff --git a/lua/focus/modules/resizer.lua b/lua/focus/modules/resizer.lua
index 2dbc357..1d83cdc 100644
--- a/lua/focus/modules/resizer.lua
+++ b/lua/focus/modules/resizer.lua
@@ -54,7 +54,11 @@ function M.autoresize(config)
         width = config.autoresize.width
     else
         width = golden_ratio_width()
-        if config.autoresize.minwidth > 0 then
+        if config.autoresize.focusedwindow_minwidth > 0 then
+            if width < config.autoresize.focusedwindow_minwidth then
+                width = config.autoresize.focusedwindow_minwidth
+            end
+        elseif config.autoresize.minwidth > 0 then
             width = math.max(width, config.autoresize.minwidth)
         elseif width < golden_ratio_minwidth() then
             width = golden_ratio_minwidth()
@@ -66,7 +70,11 @@ function M.autoresize(config)
         height = config.autoresize.height
     else
         height = golden_ratio_height()
-        if config.autoresize.minheight > 0 then
+        if config.autoresize.focusedwindow_minheight > 0 then
+            if height < config.autoresize.focusedwindow_minheight then
+                height = config.autoresize.focusedwindow_minheight
+            end
+        elseif config.autoresize.minheight > 0 then
             height = math.max(height, config.autoresize.minheight)
         elseif height < golden_ratio_minheight() then
             height = golden_ratio_minheight()
@@ -121,13 +129,19 @@ function M.split_resizer(config, goal) --> Only resize normal buffers, set qf to
         vim.o.winheight = 1
         return
     else
-        if config.autoresize.minwidth > 0 then
+        if
+            config.autoresize.minwidth > 0
+            and config.autoresize.focusedwindow_minwidth <= 0
+        then
             if vim.o.winwidth < config.autoresize.minwidth then
                 vim.o.winwidth = config.autoresize.minwidth
             end
             vim.o.winminwidth = config.autoresize.minwidth
         end
-        if config.autoresize.minheight > 0 then
+        if
+            config.autoresize.minheight > 0
+            and config.autoresize.focusedwindow_minheight <= 0
+        then
             if vim.o.winheight < config.autoresize.minheight then
                 vim.o.winheight = config.autoresize.minheight
             end

From 99db1e1edf94c2c9a38132010bfbc72360697f28 Mon Sep 17 00:00:00 2001
From: MrZeLee <mrzelee404@gmail.com>
Date: Sun, 9 Mar 2025 22:25:30 +0000
Subject: [PATCH 2/2] test: add tests for the focused window minwidth and
 minheight options

Testing focusedwindow_minwidth and focusedwindow_minheight when value is bigger and smaller then the expected width and height value from golden ratio function. Testing integration of focusedwindow_minwidth and focusedwindow_minheight with minwidth and minheight values.
---
 tests/test_autoresize.lua | 123 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 123 insertions(+)

diff --git a/tests/test_autoresize.lua b/tests/test_autoresize.lua
index 5c5d403..a70d6f3 100644
--- a/tests/test_autoresize.lua
+++ b/tests/test_autoresize.lua
@@ -211,6 +211,129 @@ T['autoresize']['vsplit minwidth'] = function()
     validate_win_dims(win_id_right, { 30, 23 })
 end
 
+-- focused minwidth for vertical splits
+T['autoresize']['vsplit focusedwindow_minwidth'] = function()
+    reload_module({ autoresize = { focusedwindow_minwidth = 55 } })
+    edit(lorem_ipsum_file)
+    child.set_cursor(15, 1)
+    child.cmd('vsplit')
+    local resize_state = child.get_resize_state()
+    local win_id_left = resize_state.windows[1]
+    local win_id_right = resize_state.windows[2]
+
+    eq(win_id_left, child.api.nvim_get_current_win())
+
+    -- Expect the left window's width to be at least 55.
+    -- In the default test, the left width is 49.
+    -- With forced focusedwindow_minwidth, we now expect 55.
+    validate_win_dims(win_id_left, { 55, 23 })
+    -- The right window should take the remaining width
+    validate_win_dims(win_id_right, { 24, 23 })
+end
+
+-- focused minwidth for vertical splits inferior to the default one obtained by
+-- the golden ratio
+T['autoresize']['vsplit focusedwindow_minwidth < golden_ratio expected value'] = function()
+    reload_module({ autoresize = { focusedwindow_minwidth = 40 } })
+    edit(lorem_ipsum_file)
+    child.set_cursor(15, 1)
+    child.cmd('vsplit')
+    local resize_state = child.get_resize_state()
+    local win_id_left = resize_state.windows[1]
+    local win_id_right = resize_state.windows[2]
+
+    eq(win_id_left, child.api.nvim_get_current_win())
+
+    -- The expected value is the golden ratio one
+    validate_win_dims(win_id_left, { 49, 23 })
+    -- The right window should take the remaining width
+    validate_win_dims(win_id_right, { 30, 23 })
+end
+
+-- focused minheight for horizontal splits
+T['autoresize']['split focusedwindow_minheight'] = function()
+    reload_module({ autoresize = { focusedwindow_minheight = 17 } })
+    edit(lorem_ipsum_file)
+    child.set_cursor(15, 1)
+    child.cmd('split')
+    local resize_state = child.get_resize_state()
+    local win_id_upper = resize_state.windows[1]
+    local win_id_lower = resize_state.windows[2]
+
+    -- In the default test, the upper window height is 15.
+    -- With forced focusedwindow_minheight, we now expect 17.
+    validate_win_dims(win_id_upper, { 80, 17 })
+    -- The lower window gets the rest (assuming total height used equals 22)
+    validate_win_dims(win_id_lower, { 80, 5 })
+end
+
+-- focused minheight for horizontal splits inferior to the default one obtained by
+-- the golden ratio
+T['autoresize']['split focusedwindow_minheight < golden_ratio expected value'] = function()
+    reload_module({ autoresize = { focusedwindow_minheight = 10 } })
+    edit(lorem_ipsum_file)
+    child.set_cursor(15, 1)
+    child.cmd('split')
+    local resize_state = child.get_resize_state()
+    local win_id_upper = resize_state.windows[1]
+    local win_id_lower = resize_state.windows[2]
+
+    -- The expected value is the golden ratio one
+    validate_win_dims(win_id_upper, { 80, 15 })
+    -- The lower window gets the rest
+    validate_win_dims(win_id_lower, { 80, 7 })
+end
+
+-- focused minwidth for vertical splits with minwidth + focusedwindow_minwidth >
+-- maxwidth.
+T['autoresize']['vsplit minwidth focusedwindow_minwidth'] = function()
+    reload_module({
+        autoresize = { minwidth = 20, focusedwindow_minwidth = 80 },
+    })
+    edit(lorem_ipsum_file)
+    child.set_cursor(15, 1)
+    child.cmd('vsplit')
+    local resize_state = child.get_resize_state()
+    local win_id_left = resize_state.windows[1]
+    local win_id_right = resize_state.windows[2]
+
+    eq(win_id_left, child.api.nvim_get_current_win())
+
+    -- Expect the left window's width to be at least the maxwidth (80) minus 21,
+    -- 20 from the right window as the minimum width possible and 1 from the
+    -- separator.
+    -- With forced focusedwindow_minwidth, we now expect 59.
+    validate_win_dims(win_id_left, { 78, 23 })
+    -- The right window should have the minwidth.
+    validate_win_dims(win_id_right, { 1, 23 })
+
+    child.api.nvim_set_current_win(win_id_right)
+
+    validate_win_dims(win_id_left, { 1, 23 })
+    -- The right window should have the minwidth.
+    validate_win_dims(win_id_right, { 78, 23 })
+end
+
+-- focused minheight for horizontal splits with minheight as 0
+T['autoresize']['split minheight focusedwindow_minheight'] = function()
+    reload_module({
+        autoresize = { minheight = 10, focusedwindow_minheight = 25 },
+    })
+    edit(lorem_ipsum_file)
+    child.set_cursor(15, 1)
+    child.cmd('split')
+    local resize_state = child.get_resize_state()
+    local win_id_upper = resize_state.windows[1]
+    local win_id_lower = resize_state.windows[2]
+
+    -- Expect the upper window's height to be at least the maxheight (23) minus 2,
+    -- 1 from the lower window as the minimum height and 1 from the separator.
+    -- With forced focusedwindow_minheight, we now expect 21.
+    validate_win_dims(win_id_upper, { 80, 21 })
+    -- The lower window should have the minimum height.
+    validate_win_dims(win_id_lower, { 80, 1 })
+end
+
 T['autoresize']['quickfix'] = function()
     reload_module({ autoresize = { height_quickfix = 10 } })
     child.cmd('vimgrep /ipsum/' .. lorem_ipsum_file)