Compare commits
No commits in common. "master" and "gh-pages" have entirely different histories.
6
.gitignore
vendored
6
.gitignore
vendored
@ -1,6 +0,0 @@
|
|||||||
*.gem
|
|
||||||
.rvmrc
|
|
||||||
.local*
|
|
||||||
.yardoc
|
|
||||||
doc
|
|
||||||
pkg
|
|
@ -1,2 +0,0 @@
|
|||||||
rvm: 1.9.2
|
|
||||||
script: "bundle exec rake spec"
|
|
32
Gemfile.lock
32
Gemfile.lock
@ -1,32 +0,0 @@
|
|||||||
PATH
|
|
||||||
remote: .
|
|
||||||
specs:
|
|
||||||
teamocil (0.3.2)
|
|
||||||
|
|
||||||
GEM
|
|
||||||
remote: http://rubygems.org/
|
|
||||||
specs:
|
|
||||||
diff-lcs (1.1.3)
|
|
||||||
maruku (0.6.0)
|
|
||||||
syntax (>= 1.0.0)
|
|
||||||
rake (0.9.2)
|
|
||||||
rspec (2.6.0)
|
|
||||||
rspec-core (~> 2.6.0)
|
|
||||||
rspec-expectations (~> 2.6.0)
|
|
||||||
rspec-mocks (~> 2.6.0)
|
|
||||||
rspec-core (2.6.4)
|
|
||||||
rspec-expectations (2.6.0)
|
|
||||||
diff-lcs (~> 1.1.2)
|
|
||||||
rspec-mocks (2.6.0)
|
|
||||||
syntax (1.0.0)
|
|
||||||
yard (0.7.5)
|
|
||||||
|
|
||||||
PLATFORMS
|
|
||||||
ruby
|
|
||||||
|
|
||||||
DEPENDENCIES
|
|
||||||
maruku
|
|
||||||
rake
|
|
||||||
rspec
|
|
||||||
teamocil!
|
|
||||||
yard
|
|
3
LICENSE
3
LICENSE
@ -1,3 +0,0 @@
|
|||||||
Copyright 2011-2012 Rémi Prévost.
|
|
||||||
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.
|
|
204
README.md
204
README.md
@ -1,204 +0,0 @@
|
|||||||
# Teamocil
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ gem install teamocil
|
|
||||||
$ mkdir ~/.teamocil
|
|
||||||
$ teamocil --edit sample
|
|
||||||
$ tmux
|
|
||||||
$ teamocil sample
|
|
||||||
```
|
|
||||||
|
|
||||||
## Options
|
|
||||||
|
|
||||||
* `--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.
|
|
||||||
* `--edit` opens the layout file (whether or not `--layout` is used) with `$EDITOR`.
|
|
||||||
* `--list` lists all available layouts.
|
|
||||||
|
|
||||||
## Layout file structure
|
|
||||||
|
|
||||||
A layout file is a single YAML file located in `~/.teamocil` (eg. `~/.teamocil/my-project.yml`).
|
|
||||||
|
|
||||||
### Session
|
|
||||||
|
|
||||||
You can wrap your entire layout file in a `session` and Teamocil will rename the current session (so that you can find it more easily when running `tmux list-sessions`) before creating your windows.
|
|
||||||
|
|
||||||
#### Keys
|
|
||||||
|
|
||||||
* `name` (the name of the session)
|
|
||||||
|
|
||||||
#### Example
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
session:
|
|
||||||
name: "my-awesome-session"
|
|
||||||
windows:
|
|
||||||
[windows list]
|
|
||||||
```
|
|
||||||
|
|
||||||
### Windows
|
|
||||||
|
|
||||||
If you are not using a top-level `session` key, then the first key of your layout file will be `windows`, an array of window items.
|
|
||||||
|
|
||||||
#### Item keys
|
|
||||||
|
|
||||||
* `name` (the name that will appear in `tmux` statusbar)
|
|
||||||
* `root` (the directory in which every split will be created)
|
|
||||||
* `filters` (a hash of `before` and `after` commands to run for each split)
|
|
||||||
* `splits` (an array of split items)
|
|
||||||
* `options` (a hash of tmux options, see `man tmux` for a list)
|
|
||||||
|
|
||||||
#### Example
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
windows:
|
|
||||||
- name: "my-first-window"
|
|
||||||
options:
|
|
||||||
synchronize-panes: true
|
|
||||||
root: "~/Projects/foo-www"
|
|
||||||
filters:
|
|
||||||
before:
|
|
||||||
- "echo 'Let’s use ruby-1.9.2 for each split in this window.'"
|
|
||||||
- "rvm use 1.9.2"
|
|
||||||
splits:
|
|
||||||
[splits list]
|
|
||||||
- name: "my-second-window"
|
|
||||||
root: "~/Projects/foo-api"
|
|
||||||
splits:
|
|
||||||
[splits list]
|
|
||||||
- name: "my-third-window"
|
|
||||||
root: "~/Projects/foo-daemons"
|
|
||||||
splits:
|
|
||||||
[splits list]
|
|
||||||
```
|
|
||||||
|
|
||||||
### Splits
|
|
||||||
|
|
||||||
Every window must define an array of splits that will be created within it. A vertical or horizontal split will be created, depending on whether the `width` or `height` parameter is used.
|
|
||||||
|
|
||||||
#### Item keys
|
|
||||||
|
|
||||||
* `cmd` (the commands to initially execute in the split)
|
|
||||||
* `width` (the split width, in percentage)
|
|
||||||
* `height` (the split width, in percentage)
|
|
||||||
* `target` (the split to set focus on, before creating the current one)
|
|
||||||
|
|
||||||
#### Example
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
windows:
|
|
||||||
- name: "my-first-window"
|
|
||||||
root: "~/Projects/foo-www"
|
|
||||||
filters:
|
|
||||||
before: "rvm use 1.9.2"
|
|
||||||
after: "echo 'I am done initializing this split.'"
|
|
||||||
splits:
|
|
||||||
- cmd: "git status"
|
|
||||||
- cmd: "bundle exec rails server --port 4000"
|
|
||||||
width: 50
|
|
||||||
- cmd:
|
|
||||||
- "sudo service memcached start"
|
|
||||||
- "sudo service mongodb start"
|
|
||||||
height: 50
|
|
||||||
```
|
|
||||||
|
|
||||||
## Layout examples
|
|
||||||
|
|
||||||
See more example files in the `examples` directory.
|
|
||||||
|
|
||||||
### Simple two splits window
|
|
||||||
|
|
||||||
#### Content of `~/.teamocil/sample-1.yml`
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
windows:
|
|
||||||
- name: "sample-two-splits"
|
|
||||||
root: "~/Code/sample/www"
|
|
||||||
splits:
|
|
||||||
- cmd: ["pwd", "ls -la"]
|
|
||||||
- cmd: "rails server --port 3000"
|
|
||||||
width: 50
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
#### Result of `$ teamocil sample-1`
|
|
||||||
|
|
||||||
.------------------.------------------.
|
|
||||||
| (0) | (1) |
|
|
||||||
| | |
|
|
||||||
| | |
|
|
||||||
| | |
|
|
||||||
| | |
|
|
||||||
| | |
|
|
||||||
| | |
|
|
||||||
| | |
|
|
||||||
| | |
|
|
||||||
'------------------'------------------'
|
|
||||||
|
|
||||||
### Four tiled splits window
|
|
||||||
|
|
||||||
#### Content of `~/.teamocil/sample-2.yml`
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
windows:
|
|
||||||
- name: "sample-four-splits"
|
|
||||||
root: "~/Code/sample/www"
|
|
||||||
splits:
|
|
||||||
- cmd: "pwd"
|
|
||||||
- cmd: "pwd"
|
|
||||||
width: 50
|
|
||||||
- cmd: "pwd"
|
|
||||||
height: 50
|
|
||||||
target: "bottom-right"
|
|
||||||
- cmd: "pwd"
|
|
||||||
height: 50
|
|
||||||
target: "bottom-left"
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Result of `$ teamocil sample-2`
|
|
||||||
|
|
||||||
.------------------.------------------.
|
|
||||||
| (0) | (1) |
|
|
||||||
| | |
|
|
||||||
| | |
|
|
||||||
| | |
|
|
||||||
|------------------|------------------|
|
|
||||||
| (3) | (2) |
|
|
||||||
| | |
|
|
||||||
| | |
|
|
||||||
| | |
|
|
||||||
'------------------'------------------'
|
|
||||||
|
|
||||||
## Extras
|
|
||||||
|
|
||||||
### Zsh autocompletion
|
|
||||||
|
|
||||||
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
|
|
||||||
```
|
|
||||||
|
|
||||||
## Todo list
|
|
||||||
|
|
||||||
* Making sure the layout is valid before executing it (ie. throw exceptions).
|
|
||||||
* Add more specs.
|
|
||||||
|
|
||||||
## Contributors
|
|
||||||
|
|
||||||
Feel free to contribute and submit issues/pull requests [on GitHub](https://github.com/remiprev/teamocil/issues), just like these fine folks did:
|
|
||||||
|
|
||||||
* Samuel Garneau ([garno](https://github.com/garno))
|
|
||||||
* 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
|
|
||||||
|
|
||||||
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.
|
|
28
Rakefile
28
Rakefile
@ -1,28 +0,0 @@
|
|||||||
require "bundler"
|
|
||||||
Bundler.require(:development)
|
|
||||||
|
|
||||||
require "bundler/gem_tasks"
|
|
||||||
require "rspec/core/rake_task"
|
|
||||||
|
|
||||||
task :default => :spec
|
|
||||||
|
|
||||||
desc "Run all specs"
|
|
||||||
RSpec::Core::RakeTask.new(:spec) do |task| # {{{
|
|
||||||
task.pattern = "spec/**/*_spec.rb"
|
|
||||||
task.rspec_opts = "--colour --format=documentation"
|
|
||||||
end # }}}
|
|
||||||
|
|
||||||
desc "Generate YARD Documentation"
|
|
||||||
YARD::Rake::YardocTask.new do |task| # {{{
|
|
||||||
task.options = [
|
|
||||||
"-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 # }}}
|
|
@ -1,8 +0,0 @@
|
|||||||
#!/usr/bin/env ruby
|
|
||||||
|
|
||||||
$:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
|
|
||||||
|
|
||||||
require 'yaml'
|
|
||||||
require 'teamocil'
|
|
||||||
|
|
||||||
Teamocil::CLI.new(ARGV, ENV)
|
|
@ -1,12 +0,0 @@
|
|||||||
windows:
|
|
||||||
- name: simple-four-splits
|
|
||||||
splits:
|
|
||||||
- cmd: "echo 'first split'"
|
|
||||||
- cmd: "echo 'second split'"
|
|
||||||
width: 50
|
|
||||||
- cmd: "echo 'fourth split'"
|
|
||||||
height: 50
|
|
||||||
target: bottom-right
|
|
||||||
- cmd: "echo 'third split'"
|
|
||||||
height: 50
|
|
||||||
target: bottom-left
|
|
@ -1,12 +0,0 @@
|
|||||||
windows:
|
|
||||||
- name: simple-one-and-three-splits
|
|
||||||
splits:
|
|
||||||
- cmd: "echo 'first split'"
|
|
||||||
- cmd: "echo 'second split'"
|
|
||||||
width: 50
|
|
||||||
- cmd: "echo 'third split'"
|
|
||||||
height: 66
|
|
||||||
target: bottom-right
|
|
||||||
- cmd: "echo 'fourth split'"
|
|
||||||
height: 50
|
|
||||||
target: bottom-right
|
|
@ -1,18 +0,0 @@
|
|||||||
windows:
|
|
||||||
- name: simple-six-splits
|
|
||||||
splits:
|
|
||||||
- cmd: "echo 'first split'"
|
|
||||||
- cmd: "echo 'second split'"
|
|
||||||
width: 50
|
|
||||||
- cmd: "echo 'fourth split'"
|
|
||||||
height: 66
|
|
||||||
target: bottom-right
|
|
||||||
- cmd: "echo 'third split'"
|
|
||||||
height: 66
|
|
||||||
target: bottom-left
|
|
||||||
- cmd: "echo 'sixth split'"
|
|
||||||
height: 50
|
|
||||||
target: bottom-right
|
|
||||||
- cmd: "echo 'fifth split'"
|
|
||||||
height: 50
|
|
||||||
target: bottom-left
|
|
@ -1,6 +0,0 @@
|
|||||||
windows:
|
|
||||||
- name: simple-two-horizontal-splits
|
|
||||||
splits:
|
|
||||||
- cmd: "echo 'first split'"
|
|
||||||
- cmd: "echo 'second split'"
|
|
||||||
height: 50
|
|
@ -1,6 +0,0 @@
|
|||||||
windows:
|
|
||||||
- name: simple-two-vertical-splits
|
|
||||||
splits:
|
|
||||||
- cmd: "echo 'first split'"
|
|
||||||
- cmd: "echo 'second split'"
|
|
||||||
width: 50
|
|
15
index.html
Normal file
15
index.html
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title></title>
|
||||||
|
<script src="http://cdnjs.cloudflare.com/ajax/libs/documentup/latest.min.js"></script>
|
||||||
|
<script>
|
||||||
|
DocumentUp.document({
|
||||||
|
repo: "remiprev/teamocil",
|
||||||
|
travis: true
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body></body>
|
||||||
|
</html>
|
@ -1,5 +0,0 @@
|
|||||||
module Teamocil
|
|
||||||
VERSION = "0.3.2"
|
|
||||||
autoload :Layout, "teamocil/layout"
|
|
||||||
autoload :CLI, "teamocil/cli"
|
|
||||||
end
|
|
@ -1,94 +0,0 @@
|
|||||||
require 'optparse'
|
|
||||||
require 'fileutils'
|
|
||||||
|
|
||||||
module Teamocil
|
|
||||||
# This class handles interaction with the `tmux` utility.
|
|
||||||
class CLI
|
|
||||||
|
|
||||||
attr_accessor :layout, :layouts
|
|
||||||
|
|
||||||
# Initialize a new run of `tmux`
|
|
||||||
#
|
|
||||||
# @param argv [Hash] the command line parameters hash (usually `ARGV`).
|
|
||||||
# @param env [Hash] the environment variables hash (usually `ENV`).
|
|
||||||
def initialize(argv, env) # {{{
|
|
||||||
parse_options! argv
|
|
||||||
layout_path = File.join("#{env["HOME"]}", ".teamocil")
|
|
||||||
|
|
||||||
if @options.include?(:list)
|
|
||||||
@layouts = get_layouts(layout_path)
|
|
||||||
return print_layouts
|
|
||||||
end
|
|
||||||
|
|
||||||
if @options.include?(:layout)
|
|
||||||
file = @options[:layout]
|
|
||||||
else
|
|
||||||
file = ::File.join(layout_path, "#{argv[0]}.yml")
|
|
||||||
end
|
|
||||||
|
|
||||||
if @options[:edit]
|
|
||||||
::FileUtils.touch file unless File.exists?(file)
|
|
||||||
|
|
||||||
Kernel.system("$EDITOR \"#{file}\"")
|
|
||||||
else
|
|
||||||
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)
|
|
||||||
@layout = Teamocil::Layout.new(parsed_layout, @options)
|
|
||||||
@layout.compile!
|
|
||||||
@layout.execute_commands(@layout.generate_commands)
|
|
||||||
end
|
|
||||||
end # }}}
|
|
||||||
|
|
||||||
# Parse the command line options
|
|
||||||
def parse_options!(args) # {{{
|
|
||||||
@options = {}
|
|
||||||
opts = ::OptionParser.new do |opts|
|
|
||||||
opts.banner = "Usage: teamocil [options] <layout>
|
|
||||||
|
|
||||||
Options:
|
|
||||||
"
|
|
||||||
opts.on("--here", "Set up the first window in the current window") do
|
|
||||||
@options[:here] = true
|
|
||||||
end
|
|
||||||
|
|
||||||
opts.on("--edit", "Edit the YAML layout file instead of using it") do
|
|
||||||
@options[:edit] = true
|
|
||||||
end
|
|
||||||
|
|
||||||
opts.on("--layout [LAYOUT]", "Use a specific layout file, instead of `~/.teamocil/<layout>.yml`") do |layout|
|
|
||||||
@options[:layout] = layout
|
|
||||||
end
|
|
||||||
|
|
||||||
opts.on("--list", "List all available layouts in `~/.teamocil/`") do
|
|
||||||
@options[:list] = true
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
opts.parse! args
|
|
||||||
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 # }}}
|
|
||||||
|
|
||||||
# Print an error message and exit the utility
|
|
||||||
#
|
|
||||||
# @param msg [Mixed] something to print before exiting.
|
|
||||||
def bail(msg) # {{{
|
|
||||||
STDERR.puts "[teamocil] #{msg}"
|
|
||||||
exit 1
|
|
||||||
end # }}}
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,46 +0,0 @@
|
|||||||
module Teamocil
|
|
||||||
|
|
||||||
# This class act as a wrapper around a tmux YAML layout file
|
|
||||||
class Layout
|
|
||||||
autoload :Session, "teamocil/layout/session"
|
|
||||||
autoload :Window, "teamocil/layout/window"
|
|
||||||
autoload :Split, "teamocil/layout/split"
|
|
||||||
|
|
||||||
attr_reader :session
|
|
||||||
|
|
||||||
# Initialize a new layout from a hash
|
|
||||||
#
|
|
||||||
# @param layout [Hash] the parsed layout
|
|
||||||
# @param options [Hash] some options
|
|
||||||
def initialize(layout, options={}) # {{{
|
|
||||||
@layout = layout
|
|
||||||
@options = options
|
|
||||||
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 # {{{
|
|
||||||
@session.generate_commands
|
|
||||||
end # }}}
|
|
||||||
|
|
||||||
# Compile the layout into objects
|
|
||||||
#
|
|
||||||
# @return [Session]
|
|
||||||
def compile! # {{{
|
|
||||||
if @layout["session"].nil?
|
|
||||||
@session = Session.new @options, "windows" => @layout["windows"]
|
|
||||||
else
|
|
||||||
@session = Session.new @options, @layout["session"]
|
|
||||||
end
|
|
||||||
end # }}}
|
|
||||||
|
|
||||||
# Execute each command in the shell
|
|
||||||
#
|
|
||||||
# @param commands [Array] an array of complete commands to send to the shell
|
|
||||||
def execute_commands(commands) # {{{
|
|
||||||
`#{commands.join("; ")}`
|
|
||||||
end # }}}
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
@ -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"
|
|
43
spec/fixtures/layouts.yml
vendored
43
spec/fixtures/layouts.yml
vendored
@ -1,43 +0,0 @@
|
|||||||
# Simple two windows layout
|
|
||||||
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'"
|
|
||||||
- "echo 'bar in an array'"
|
|
||||||
target: bottom-right
|
|
||||||
- cmd: "echo 'bar again'"
|
|
||||||
width: 50
|
|
||||||
|
|
||||||
# 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 'foo'"
|
|
||||||
- cmd: "echo 'foo again'"
|
|
||||||
width: 50
|
|
||||||
|
|
||||||
three-windows-within-a-session:
|
|
||||||
session:
|
|
||||||
name: "my awesome session"
|
|
||||||
windows:
|
|
||||||
- name: "first window"
|
|
||||||
- name: "second window"
|
|
||||||
- name: "third window"
|
|
@ -1,98 +0,0 @@
|
|||||||
require File.join(File.dirname(__FILE__), "spec_helper.rb")
|
|
||||||
|
|
||||||
describe Teamocil::Layout do
|
|
||||||
|
|
||||||
context "compiling" do
|
|
||||||
|
|
||||||
before do # {{{
|
|
||||||
@layout = Teamocil::Layout.new(layouts["two-windows"], {})
|
|
||||||
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 # {{{
|
|
||||||
session = @layout.compile!
|
|
||||||
session.windows[0].name.should == "foo"
|
|
||||||
session.windows[1].name.should == "bar"
|
|
||||||
end # }}}
|
|
||||||
|
|
||||||
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" 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 # {{{
|
|
||||||
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 # }}}
|
|
||||||
|
|
||||||
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
|
|
@ -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
|
|
@ -1,19 +0,0 @@
|
|||||||
$:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
|
|
||||||
|
|
||||||
require 'yaml'
|
|
||||||
require 'teamocil'
|
|
||||||
require File.join(File.dirname(__FILE__), "./mock/layout.rb")
|
|
||||||
require File.join(File.dirname(__FILE__), "./mock/cli.rb")
|
|
||||||
|
|
||||||
module Helpers
|
|
||||||
|
|
||||||
def layouts # {{{
|
|
||||||
return @@examples if defined?(@@examples)
|
|
||||||
@@examples = YAML.load_file(File.join(File.dirname(__FILE__), "fixtures/layouts.yml"))
|
|
||||||
end # }}}
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
RSpec.configure do |c|
|
|
||||||
c.include Helpers
|
|
||||||
end
|
|
@ -1,28 +0,0 @@
|
|||||||
# encoding: utf-8
|
|
||||||
|
|
||||||
$:.push File.expand_path("../lib", __FILE__)
|
|
||||||
require "teamocil"
|
|
||||||
|
|
||||||
spec = Gem::Specification.new do |s|
|
|
||||||
# Metadata
|
|
||||||
s.name = "teamocil"
|
|
||||||
s.version = Teamocil::VERSION
|
|
||||||
s.platform = Gem::Platform::RUBY
|
|
||||||
s.authors = "Rémi Prévost"
|
|
||||||
s.email = "remi@exomel.com"
|
|
||||||
s.homepage = "http://github.com/remiprev/teamocil"
|
|
||||||
s.summary = "Easy window and split layouts for tmux"
|
|
||||||
s.description = "Teamocil helps you set up window and splits layouts for tmux using YAML configuration files."
|
|
||||||
|
|
||||||
# Manifest
|
|
||||||
s.files = `git ls-files`.split("\n")
|
|
||||||
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
|
||||||
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
|
||||||
s.require_paths = ["lib"]
|
|
||||||
|
|
||||||
# Dependencies
|
|
||||||
s.add_development_dependency "rake"
|
|
||||||
s.add_development_dependency "rspec"
|
|
||||||
s.add_development_dependency "yard"
|
|
||||||
s.add_development_dependency "maruku"
|
|
||||||
end
|
|
Loading…
Reference in New Issue
Block a user