termup 2.0.3 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 8702c31b85da161fafb1b949d630708e70502a61
4
+ data.tar.gz: 750ecb147eafa0f15e370dd9fb0116f0dcd3451d
5
+ SHA512:
6
+ metadata.gz: 95d9c08e02a3db81ecf7b6b220835b2a8271b889d501ff836b3f22181542547f554ad298f42fd6c2703779b0fb776728575b64e12d1a1b984b9437df19fdb87e
7
+ data.tar.gz: 06d58b6a0a0576dadde3cc187385bdf269a4ee48f191cc300279d32ef690c5e41711307a3deb5756a853d2b58a84f6e34017acb359d0a2821a233a8b615773cb
data/README.md CHANGED
@@ -5,11 +5,12 @@ Automate opening up terminal tabs (or split panes) with a set of routine command
5
5
 
6
6
  It's the easiest way to get started for your projects every day.
7
7
 
8
- Compatible with Terminal.app and iTerm 2 on Mac OS X 10.6 - 10.8 and Ruby 1.8.7 - 1.9.3.
8
+ Compatible with Terminal.app and iTerm2 v3 on OSX Yosemite or later.
9
9
 
10
10
  ![Split Panes](https://github.com/kenn/termup/raw/master/images/split_panes.png)
11
11
 
12
- **iTerm 1 users:** Termup 2.0 and up is not compatible with iTerm 1. Install 1.3.1 by `gem install termup -v=1.3.1`
12
+ **For iTerm2 v2 or earlier:** Termup 3.0 and up is not compatible with iTerm2 v2. Install Termup 2 by `gem install termup -v=2.0.3`
13
+ **For iTerm 1:** Termup 2.0 and up is not compatible with iTerm 1. Install 1.3.1 by `gem install termup -v=1.3.1`
13
14
 
14
15
  Installation
15
16
  ------------
@@ -20,6 +21,11 @@ $ gem install termup
20
21
 
21
22
  Note that you need to prepend `sudo` if you're using the OSX pre-installed Ruby.
22
23
 
24
+ Changelog
25
+ ---------
26
+
27
+ Termup v3.0 is a complete rewrite using the new JavaScript for Automation which was introduced with OSX Yosemite.
28
+
23
29
  Usage
24
30
  -----
25
31
 
@@ -20,6 +20,6 @@ tabs:
20
20
  commands:
21
21
  - echo pane4
22
22
  properties:
23
- foreground_color: yellow
24
- background_color: blue
23
+ foreground_color: [65535,0,0]
24
+ background_color: [0,0,65535]
25
25
  transparency: 0.1
@@ -1,5 +1,15 @@
1
+ require 'yaml'
2
+ require 'pathname'
3
+ require 'execjs/runtimes/jxa'
4
+
5
+ ExecJS.runtime = ExecJS::Runtimes::JXA
6
+
1
7
  module Termup
8
+ Dir = Pathname.new(ENV['HOME']).join('.config/termup')
9
+
2
10
  autoload :Base, 'termup/base'
3
11
  autoload :Cli, 'termup/cli'
4
- autoload :Handler, 'termup/handler'
12
+ autoload :Iterm, 'termup/iterm'
13
+ autoload :Process, 'termup/process'
14
+ autoload :Terminal, 'termup/terminal'
5
15
  end
@@ -1,90 +1,34 @@
1
- require 'yaml'
2
-
3
1
  module Termup
4
2
  class Base
5
- def initialize(project)
6
- @handler = Termup::Handler.new
7
-
8
- config = YAML.load(File.read("#{TERMUP_DIR}/#{project}.yml"))
9
- @tabs = config['tabs']
10
-
11
- # Config file compatibility checking
12
- if @tabs.is_a?(Array) and @tabs.first.is_a?(Hash)
13
- abort 'YAML syntax for config has been changed. See https://github.com/kenn/termup for details.'
14
- end
15
-
16
- @options = config['options'] || {}
17
- @iterm_options = @options['iterm']
18
-
19
- # Split panes for iTerm 2
20
- split_panes if @handler.iterm? and @iterm_options
21
-
22
- # Setting up tabs / panes
23
- @tabs.each_with_index do |(tabname, values), index|
24
- # Set tab title
25
- @handler.set_property(:name, tabname)
26
-
27
- # Run commands
28
- (advanced_iterm? ? values['commands'] : values).each do |command|
29
- @handler.run command
30
- end
31
-
32
- # Layout
33
- if advanced_iterm?
34
- values['properties'].each do |key, value|
35
- @handler.set_property(key, value)
36
- end if values['properties']
37
-
38
- values['layout'].each do |command|
39
- layout command
40
- end if values['layout']
41
- else
42
- # Move to next
43
- if @iterm_options
44
- layout :goto_next_pane
45
- else
46
- if index < @tabs.size - 1
47
- layout :new_tab
48
- sleep 0.01 # Allow some time to complete opening a new tab
49
- else
50
- layout :goto_next_tab # Back home
51
- end
52
- end
53
- end
54
- end
3
+ def initialize(project, process)
4
+ @config = YAML.load_file(Termup::Dir.join("#{project}.yml"))
5
+ @options = @config['options'] || {}
6
+ @tabs = @config['tabs'] || {}
7
+ @process = process
8
+ @lines = []
55
9
  end
56
10
 
57
- def split_panes
58
- width, height = @iterm_options['width'], @iterm_options['height']
59
- return unless width and height
60
-
61
- (width - 1).times do
62
- layout :split_vertically
63
- end
64
- layout :goto_next_pane # Back home
65
- width.times do
66
- (height - 1).times do
67
- layout :split_horizontally
68
- end
69
- layout :goto_next_pane # Move to next, or back home
70
- end
11
+ def start
12
+ script = <<-JS
13
+ var app = Application(#{@process.pid});
14
+ var se = Application('System Events');
15
+ app.activate();
16
+ #{@lines.join(';')}
17
+ JS
18
+ ExecJS.exec script
71
19
  end
72
20
 
73
- def advanced_iterm?
74
- unless defined?(@advanced_iterm)
75
- @advanced_iterm = case @tabs.values.first
76
- when Hash then true
77
- when Array then false
78
- else
79
- abort 'invalid YAML format'
80
- end
81
- abort 'advanced config only supported for iTerm' if @advanced_iterm and !@handler.iterm?
82
- end
83
- @advanced_iterm
84
- end
21
+ protected
85
22
 
86
- def layout(command)
87
- @handler.layout(command)
23
+ def hit(key, *using)
24
+ # activate
25
+ using = using.map{|i| i.to_s.gsub(/_/,' ') }
26
+ case key
27
+ when Integer
28
+ @lines << "se.keyCode(#{key}, { using: #{using} })"
29
+ when String
30
+ @lines << "se.keystroke('#{key}', { using: #{using} })"
31
+ end
88
32
  end
89
33
  end
90
34
  end
@@ -1,8 +1,6 @@
1
1
  require 'thor'
2
2
 
3
3
  module Termup
4
- TERMUP_DIR = File.join(ENV['HOME'],'.config','termup')
5
-
6
4
  class Cli < Thor
7
5
  include Thor::Actions
8
6
 
@@ -26,8 +24,8 @@ module Termup
26
24
 
27
25
  desc 'edit PROJECT', 'Edit termup project (Shortcut: e)'
28
26
  def edit(project)
29
- unless File.exists?(path(project))
30
- empty_directory TERMUP_DIR
27
+ unless path(project).exist?
28
+ empty_directory Termup::Dir
31
29
  if options['iterm_advanced']
32
30
  template 'templates/iterm_advanced.yml', path(project)
33
31
  elsif options['iterm_basic']
@@ -42,20 +40,25 @@ module Termup
42
40
 
43
41
  desc 'list', 'List termup projects (Shortcut: l)'
44
42
  def list
45
- projects = Dir["#{TERMUP_DIR}/*.yml"].map{|file| File.basename(file,'.yml') }
43
+ projects = Pathname.glob(Termup::Dir.join('*.yml')).map{|f| f.basename('.yml') }
46
44
  say "Your projects: #{projects.join(', ')}"
47
45
  end
48
46
 
49
47
  desc 'start PROJECT', 'Start termup project (Shortcut: s)'
50
48
  def start(project)
51
- say "project \"#{project}\" doesn't exist!" and return unless File.exists?(path(project))
52
- Termup::Base.new(project)
49
+ say "project \"#{project}\" doesn't exist!" and return unless path(project).exist?
50
+ process = Termup::Process.new
51
+ if process.iterm?
52
+ Termup::Iterm.new(project, process).start
53
+ else
54
+ Termup::Terminal.new(project, process).start
55
+ end
53
56
  end
54
57
 
55
58
  protected
56
59
 
57
60
  def path(project)
58
- "#{TERMUP_DIR}/#{project}.yml"
61
+ Termup::Dir.join("#{project}.yml")
59
62
  end
60
63
  end
61
64
  end
@@ -0,0 +1,96 @@
1
+ module Termup
2
+ class Iterm < Base
3
+ def start
4
+ split_panes if @options['iterm']
5
+
6
+ # Setting up tabs
7
+ @tabs.each.with_index do |(tabname, values), index|
8
+ set_property(:name, tabname) # Set tab title
9
+
10
+ if advanced_iterm?
11
+ values['commands'].each do |command|
12
+ run(command)
13
+ end
14
+
15
+ values['properties'].each do |key, value|
16
+ set_property(key, value)
17
+ end if values['properties']
18
+
19
+ values['layout'].each do |command|
20
+ layout command
21
+ end if values['layout']
22
+ else
23
+ values.each do |command|
24
+ run(command)
25
+ end
26
+
27
+ layout :goto_next_pane
28
+ end
29
+ end
30
+
31
+ super
32
+ end
33
+
34
+ private
35
+
36
+ def split_panes
37
+ width, height = @options['iterm']['width'], @options['iterm']['height']
38
+ return unless width && height
39
+
40
+ (width - 1).times do
41
+ layout :split_vertically
42
+ end
43
+ layout :goto_next_pane # Back home
44
+ width.times do
45
+ (height - 1).times do
46
+ layout :split_horizontally
47
+ end
48
+ layout :goto_next_pane # Move to next, or back home
49
+ end
50
+ end
51
+
52
+ def run(command)
53
+ @lines << "app.currentWindow().currentSession().write({'text':'#{command}'})"
54
+ end
55
+
56
+ def set_property(key, value)
57
+ value = value.inspect if value.is_a?(String)
58
+ @lines << "app.currentWindow().currentSession().#{camelize(key)}.set(#{value})"
59
+ end
60
+
61
+ def advanced_iterm?
62
+ unless defined?(@advanced_iterm)
63
+ @advanced_iterm = case @tabs.values.first
64
+ when Hash then true
65
+ when Array then false
66
+ else
67
+ abort 'invalid YAML format'
68
+ end
69
+ end
70
+ @advanced_iterm
71
+ end
72
+
73
+ def camelize(string)
74
+ string.to_s.split('_').map.with_index{|s,index| index.zero? ? s : s.slice(0).upcase + s.slice(1..-1) }.join
75
+ end
76
+
77
+ def layout(command)
78
+ case command.to_sym
79
+ when :new_tab then hit 't', :command_down
80
+ when :close_tab then hit 'w', :command_down
81
+ when :goto_previous_tab then hit '[', :command_down, :shift_down
82
+ when :goto_next_tab then hit ']', :command_down, :shift_down
83
+ when :goto_previous_pane then hit '[', :command_down
84
+ when :goto_next_pane then hit ']', :command_down
85
+ when :split_vertically then hit 'd', :command_down
86
+ when :split_horizontally then hit 'd', :command_down, :shift_down
87
+ when :go_left then hit 123, :command_down, :option_down
88
+ when :go_right then hit 124, :command_down, :option_down
89
+ when :go_down then hit 125, :command_down, :option_down
90
+ when :go_up then hit 126, :command_down, :option_down
91
+ else
92
+ abort "Unknown iTerm2.app command: #{command}"
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,40 @@
1
+ module Termup
2
+ class Process
3
+ attr_reader :pid
4
+
5
+ def initialize
6
+ lookup_pid
7
+ end
8
+
9
+ def terminal?
10
+ app_name == 'Terminal'
11
+ end
12
+
13
+ def iterm?
14
+ app_name == 'iTerm'
15
+ end
16
+
17
+ def app_name
18
+ @app_name ||= ExecJS.eval "Application(#{@pid}).name()"
19
+ end
20
+
21
+ def lookup_pid
22
+ pid = ::Process.ppid
23
+ pids = []
24
+
25
+ # Go up the process tree to find term-like process
26
+ while pid > 1 do
27
+ pids << pid if term_like_pids.include?(pid)
28
+ pid = `ps -p #{pid} -o ppid=`.strip.to_i
29
+ end
30
+
31
+ abort 'terminal pid not found!' if pids.empty?
32
+
33
+ @pid = pids.last
34
+ end
35
+
36
+ def term_like_pids
37
+ @term_like_pids ||= `ps x | grep Term`.split("\n").reject{|i| i =~ /grep/ }.map{|i| i.match(/\d+/).to_s.to_i }
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,32 @@
1
+ module Termup
2
+ class Terminal < Base
3
+ def start
4
+ # Setting up tabs / panes
5
+ @tabs.each.with_index do |(tabname, values), index|
6
+ values.each do |command|
7
+ @lines << "app.doScript('#{command}', { in: app.windows[0].tabs.last() })"
8
+ end
9
+
10
+ if index < @tabs.size - 1
11
+ layout :new_tab
12
+ sleep 0.01 # Allow some time to complete opening a new tab
13
+ else
14
+ layout :goto_next_tab # Back home
15
+ end
16
+ end
17
+
18
+ super
19
+ end
20
+
21
+ def layout(command)
22
+ case command.to_sym
23
+ when :new_tab then hit 't', :command_down
24
+ when :close_tab then hit 'w', :command_down
25
+ when :goto_previous_tab then hit '[', :command_down, :shift_down
26
+ when :goto_next_tab then hit ']', :command_down, :shift_down
27
+ else
28
+ abort "Unknown Terminal.app command: #{command}"
29
+ end
30
+ end
31
+ end
32
+ end
@@ -1,3 +1,3 @@
1
1
  module Termup
2
- VERSION = '2.0.3'
2
+ VERSION = '3.0.0'
3
3
  end
@@ -2,19 +2,19 @@
2
2
  require File.expand_path('../lib/termup/version', __FILE__)
3
3
 
4
4
  Gem::Specification.new do |gem|
5
- gem.authors = ["Kenn Ejima"]
6
- gem.email = ["kenn.ejima@gmail.com"]
5
+ gem.authors = ['Kenn Ejima']
6
+ gem.email = ['kenn.ejima@gmail.com']
7
7
  gem.description = %q{Setup terminal tabs with preset commands for your projects}
8
8
  gem.summary = %q{Setup terminal tabs with preset commands for your projects}
9
- gem.homepage = "https://github.com/kenn/termup"
9
+ gem.homepage = 'https://github.com/kenn/termup'
10
10
 
11
11
  gem.files = `git ls-files`.split($\).reject{|f| f =~ /^images/ } # Exclude images
12
12
  gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
13
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
- gem.name = "termup"
15
- gem.require_paths = ["lib"]
14
+ gem.name = 'termup'
15
+ gem.require_paths = ['lib']
16
16
  gem.version = Termup::VERSION
17
17
 
18
- gem.add_runtime_dependency 'rb-appscript', '~> 0.6.1'
19
- gem.add_runtime_dependency 'thor', '~> 0.16.0'
18
+ gem.add_dependency 'execjs-runtimes-jxa', '~> 0.1'
19
+ gem.add_dependency 'thor', '~> 0.16'
20
20
  end
metadata CHANGED
@@ -1,48 +1,43 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: termup
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.3
5
- prerelease:
4
+ version: 3.0.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - Kenn Ejima
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2012-12-12 00:00:00.000000000 Z
11
+ date: 2016-06-13 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
- name: rb-appscript
16
- prerelease: false
14
+ name: execjs-runtimes-jxa
17
15
  requirement: !ruby/object:Gem::Requirement
18
16
  requirements:
19
- - - ~>
17
+ - - "~>"
20
18
  - !ruby/object:Gem::Version
21
- version: 0.6.1
22
- none: false
19
+ version: '0.1'
23
20
  type: :runtime
21
+ prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
23
  requirements:
26
- - - ~>
24
+ - - "~>"
27
25
  - !ruby/object:Gem::Version
28
- version: 0.6.1
29
- none: false
26
+ version: '0.1'
30
27
  - !ruby/object:Gem::Dependency
31
28
  name: thor
32
- prerelease: false
33
29
  requirement: !ruby/object:Gem::Requirement
34
30
  requirements:
35
- - - ~>
31
+ - - "~>"
36
32
  - !ruby/object:Gem::Version
37
- version: 0.16.0
38
- none: false
33
+ version: '0.16'
39
34
  type: :runtime
35
+ prerelease: false
40
36
  version_requirements: !ruby/object:Gem::Requirement
41
37
  requirements:
42
- - - ~>
38
+ - - "~>"
43
39
  - !ruby/object:Gem::Version
44
- version: 0.16.0
45
- none: false
40
+ version: '0.16'
46
41
  description: Setup terminal tabs with preset commands for your projects
47
42
  email:
48
43
  - kenn.ejima@gmail.com
@@ -51,7 +46,7 @@ executables:
51
46
  extensions: []
52
47
  extra_rdoc_files: []
53
48
  files:
54
- - .gitignore
49
+ - ".gitignore"
55
50
  - Gemfile
56
51
  - LICENSE
57
52
  - README.md
@@ -63,32 +58,32 @@ files:
63
58
  - lib/termup.rb
64
59
  - lib/termup/base.rb
65
60
  - lib/termup/cli.rb
66
- - lib/termup/handler.rb
61
+ - lib/termup/iterm.rb
62
+ - lib/termup/process.rb
63
+ - lib/termup/terminal.rb
67
64
  - lib/termup/version.rb
68
65
  - termup.gemspec
69
66
  homepage: https://github.com/kenn/termup
70
67
  licenses: []
68
+ metadata: {}
71
69
  post_install_message:
72
70
  rdoc_options: []
73
71
  require_paths:
74
72
  - lib
75
73
  required_ruby_version: !ruby/object:Gem::Requirement
76
74
  requirements:
77
- - - ! '>='
75
+ - - ">="
78
76
  - !ruby/object:Gem::Version
79
77
  version: '0'
80
- none: false
81
78
  required_rubygems_version: !ruby/object:Gem::Requirement
82
79
  requirements:
83
- - - ! '>='
80
+ - - ">="
84
81
  - !ruby/object:Gem::Version
85
82
  version: '0'
86
- none: false
87
83
  requirements: []
88
84
  rubyforge_project:
89
- rubygems_version: 1.8.24
85
+ rubygems_version: 2.5.1
90
86
  signing_key:
91
- specification_version: 3
87
+ specification_version: 4
92
88
  summary: Setup terminal tabs with preset commands for your projects
93
89
  test_files: []
94
- has_rdoc:
@@ -1,111 +0,0 @@
1
- require 'appscript'
2
-
3
- module Termup
4
- class Handler
5
- def app(*args)
6
- Appscript.app(*args)
7
- end
8
-
9
- def run(command)
10
- if terminal?
11
- app_process.do_script(command, :in => app_process.windows[1].tabs.last.get)
12
- else
13
- app_process.current_terminal.current_session.write(:text => command)
14
- end
15
- end
16
-
17
- def set_property(key, value)
18
- if iterm?
19
- app_process.current_terminal.current_session.send(key).set(value)
20
- else
21
- # No-op for terminal for now
22
- end
23
- end
24
-
25
- def activate
26
- app_process.activate
27
- end
28
-
29
- def hit(key, *using)
30
- activate
31
- case key
32
- when Integer
33
- app('System Events').processes[app_name].key_code key, using && { :using => using }
34
- when String
35
- app('System Events').processes[app_name].keystroke key, using && { :using => using }
36
- end
37
- end
38
-
39
- def terminal?
40
- app_name == 'Terminal'
41
- end
42
-
43
- def iterm?
44
- app_name == 'iTerm'
45
- end
46
-
47
- def app_name
48
- @app_name ||= app_process.name.get
49
- end
50
-
51
- def app_process
52
- @app_process ||= app.by_pid(term_pid)
53
- end
54
-
55
- def term_pid
56
- return @term_pid if @term_pid
57
-
58
- pid = Process.ppid
59
-
60
- # Go up the process tree to find term-like process
61
- 100.times do
62
- ppid = `ps -p #{pid} -o ppid=`.strip.to_i
63
-
64
- abort 'terminal pid not found!' if ppid == 1
65
-
66
- if term_like_pids.include?(ppid)
67
- @term_pid = ppid
68
- break
69
- end
70
-
71
- pid = ppid
72
- end
73
-
74
- @term_pid
75
- end
76
-
77
- def term_like_pids
78
- @term_like_pids ||= `ps x | grep Term`.split("\n").reject{|i| i =~ /grep/ }.map{|i| i.match(/\d+/).to_s.to_i }
79
- end
80
-
81
- def layout(command)
82
- if iterm?
83
- case command.to_sym
84
- when :new_tab then hit 't', :command_down
85
- when :close_tab then hit 'w', :command_down
86
- when :goto_previous_tab then hit '[', :command_down, :shift_down
87
- when :goto_next_tab then hit ']', :command_down, :shift_down
88
- when :goto_previous_pane then hit '[', :command_down
89
- when :goto_next_pane then hit ']', :command_down
90
- when :split_vertically then hit 'd', :command_down
91
- when :split_horizontally then hit 'd', :command_down, :shift_down
92
- when :go_left then hit 123, :command_down, :option_down
93
- when :go_right then hit 124, :command_down, :option_down
94
- when :go_down then hit 125, :command_down, :option_down
95
- when :go_up then hit 126, :command_down, :option_down
96
- else
97
- abort "Unknown iTerm2.app command: #{command}"
98
- end
99
- else
100
- case command.to_sym
101
- when :new_tab then hit 't', :command_down
102
- when :close_tab then hit 'w', :command_down
103
- when :goto_previous_tab then hit '[', :command_down, :shift_down
104
- when :goto_next_tab then hit ']', :command_down, :shift_down
105
- else
106
- abort "Unknown Terminal.app command: #{command}"
107
- end
108
- end
109
- end
110
- end
111
- end