├── .gitignore ├── .rspec ├── .travis.yml ├── Gemfile ├── README.md ├── Rakefile ├── Tmuxfile.erb ├── bin ├── console └── setup ├── exe └── tummy ├── lib ├── tummy.rb └── tummy │ └── version.rb ├── spec ├── spec_helper.rb └── tummy_spec.rb └── tummy.gemspec /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /.yardoc 3 | /Gemfile.lock 4 | /_yardoc/ 5 | /coverage/ 6 | /doc/ 7 | /pkg/ 8 | /spec/reports/ 9 | /tmp/ 10 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --format documentation 2 | --color 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | rvm: 3 | - 2.2.4 4 | before_install: gem install bundler -v 1.11.2 5 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Specify your gem's dependencies in tummy.gemspec 4 | gemspec 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Gem Version](https://badge.fury.io/rb/tummy.svg)](https://rubygems.org/gems/tummy) 2 | # Tummy 3 | 4 | Use a Tmuxfile with your tmux configuration to easily manage sessions 5 | 6 | ![Demo](https://i.imgur.com/pBSixTt.gif) 7 | 8 | ## Installation 9 | 10 | $ gem install tummy 11 | 12 | ## Usage 13 | 14 | put a `Tmuxfile` in your app's root by running the `tummy init` command 15 | 16 | example Tmuxfile 17 | 18 | ```ruby 19 | session "z2-web" 20 | directory "/home/minhajuddin/z2/web" 21 | 22 | window "src", [ 23 | pane("vim TODO"), 24 | ] 25 | 26 | window "server-iex", [ 27 | pane("iex -S mix phoenix.server"), 28 | # the last argument is passed to tmux as raw arguments 29 | pane("iex -S mix", :horizontal, "-l 20"), 30 | pane("git status", :vertical), 31 | ] 32 | 33 | window "play", [ 34 | pane("echo hey"), 35 | pane("date", :horizontal), 36 | pane("echo awesome", :vertical), 37 | ] 38 | 39 | # if you comment this out it will focus the first window when the session is started 40 | focus_window "server-iex" 41 | # focus_window 0 # you can even focus a window by index starting at 0 42 | 43 | ``` 44 | 45 | Now whenever you run the `tummy` command from this directory it will setup your sessions properly 46 | The `Tmuxfile` is a regular ruby file. If a tmux session with this name is already running it will just connect to that session 47 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | require "rspec/core/rake_task" 3 | 4 | RSpec::Core::RakeTask.new(:spec) 5 | 6 | task :default => :spec 7 | -------------------------------------------------------------------------------- /Tmuxfile.erb: -------------------------------------------------------------------------------- 1 | session "<%= current_directory_name %>" 2 | directory "<%= cwd %>" 3 | 4 | window "src", [ 5 | pane("vim TODO"), 6 | ] 7 | 8 | window "server-iex", [ 9 | pane("iex -S mix phoenix.server"), 10 | # the last argument is passed to tmux as raw arguments 11 | pane("iex -S mix", :horizontal, "-l 8"), 12 | pane("git status", :vertical), 13 | ] 14 | 15 | window "play", [ 16 | pane("echo hey"), 17 | pane("date", :horizontal), 18 | pane("echo awesome", :vertical), 19 | ] 20 | 21 | # if you comment this out it will focus the first window when the session is started 22 | focus_window "server-iex" 23 | # focus_window 0 # you can even focus a window by index starting at 0 24 | 25 | # vim: filetype=ruby 26 | -------------------------------------------------------------------------------- /bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require "bundler/setup" 4 | require "tummy" 5 | 6 | # You can add fixtures and/or initialization code here to make experimenting 7 | # with your gem easier. You can also use a different console, if you like. 8 | 9 | # (If you use this, don't forget to add pry to your Gemfile!) 10 | # require "pry" 11 | # Pry.start 12 | 13 | require "irb" 14 | IRB.start 15 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | set -vx 5 | 6 | bundle install 7 | 8 | # Do any other automated setup that you need to do here 9 | -------------------------------------------------------------------------------- /exe/tummy: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | class Runner 4 | attr_reader :tmux 5 | def initialize(tmux) 6 | @tmux = tmux 7 | end 8 | 9 | def run 10 | # if this session is already open let us attach it to that 11 | attach_to_running_session 12 | create_session 13 | create_windows 14 | focus_window 15 | attach_to_running_session 16 | end 17 | 18 | def session; tmux.session_name end 19 | def directory; tmux.directory_name end 20 | 21 | def focus_window 22 | run_cmd "tmux select-window -t #{session}:#{tmux.focused_window}" 23 | end 24 | 25 | def create_session 26 | run_cmd "tmux new-session -d -s #{session} -c #{directory} -n #{tmux.windows.first.name}" 27 | end 28 | 29 | def create_windows 30 | # the first window is already created 31 | create_panes(tmux.windows.first) 32 | tmux.windows.drop(1).each{|window| create_window(window)} 33 | end 34 | 35 | def create_window(window) 36 | run_cmd "tmux new-window -n #{window.name} -t #{session} -c #{directory}" 37 | create_panes(window) 38 | end 39 | 40 | def create_panes(window) 41 | # the first pane is already created 42 | panes = window.panes 43 | run_cmd %Q[tmux send-keys -t #{session} "#{panes.first.cmd}" C-m] 44 | panes.drop(1).each do |pane| 45 | run_cmd "tmux split-window #{"-h" if pane.alignment == :vertical} -t #{session}:#{window.index} -c #{directory} #{pane.raw_args}" 46 | run_cmd "tmux send-keys -t #{session}:#{window.index}.#{pane.index} \"#{pane.cmd}\" C-m" 47 | end 48 | end 49 | 50 | def attach_to_running_session 51 | if run_cmd("tmux has-session -t #{session}") 52 | exec "tmux attach-session -t #{session}" 53 | end 54 | end 55 | 56 | def run_cmd(cmd) 57 | puts "> #{cmd}" 58 | system(cmd) 59 | end 60 | end 61 | 62 | class Tmux 63 | 64 | attr_reader :windows 65 | 66 | def initialize(filepath) 67 | @filepath = filepath 68 | @windows = [] 69 | end 70 | 71 | def run 72 | config = File.read(@filepath) 73 | self.instance_eval config 74 | Runner.new(self).run 75 | end 76 | 77 | def focus_window(window_name_or_index) 78 | @focused_window = window_name_or_index 79 | end 80 | 81 | def focused_window 82 | # try to find the window with the name 83 | index = @windows.find_index{|w| w.name == @focused_window} 84 | 85 | index || @focused_window.to_i 86 | end 87 | 88 | def session(name) 89 | @session = name 90 | end 91 | 92 | def session_name; @session end 93 | def directory_name; @directory end 94 | 95 | def directory(directory) 96 | @directory = directory 97 | end 98 | 99 | Window = Struct.new(:name, :panes, :index) 100 | def window(name, panes) 101 | @windows << Window.new(name, panes.each_with_index.map{|x, i| x.index = i; x}, @windows.length) 102 | end 103 | 104 | Pane = Struct.new(:cmd, :alignment, :index, :raw_args) 105 | def pane(cmd, alignment = :vertical, raw_args = "") 106 | Pane.new(cmd, alignment, 0, raw_args) 107 | end 108 | end 109 | 110 | # init a Tmuxfile 111 | cmd = ARGV.first 112 | if cmd.to_s.strip.downcase == "help" 113 | puts <<-EOS 114 | Usage: 115 | 116 | # initialize a Tmuxfile 117 | $ tummy init 118 | 119 | # run a tummy session 120 | $ tummy TmuxFilePath 121 | 122 | # run a tmux session for the default `Tmuxfile` 123 | $ tummy 124 | EOS 125 | exit(0) 126 | end 127 | 128 | if cmd.to_s.strip.downcase == "init" 129 | if File.exists?("Tmuxfile") 130 | puts "ERROR: Tmuxfile is already present in the current directory" 131 | exit(-1) 132 | end 133 | 134 | require 'fileutils' 135 | require 'erb' 136 | raw_template = File.read File.expand_path(File.join(__FILE__, "../../Tmuxfile.erb")) 137 | erb_template = ERB.new(raw_template) 138 | cwd = FileUtils.pwd 139 | current_directory_name = File.basename(cwd) 140 | File.write("./Tmuxfile", erb_template.result(binding)) 141 | puts "created a Tmuxfile in the current directory" 142 | exit(0) 143 | end 144 | 145 | # open a session 146 | file = ARGV.first || "Tmuxfile" 147 | if !file || !File.exists?(file) 148 | puts "#{file} not found in current directory" 149 | puts "you can initialize a new file by running `tummy init`" 150 | exit(-1) 151 | end 152 | Tmux.new(file).run 153 | -------------------------------------------------------------------------------- /lib/tummy.rb: -------------------------------------------------------------------------------- 1 | require "tummy/version" 2 | 3 | module Tummy 4 | # Your code goes here... 5 | end 6 | -------------------------------------------------------------------------------- /lib/tummy/version.rb: -------------------------------------------------------------------------------- 1 | module Tummy 2 | VERSION = "1.2.1" 3 | end 4 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__) 2 | require 'tummy' 3 | -------------------------------------------------------------------------------- /spec/tummy_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Tummy do 4 | it 'has a version number' do 5 | expect(Tummy::VERSION).not_to be nil 6 | end 7 | 8 | it 'does something useful' do 9 | expect(false).to eq(true) 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /tummy.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | lib = File.expand_path('../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require 'tummy/version' 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = "tummy" 8 | spec.version = Tummy::VERSION 9 | spec.authors = ["Khaja Minhajuddin"] 10 | spec.email = ["minhajuddin@cosmicvent.com"] 11 | 12 | spec.summary = %q{use a Tmuxfile to create a tmux sessino} 13 | spec.homepage = "https://minhajuddin.com" 14 | 15 | # Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or 16 | # delete this section to allow pushing this gem to any host. 17 | if spec.respond_to?(:metadata) 18 | spec.metadata['allowed_push_host'] = 'https://rubygems.org' 19 | else 20 | raise "RubyGems 2.0 or newer is required to protect against public gem pushes." 21 | end 22 | 23 | spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } 24 | spec.bindir = "exe" 25 | spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } 26 | spec.require_paths = ["lib"] 27 | 28 | spec.add_development_dependency "bundler", "~> 2.1.0" 29 | spec.add_development_dependency "rake", "~> 13.0.0" 30 | spec.add_development_dependency "rspec", "~> 3.0" 31 | end 32 | --------------------------------------------------------------------------------