Compare commits
No commits in common. "master" and "feature-refactor-classes" have entirely different histories.
master
...
feature-re
1
.gitignore
vendored
1
.gitignore
vendored
@ -3,4 +3,3 @@
|
|||||||
.local*
|
.local*
|
||||||
.yardoc
|
.yardoc
|
||||||
doc
|
doc
|
||||||
pkg
|
|
||||||
|
@ -1,2 +0,0 @@
|
|||||||
rvm: 1.9.2
|
|
||||||
script: "bundle exec rake spec"
|
|
13
.yardopts
Normal file
13
.yardopts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
--title "Teamocil"
|
||||||
|
|
||||||
|
--no-cache
|
||||||
|
--protected
|
||||||
|
--no-private
|
||||||
|
|
||||||
|
--markup "markdown"
|
||||||
|
--markup-provider "maruku"
|
||||||
|
|
||||||
|
--format html
|
||||||
|
|
||||||
|
"README.mkd"
|
||||||
|
"lib/**/*.rb"
|
@ -1,7 +1,7 @@
|
|||||||
PATH
|
PATH
|
||||||
remote: .
|
remote: .
|
||||||
specs:
|
specs:
|
||||||
teamocil (0.3.2)
|
teamocil (0.1.11)
|
||||||
|
|
||||||
GEM
|
GEM
|
||||||
remote: http://rubygems.org/
|
remote: http://rubygems.org/
|
||||||
@ -19,7 +19,7 @@ GEM
|
|||||||
diff-lcs (~> 1.1.2)
|
diff-lcs (~> 1.1.2)
|
||||||
rspec-mocks (2.6.0)
|
rspec-mocks (2.6.0)
|
||||||
syntax (1.0.0)
|
syntax (1.0.0)
|
||||||
yard (0.7.5)
|
yard (0.7.2)
|
||||||
|
|
||||||
PLATFORMS
|
PLATFORMS
|
||||||
ruby
|
ruby
|
||||||
|
2
LICENSE
2
LICENSE
@ -1,3 +1,3 @@
|
|||||||
Copyright 2011-2012 Rémi Prévost.
|
Copyright 2011 Rémi Prévost.
|
||||||
You may use this work without restrictions, as long as this notice is included.
|
You may use this work without restrictions, as long as this notice is included.
|
||||||
The work is provided "as is" without warranty of any kind, neither express nor implied.
|
The work is provided "as is" without warranty of any kind, neither express nor implied.
|
||||||
|
157
README.md
157
README.md
@ -2,24 +2,19 @@
|
|||||||
|
|
||||||
Teamocil is a simple tool used to automatically create sessions, windows and splits in [tmux](http://tmux.sourceforge.net/) with YAML files.
|
Teamocil is a simple tool used to automatically create sessions, windows and splits in [tmux](http://tmux.sourceforge.net/) with YAML files.
|
||||||
|
|
||||||
[![Build Status](https://secure.travis-ci.org/remiprev/teamocil.png)](http://travis-ci.org/remiprev/teamocil)
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
```bash
|
$ gem install teamocil
|
||||||
$ gem install teamocil
|
$ mkdir ~/.teamocil
|
||||||
$ mkdir ~/.teamocil
|
$ teamocil --edit sample
|
||||||
$ teamocil --edit sample
|
$ tmux
|
||||||
$ tmux
|
$ teamocil sample
|
||||||
$ teamocil sample
|
|
||||||
```
|
|
||||||
|
|
||||||
## Options
|
## Options
|
||||||
|
|
||||||
* `--here` opens the session in the current window, it doesn’t create an empty first window.
|
* `--here` opens the session in the current window, it doesn’t create an empty first window.
|
||||||
* `--layout` takes a custom file path to a YAML layout file.
|
* `--layout` takes a custom file path to a YAML layout file.
|
||||||
* `--edit` opens the layout file (whether or not `--layout` is used) with `$EDITOR`.
|
* `--edit` opens the layout file (whether or not `--layout` is used) with `$EDITOR`.
|
||||||
* `--list` lists all available layouts.
|
|
||||||
|
|
||||||
## Layout file structure
|
## Layout file structure
|
||||||
|
|
||||||
@ -35,12 +30,10 @@ You can wrap your entire layout file in a `session` and Teamocil will rename the
|
|||||||
|
|
||||||
#### Example
|
#### Example
|
||||||
|
|
||||||
```yaml
|
session:
|
||||||
session:
|
name: my-awesome-session
|
||||||
name: "my-awesome-session"
|
windows:
|
||||||
windows:
|
[windows list]
|
||||||
[windows list]
|
|
||||||
```
|
|
||||||
|
|
||||||
### Windows
|
### Windows
|
||||||
|
|
||||||
@ -56,27 +49,27 @@ If you are not using a top-level `session` key, then the first key of your layou
|
|||||||
|
|
||||||
#### Example
|
#### Example
|
||||||
|
|
||||||
```yaml
|
windows:
|
||||||
windows:
|
- name: my-first-window
|
||||||
- name: "my-first-window"
|
options:
|
||||||
options:
|
synchronize-panes: true
|
||||||
synchronize-panes: true
|
root: ~/Projects/foo-www
|
||||||
root: "~/Projects/foo-www"
|
filters:
|
||||||
filters:
|
before:
|
||||||
before:
|
- "echo 'Let’s use ruby-1.9.2 for each split in this window.'"
|
||||||
- "echo 'Let’s use ruby-1.9.2 for each split in this window.'"
|
- "rvm use 1.9.2"
|
||||||
- "rvm use 1.9.2"
|
splits:
|
||||||
splits:
|
[splits list]
|
||||||
[splits list]
|
- name: my-second-window
|
||||||
- name: "my-second-window"
|
root: ~/Projects/foo-api
|
||||||
root: "~/Projects/foo-api"
|
filters:
|
||||||
splits:
|
after: "rvm use 1.9.2"
|
||||||
[splits list]
|
splits:
|
||||||
- name: "my-third-window"
|
[splits list]
|
||||||
root: "~/Projects/foo-daemons"
|
- name: my-third-window
|
||||||
splits:
|
root: ~/Projects/foo-daemons
|
||||||
[splits list]
|
splits:
|
||||||
```
|
[splits list]
|
||||||
|
|
||||||
### Splits
|
### Splits
|
||||||
|
|
||||||
@ -91,22 +84,20 @@ Every window must define an array of splits that will be created within it. A ve
|
|||||||
|
|
||||||
#### Example
|
#### Example
|
||||||
|
|
||||||
```yaml
|
windows:
|
||||||
windows:
|
- name: my-first-window
|
||||||
- name: "my-first-window"
|
root: ~/Projects/foo-www
|
||||||
root: "~/Projects/foo-www"
|
filters:
|
||||||
filters:
|
before: "rvm use 1.9.2"
|
||||||
before: "rvm use 1.9.2"
|
after: "echo 'I am done initializing this split.'"
|
||||||
after: "echo 'I am done initializing this split.'"
|
splits:
|
||||||
splits:
|
- cmd: "git status"
|
||||||
- cmd: "git status"
|
- cmd: "bundle exec rails server --port 4000"
|
||||||
- cmd: "bundle exec rails server --port 4000"
|
width: 50
|
||||||
width: 50
|
- cmd:
|
||||||
- cmd:
|
- sudo service memcached start
|
||||||
- "sudo service memcached start"
|
- sudo service mongodb start
|
||||||
- "sudo service mongodb start"
|
height: 50
|
||||||
height: 50
|
|
||||||
```
|
|
||||||
|
|
||||||
## Layout examples
|
## Layout examples
|
||||||
|
|
||||||
@ -116,16 +107,15 @@ See more example files in the `examples` directory.
|
|||||||
|
|
||||||
#### Content of `~/.teamocil/sample-1.yml`
|
#### Content of `~/.teamocil/sample-1.yml`
|
||||||
|
|
||||||
```yaml
|
windows:
|
||||||
windows:
|
- name: sample-two-splits
|
||||||
- name: "sample-two-splits"
|
root: ~/Code/sample/www
|
||||||
root: "~/Code/sample/www"
|
splits:
|
||||||
splits:
|
- cmd:
|
||||||
- cmd: ["pwd", "ls -la"]
|
- pwd
|
||||||
- cmd: "rails server --port 3000"
|
- ls -la
|
||||||
width: 50
|
- cmd: rails server --port 3000
|
||||||
```
|
width: 50
|
||||||
|
|
||||||
|
|
||||||
#### Result of `$ teamocil sample-1`
|
#### Result of `$ teamocil sample-1`
|
||||||
|
|
||||||
@ -145,21 +135,19 @@ windows:
|
|||||||
|
|
||||||
#### Content of `~/.teamocil/sample-2.yml`
|
#### Content of `~/.teamocil/sample-2.yml`
|
||||||
|
|
||||||
```yaml
|
windows:
|
||||||
windows:
|
- name: sample-four-splits
|
||||||
- name: "sample-four-splits"
|
root: ~/Code/sample/www
|
||||||
root: "~/Code/sample/www"
|
splits:
|
||||||
splits:
|
- cmd: pwd
|
||||||
- cmd: "pwd"
|
- cmd: pwd
|
||||||
- cmd: "pwd"
|
width: 50
|
||||||
width: 50
|
- cmd: pwd
|
||||||
- cmd: "pwd"
|
height: 50
|
||||||
height: 50
|
target: bottom-right
|
||||||
target: "bottom-right"
|
- cmd: pwd
|
||||||
- cmd: "pwd"
|
height: 50
|
||||||
height: 50
|
target: bottom-left
|
||||||
target: "bottom-left"
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Result of `$ teamocil sample-2`
|
#### Result of `$ teamocil sample-2`
|
||||||
|
|
||||||
@ -181,14 +169,13 @@ windows:
|
|||||||
|
|
||||||
To get autocompletion when typing `teamocil <Tab>` in a zsh session, add this line to your `~/.zshrc` file:
|
To get autocompletion when typing `teamocil <Tab>` in a zsh session, add this line to your `~/.zshrc` file:
|
||||||
|
|
||||||
```zsh
|
compctl -g '~/.teamocil/*(:t:r)' teamocil
|
||||||
compctl -g '~/.teamocil/*(:t:r)' teamocil
|
|
||||||
```
|
|
||||||
|
|
||||||
## Todo list
|
## Todo list
|
||||||
|
|
||||||
* Making sure the layout is valid before executing it (ie. throw exceptions).
|
* Support tmux options for windows and splits (eg. `synchronize-panes`)
|
||||||
* Add more specs.
|
* Making sure the layout is valid before executing it
|
||||||
|
* Refactor the `Layout` class to make it “test-friendly”
|
||||||
|
|
||||||
## Contributors
|
## Contributors
|
||||||
|
|
||||||
@ -197,8 +184,6 @@ Feel free to contribute and submit issues/pull requests [on GitHub](https://gith
|
|||||||
* Samuel Garneau ([garno](https://github.com/garno))
|
* Samuel Garneau ([garno](https://github.com/garno))
|
||||||
* Jimmy Bourassa ([jbourassa](https://github.com/jbourassa))
|
* Jimmy Bourassa ([jbourassa](https://github.com/jbourassa))
|
||||||
|
|
||||||
Take a look at the `spec` folder before you do, and make sure `bundle exec rake spec` passes after your modifications :)
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Teamocil is © 2011-2012 [Rémi Prévost](http://exomel.com) and may be freely distributed under the [LITL license](http://litl.info/). See the `LICENSE` file.
|
Teamocil is © 2011 [Rémi Prévost](http://exomel.com) and may be freely distributed under the [LITL license](http://litl.info/). See the `LICENSE` file.
|
||||||
|
29
Rakefile
29
Rakefile
@ -1,28 +1,11 @@
|
|||||||
require "bundler"
|
|
||||||
Bundler.require(:development)
|
|
||||||
|
|
||||||
require "bundler/gem_tasks"
|
require "bundler/gem_tasks"
|
||||||
require "rspec/core/rake_task"
|
|
||||||
|
|
||||||
task :default => :spec
|
desc "Run specs"
|
||||||
|
task :spec do # {{{
|
||||||
desc "Run all specs"
|
sh "bundle exec rspec --color --format=nested #{Dir.glob(File.join(File.dirname(__FILE__), "spec/**/*_spec.rb")).join(" ")}"
|
||||||
RSpec::Core::RakeTask.new(:spec) do |task| # {{{
|
|
||||||
task.pattern = "spec/**/*_spec.rb"
|
|
||||||
task.rspec_opts = "--colour --format=documentation"
|
|
||||||
end # }}}
|
end # }}}
|
||||||
|
|
||||||
desc "Generate YARD Documentation"
|
desc "Generate documentation"
|
||||||
YARD::Rake::YardocTask.new do |task| # {{{
|
task :doc do # {{{
|
||||||
task.options = [
|
sh "bundle exec yard doc"
|
||||||
"-o", File.expand_path("../doc", __FILE__),
|
|
||||||
"--readme=README.md",
|
|
||||||
"--markup=markdown",
|
|
||||||
"--markup-provider=maruku",
|
|
||||||
"--no-private",
|
|
||||||
"--no-cache",
|
|
||||||
"--protected",
|
|
||||||
"--title=Teamocil",
|
|
||||||
]
|
|
||||||
task.files = ["lib/**/*.rb"]
|
|
||||||
end # }}}
|
end # }}}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
module Teamocil
|
module Teamocil
|
||||||
VERSION = "0.3.2"
|
VERSION = "0.2"
|
||||||
autoload :Layout, "teamocil/layout"
|
autoload :Layout, "teamocil/layout"
|
||||||
autoload :CLI, "teamocil/cli"
|
autoload :CLI, "teamocil/cli"
|
||||||
end
|
end
|
||||||
|
@ -5,44 +5,34 @@ module Teamocil
|
|||||||
# This class handles interaction with the `tmux` utility.
|
# This class handles interaction with the `tmux` utility.
|
||||||
class CLI
|
class CLI
|
||||||
|
|
||||||
attr_accessor :layout, :layouts
|
|
||||||
|
|
||||||
# Initialize a new run of `tmux`
|
# Initialize a new run of `tmux`
|
||||||
#
|
#
|
||||||
# @param argv [Hash] the command line parameters hash (usually `ARGV`).
|
# @param argv [Hash] the command line parameters hash (usually `ARGV`).
|
||||||
# @param env [Hash] the environment variables hash (usually `ENV`).
|
# @param env [Hash] the environment variables hash (usually `ENV`).
|
||||||
def initialize(argv, env) # {{{
|
def initialize(argv, env) # {{{
|
||||||
parse_options! argv
|
bail "You must be in a tmux session to use teamocil" unless env["TMUX"]
|
||||||
layout_path = File.join("#{env["HOME"]}", ".teamocil")
|
|
||||||
|
|
||||||
if @options.include?(:list)
|
|
||||||
@layouts = get_layouts(layout_path)
|
|
||||||
return print_layouts
|
|
||||||
end
|
|
||||||
|
|
||||||
|
parse_options!
|
||||||
if @options.include?(:layout)
|
if @options.include?(:layout)
|
||||||
file = @options[:layout]
|
file = options[:layout]
|
||||||
else
|
else
|
||||||
file = ::File.join(layout_path, "#{argv[0]}.yml")
|
file = ::File.join("#{env["HOME"]}/.teamocil", "#{argv[0]}.yml")
|
||||||
end
|
end
|
||||||
|
|
||||||
if @options[:edit]
|
if @options[:edit]
|
||||||
::FileUtils.touch file unless File.exists?(file)
|
::FileUtils.touch file unless File.exists?(file)
|
||||||
|
system("$EDITOR \"#{file}\"")
|
||||||
Kernel.system("$EDITOR \"#{file}\"")
|
|
||||||
else
|
else
|
||||||
bail "There is no file \"#{file}\"" unless File.exists?(file)
|
bail "There is no file \"#{file}\"" unless File.exists?(file)
|
||||||
bail "You must be in a tmux session to use teamocil" unless env["TMUX"]
|
|
||||||
|
|
||||||
parsed_layout = YAML.load_file(file)
|
parsed_layout = YAML.load_file(file)
|
||||||
@layout = Teamocil::Layout.new(parsed_layout, @options)
|
layout = Teamocil::Layout.new(parsed_layout, @options)
|
||||||
@layout.compile!
|
layout.compile!
|
||||||
@layout.execute_commands(@layout.generate_commands)
|
layout.execute_commands(layout.generate_commands)
|
||||||
end
|
end
|
||||||
end # }}}
|
end # }}}
|
||||||
|
|
||||||
# Parse the command line options
|
# Parse the command line options
|
||||||
def parse_options!(args) # {{{
|
def parse_options! # {{{
|
||||||
@options = {}
|
@options = {}
|
||||||
opts = ::OptionParser.new do |opts|
|
opts = ::OptionParser.new do |opts|
|
||||||
opts.banner = "Usage: teamocil [options] <layout>
|
opts.banner = "Usage: teamocil [options] <layout>
|
||||||
@ -57,36 +47,19 @@ module Teamocil
|
|||||||
@options[:edit] = true
|
@options[:edit] = true
|
||||||
end
|
end
|
||||||
|
|
||||||
opts.on("--layout [LAYOUT]", "Use a specific layout file, instead of `~/.teamocil/<layout>.yml`") do |layout|
|
opts.on("--layout [LAYOUT]", "Use a specific layout file, instead of ~/.teamocil/<layout>.yml") do |layout|
|
||||||
@options[:layout] = layout
|
@options[:layout] = layout
|
||||||
end
|
end
|
||||||
|
|
||||||
opts.on("--list", "List all available layouts in `~/.teamocil/`") do
|
|
||||||
@options[:list] = true
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
end
|
||||||
opts.parse! args
|
opts.parse!
|
||||||
end # }}}
|
|
||||||
|
|
||||||
# Return an array of available layouts
|
|
||||||
#
|
|
||||||
# @param path [String] the path used to look for layouts
|
|
||||||
def get_layouts(path) # {{{
|
|
||||||
Dir.glob(File.join(path, "*.yml")).map { |file| File.basename(file).gsub(/\..+$/, "") }.sort
|
|
||||||
end # }}}
|
|
||||||
|
|
||||||
# Print each layout on a single line
|
|
||||||
def print_layouts # {{{
|
|
||||||
STDOUT.puts @layouts.join("\n")
|
|
||||||
exit 0
|
|
||||||
end # }}}
|
end # }}}
|
||||||
|
|
||||||
# Print an error message and exit the utility
|
# Print an error message and exit the utility
|
||||||
#
|
#
|
||||||
# @param msg [Mixed] something to print before exiting.
|
# @param msg [Mixed] something to print before exiting.
|
||||||
def bail(msg) # {{{
|
def bail(msg) # {{{
|
||||||
STDERR.puts "[teamocil] #{msg}"
|
puts "[teamocil] #{msg}"
|
||||||
exit 1
|
exit 1
|
||||||
end # }}}
|
end # }}}
|
||||||
|
|
||||||
|
@ -2,11 +2,135 @@ 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
|
class Layout
|
||||||
autoload :Session, "teamocil/layout/session"
|
|
||||||
autoload :Window, "teamocil/layout/window"
|
|
||||||
autoload :Split, "teamocil/layout/split"
|
|
||||||
|
|
||||||
attr_reader :session
|
# 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
|
# Initialize a new layout from a hash
|
||||||
#
|
#
|
||||||
|
@ -1,31 +0,0 @@
|
|||||||
module Teamocil
|
|
||||||
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, window_index| Window.new(self, window_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
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,64 +0,0 @@
|
|||||||
module Teamocil
|
|
||||||
class Layout
|
|
||||||
|
|
||||||
# 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?
|
|
||||||
init_command = ""
|
|
||||||
unless @index == 0
|
|
||||||
if !@width.nil?
|
|
||||||
init_command = "tmux split-window -h -p #{@width}"
|
|
||||||
elsif !@height.nil?
|
|
||||||
init_command = "tmux split-window -p #{@height}"
|
|
||||||
else
|
|
||||||
init_command = "tmux split-window"
|
|
||||||
end
|
|
||||||
init_command << " -t #{@target}" unless @target.nil?
|
|
||||||
commands << init_command
|
|
||||||
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?
|
|
||||||
|
|
||||||
# Set the TEAMOCIL environment variable
|
|
||||||
@cmd.unshift "export TEAMOCIL=1"
|
|
||||||
|
|
||||||
# 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
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,55 +0,0 @@
|
|||||||
module Teamocil
|
|
||||||
class Layout
|
|
||||||
|
|
||||||
# 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"] || {}
|
|
||||||
|
|
||||||
@splits = attrs["splits"] || []
|
|
||||||
@splits = @splits.each_with_index.map { |split, split_index| Split.new(self, split_index, split) }
|
|
||||||
|
|
||||||
@filters = attrs["filters"] || {}
|
|
||||||
@filters["before"] ||= []
|
|
||||||
@filters["after"] ||= []
|
|
||||||
|
|
||||||
@index = index
|
|
||||||
@session = session
|
|
||||||
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
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,43 +0,0 @@
|
|||||||
require File.join(File.dirname(__FILE__), "spec_helper.rb")
|
|
||||||
|
|
||||||
describe Teamocil::CLI do
|
|
||||||
|
|
||||||
context "executing" do
|
|
||||||
|
|
||||||
context "not in tmux" do
|
|
||||||
|
|
||||||
before do # {{{
|
|
||||||
@fake_env = { "TMUX" => 1, "HOME" => File.join(File.dirname(__FILE__), "fixtures") }
|
|
||||||
end # }}}
|
|
||||||
|
|
||||||
it "should allow editing" do # {{{
|
|
||||||
FileUtils.stub(:touch)
|
|
||||||
Kernel.should_receive(:system).with(any_args())
|
|
||||||
|
|
||||||
@cli = Teamocil::CLI.new(["--edit", "my-layout"], @fake_env)
|
|
||||||
end # }}}
|
|
||||||
end
|
|
||||||
|
|
||||||
context "in tmux" do
|
|
||||||
|
|
||||||
before do # {{{
|
|
||||||
@fake_env = { "TMUX" => 1, "HOME" => File.join(File.dirname(__FILE__), "fixtures") }
|
|
||||||
end # }}}
|
|
||||||
|
|
||||||
it "creates a layout" do # {{{
|
|
||||||
@cli = Teamocil::CLI.new(["sample"], @fake_env)
|
|
||||||
@cli.layout.session.name.should == "sample"
|
|
||||||
@cli.layout.session.windows.length.should == 2
|
|
||||||
@cli.layout.session.windows.first.name.should == "foo"
|
|
||||||
@cli.layout.session.windows.last.name.should == "bar"
|
|
||||||
end # }}}
|
|
||||||
|
|
||||||
it "lists available layouts" do # {{{
|
|
||||||
@cli = Teamocil::CLI.new(["--list"], @fake_env)
|
|
||||||
@cli.layouts.should == ["sample", "sample-2"]
|
|
||||||
end # }}}
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
10
spec/fixtures/.teamocil/sample-2.yml
vendored
10
spec/fixtures/.teamocil/sample-2.yml
vendored
@ -1,10 +0,0 @@
|
|||||||
session:
|
|
||||||
name: sample-2
|
|
||||||
root: ~
|
|
||||||
windows:
|
|
||||||
- name: "foo"
|
|
||||||
splits:
|
|
||||||
- cmd: "pwd"
|
|
||||||
- name: "bar"
|
|
||||||
splits:
|
|
||||||
- cmd: "pwd"
|
|
10
spec/fixtures/.teamocil/sample.yml
vendored
10
spec/fixtures/.teamocil/sample.yml
vendored
@ -1,10 +0,0 @@
|
|||||||
session:
|
|
||||||
name: sample
|
|
||||||
root: ~
|
|
||||||
windows:
|
|
||||||
- name: "foo"
|
|
||||||
splits:
|
|
||||||
- cmd: "pwd"
|
|
||||||
- name: "bar"
|
|
||||||
splits:
|
|
||||||
- cmd: "pwd"
|
|
9
spec/fixtures/layouts.yml
vendored
9
spec/fixtures/layouts.yml
vendored
@ -13,7 +13,6 @@ two-windows:
|
|||||||
- cmd:
|
- cmd:
|
||||||
- "echo 'bar'"
|
- "echo 'bar'"
|
||||||
- "echo 'bar in an array'"
|
- "echo 'bar in an array'"
|
||||||
target: bottom-right
|
|
||||||
- cmd: "echo 'bar again'"
|
- cmd: "echo 'bar again'"
|
||||||
width: 50
|
width: 50
|
||||||
|
|
||||||
@ -33,11 +32,3 @@ two-windows-with-filters:
|
|||||||
- cmd: "echo 'foo'"
|
- cmd: "echo 'foo'"
|
||||||
- cmd: "echo 'foo again'"
|
- cmd: "echo 'foo again'"
|
||||||
width: 50
|
width: 50
|
||||||
|
|
||||||
three-windows-within-a-session:
|
|
||||||
session:
|
|
||||||
name: "my awesome session"
|
|
||||||
windows:
|
|
||||||
- name: "first window"
|
|
||||||
- name: "second window"
|
|
||||||
- name: "third window"
|
|
||||||
|
@ -2,19 +2,15 @@ require File.join(File.dirname(__FILE__), "spec_helper.rb")
|
|||||||
|
|
||||||
describe Teamocil::Layout do
|
describe Teamocil::Layout do
|
||||||
|
|
||||||
|
context "initializing" do
|
||||||
|
end
|
||||||
|
|
||||||
context "compiling" do
|
context "compiling" do
|
||||||
|
|
||||||
before do # {{{
|
before :each do # {{{
|
||||||
@layout = Teamocil::Layout.new(layouts["two-windows"], {})
|
@layout = Teamocil::Layout.new(layouts["two-windows"], {})
|
||||||
end # }}}
|
end # }}}
|
||||||
|
|
||||||
it "creates windows" do # {{{
|
|
||||||
session = @layout.compile!
|
|
||||||
session.windows.each do |window|
|
|
||||||
window.should be_an_instance_of Teamocil::Layout::Window
|
|
||||||
end
|
|
||||||
end # }}}
|
|
||||||
|
|
||||||
it "creates windows with names" do # {{{
|
it "creates windows with names" do # {{{
|
||||||
session = @layout.compile!
|
session = @layout.compile!
|
||||||
session.windows[0].name.should == "foo"
|
session.windows[0].name.should == "foo"
|
||||||
@ -27,13 +23,6 @@ describe Teamocil::Layout do
|
|||||||
session.windows[1].root.should == "/bar"
|
session.windows[1].root.should == "/bar"
|
||||||
end # }}}
|
end # }}}
|
||||||
|
|
||||||
it "creates splits" do # {{{
|
|
||||||
session = @layout.compile!
|
|
||||||
session.windows.first.splits.each do |split|
|
|
||||||
split.should be_an_instance_of Teamocil::Layout::Split
|
|
||||||
end
|
|
||||||
end # }}}
|
|
||||||
|
|
||||||
it "creates splits with dimensions" do # {{{
|
it "creates splits with dimensions" do # {{{
|
||||||
session = @layout.compile!
|
session = @layout.compile!
|
||||||
session.windows.first.splits[0].width.should == nil
|
session.windows.first.splits[0].width.should == nil
|
||||||
@ -68,31 +57,5 @@ describe Teamocil::Layout do
|
|||||||
session.windows.first.filters["after"].last.should == "echo second after filter"
|
session.windows.first.filters["after"].last.should == "echo second after filter"
|
||||||
end # }}}
|
end # }}}
|
||||||
|
|
||||||
it "should handle blank filters" do # {{{
|
|
||||||
session = @layout.compile!
|
|
||||||
session.windows.first.filters.should have_key "after"
|
|
||||||
session.windows.first.filters.should have_key "before"
|
|
||||||
session.windows.first.filters["after"].should be_empty
|
|
||||||
session.windows.first.filters["before"].should be_empty
|
|
||||||
end # }}}
|
|
||||||
|
|
||||||
it "should handle splits without a target" do # {{{
|
|
||||||
session = @layout.compile!
|
|
||||||
session.windows.last.splits.last.target.should == nil
|
|
||||||
end # }}}
|
|
||||||
|
|
||||||
it "should handle splits with a target" do # {{{
|
|
||||||
session = @layout.compile!
|
|
||||||
session.windows.last.splits.first.target.should == "bottom-right"
|
|
||||||
end # }}}
|
|
||||||
|
|
||||||
it "should handle windows within a session" do # {{{
|
|
||||||
layout = Teamocil::Layout.new(layouts["three-windows-within-a-session"], {})
|
|
||||||
session = layout.compile!
|
|
||||||
session.windows.length.should == 3
|
|
||||||
session.name.should == "my awesome session"
|
|
||||||
end # }}}
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
@ -1,20 +0,0 @@
|
|||||||
module Teamocil
|
|
||||||
module Mock
|
|
||||||
module CLI
|
|
||||||
|
|
||||||
def self.included(base) # {{{
|
|
||||||
base.class_eval do
|
|
||||||
|
|
||||||
# Do not print anything
|
|
||||||
def print_layouts
|
|
||||||
# Nothing
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end # }}}
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
Teamocil::CLI.send :include, Teamocil::Mock::CLI
|
|
@ -1,20 +0,0 @@
|
|||||||
module Teamocil
|
|
||||||
module Mock
|
|
||||||
module Layout
|
|
||||||
|
|
||||||
def self.included(base) # {{{
|
|
||||||
base.class_eval do
|
|
||||||
|
|
||||||
# Do not execute anything
|
|
||||||
def execute_commands(commands)
|
|
||||||
# Nothing
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end # }}}
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
Teamocil::Layout.send :include, Teamocil::Mock::Layout
|
|
@ -2,8 +2,6 @@ $:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
|
|||||||
|
|
||||||
require 'yaml'
|
require 'yaml'
|
||||||
require 'teamocil'
|
require 'teamocil'
|
||||||
require File.join(File.dirname(__FILE__), "./mock/layout.rb")
|
|
||||||
require File.join(File.dirname(__FILE__), "./mock/cli.rb")
|
|
||||||
|
|
||||||
module Helpers
|
module Helpers
|
||||||
|
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
# encoding: utf-8
|
# -*- encoding: utf-8 -*-
|
||||||
|
|
||||||
$:.push File.expand_path("../lib", __FILE__)
|
$:.push File.expand_path("../lib", __FILE__)
|
||||||
require "teamocil"
|
require "teamocil"
|
||||||
|
|
||||||
@ -25,4 +24,5 @@ spec = Gem::Specification.new do |s|
|
|||||||
s.add_development_dependency "rspec"
|
s.add_development_dependency "rspec"
|
||||||
s.add_development_dependency "yard"
|
s.add_development_dependency "yard"
|
||||||
s.add_development_dependency "maruku"
|
s.add_development_dependency "maruku"
|
||||||
|
|
||||||
end
|
end
|
||||||
|
Loading…
Reference in New Issue
Block a user