Class: Terminitor::ItermCore

Inherits:
AbstractCore show all
Includes:
Appscript
Defined in:
lib/terminitor/cores/iterm_core.rb

Overview

Mac OS X Core for Terminitor This Core manages all the interaction with Appscript and the Terminal

Constant Summary collapse

ALLOWED_OPTIONS =
{
  :window => [:bounds, :visible, :miniaturized],
  :tab => [:settings, :selected]
}

Instance Attribute Summary

Attributes inherited from AbstractCore

#termfile, #terminal, #windows, #working_dir

Instance Method Summary collapse

Methods inherited from AbstractCore

#load_termfile, #process!, #setup!

Constructor Details

#initialize(path) ⇒ ItermCore

Initialize @terminal with Terminal.app, Load the Windows, store the Termfile Terminitor::MacCore.new(‘/path’)



14
15
16
17
18
19
# File 'lib/terminitor/cores/iterm_core.rb', line 14

def initialize(path)
  super
  @terminal = app('iTerm')
  @windows  = @terminal.terminals
  @delayed_options = []
end

Instance Method Details

#active_windowObject

Returns the active window i.e. the active terminal session in iTerm



46
47
48
# File 'lib/terminitor/cores/iterm_core.rb', line 46

def active_window
  current_terminal.current_session
end

#call_ui_action(menu, submenu, action) ⇒ Object



189
190
191
192
193
# File 'lib/terminitor/cores/iterm_core.rb', line 189

def call_ui_action(menu, submenu, action)
  menu = iterm_menu.menu_bar_items[menu].menus[menu]
  menu = menu.menu_items[submenu].menus[submenu] if submenu
  menu.menu_items[action].click
end

#current_terminalObject

Returns the current terminal i.e. the active iTerm window



51
52
53
# File 'lib/terminitor/cores/iterm_core.rb', line 51

def current_terminal
  @terminal.current_terminal
end

#execute_command(cmd, options = {}) ⇒ Object

executes the given command via appscript execute_command ‘cd /path/to’, :in => #<tab>



23
24
25
26
27
28
29
# File 'lib/terminitor/cores/iterm_core.rb', line 23

def execute_command(cmd, options = {})
  if options[:in]
    options[:in].write(:text => "#{cmd}")
  else
    active_window.write(:text => "#{cmd}")
  end
end

#execute_pane_commands(pane_commands, tab_commands) ⇒ Object



176
177
178
179
# File 'lib/terminitor/cores/iterm_core.rb', line 176

def execute_pane_commands(pane_commands, tab_commands)
  pane_commands = tab_commands + pane_commands
  pane_commands.each { |cmd| execute_command cmd}
end

#first_pane_level_split(panes, tab_commands) ⇒ Object



143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# File 'lib/terminitor/cores/iterm_core.rb', line 143

def first_pane_level_split(panes, tab_commands)
  first_pane = true
  split_v_counter = 0
  panes.keys.sort.each do |pane_key|
    pane_content = panes[pane_key]
    unless first_pane
      split_v
      split_v_counter += 1 
    end
    first_pane = false if first_pane
    pane_commands = pane_content[:commands] 
    execute_pane_commands(pane_commands, tab_commands)
  end
  split_v_counter.times { select_pane 'Left' }
end

#handle_panes(tab_content) ⇒ Object



136
137
138
139
140
141
# File 'lib/terminitor/cores/iterm_core.rb', line 136

def handle_panes(tab_content)
  panes = tab_content[:panes]
  tab_commands = tab_content[:commands]
  first_pane_level_split(panes, tab_commands)
  second_pane_level_split(panes, tab_commands)
end

#handle_subpanes(subpanes, tab_commands) ⇒ Object



168
169
170
171
172
173
174
# File 'lib/terminitor/cores/iterm_core.rb', line 168

def handle_subpanes(subpanes, tab_commands)
  subpanes.keys.sort.each do |subpane_key|
    subpane_commands = subpanes[subpane_key][:commands]
    split_h
    execute_pane_commands(subpane_commands, tab_commands)
  end
end

#iterm_menuObject

Methods for splitting panes (GUI_scripting)



184
185
186
187
# File 'lib/terminitor/cores/iterm_core.rb', line 184

def iterm_menu
  terminal_process = Appscript.app("System Events").processes["iTerm"]
  terminal_process.menu_bars.first
end

#open_tab(options = nil) ⇒ Object

Opens a new tab, iterm sets focus on new tab TODO : handle options (?)



33
34
35
# File 'lib/terminitor/cores/iterm_core.rb', line 33

def open_tab(options = nil)
  current_terminal.launch_ :session => 'New session'
end

#open_window(options = nil) ⇒ Object

Open new window, applies settings to the first tab. iterm sets focus on new tab TODO : handle options (?)



40
41
42
43
# File 'lib/terminitor/cores/iterm_core.rb', line 40

def open_window(options = nil)
  window  = terminal.make( :new => :terminal )
  window.launch_ :session => 'New session'
end

#run_in_window(window_name, window_content, options = {}) ⇒ Object

this command will run commands in the designated window run_in_window ‘window1’, => [‘ls’,‘ok’]

Parameters:

  • name (String)

    of window

  • Hash (Hash)

    of window’s content extracted from Termfile

  • Hash (Hash)

    of options



99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/terminitor/cores/iterm_core.rb', line 99

def run_in_window(window_name, window_content, options = {})
  window_options = window_content[:options]
  first_tab = true
  window_content[:tabs].keys.sort.each do |tab_key|
    tab_content = window_content[:tabs][tab_key]
    # Open window on first 'tab' statement
    # first tab is already opened in the new window, so first tab should be
    # opened as a new tab in default window only
    tab_options = tab_content[:options]
    tab_name    = tab_options[:name] if tab_options
    if first_tab && !options[:default]
      first_tab = false
      combined_options = (window_options.to_a + tab_options.to_a).inject([]) {|arr, pair| arr += pair }
      window_options = Hash[*combined_options] # safe merge
      tab = window_options.empty? ? open_window(nil) : open_window(window_options)
    else
    # give us the current window if its default, else open a tab.
      tab = ( tab_key == 'default' ? active_window : open_tab(tab_options) )
    end
    # append our before block commands.
    tab_content[:commands].insert(0, window_content[:before]).flatten! if window_content[:before]
    # clean up prompt
    tab_content[:commands].insert(0, 'clear') if tab_name || !@working_dir.to_s.empty?
    # add title to tab
    tab_content[:commands].insert(0, "PS1=\"$PS1\\[\\e]2;#{tab_name}\\a\\]\"") if tab_name
    tab_content[:commands].insert(0, "cd \"#{@working_dir}\"") unless @working_dir.to_s.empty?
    # if tab_content hash has a key :panes we know this tab should be split
    # we can execute tab commands if there is no key :panes
    if tab_content.key?(:panes)
      handle_panes(tab_content) 
    else
      tab_content[:commands].each { |cmd| execute_command(cmd, :in => tab) }
    end
  end
  set_delayed_options
end

#second_pane_level_split(panes, tab_commands) ⇒ Object



159
160
161
162
163
164
165
166
# File 'lib/terminitor/cores/iterm_core.rb', line 159

def second_pane_level_split(panes, tab_commands)
  panes.keys.sort.each do |pane_key|
    pane_content = panes[pane_key]
    handle_subpanes(pane_content[:panes], tab_commands) if pane_content.has_key? :panes
    # select next vertical pane
    select_pane 'Right'
  end
end

#select_pane(direction) ⇒ Object

to select panes; iTerm’s Appscript select method does not work as expected, we have to select via menu instead



205
206
207
208
209
210
211
212
# File 'lib/terminitor/cores/iterm_core.rb', line 205

def select_pane(direction)
  valid_directions = %w[Above Below Left Right]
  if valid_directions.include?(direction)
    call_ui_action("Window", "Select Split Pane", "Select Pane #{direction}")
  else
    puts "Error: #{direction} is not a valid direction to select a pane; Only Above/Below/Left/Right are valid directions"
  end
end

#set_delayed_optionsObject

Apply delayed options and remove them from the queue



87
88
89
90
91
92
# File 'lib/terminitor/cores/iterm_core.rb', line 87

def set_delayed_options
  @delayed_options.length.times do 
    option = @delayed_options.shift
    option[:object].instance_eval(option[:option]).set(option[:value])
  end
end

#set_options(object, options = {}) ⇒ Object

Sets options of the given object



56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/terminitor/cores/iterm_core.rb', line 56

def set_options(object, options = {})
  options.each_pair do |option, value|
    case option
    when :settings   # works for windows and tabs, for example :settings => "Grass"
      begin
        object.current_settings.set(@terminal.settings_sets[value])
      rescue Appscript::CommandError => e
        puts "Error: invalid settings set '#{value}'"
      end
    when :bounds # works only for windows, for example :bounds => [10,20,300,200]
      # the only working sequence to restore window size and position! 
      object.bounds.set(value)
      object.frame.set(value)
      object.position.set(value)
    when :selected # works for tabs, for example tab :active => true
      delayed_option(option, value, object)
    when :miniaturized # works for windows only
      delayed_option(option, value, object)
    when :name
      # ignore it.
    else # trying to apply any other option
      begin
        object.instance_eval(option.to_s).set(value)
      rescue
        puts "Error setting #{option} = #{value} on #{object.inspect}"
      end
    end
  end
end

#split_hObject



199
200
201
# File 'lib/terminitor/cores/iterm_core.rb', line 199

def split_h
  call_ui_action("Shell", nil, "Split Horizontally With Same Profile")
end

#split_vObject



195
196
197
# File 'lib/terminitor/cores/iterm_core.rb', line 195

def split_v
  call_ui_action("Shell", nil, "Split Vertically With Same Profile")
end