Last active
January 20, 2017 00:48
-
-
Save genki/cab6692e08ffc4feb541ac1ef6e234c6 to your computer and use it in GitHub Desktop.
Ruby script to balance only siblings of the current pane
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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}'" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
in your
~/.tmux.conf