Skip to content

Commit 581684a

Browse files
committed
Automatically layout cols after opening, closing, moving wins
This change was motivated by the unsatisfying result of closing wins when the space of the closed win was often moved into the "wrong" window. Now the whole column is resized which feels much more natural because I often used a right click after closing a win to get a decent layout again which I also had to tweak. I might never have to resize wins manually. And with the automatic resizing in place the logical conclusion was to also make it happen after opening or moving wins. The layouting works a bit differently than the right-click on a status bar before this: Instead of limitting the height of a win to the number of lines in its buf we now simply toggle minimization of each win by right-clicking its status bar. The layouting is triggered by autocmds which means that it also happens after using builtin vim commands that open or close windows.
1 parent e65020f commit 581684a

2 files changed

Lines changed: 101 additions & 74 deletions

File tree

README.md

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,22 +49,29 @@ Bringing the spirit of Plan 9 acme to vim.
4949
* Manage windows with the mouse:
5050

5151
A window can be closed by middle-clicking its layout box at the
52-
beginning of its status bar. The space of the closed window is put into
53-
the focused one if they are in the same column.
52+
beginning of its status bar.
5453

5554
A window can be moved by dragging its status bar while holding down the
5655
right mouse button. The window is moved above or below the one over
5756
which the button is released if both of them are in the same column.
5857
Otherwise the dragged window is re-opened as a new split of the window
5958
under the mouse.
6059

61-
Right-clicking a status bar resizes the windows above it. The available
62-
space is distributed equally among them. But each window is limited to
63-
the number of lines in its buffer and any saved space is given to the
64-
windows above.
60+
Right-clicking the status bar of the top window in a column toggles if
61+
it equally shares the premium space at the top of the column with the
62+
window below. Right-clicking any other window in a column toggles
63+
minimization of that window.
6564

6665
A window can be moved to a new column by right-clicking its layout box.
6766

67+
* Automatically resize windows:
68+
69+
All non-minimized windows get a roughly equal amount of space but the
70+
top or the top two windows get more space than the windows below them.
71+
72+
Instead of resizing windows manually it is better to change their order
73+
in the column or minimize the less important ones.
74+
6875
* Send text to commands:
6976

7077
Middle-clicking in scratch windows sends the text to the command

plugin/acme.vim

Lines changed: 88 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -164,20 +164,12 @@ function s:Receiver(b)
164164
return has_key(s:scratch, a:b) && s:Jobs(a:b) != []
165165
endfunc
166166

167-
function s:SplitSize(n, rel)
167+
function s:New(cmd)
168168
let min = &winminheight > 0 ? 2 * &winminheight + 1 : 2
169169
if winheight(0) < min
170170
exe min.'wincmd _'
171171
endif
172-
let h = winheight(0)
173-
let stat = 1 + (winnr('$') == 1 && &laststatus == 1)
174-
return a:rel == '=' ? a:n : a:rel == '<'
175-
\ ? min([abs(a:n), h - stat - max([&winminheight, 1])])
176-
\ : max([a:n, h - s:Fit(win_getid(), (h - stat) / 2) - stat])
177-
endfunc
178-
179-
function s:New(cmd)
180-
exe s:SplitSize(10, '>').a:cmd
172+
exe a:cmd
181173
endfunc
182174

183175
function s:Argv(cmd)
@@ -234,7 +226,7 @@ function s:InDir(path, dir)
234226
endfunc
235227

236228
function s:ErrorSplitPos(name)
237-
let [w, match, mod, rel] = [0, 0, '', '']
229+
let [w, match, mod] = [0, 0, '']
238230
let dir = fnamemodify(s:Path(a:name), ':h')
239231
for i in reverse(range(1, winnr('$')))
240232
let b = winbufnr(i)
@@ -245,11 +237,11 @@ function s:ErrorSplitPos(name)
245237
let n = max([s:InDir(d, dir), s:InDir(dir, d)])
246238
if n > match
247239
let [w, match] = [i, n]
248-
let [mod, rel] = f == 'guide' || f == '+Errors'
249-
\ ? ['abo', '>'] : ['bel', '=']
240+
let mod = f == 'guide' || f == '+Errors'
241+
\ ? 'abo' : 'bel'
250242
endif
251243
endfor
252-
return w != 0 ? [w, mod, rel] : [winnr('$'), 'bel', '=']
244+
return w != 0 ? [w, mod] : [winnr('$'), 'bel']
253245
endfunc
254246

255247
function s:ErrorLoad(name)
@@ -269,15 +261,15 @@ function s:ErrorOpen(name, ...)
269261
if w != 0
270262
exe w.'wincmd w'
271263
else
272-
let [w, mod, rel] = s:ErrorSplitPos(a:name)
264+
let [w, mod] = s:ErrorSplitPos(a:name)
273265
exe w.'wincmd w'
274266
let b = s:ErrorLoad(a:name)
275267
for job in s:jobs
276268
if ch_getbufnr(job.h, 'out') == b && job.buf != b
277269
let job.buf = b
278270
endif
279271
endfor
280-
exe mod s:SplitSize(10, rel).'sp | b '.b
272+
call s:New(mod.' sb '.b)
281273
endif
282274
if a:0 == 0
283275
elseif line('$') == 1 && getline(1) == ''
@@ -685,23 +677,6 @@ function s:WinCol(w)
685677
return col
686678
endfunc
687679

688-
function s:CloseWin(w)
689-
let h = winheight(a:w) + 1
690-
let col = s:WinCol(a:w)
691-
let [i, j] = [index(col, a:w), index(col, win_getid())]
692-
let sb = &splitbelow
693-
let &splitbelow = 0
694-
exe win_id2win(a:w).'close!'
695-
let &splitbelow = sb
696-
if j == -1
697-
return
698-
endif
699-
let d = i - j
700-
for i in d < 0 ? range(d + 1, -1) : reverse(range(d))
701-
call win_move_statusline(winnr() + i, d < 0 ? -h : h)
702-
endfor
703-
endfunc
704-
705680
function s:RestWinVars(w, vars)
706681
let vars = getwinvar(a:w, '&')
707682
for v in keys(a:vars)
@@ -727,23 +702,28 @@ function s:MoveWin(w, other, below)
727702
endfor
728703
noa exe win_id2win(p).'wincmd w'
729704
noa exe win_id2win(w).'wincmd w'
705+
call s:Layout(s:WinCol(a:w))
730706
else
707+
let minimized = s:Minimized(a:w)
731708
let v = winsaveview()
732709
let vars = getwinvar(0, '&')
733710
noa exe win_id2win(a:other).'wincmd w'
734-
let h = s:SplitSize(1, '>')
735-
noa exe (a:below ? 'bel' : 'abo') h.'sp'
736-
noa exe 'b' winbufnr(a:w)
737-
call winrestview(v)
711+
noa call s:New((a:below ? 'bel' : 'abo').' sb '.winbufnr(a:w))
738712
let nw = win_getid()
713+
call s:RestWinVars(nw, vars)
714+
call winrestview(v)
715+
let s:minimized[nw] = minimized
739716
noa exe win_id2win(p != a:w ? p : nw).'wincmd w'
740717
noa exe win_id2win(w != a:w ? w : nw).'wincmd w'
741-
noa call s:CloseWin(a:w)
742-
call s:RestWinVars(nw, vars)
718+
noa exe win_id2win(a:w).'close!'
719+
call remove(col, i)
720+
call s:Layout(col)
721+
call s:Layout(s:WinCol(nw))
743722
endif
744723
endfunc
745724

746725
function s:NewCol(w)
726+
let col = s:WinCol(a:w)
747727
let w = win_getid()
748728
let p = win_getid(winnr('#'))
749729
noa exe win_id2win(a:w).'wincmd w'
@@ -753,7 +733,9 @@ function s:NewCol(w)
753733
endif
754734
noa exe win_id2win(p).'wincmd w'
755735
noa exe win_id2win(w).'wincmd w'
756-
call s:CloseWin(a:w)
736+
noa exe win_id2win(a:w).'close!'
737+
call remove(col, index(col, a:w))
738+
call s:Layout(col)
757739
endfunc
758740

759741
function s:Scroll(topline)
@@ -762,7 +744,7 @@ function s:Scroll(topline)
762744
call winrestview(v)
763745
endfunc
764746

765-
function s:FitHeight(w, l)
747+
function s:LineHeight(w, l)
766748
" Only works without fold, number & sign columns and just with
767749
" line wrapping, e.g. 'nobreakindent', 'nolinebreak' & 'nolist'
768750
let lw = !getwinvar(a:w, '&wrap') ? 1 :
@@ -771,42 +753,62 @@ function s:FitHeight(w, l)
771753
return (max([lw, 1]) + ww - 1) / ww
772754
endfunc
773755

774-
function s:Fit(w, h, ...)
775-
if fnamemodify(bufname(winbufnr(a:w)), ':t') == 'guide'
776-
let h = s:FitHeight(a:w, 1)
777-
if a:0 == 0 || a:w != a:1 || h != winheight(a:w)
778-
call win_execute(a:w, 'normal! gg')
779-
return h
756+
function s:Fit(col)
757+
for w in a:col
758+
if s:Minimized(w)
759+
continue
780760
endif
781-
endif
782-
let h = 0
783-
let top = line('$', a:w) + 1
784-
while top > 1
785-
let h += s:FitHeight(a:w, top - 1)
786-
if h > a:h
787-
break
761+
let h = 0
762+
let wh = winheight(w)
763+
let top = line('$', w) + 1
764+
while top > 1
765+
let h += s:LineHeight(w, top - 1)
766+
if h > wh
767+
break
768+
endif
769+
let top -= 1
770+
endwhile
771+
if top < getwininfo(w)[0].topline
772+
call win_execute(w, 'noa call s:Scroll('.top.')')
788773
endif
789-
let top -= 1
790-
endwhile
791-
call timer_start(0, {_ ->
792-
\ win_execute(a:w, 'noa call s:Scroll('.top.')')})
793-
return min([h, a:h])
774+
endfor
794775
endfunc
795776

796-
function s:Zoom(w)
797-
let col = s:WinCol(a:w)
798-
let col = slice(col, 0, index(col, a:w) + 1)
799-
let h = reduce(col, {s, w -> s + winheight(w)}, 0)
800-
let n = len(col)
801-
for w in reverse(col)
802-
let s = s:Fit(w, h / n, a:w)
777+
function s:Layout(col)
778+
let h = reduce(a:col, {s, w -> s + winheight(w)}, 0)
779+
let n = len(a:col)
780+
for w in reverse(a:col)
803781
if n == 1
804782
break
805783
endif
784+
if s:Minimized(w)
785+
if fnamemodify(bufname(winbufnr(w)), ':t') == 'guide'
786+
call win_execute(w, 'normal! gg')
787+
endif
788+
let s = 1
789+
else
790+
let s = h / (n + n * (n > s:tops))
791+
endif
806792
call win_move_statusline(win_id2win(w) - 1, winheight(w) - s)
807793
let h -= s
808794
let n -= 1
809795
endfor
796+
call timer_start(0, {_ -> s:Fit(a:col)})
797+
endfunc
798+
799+
function s:Minimized(w)
800+
let isguide = fnamemodify(bufname(winbufnr(a:w)), ':t') == 'guide'
801+
return get(s:minimized, a:w, isguide)
802+
endfunc
803+
804+
function s:Minimize(w)
805+
let col = s:WinCol(a:w)
806+
if index(col, a:w) == 0
807+
let s:tops = s:tops == 1 ? 2 : 1
808+
else
809+
let s:minimized[a:w] = !s:Minimized(a:w)
810+
endif
811+
call s:Layout(col)
810812
endfunc
811813

812814
function s:InSel()
@@ -856,7 +858,7 @@ function s:MiddleRelease(click)
856858
\ p.winrow <= winheight(p.winid)
857859
" off the statusline
858860
elseif p.wincol < 3
859-
call s:CloseWin(p.winid)
861+
exe win_id2win(p.winid).'close!'
860862
endif
861863
return
862864
endif
@@ -894,7 +896,7 @@ function s:RightRelease(click)
894896
elseif p.wincol < 3
895897
call s:NewCol(p.winid)
896898
else
897-
call s:Zoom(p.winid)
899+
call s:Minimize(p.winid)
898900
endif
899901
return
900902
endif
@@ -1171,6 +1173,20 @@ function s:BufWinLeave()
11711173
endif
11721174
endfunc
11731175

1176+
function s:WinClosed(w)
1177+
if has_key(s:minimized, a:w)
1178+
call remove(s:minimized, a:w)
1179+
endif
1180+
let col = s:WinCol(a:w)
1181+
call remove(col, index(col, a:w))
1182+
call timer_start(0, {_ -> s:Layout(col)})
1183+
endfunc
1184+
1185+
function s:WinNew(w)
1186+
let col = s:WinCol(a:w)
1187+
call timer_start(0, {_ -> s:Layout(col)})
1188+
endfunc
1189+
11741190
augroup acme_vim
11751191
au!
11761192
au BufEnter * call s:ListDir()
@@ -1180,6 +1196,8 @@ au TextChanged,TextChangedI guide setl nomodified
11801196
au VimEnter * call s:ReloadDirs(winnr())
11811197
au VimResized * call s:ReloadDirs(0)
11821198
au WinResized * call s:ReloadDirs(0)
1199+
au WinClosed * call s:WinClosed(str2nr(expand("<amatch>")))
1200+
au WinNew * call s:WinNew(win_getid())
11831201
augroup END
11841202

11851203
if exists("s:ctrlexe")
@@ -1199,7 +1217,9 @@ let s:editbufs = {}
11991217
let s:editcids = {}
12001218
let s:editcmds = {}
12011219
let s:jobs = []
1220+
let s:minimized = {}
12021221
let s:scratch = {}
1222+
let s:tops = 1
12031223

12041224
if s:ctrlexe != ''
12051225
let s:ctrl = job_start([s:ctrlexe], {

0 commit comments

Comments
 (0)