Skip to content

Instantly share code, notes, and snippets.

@genki
Last active January 20, 2017 00:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save genki/cab6692e08ffc4feb541ac1ef6e234c6 to your computer and use it in GitHub Desktop.
Save genki/cab6692e08ffc4feb541ac1ef6e234c6 to your computer and use it in GitHub Desktop.
Ruby script to balance only siblings of the current pane
#!/usr/bin/env ruby
class Parser
def initialize(input)
@input = input
@s = 0
end
def parse
csum = scan_csum; pick
w, h = scan_wh; pick
x = scan_d; pick
y = scan_d
c = case pick
when '['; scan_panes :vert, ']'
when '{'; scan_panes :horz, '}'
else []
end
[:win, csum, w, h, x, y, c]
end
private
def scan_panes(type, last)
panes = []
loop do
panes << scan_pane
case peek
when ','; pick
when last; pick; break
end
end
[type, panes]
end
def scan_pane
w, h = scan_wh; pick
x = scan_d; pick
y = scan_d
c = case pick
when '['; scan_panes :vert, ']'
when '{'; scan_panes :horz, '}'
else scan_d
end
[:pane, w, h, x, y, c]
end
def scan_csum
case peek
when /\h/; pick + scan_csum
when ','; ''
end
end
def scan_wh
w = scan_d; pick
h = scan_d
[w, h]
end
def scan_d
case peek
when /\d/; pick + scan_d
else ''
end
end
def pick; @input[@s] ensure @s += 1 end
def peek; @input[@s] end
end
class Layout
def initialize(layout)
@out = ''
visit layout
end
def output
csum = layout_checksum @out
"#{csum},#{@out}"
end
private
def layout_checksum(layout)
csum = 0;
layout.each_byte do |b|
csum = (csum >> 1) + ((csum & 1) << 15)
csum = (csum + b) & 0xFFFF
end
"%04x" % csum
end
def visit(lo)
case lo[0]
when :win
_, csum, w, h, x, y, c = lo
@out << "#{w}x#{h},#{x},#{y}"
visit c
when :vert
@out << '['
lo[1].each{|i| visit i; @out << ','}
@out.chop! if !lo[1].empty?
@out << ']'
when :horz
@out << '{'
lo[1].each{|i| visit i; @out << ','}
@out.chop! if !lo[1].empty?
@out << '}'
when :pane
_, w, h, x, y, c = lo
@out << "#{w}x#{h},#{x},#{y}"
case c
when Array; visit c
else @out << ",#{c}"
end
end
end
end
class Balancer
def balance(lo, pid, depth)
if depth.nil?
STDERR.puts <<-EOH
USAGE: #{$0} {0|1}
0: balance sibling panes even
1: balance parent's sibling panes even
EOH
exit 1
end
win, csum, w, h, x, y, c = lo
d0 = ->(cs){cs.any?{|c| c[5] == pid}}
d1 = ->(cs){cs.any?{|c1| c1[5].is_a?(Array) && d0[c1[5][1]]}}
balance_panes c, w, h, &[d0, d1][depth.to_i]
end
def splat(x,n,i)
m = x - n + 1
m/n + (i < m%n ? 1:0)
end
def fit_panes(c,dir,idx,v)
c[1].each do |i|
i[idx] = v if dir != c[0]
balance_panes i[5], i[1], i[2], &->(cs){1} if i[5].is_a?(Array)
end
end
def rebalance(cs, dir, idx, finder, *wh)
if finder[cs]
n = cs.size
cs.each_with_index do |pane,i|
v = pane[idx] = splat wh[idx-1], n, i
fit_panes pane[5], dir, idx,v if pane[5].is_a?(Array)
end
else
cs.select do |i|
pane, pw, ph, x, y, c = i
balance_panes c, pw, ph, &finder if c.is_a?(Array)
end
end
end
def balance_panes(c, w, h, &finder)
case c[0]
when :vert; rebalance c[1], :vert, 2, finder, w.to_i, h.to_i
when :horz; rebalance c[1], :horz, 1, finder, w.to_i, h.to_i
end
end
end
pid = `tmux display -p "\#{pane_id}"`.scan(/\d+/)[0]
lo = `tmux display -p "\#{window_visible_layout}"`
x = Parser.new(lo).parse
Balancer.new.balance x, pid, ARGV.shift
lo = Layout.new(x).output
system "tmux select-layout '#{lo}'"
@genki
Copy link
Author

genki commented Jan 19, 2017

in your ~/.tmux.conf

bind * run-shell "/path/to/tmux_even_pane 0"
bind + run-shell "/path/to/tmux_even_pane 1"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment