From 81a84621033d909bf1b0a0aef543a124a7fe2c2e Mon Sep 17 00:00:00 2001 From: John Bintz Date: Sat, 2 Jul 2011 16:57:31 -0400 Subject: [PATCH] color tab support --- iterm_window.gemspec | 2 + lib/iterm_window.rb | 57 ++++++++++--- spec/iterm_window_spec.rb | 174 ++++++++++++++++++++++++++++++++++---- spec/spec_helper.rb | 4 + 4 files changed, 210 insertions(+), 27 deletions(-) diff --git a/iterm_window.gemspec b/iterm_window.gemspec index 0181946..8a3366d 100644 --- a/iterm_window.gemspec +++ b/iterm_window.gemspec @@ -11,4 +11,6 @@ Gem::Specification.new do |s| s.has_rdoc = true s.add_development_dependency 'rspec', '~> 2.6.0' + s.add_development_dependency 'fakefs' + s.add_development_dependency 'mocha' end diff --git a/lib/iterm_window.rb b/lib/iterm_window.rb index 374cd2c..958564d 100755 --- a/lib/iterm_window.rb +++ b/lib/iterm_window.rb @@ -67,15 +67,18 @@ # first_tab.select # brings first tab back to focus # end +require 'tempfile' # The ItermWindow class models an iTerm terminal window and allows for full control via Ruby commands. class ItermWindow + attr_reader :tab_color_files # While you can directly use ItermWindow.new, using either ItermWindow.open or # ItermWindow.current is the preferred method. def initialize @buffer = [] @tabs = {} + @tab_color_files = [] end # Creates a new terminal window, runs the block on it @@ -105,16 +108,18 @@ class ItermWindow # Outputs a single line of Applescript code def output(command) - @buffer << command.gsub(/'/, '"') + @buffer << command.gsub("'", '"').gsub('\\', '\\\\\\') end + def concatenated_buffer + @buffer.join("\n") + end private # Outputs @buffer to the command line as an osascript function def send_output - buffer_str = @buffer.map {|line| "-e '#{line}'"}.join(' ') - shell_out "osascript #{buffer_str}" + shell_out end # Initializes the terminal window @@ -137,16 +142,18 @@ class ItermWindow create_tab_convenience_method(name) end - private - def create_tab_convenience_method(name) (class << self; self; end).send(:define_method, name) do @tabs[name] end end - def shell_out(str) - %x{str} + def shell_out + Tempfile.open('iterm') do |f| + f.print concatenated_buffer + f.close + system %{osascript #{f.path}} + end end @@ -194,6 +201,15 @@ class ItermWindow end end + # Sets the title of the tab (ie the text on the iTerm tab itself) + def tab_color(color) + if @currently_executing_block + output "write text 'cat #{file = create_tab_color_file(color)} && rm #{file}'" + else + execute_block { tab_color(color) } + end + end + # Runs a block on this tab with proper opening and closing statements def execute_block(&block) @currently_executing_block = true @@ -203,13 +219,34 @@ class ItermWindow @currently_executing_block = false end + def self.create_tab_color(color) + raise ArgumentError.new("bad hex color: #{color}") if color.downcase[%r{[^a-f0-9]}] || !([ 3, 6 ].include?(color.length)) + %w{red green blue}.zip(color.scan( + (case color.length + when 3 + /./ + when 6 + /../ + end) + ).collect { |part| + part += part if part.length == 1 + part.hex + }).collect do |color, brightness| + "\033]6;1;bg;#{color};brightness;#{brightness}\a" + end.join + end + private def output(command) @window.output command end - + def create_tab_color_file(color) + file = Tempfile.open('iterm').path + '.tc' + File.open(file, 'wb') { |f| f.puts self.class.create_tab_color(color) } + @window.tab_color_files << file + file + end end - -end \ No newline at end of file +end diff --git a/spec/iterm_window_spec.rb b/spec/iterm_window_spec.rb index 0dad55b..6e712ba 100644 --- a/spec/iterm_window_spec.rb +++ b/spec/iterm_window_spec.rb @@ -4,12 +4,13 @@ require 'iterm_window' describe ItermWindow do before(:each) do @window = ItermWindow.new + Kernel.stubs(:system) end describe ".current" do it "should instantiate the current window and run the block" do - ItermWindow.should_receive(:new).and_return(@window) - @window.should_receive(:run).with(:current) + ItermWindow.expects(:new).returns(@window) + @window.expects(:run).with(:current) ItermWindow.current do end @@ -18,8 +19,8 @@ describe ItermWindow do describe ".open" do it "should instantiate a new window and run the block" do - ItermWindow.should_receive(:new).and_return(@window) - @window.should_receive(:run).with(:new) + ItermWindow.expects(:new).returns(@window) + @window.expects(:run).with(:new) ItermWindow.open do end @@ -28,12 +29,25 @@ describe ItermWindow do describe "opening a tab (example 1)" do before(:each) do - ItermWindow.should_receive(:new).and_return(@window) + ItermWindow.expects(:new).returns(@window) end it "should generate and run the right Applescript" do - desired = "osascript -e 'tell application \"iTerm\"' -e 'activate' -e 'set myterm to (make new terminal)' -e 'tell myterm' -e 'launch session \"Default Session\"' -e 'set my_tab_tty to the tty of the last session' -e 'tell session id my_tab_tty' -e 'write text \"cd ~/projects/my_project/trunk\"' -e 'write text \"mate ./\"' -e 'end tell' -e 'end tell' -e 'end tell'" - @window.should_receive(:shell_out).with(desired) + desired = (<<-CMD).strip +tell application "iTerm" +activate +set myterm to (make new terminal) +tell myterm +launch session "Default Session" +set my_tab_tty to the tty of the last session +tell session id my_tab_tty +write text \"cd ~/projects/my_project/trunk\" +write text \"mate ./\" +end tell +end tell +end tell +CMD + @window.expects(:shell_out) ItermWindow.open do open_tab :my_tab do @@ -41,17 +55,47 @@ describe ItermWindow do write "mate ./" end end + + @window.concatenated_buffer.should == desired end end describe "open multiple tabs (example 2)" do before(:each) do - ItermWindow.should_receive(:new).and_return(@window) + ItermWindow.expects(:new).returns(@window) end it "should generate and run the right Applescript" do - desired = "osascript -e 'tell application \"iTerm\"' -e 'activate' -e 'set myterm to first terminal' -e 'tell myterm' -e 'launch session \"Default Session\"' -e 'set project_dir_tty to the tty of the last session' -e 'tell session id project_dir_tty' -e 'write text \"cd ~/projects/my_project/trunk\"' -e 'write text \"mate ./\"' -e 'set name to \"MyProject Dir\"' -e 'end tell' -e 'launch session \"Default Session\"' -e 'set server_tty to the tty of the last session' -e 'tell session id server_tty' -e 'write text \"cd ~/projects/my_project/trunk\"' -e 'write text \"script/server -p 3005\"' -e 'set name to \"MyProject Server\"' -e 'end tell' -e 'launch session \"Default Session\"' -e 'set console_tty to the tty of the last session' -e 'tell session id console_tty' -e 'write text \"cd ~/projects/my_project/trunk\"' -e 'write text \"script/console\"' -e 'set name to \"MyProject Console\"' -e 'end tell' -e 'end tell' -e 'end tell'" - @window.should_receive(:shell_out).with(desired) + desired = (<<-CMD).strip +tell application "iTerm" +activate +set myterm to first terminal +tell myterm +launch session "Default Session" +set project_dir_tty to the tty of the last session +tell session id project_dir_tty +write text "cd ~/projects/my_project/trunk" +write text "mate ./" +set name to "MyProject Dir" +end tell +launch session "Default Session" +set server_tty to the tty of the last session +tell session id server_tty +write text "cd ~/projects/my_project/trunk" +write text "script/server -p 3005" +set name to "MyProject Server" +end tell +launch session "Default Session" +set console_tty to the tty of the last session +tell session id console_tty +write text "cd ~/projects/my_project/trunk" +write text "script/console" +set name to "MyProject Console" +end tell +end tell +end tell +CMD + @window.expects(:shell_out) ItermWindow.current do open_tab :project_dir do @@ -72,17 +116,37 @@ describe ItermWindow do set_title "MyProject Console" end end + + @window.concatenated_buffer.should == desired end end describe "open tabs using bookmarks (example 3)" do before(:each) do - ItermWindow.should_receive(:new).and_return(@window) + ItermWindow.expects(:new).returns(@window) end it "should generate and run the correct Applescript" do - desired = "osascript -e 'tell application \"iTerm\"' -e 'activate' -e 'set myterm to first terminal' -e 'tell myterm' -e 'launch session \"Default Session\"' -e 'set project_dir_tty to the tty of the last session' -e 'tell session id project_dir_tty' -e 'write text \"cd ~/projects/my_project/trunk\"' -e 'write text \"mate ./\"' -e 'end tell' -e 'launch session \"MyProject Server\"' -e 'set server_tty to the tty of the last session' -e 'launch session \"MyProject Console\"' -e 'set console_tty to the tty of the last session' -e 'select session id project_dir_tty' -e 'end tell' -e 'end tell'" - @window.should_receive(:shell_out).with(desired) + desired = (<<-CMD).strip +tell application "iTerm" +activate +set myterm to first terminal +tell myterm +launch session "Default Session" +set project_dir_tty to the tty of the last session +tell session id project_dir_tty +write text "cd ~/projects/my_project/trunk" +write text "mate ./" +end tell +launch session "MyProject Server" +set server_tty to the tty of the last session +launch session "MyProject Console" +set console_tty to the tty of the last session +select session id project_dir_tty +end tell +end tell +CMD + @window.stubs(:shell_out) ItermWindow.current do open_tab :project_dir do @@ -95,17 +159,38 @@ describe ItermWindow do project_dir.select end + + @window.concatenated_buffer.should == desired end end - + describe "switching between tabs (example 4)" do before(:each) do - ItermWindow.should_receive(:new).and_return(@window) + ItermWindow.expects(:new).returns(@window) end it "should generate and run the correct Applescript" do - desired = "osascript -e 'tell application \"iTerm\"' -e 'activate' -e 'set myterm to (make new terminal)' -e 'tell myterm' -e 'launch session \"Default Session\"' -e 'set first_tab_tty to the tty of the last session' -e 'launch session \"Default Session\"' -e 'set second_tab_tty to the tty of the last session' -e 'tell session id first_tab_tty' -e 'write text \"cd ~/projects\"' -e 'write text \"ls\"' -e 'end tell' -e 'tell session id second_tab_tty' -e 'write text \"echo \"hello there!\"\"' -e 'end tell' -e 'select session id first_tab_tty' -e 'end tell' -e 'end tell'" - @window.should_receive(:shell_out).with(desired) + desired = (<<-CMD).strip +tell application "iTerm" +activate +set myterm to (make new terminal) +tell myterm +launch session "Default Session" +set first_tab_tty to the tty of the last session +launch session "Default Session" +set second_tab_tty to the tty of the last session +tell session id first_tab_tty +write text "cd ~/projects" +write text "ls" +end tell +tell session id second_tab_tty +write text "echo "hello there!"" +end tell +select session id first_tab_tty +end tell +end tell +CMD + @window.expects(:shell_out) ItermWindow.open do open_tab :first_tab @@ -117,6 +202,61 @@ describe ItermWindow do second_tab.write "echo 'hello there!'" first_tab.select end + + @window.concatenated_buffer.should == desired + end + end + + describe 'tab color' do + before(:each) do + ItermWindow.expects(:new).returns(@window) + end + + it "should generate and run the correct Applescript" do + @window.expects(:shell_out) + ItermWindow::Tab.any_instance.expects(:create_tab_color_file).with("FF00AA") + + ItermWindow.open do + open_tab :first_tab do + tab_color "FF00AA" + end + end + end + end + + describe ".create_tab_color" do + subject { ItermWindow::Tab.create_tab_color(color) } + + context 'bad hex color' do + [ "whatever", "F00F" ].each do |bad| + context bad do + let(:color) { bad } + + it 'should raise an exception on bad hex color' do + expect { subject }.to raise_error(ArgumentError, /bad hex color/) + end + end + end + end + + context 'long hex color' do + let(:color) { "FF00AA" } + + it 'should create an escape sequence to execute to change a tab color' do + subject.should match(/red;brightness;255/) + subject.should match(/green;brightness;0/) + subject.should match(/blue;brightness;170/) + end + end + + context 'short hex color' do + let(:color) { "F0A" } + + it 'should create an escape sequence to execute to change a tab color' do + subject.should match(/red;brightness;255/) + subject.should match(/green;brightness;0/) + subject.should match(/blue;brightness;170/) + end end end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index e69de29..d472384 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -0,0 +1,4 @@ +RSpec.configure do |c| + c.mock_with :mocha +end +