Refactor the Layout class

This commit is contained in:
Rémi Prévost 2011-10-23 19:47:05 -04:00
parent a424684c5a
commit 847c08fef5
5 changed files with 208 additions and 101 deletions

1
.gitignore vendored
View File

@ -2,3 +2,4 @@
.rvmrc
.local*
.yardoc
doc

View File

@ -26,7 +26,8 @@ module Teamocil
bail "There is no file \"#{file}\"" unless File.exists?(file)
parsed_layout = YAML.load_file(file)
layout = Teamocil::Layout.new(parsed_layout, @options)
layout.to_tmux
layout.compile!
layout.execute_commands(layout.generate_commands)
end
end # }}}

View File

@ -1,7 +1,137 @@
module Teamocil
# This class act as a wrapper around a tmux YAML layout file.
# This class act as a wrapper around a tmux YAML layout file
class Layout
# This class represents a session within tmux
class Session
attr_reader :options, :windows, :name
# Initialize a new tmux session
#
# @param options [Hash] the options, mostly passed by the CLI
# @param attrs [Hash] the session data from the layout file
def initialize(options, attrs={}) # {{{
@name = attrs["name"]
@windows = attrs["windows"].each_with_index.map { |window, index| Window.new(self, index, window) }
@options = options
end # }}}
# Generate commands to send to tmux
#
# @return [Array]
def generate_commands # {{{
commands = []
commands << "tmux rename-session \"#{@name}\"" unless @name.nil?
commands << @windows.map(&:generate_commands)
commands << "tmux select-pane -t 0"
commands
end # }}}
end
# This class represents a window within tmux
class Window
attr_reader :filters, :root, :splits, :options, :index, :name
# Initialize a new tmux window
#
# @param session [Session] the session where the window is initialized
# @param index [Fixnnum] the window index
# @param attrs [Hash] the window data from the layout file
def initialize(session, index, attrs={}) # {{{
@name = attrs["name"]
@root = attrs["root"]
@options = attrs["options"]
@filters = attrs["filters"]
@splits = attrs["splits"].each_with_index.map { |split, index| Split.new(self, index, split) }
@index = index
@session = session
@options ||= {}
@filters ||= {}
@filters["before"] ||= []
@filters["after"] ||= []
end # }}}
# Generate commands to send to tmux
#
# @return [Array]
def generate_commands # {{{
commands = []
if @session.options.include?(:here) and @index == 0
commands << "tmux rename-window \"#{@name}\""
else
commands << "tmux new-window -n \"#{@name}\""
end
commands << @splits.map(&:generate_commands)
@options.each_pair do |option, value|
value = "on" if value === true
value = "off" if value === false
commands << "tmux set-window-option #{option} #{value}"
end
commands
end # }}}
end
# This class represents a split within a tmux window
class Split
attr_reader :width, :height, :cmd, :index, :target
# Initialize a new tmux split
#
# @param session [Session] the window where the split is initialized
# @param index [Fixnnum] the split index
# @param attrs [Hash] the split data from the layout file
def initialize(window, index, attrs={}) # {{{
@height = attrs["height"]
@width = attrs["width"]
@cmd = attrs["cmd"]
@target = attrs["target"]
@window = window
@index = index
end # }}}
# Generate commands to send to tmux
#
# @return [Array]
def generate_commands # {{{
commands = []
# Is it a vertical or horizontal split?
unless @index == 0
if !@width.nil?
commands << "tmux split-window -h -p #{@width}"
elsif !@height.nil?
commands << "tmux split-window -p #{@height}"
else
commands << "tmux split-window"
end
commands << " -t #{@target}" unless @target.nil?
end
# Wrap all commands around filters
@cmd = [@window.filters["before"]] + [@cmd] + [@window.filters["after"]]
# If a `root` key exist, start each split in this directory
@cmd.unshift "cd \"#{@window.root}\"" unless @window.root.nil?
# Execute each split command
@cmd.flatten.compact.each do |command|
commands << "tmux send-keys -t #{@index} \"#{command}\""
commands << "tmux send-keys -t #{@index} Enter"
end
commands
end # }}}
end
# Initialize a new layout from a hash
#
# @param layout [Hash] the parsed layout
@ -11,79 +141,22 @@ module Teamocil
@options = options
end # }}}
# Generate commands and sends them to tmux
def to_tmux # {{{
commands = generate_commands
execute_commands(commands)
end # }}}
# Generate tmux commands based on the data found in the layout file
#
# @return [Array] an array of shell commands to send
def generate_commands # {{{
output = []
@session.generate_commands
end # }}}
# Support renaming of current session
# Compile the layout into objects
#
# @return [Session]
def compile! # {{{
if @layout["session"].nil?
windows = @layout["windows"]
@session = Session.new @options, "windows" => @layout["windows"]
else
output << "tmux rename-session \"#{@layout["session"]["name"]}\"" if @layout["session"]["name"]
windows = @layout["session"]["windows"]
@session = Session.new @options, @layout["session"]
end
windows.each_with_index do |window, window_index|
# Create a new window unless we used the `--here` option
if @options.include?(:here) and window_index == 0
output << "tmux rename-window \"#{window["name"]}\""
else
output << "tmux new-window -n \"#{window["name"]}\""
end
# Make sure we have all the keys we need
window["options"] ||= {}
window["filters"] ||= {}
window["filters"]["before"] ||= []
window["filters"]["after"] ||= []
# Create splits
window["splits"].each_with_index do |split, split_index|
unless split_index == 0
if split.include?("width")
cmd = "tmux split-window -h -p #{split["width"]}"
elsif split.include?("height")
cmd = "tmux split-window -p #{split["height"]}"
else
cmd = "tmux split-window"
end
cmd << " -t #{split["target"]}" if split.include?("target")
output << cmd
end
# Wrap all commands around filters
split["cmd"] = [window["filters"]["before"]] + [split["cmd"]] + [window["filters"]["after"]]
# If a `root` key exist, start each split in this directory
split["cmd"].unshift "cd \"#{window["root"]}\"" if window.include?("root")
# Execute each split command
split["cmd"].flatten.compact.each do |command|
output << "tmux send-keys -t #{split_index} \"#{command}\""
output << "tmux send-keys -t #{split_index} Enter"
end
end
# Set tmux options
window["options"].each_pair do |option, value|
value = "on" if value === true
value = "off" if value === false
output << "tmux set-window-option #{option} #{value}"
end
end
# Set the focus in the first split
output << "tmux select-pane -t 0"
end # }}}
# Execute each command in the shell

View File

@ -2,36 +2,33 @@
two-windows:
windows:
- name: "foo"
root: "/foo"
splits:
- cmd: "echo 'foo'"
- cmd: "echo 'foo again'"
width: 50
- name: "bar"
root: "/bar"
splits:
- cmd: "echo 'bar'"
- cmd:
- "echo 'bar'"
- "echo 'bar in an array'"
- cmd: "echo 'bar again'"
width: 50
# Simple two windows layout in session
two-windows-in-a-session:
session:
name: my-new-session
windows:
- name: "foo"
splits:
- cmd: "echo 'foo'"
- cmd: "echo 'foo again'"
width: 50
- name: "bar"
splits:
- cmd: "echo 'bar'"
- cmd: "echo 'bar again'"
width: 50
four-splits:
# Simple two windows layout with filters
two-windows-with-filters:
windows:
- name: "foo"
root: "/foo"
filters:
before:
- "echo first before filter"
- "echo second before filter"
after:
- "echo first after filter"
- "echo second after filter"
splits:
- cmd: "echo 1"
- cmd: "echo 2"
- cmd: "echo 3"
- cmd: "echo 4"
- cmd: "echo 'foo'"
- cmd: "echo 'foo again'"
width: 50

View File

@ -1,25 +1,60 @@
require File.join(File.dirname(__FILE__), "spec_helper.rb")
describe Teamocil::Layout do
context "initializing" do
end
it "creates windows" do # {{{
layout = Teamocil::Layout.new(layouts["two-windows"], {})
commands = layout.generate_commands
commands.grep(/new-window/).length.should == 2
context "compiling" do
before :each do # {{{
@layout = Teamocil::Layout.new(layouts["two-windows"], {})
end # }}}
it "renames the current session" do # {{{
layout = Teamocil::Layout.new(layouts["two-windows-in-a-session"], {})
commands = layout.generate_commands
commands.grep(/rename-session/).first.should == "tmux rename-session \"my-new-session\""
commands.grep(/new-window/).length.should == 2
it "creates windows with names" do # {{{
session = @layout.compile!
session.windows[0].name.should == "foo"
session.windows[1].name.should == "bar"
end # }}}
it "creates splits" do # {{{
layout = Teamocil::Layout.new(layouts["four-splits"], {})
commands = layout.generate_commands
commands.grep(/split-window/).length.should == 3
it "creates windows with root paths" do # {{{
session = @layout.compile!
session.windows[0].root.should == "/foo"
session.windows[1].root.should == "/bar"
end # }}}
it "creates splits with dimensions" do # {{{
session = @layout.compile!
session.windows.first.splits[0].width.should == nil
session.windows.first.splits[1].width.should == 50
end # }}}
it "creates splits with commands specified in strings" do # {{{
session = @layout.compile!
session.windows.first.splits[0].cmd.should == "echo 'foo'"
end # }}}
it "creates splits with commands specified in an array" do # {{{
session = @layout.compile!
session.windows.last.splits[0].cmd.length.should == 2
session.windows.last.splits[0].cmd.first.should == "echo 'bar'"
session.windows.last.splits[0].cmd.last.should == "echo 'bar in an array'"
end # }}}
it "creates windows with before filters" do # {{{
layout = Teamocil::Layout.new(layouts["two-windows-with-filters"], {})
session = layout.compile!
session.windows.first.filters["before"].length.should == 2
session.windows.first.filters["before"].first.should == "echo first before filter"
session.windows.first.filters["before"].last.should == "echo second before filter"
end # }}}
it "creates windows with after filters" do # {{{
layout = Teamocil::Layout.new(layouts["two-windows-with-filters"], {})
session = layout.compile!
session.windows.first.filters["after"].length.should == 2
session.windows.first.filters["after"].first.should == "echo first after filter"
session.windows.first.filters["after"].last.should == "echo second after filter"
end # }}}
end