myterm 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/._repath.rb +19 -38
- data/.gitignore +1 -0
- data/.repath.sh +5 -2
- data/CHANGELOG.md +9 -0
- data/Gemfile.lock +5 -1
- data/README.md +14 -4
- data/VERSION +1 -0
- data/lib/myterm/api.rb +167 -148
- data/lib/myterm/cli.rb +119 -124
- data/lib/myterm/vendor/{face → skylab/face}/cli.rb +36 -21
- data/myterm.gemspec +7 -3
- metadata +34 -11
- data/lib/myterm/version.rb +0 -4
data/._repath.rb
CHANGED
@@ -2,49 +2,30 @@
|
|
2
2
|
|
3
3
|
payload = lambda do
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
}
|
10
|
-
end
|
5
|
+
dir = File.expand_path('../bin', __FILE__)
|
6
|
+
File.directory?(dir) or return {
|
7
|
+
:message => "not a directory, won't add to PATH: #{dir}"
|
8
|
+
}
|
11
9
|
|
12
10
|
path = ENV['PATH']
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
return {
|
19
|
-
:message => "bin folder \"#{bin_folder}\" not found in path \"#{path}\""
|
20
|
-
}
|
21
|
-
end
|
22
|
-
return {
|
23
|
-
:new_path => ( [bin_folder] + [path_parts] ).join(':'),
|
24
|
-
:message => "rewriting path to have bin folder at the beginning",
|
25
|
-
:success => true
|
26
|
-
}
|
27
|
-
else
|
28
|
-
if 0 == path.index(bin_folder)
|
29
|
-
return {
|
30
|
-
:message => "bin folder is already at front of PATH",
|
31
|
-
:success => true
|
32
|
-
}
|
33
|
-
else
|
34
|
-
return {
|
35
|
-
:message => "bin folder is in path but not at front. use -F to rewrite PATH",
|
36
|
-
:status => :not_front,
|
37
|
-
:success => true
|
38
|
-
}
|
39
|
-
end
|
40
|
-
end
|
41
|
-
else
|
42
|
-
path_parts.unshift(bin_folder)
|
43
|
-
return {
|
44
|
-
:new_path => path_parts.join(':'),
|
11
|
+
parts = path.split(':')
|
12
|
+
case parts.index(dir)
|
13
|
+
when nil
|
14
|
+
{
|
15
|
+
:new_path => ([dir] + parts).join(':'),
|
45
16
|
:message => "prepending bin folder to the beginning of the PATH.",
|
46
17
|
:success => true
|
47
18
|
}
|
19
|
+
when 0
|
20
|
+
{ :message => "already at front of path: \"#{dir}\"",
|
21
|
+
:success => true
|
22
|
+
}
|
23
|
+
else
|
24
|
+
{
|
25
|
+
:new_path => ([dir] + parts.reject{ |x| x == path }).join(':'),
|
26
|
+
:message => "rewriting path to have bin folder at the beginning",
|
27
|
+
:success => true
|
28
|
+
}
|
48
29
|
end
|
49
30
|
|
50
31
|
end.call
|
data/.gitignore
CHANGED
data/.repath.sh
CHANGED
data/CHANGELOG.md
ADDED
data/Gemfile.lock
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
myterm (0.0.
|
4
|
+
myterm (0.0.3)
|
5
5
|
highline
|
6
6
|
rb-appscript
|
7
|
+
rmagick
|
7
8
|
|
8
9
|
GEM
|
9
10
|
remote: http://rubygems.org/
|
@@ -13,7 +14,9 @@ GEM
|
|
13
14
|
highline (1.6.2)
|
14
15
|
linecache19 (0.5.12)
|
15
16
|
ruby_core_source (>= 0.1.4)
|
17
|
+
rake (0.9.2.2)
|
16
18
|
rb-appscript (0.6.1)
|
19
|
+
rmagick (2.13.1)
|
17
20
|
ruby-debug-base19 (0.11.25)
|
18
21
|
columnize (>= 0.3.1)
|
19
22
|
linecache19 (>= 0.5.11)
|
@@ -30,4 +33,5 @@ PLATFORMS
|
|
30
33
|
|
31
34
|
DEPENDENCIES
|
32
35
|
myterm!
|
36
|
+
rake
|
33
37
|
ruby-debug19
|
data/README.md
CHANGED
@@ -13,7 +13,13 @@
|
|
13
13
|
|
14
14
|
`myterm` attempts to be as self-documenting as possible. Try `myterm -h` for help.
|
15
15
|
|
16
|
-
The most common use case for me personally is something like:
|
16
|
+
The most common use case for me personally is usually something like:
|
17
|
+
|
18
|
+
myterm bg ohai
|
19
|
+
|
20
|
+
where "ohai" is some label that I want to appear in the background of the iTerm window.
|
21
|
+
|
22
|
+
More ambitiously, I might do:
|
17
23
|
|
18
24
|
myterm bg --exec node server.js
|
19
25
|
|
@@ -26,9 +32,12 @@ which will display the string "node server.js" up in the background and then run
|
|
26
32
|
|
27
33
|
`myterm` needs ImageMagick and its `convert` executable to be in your path.
|
28
34
|
|
35
|
+
Installing ImageMagick on your mac is "easy" with macports, provided that you don't run into hiccups. (But be prepared for it to take something like5 minutes.)
|
36
|
+
|
29
37
|
`myterm` needs some font file to use. It has an installer that will try to grab the [Simple Life](http://www.dafont.com/simple-life.font) font by Michael Strobel.
|
30
38
|
|
31
|
-
Additionally it needs whatever its gem dependencie(s) are
|
39
|
+
Additionally it needs whatever its gem dependencie(s) are (which at the time of this writing are: rb-appscript, highline, rmagick). Installing the myterm ruby gem should install these dependencies. If you are developing myterm you could also install them with "bundle install".
|
40
|
+
|
32
41
|
|
33
42
|
|
34
43
|
|
@@ -40,8 +49,9 @@ Additionally it needs whatever its gem dependencie(s) are, which at the time of
|
|
40
49
|
|
41
50
|
mkdir ~/src; cd ~/src
|
42
51
|
git clone git@github.com:hipe/myterm.git; cd myterm
|
43
|
-
gem build
|
44
|
-
gem install
|
52
|
+
gem build myterm.gemspec
|
53
|
+
gem install myterm-VERSION.gem # where VERSION is whatever version was built.
|
54
|
+
|
45
55
|
|
46
56
|
|
47
57
|
## Support
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.4
|
data/lib/myterm/api.rb
CHANGED
@@ -1,189 +1,208 @@
|
|
1
1
|
module Skylab; end
|
2
|
-
module Skylab::Myterm; end
|
3
2
|
|
4
|
-
|
3
|
+
module Skylab::Myterm
|
5
4
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
5
|
+
class ValidationError < RuntimeError ; end
|
6
|
+
|
7
|
+
module Color
|
8
|
+
def alpha= mixed
|
9
|
+
if mixed.kind_of?(String)
|
10
|
+
md = /\A(\d+(?:\.\d+)?)%?\z/.match(mixed) or
|
11
|
+
raise ValidationError.new("invalid format for percent #{val.inspect} -- expecting e.g. \"58%\"")
|
12
|
+
mixed = md[1].to_f
|
13
|
+
end
|
14
|
+
(0.0..100.0).include?(mixed) or
|
15
|
+
raise ValidationError.new("Percent value (#{mixed}%) must be between 0 and 100 inclusive.")
|
16
|
+
self[3] = ChannelScalarNormalized[mixed / 100.0]
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_hex
|
20
|
+
'#' + self.map{ |x| int_to_hex(x) }.join('')
|
21
|
+
end
|
22
|
+
|
23
|
+
TARGET_PLACES = 2 # each component of an #rrggbb hexadecimal color has 2 places
|
24
|
+
|
25
|
+
DIVISOR = 16 ** TARGET_PLACES
|
26
|
+
|
27
|
+
def int_to_hex int
|
28
|
+
int.respond_to?(:to_hex) and return int.to_hex
|
29
|
+
(int.to_f / DIVISOR).round.to_s(16).rjust(TARGET_PLACES, '0')
|
30
|
+
end
|
23
31
|
end
|
24
|
-
|
25
|
-
|
32
|
+
|
33
|
+
class << Color
|
34
|
+
def [] obj
|
35
|
+
obj.extend self
|
36
|
+
end
|
37
|
+
|
38
|
+
def dup color
|
39
|
+
self[color.dup]
|
40
|
+
end
|
26
41
|
end
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
42
|
+
|
43
|
+
module ChannelScalarNormalized
|
44
|
+
def to_hex
|
45
|
+
(('ff'.to_i(16).to_f * self).to_i).to_s(16).rjust(2, '0')
|
46
|
+
end
|
32
47
|
end
|
33
|
-
end
|
34
48
|
|
35
|
-
|
36
|
-
|
37
|
-
|
49
|
+
class << ChannelScalarNormalized
|
50
|
+
def [] obj
|
51
|
+
obj.extend self
|
52
|
+
end
|
38
53
|
end
|
39
|
-
|
40
|
-
|
54
|
+
|
55
|
+
class ImageBuilder
|
56
|
+
def build_text_drawing img
|
57
|
+
@lines.empty? and return fail("foo")
|
58
|
+
#@todo setters for everything etc
|
59
|
+
draw = Magick::Draw.new
|
60
|
+
draw.gravity = @opts[:gravity] || Magick::NorthEastGravity
|
61
|
+
@opts[:fill] ||= '#662020'
|
62
|
+
@opts[:fill].kind_of?(Proc) and @opts[:fill] = @opts[:fill].call(self)
|
63
|
+
draw.fill = @opts[:fill]
|
64
|
+
draw.font = @opts[:font] || "#{ENV['HOME']}/.fonts/SimpleLife.ttf"
|
65
|
+
draw.font_style = @opts[:font_style] || Magick::NormalStyle
|
66
|
+
draw.pointsize = @opts[:point_size] || 60
|
67
|
+
draw.text_antialias = @opts.key?(:text_antialias) ? @opts[:text_antialias] : true
|
68
|
+
draw.annotate(img, 0,0,20,10, @lines.first)
|
69
|
+
if @lines.length > 1
|
70
|
+
second_line = @lines[1..-1].join(' ')
|
71
|
+
draw.annotate(img, 0,0,20,80, second_line) do
|
72
|
+
self.pointsize = 30
|
73
|
+
end
|
74
|
+
end
|
75
|
+
nil # draw not needed at this point
|
76
|
+
end
|
77
|
+
private :build_text_drawing
|
78
|
+
|
79
|
+
def initialize iterm, lines, opts
|
80
|
+
@iterm, @lines, @opts = [iterm, lines, opts]
|
81
|
+
end
|
82
|
+
|
83
|
+
attr_reader :iterm
|
84
|
+
|
85
|
+
def run
|
86
|
+
require 'RMagick'
|
87
|
+
bg_color = Color.dup(@iterm.session.background_color)
|
88
|
+
@opts.key?(:alpha_percent) and bg_color.alpha = @opts[:alpha_percent]
|
89
|
+
img = Magick::Image.new(500, 300) do # copying over hard-coded dimensions from original Dmytro script
|
90
|
+
self.background_color = bg_color.to_hex
|
91
|
+
end
|
92
|
+
build_text_drawing img
|
93
|
+
img
|
94
|
+
end
|
41
95
|
end
|
42
|
-
end
|
43
96
|
|
44
|
-
class
|
45
|
-
Myterm = Skylab::Myterm
|
46
|
-
class << self
|
97
|
+
class << ImageBuilder
|
47
98
|
def build_background_image iterm, lines, opts
|
48
99
|
new(iterm, lines, opts).run
|
49
100
|
end
|
50
101
|
end
|
51
|
-
def initialize iterm, lines, opts
|
52
|
-
@iterm, @lines, @opts = [iterm, lines, opts]
|
53
|
-
end
|
54
|
-
attr_reader :iterm
|
55
|
-
def run
|
56
|
-
require 'RMagick'
|
57
|
-
bg_color = Myterm::Color.dup(@iterm.session.background_color)
|
58
|
-
@opts.key?(:alpha_percent) and bg_color.alpha = @opts[:alpha_percent]
|
59
|
-
img = Magick::Image.new(500, 300) do # copying over hard-coded dimensions from original Dmytro script
|
60
|
-
self.background_color = bg_color.to_hex
|
61
|
-
end
|
62
|
-
build_text_drawing img
|
63
|
-
img
|
64
|
-
end
|
65
|
-
private
|
66
|
-
def build_text_drawing img
|
67
|
-
@lines.empty? and return fail("foo")
|
68
|
-
#@todo setters for everything etc
|
69
|
-
draw = Magick::Draw.new
|
70
|
-
draw.gravity = @opts[:gravity] || Magick::NorthEastGravity
|
71
|
-
@opts[:fill] ||= '#662020'
|
72
|
-
@opts[:fill].kind_of?(Proc) and @opts[:fill] = @opts[:fill].call(self)
|
73
|
-
draw.fill = @opts[:fill]
|
74
|
-
draw.font = @opts[:font] || "#{ENV['HOME']}/.fonts/SimpleLife.ttf"
|
75
|
-
draw.font_style = @opts[:font_style] || Magick::NormalStyle
|
76
|
-
draw.pointsize = @opts[:point_size] || 60
|
77
|
-
draw.text_antialias = @opts.key?(:text_antialias) ? @opts[:text_antialias] : true
|
78
|
-
draw.annotate(img, 0,0,20,10, @lines.first)
|
79
|
-
if @lines.length > 1
|
80
|
-
second_line = @lines[1..-1].join(' ')
|
81
|
-
draw.annotate(img, 0,0,20,80, second_line) do
|
82
|
-
self.pointsize = 30
|
83
|
-
end
|
84
|
-
end
|
85
|
-
nil # draw not needed at this point
|
86
|
-
end
|
87
|
-
end
|
88
102
|
|
89
|
-
class
|
90
|
-
|
103
|
+
class ItermProxy
|
104
|
+
# ItermProxy is a wrapper around everything Iterm to the extent that its AppleScript interface supports
|
105
|
+
#
|
106
|
+
MIN_LEN = 50
|
91
107
|
|
92
|
-
|
108
|
+
def app
|
109
|
+
@app ||= begin
|
110
|
+
require 'appscript'
|
111
|
+
Appscript.app('iTerm')
|
112
|
+
end
|
113
|
+
end
|
114
|
+
private :app
|
93
115
|
|
94
|
-
|
116
|
+
def invalid msg
|
117
|
+
raise ValidationError.new(msg)
|
118
|
+
end
|
119
|
+
private :invalid
|
95
120
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
121
|
+
def bounds= arr
|
122
|
+
x = arr.detect { |i| i.to_s !~ /^\d+$/ } and return invalid("expecting digit had #{x.inspect}")
|
123
|
+
x = arr[2,2].detect { |i| i.to_i < MIN_LEN } and return invalid("too small: #{x} (min: #{MIN_LEN})")
|
124
|
+
app.windows[0].bounds.set arr
|
125
|
+
end
|
101
126
|
|
102
|
-
|
103
|
-
|
104
|
-
|
127
|
+
def bounds
|
128
|
+
app.windows[0].bounds.get
|
129
|
+
end
|
105
130
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
131
|
+
def session
|
132
|
+
@session ||= begin
|
133
|
+
tty = `tty`.strip
|
134
|
+
t = s = 0
|
135
|
+
catch(:break) do
|
136
|
+
(1..app.terminals.count).each do |i|
|
137
|
+
(1..app.terminals[i].sessions.count).each do |j|
|
138
|
+
if tty == app.terminals[i].sessions[j].tty.get
|
139
|
+
throw(:break, SessionProxy.new(app, i, j))
|
140
|
+
end
|
115
141
|
end
|
116
142
|
end
|
117
|
-
|
143
|
+
nil
|
144
|
+
end or fail("couldn't ascertain current session!")
|
118
145
|
end
|
119
|
-
session or fail("couldn't ascertain current session!")
|
120
|
-
SessionProxy.new(session)
|
121
146
|
end
|
122
147
|
end
|
123
148
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
149
|
+
module AppscriptDelegator
|
150
|
+
def delegated_attr_readers *list
|
151
|
+
list.each do |property|
|
152
|
+
lambda do |_property|
|
153
|
+
define_method(_property) do
|
154
|
+
resource.send(_property).get
|
155
|
+
end
|
156
|
+
end.call(property)
|
157
|
+
end
|
130
158
|
end
|
131
|
-
end
|
132
159
|
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
160
|
+
def delegated_attr_writers *list
|
161
|
+
list.each do |property|
|
162
|
+
lambda do |_property|
|
163
|
+
define_method("#{_property}=") do |val|
|
164
|
+
resource.send(_property).set val
|
165
|
+
end
|
166
|
+
end.call(property)
|
167
|
+
end
|
168
|
+
end
|
137
169
|
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
lambda do |_property|
|
142
|
-
define_method(_property) do
|
143
|
-
@resource.send(_property).get
|
144
|
-
end
|
145
|
-
end.call(property)
|
170
|
+
def delegated_attr_accessors *list
|
171
|
+
delegated_attr_readers(*list)
|
172
|
+
delegated_attr_writers(*list)
|
146
173
|
end
|
147
174
|
end
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
175
|
+
|
176
|
+
class ItermProxy::SessionProxy
|
177
|
+
extend AppscriptDelegator
|
178
|
+
|
179
|
+
delegated_attr_accessors :background_image_path
|
180
|
+
|
181
|
+
def background_color
|
182
|
+
Color[resource.background_color.get]
|
155
183
|
end
|
156
|
-
end
|
157
|
-
def delegated_attr_accessors *list
|
158
|
-
delegated_attr_readers(*list)
|
159
|
-
delegated_attr_writers(*list)
|
160
|
-
end
|
161
|
-
end
|
162
184
|
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
@resource = session
|
167
|
-
end
|
168
|
-
extend Myterm::AppscriptDelegator
|
185
|
+
def foreground_color
|
186
|
+
Color[resource.foreground_color.get]
|
187
|
+
end
|
169
188
|
|
170
|
-
|
171
|
-
|
189
|
+
def initialize app, term_idx, session_idx
|
190
|
+
@app, @term_idx, @sessions_idx = [app, term_idx, session_idx]
|
191
|
+
end
|
172
192
|
|
173
|
-
|
174
|
-
|
175
|
-
|
193
|
+
def resource
|
194
|
+
@app.terminals[@term_idx].sessions[@sessions_idx]
|
195
|
+
end
|
176
196
|
|
177
|
-
|
178
|
-
Myterm::Color[@resource.foreground_color.get]
|
197
|
+
delegated_attr_readers :tty
|
179
198
|
end
|
180
199
|
end
|
181
200
|
|
182
|
-
module Skylab
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
(('ff'.to_i(16).to_f * self).to_i).to_s(16).rjust(2, '0')
|
201
|
+
module Skylab
|
202
|
+
class << Myterm
|
203
|
+
def version
|
204
|
+
File.read(File.expand_path('../../../VERSION', __FILE__))
|
205
|
+
end
|
188
206
|
end
|
189
207
|
end
|
208
|
+
|
data/lib/myterm/cli.rb
CHANGED
@@ -1,149 +1,144 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require 'ruby-debug'
|
1
|
+
require File.expand_path('../vendor/skylab/face/cli', __FILE__)
|
2
|
+
require File.expand_path('../api', __FILE__)
|
4
3
|
require 'open3'
|
5
4
|
|
6
|
-
module Skylab; end
|
7
|
-
module Skylab::Myterm; end
|
8
5
|
|
9
|
-
module Skylab::Myterm
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
6
|
+
module Skylab::Myterm
|
7
|
+
module PathPrettifier
|
8
|
+
HOME_DIR_RE = /\A#{Regexp.escape(ENV['HOME'])}/
|
9
|
+
def pretty_path path
|
10
|
+
path.sub(HOME_DIR_RE, '~')
|
11
|
+
end
|
14
12
|
end
|
15
|
-
end
|
16
13
|
|
17
|
-
class
|
18
|
-
|
19
|
-
end
|
14
|
+
class ::Skylab::Face::Command
|
15
|
+
include PathPrettifier # eew
|
16
|
+
end
|
20
17
|
|
21
|
-
class
|
22
|
-
|
23
|
-
include Myterm::PathPrettifier
|
18
|
+
class Cli < ::Skylab::Face::Cli
|
19
|
+
include PathPrettifier
|
24
20
|
|
25
|
-
|
26
|
-
require "#{File.dirname(__FILE__)}/version"
|
27
|
-
Myterm::VERSION
|
28
|
-
end
|
21
|
+
version { ::Skylab::Myterm.version }
|
29
22
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
23
|
+
o(:bounds) do |o|
|
24
|
+
syntax "#{invocation_string} [x y width height]"
|
25
|
+
o.banner = "gets/sets the bounds of the terminal window\n#{usage_string}"
|
26
|
+
end
|
34
27
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
28
|
+
def bounds o, *a
|
29
|
+
case a.length
|
30
|
+
when 4
|
31
|
+
begin ; iterm.bounds = a ; rescue ValidationError => e ; return usage e ; end
|
32
|
+
when 0
|
33
|
+
@err.puts iterm.bounds.inspect
|
34
|
+
else
|
35
|
+
return usage("bad number of args #{a.length}: expecting 0 or 4")
|
36
|
+
end
|
43
37
|
end
|
44
|
-
end
|
45
38
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
39
|
+
o(:'bg') do |o, req|
|
40
|
+
syntax "#{invocation_string} [opts] [<text> [<text> [...]]]"
|
41
|
+
o.banner = "Generate a background image with certain text for the terminal\n#{usage_string}"
|
42
|
+
o.on('-e', '--exec <cmd ...>', 'Execute <cmd ...> in shell, also use it as text for background.') { }
|
43
|
+
o.on('-o', '--opacity PERCENT', "Percent by which to make image background opaque",
|
44
|
+
"(0%: tranparent. 100%: solid. Default: solid)") { |amt| req[:alpha_percent] = amt }
|
45
|
+
req[:font_file] = DEFAULT_FONT_FILE
|
46
|
+
o.on('--font FONTFILE.ttf', "font to use (default: #{pretty_path(req[:font_file])})") do |path|
|
47
|
+
req[:font_file] = path
|
48
|
+
end
|
49
|
+
req[:fill] = '#662020'
|
50
|
+
o.on('--fill[=COLOR]', "Write text in this color (default: #{req[:fill].inspect})",
|
51
|
+
"(when present but with no value, will use \"Text/Normal\" setting of current iTerm tab)" ) do |v|
|
52
|
+
req[:fill] = v || lambda { |img| img.iterm.session.foreground_color.to_hex }
|
53
|
+
end
|
54
|
+
o.on('-v', '--verbose', 'Be verbose.') { req[:verbose] = true }
|
55
55
|
end
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
req[:
|
56
|
+
|
57
|
+
def before_parse_bg req, args
|
58
|
+
idx = args.index { |s| %w(-e --exec).include?(s) } or return true
|
59
|
+
req[:_exec_this] = args[(idx+1)..-1]
|
60
|
+
args.replace idx == 0 ? [] : args[0..(idx-1)]
|
61
|
+
true
|
62
|
+
end
|
63
|
+
protected :before_parse_bg
|
64
|
+
|
65
|
+
def bg req, *args
|
66
|
+
if args.empty?
|
67
|
+
if req[:_exec_this]
|
68
|
+
args.any? and fail("logic error -- see before_parse_bg.")
|
69
|
+
args = req[:_exec_this]
|
70
|
+
else
|
71
|
+
return get_background
|
72
|
+
end
|
73
|
+
end
|
74
|
+
check_font(req) or return
|
75
|
+
img = ImageBuilder.build_background_image(iterm, args, req) or return false
|
76
|
+
req[:verbose] and @err.puts "(bg_color: #{img.background_color.inspect})"
|
77
|
+
outpath = "#{IMG_DIRNAME}/#{IMG_BASENAME}.#{Process.pid}.png"
|
78
|
+
img.write(outpath)
|
79
|
+
req[:verbose] and @err.puts "(setting background image to: #{outpath})" # doesn't care if --verbose
|
80
|
+
iterm.session.background_image_path = outpath
|
81
|
+
if req[:_exec_this]
|
82
|
+
@err.puts "(#{program_name} executing: #{req[:_exec_this].join(' ')})"
|
83
|
+
exec(req[:_exec_this].join(' '))
|
84
|
+
end
|
85
|
+
true
|
60
86
|
end
|
61
|
-
o.on('-v', '--verbose', 'Be verbose.') { req[:verbose] = true }
|
62
|
-
end
|
63
87
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
args.replace idx == 0 ? [] : args[0..(idx-1)]
|
68
|
-
true
|
69
|
-
end
|
70
|
-
protected :before_parse_bg
|
88
|
+
DEFAULT_FONT_FILE = "#{ENV['HOME']}/.fonts/MytermDefaultFont.ttf"
|
89
|
+
IMG_DIRNAME = '/tmp'
|
90
|
+
IMG_BASENAME = 'iTermBG'
|
71
91
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
92
|
+
private
|
93
|
+
|
94
|
+
def check_font req
|
95
|
+
File.exist?(req[:font_file]) and return true
|
96
|
+
if req[:font_file] == DEFAULT_FONT_FILE
|
97
|
+
return maybe_download_font req
|
77
98
|
else
|
78
|
-
|
99
|
+
font_not_found req
|
79
100
|
end
|
80
101
|
end
|
81
|
-
check_font(req) or return
|
82
|
-
img = Myterm::ImageBuilder.build_background_image(iterm, args, req) or return false
|
83
|
-
req[:verbose] and @err.puts "(bg_color: #{img.background_color.inspect})"
|
84
|
-
outpath = "#{ImgDirname}/#{ImgBasename}.#{Process.pid}.png"
|
85
|
-
img.write(outpath)
|
86
|
-
req[:verbose] and @err.puts "(setting background image to: #{outpath})" # doesn't care if --verbose
|
87
|
-
iterm.session.background_image_path = outpath
|
88
|
-
if req[:_exec_this]
|
89
|
-
@err.puts "(#{program_name} executing: #{req[:_exec_this].join(' ')})"
|
90
|
-
exec(req[:_exec_this].join(' '))
|
91
|
-
end
|
92
|
-
true
|
93
|
-
end
|
94
102
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
103
|
+
DEFAULT_FONT_URL = 'http://img.dafont.com/dl/?f=simple_life'
|
104
|
+
DEFAULT_FONT_FILE_NOT_SIMLINKED = "#{ENV['HOME']}/.fonts/SimpleLife.ttf"
|
105
|
+
|
106
|
+
def maybe_download_font req
|
107
|
+
target = DEFAULT_FONT_FILE_NOT_SIMLINKED
|
108
|
+
File.exist?(target) and return true
|
109
|
+
$stdin.tty? && $stdout.tty? or return font_not_found(req)
|
110
|
+
@err.write "Font file #{pretty_path(req[:font_file])} not found. "
|
111
|
+
require 'highline'
|
112
|
+
require 'fileutils'
|
113
|
+
HighLine.new.agree("Let #{program_name} download it? (Y/n) (recommended: yes)") or return false
|
114
|
+
outfile = DEFAULT_FONT_FILE_NOT_SIMLINKED.sub(/\.ttf$/, '.zip')
|
115
|
+
font_dir = File.dirname(outfile)
|
116
|
+
File.directory?(font_dir) or FileUtils.mkdir_p(font_dir, :verbose => true)
|
117
|
+
cmds = ["wget -O #{outfile} #{DEFAULT_FONT_URL}"]
|
118
|
+
cmds.push "cd #{font_dir}"
|
119
|
+
cmds.push "unzip #{outfile}"
|
120
|
+
cmds.push "ln -s #{DEFAULT_FONT_FILE_NOT_SIMLINKED} #{DEFAULT_FONT_FILE}"
|
121
|
+
cmds.push("echo 'finished installing for #{program_name}: #{pretty_path(DEFAULT_FONT_FILE_NOT_SIMLINKED)}. " <<
|
122
|
+
"Please try using it again.'")
|
123
|
+
@err.puts(cmd = cmds.join(' ; '))
|
124
|
+
exec(cmd)
|
106
125
|
end
|
107
|
-
end
|
108
|
-
|
109
|
-
DefaultFontUrl = 'http://img.dafont.com/dl/?f=simple_life'
|
110
|
-
DefaultFontFileNotSimlinked = "#{ENV['HOME']}/.fonts/SimpleLife.ttf"
|
111
|
-
|
112
|
-
def maybe_download_font req
|
113
|
-
target = DefaultFontFileNotSimlinked
|
114
|
-
File.exist?(target) and return true
|
115
|
-
$stdin.tty? && $stdout.tty? or return font_not_found(req)
|
116
|
-
@err.write "Font file #{pretty_path(req[:font_file])} not found. "
|
117
|
-
require 'highline'
|
118
|
-
require 'fileutils'
|
119
|
-
HighLine.new.agree("Let #{program_name} download it? (Y/n) (recommended: yes)") or return false
|
120
|
-
outfile = DefaultFontFileNotSimlinked.sub(/\.ttf$/, '.zip')
|
121
|
-
font_dir = File.dirname(outfile)
|
122
|
-
File.directory?(font_dir) or FileUtils.mkdir_p(font_dir, :verbose => true)
|
123
|
-
cmds = ["wget -O #{outfile} #{DefaultFontUrl}"]
|
124
|
-
cmds.push "cd #{font_dir}"
|
125
|
-
cmds.push "unzip #{outfile}"
|
126
|
-
cmds.push "ln -s #{DefaultFontFileNotSimlinked} #{DefaultFontFile}"
|
127
|
-
cmds.push("echo 'finished installing for #{program_name}: #{pretty_path(DefaultFontFileNotSimlinked)}. " <<
|
128
|
-
"Please try using it again.'")
|
129
|
-
@err.puts(cmd = cmds.join(' ; '))
|
130
|
-
exec(cmd)
|
131
|
-
end
|
132
126
|
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
127
|
+
def font_not_found req
|
128
|
+
@err.puts "font file not found: #{pretty_path(req[:font_file])}"
|
129
|
+
req.command.usage
|
130
|
+
return false
|
131
|
+
end
|
138
132
|
|
139
|
-
|
140
|
-
|
141
|
-
|
133
|
+
def iterm
|
134
|
+
@iterm ||= ItermProxy.new
|
135
|
+
end
|
142
136
|
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
137
|
+
def get_background
|
138
|
+
@err.puts "tty: #{iterm.session.tty}"
|
139
|
+
@err.puts "background_image: #{iterm.session.background_image_path.inspect}"
|
140
|
+
@err.puts "background_color: #{iterm.session.background_color}"
|
141
|
+
end
|
147
142
|
end
|
148
|
-
|
149
143
|
end
|
144
|
+
|
@@ -1,16 +1,17 @@
|
|
1
1
|
require 'optparse'
|
2
2
|
|
3
|
-
# an ultralight command-line parser (
|
3
|
+
# an ultralight command-line parser (407 lines)
|
4
4
|
# that wraps around OptParse (can do anything it does)
|
5
5
|
# with colors
|
6
6
|
# with flexible command-like options ('officious' like -v, -h)
|
7
7
|
# with commands with arguments based off of method signatures
|
8
8
|
# with subcommands, (namespaces) arbitrarily deeply nested
|
9
9
|
|
10
|
-
module
|
11
|
-
module
|
10
|
+
module Skylab; end
|
11
|
+
module Skylab::Face; end
|
12
12
|
|
13
|
-
module
|
13
|
+
module Skylab::Face::Colors
|
14
|
+
extend self
|
14
15
|
def bold str ; style str, :bright, :green end
|
15
16
|
def hi str ; style str, :green end
|
16
17
|
def ohno str ; style str, :red end
|
@@ -26,7 +27,7 @@ module Tmx::Face::Colors
|
|
26
27
|
end
|
27
28
|
end
|
28
29
|
|
29
|
-
module
|
30
|
+
module Skylab::Face
|
30
31
|
class Command
|
31
32
|
|
32
33
|
include Colors
|
@@ -80,12 +81,12 @@ module Tmx::Face
|
|
80
81
|
|
81
82
|
def parse argv
|
82
83
|
req = { }
|
83
|
-
req.send(:instance_variable_set, '@method_parameters', argv)
|
84
84
|
class << req
|
85
|
-
attr_accessor :
|
85
|
+
attr_accessor :command
|
86
|
+
attr_accessor :method_parameters
|
86
87
|
end
|
87
|
-
|
88
|
-
|
88
|
+
req.method_parameters = argv
|
89
|
+
req.command = self
|
89
90
|
begin
|
90
91
|
build_option_parser(req).parse! argv
|
91
92
|
req
|
@@ -96,10 +97,11 @@ module Tmx::Face
|
|
96
97
|
end
|
97
98
|
end
|
98
99
|
|
100
|
+
NoUsageRe = /\A#{Regexp.escape(Skylab::Face::Colors.hi('usage:'))} /
|
101
|
+
|
99
102
|
def summary
|
100
|
-
build_option_parser({}).to_s
|
101
|
-
|
102
|
-
split("\n").select{ |s| ! s.strip.empty? }
|
103
|
+
build_option_parser({}).to_s =~ /\A[\n]*([^\n]*)(\n+[^\n])?/
|
104
|
+
[ "#{$1}#{ ' [..]' if $2 }".sub(NoUsageRe, '') ]
|
103
105
|
end
|
104
106
|
|
105
107
|
def syntax *args
|
@@ -125,10 +127,8 @@ module Tmx::Face
|
|
125
127
|
def invocation_string
|
126
128
|
"#{@parent.invocation_string} #{name}"
|
127
129
|
end
|
128
|
-
alias_method :path, :invocation_string
|
129
130
|
def parent= parent
|
130
131
|
@parent and fail("won't overwrite existing parent")
|
131
|
-
@parent_protected_instance_methods = parent.class.protected_instance_methods(false).map(&:intern)
|
132
132
|
@parent = parent
|
133
133
|
end
|
134
134
|
def usage msg=nil
|
@@ -159,7 +159,8 @@ module Tmx::Face
|
|
159
159
|
# option_definitions.reject! { |a,_| '-h' == a.first }
|
160
160
|
end
|
161
161
|
def namespace name, &block
|
162
|
-
|
162
|
+
def_block = name.kind_of?(Array) ? name : [Namespace, [name], block]
|
163
|
+
command_definitions.push Namespace.add_definition(def_block)
|
163
164
|
end
|
164
165
|
def on *a, &b
|
165
166
|
block_given? or raise ArgumentError.new("block required")
|
@@ -277,6 +278,19 @@ module Tmx::Face
|
|
277
278
|
class Namespace
|
278
279
|
extend TreeDefiner, Colors
|
279
280
|
include Treeish, Nodeish, Colors
|
281
|
+
@definitions ||= []
|
282
|
+
class << self
|
283
|
+
def add_definition arr
|
284
|
+
@definitions.push arr
|
285
|
+
arr
|
286
|
+
end
|
287
|
+
def namespaces
|
288
|
+
@definitions.each_with_index do |defn, idx|
|
289
|
+
defn.kind_of?(Class) or @definitions[idx] = defn[0].build(*defn[1], &defn[2])
|
290
|
+
end
|
291
|
+
@definitions
|
292
|
+
end
|
293
|
+
end
|
280
294
|
alias_method :interface, :class
|
281
295
|
def init_for_run parent, name_as_used
|
282
296
|
@name_as_used = name_as_used
|
@@ -290,6 +304,7 @@ module Tmx::Face
|
|
290
304
|
end
|
291
305
|
alias_method :inspect, :name
|
292
306
|
def self.build name, &block
|
307
|
+
name.kind_of?(Symbol) or return name
|
293
308
|
name = name.to_s
|
294
309
|
Class.new(self).class_eval do
|
295
310
|
self.namespace_name = name
|
@@ -330,11 +345,12 @@ module Tmx::Face
|
|
330
345
|
end
|
331
346
|
end
|
332
347
|
|
333
|
-
class
|
334
|
-
|
335
|
-
|
336
|
-
include
|
337
|
-
include
|
348
|
+
class Skylab::Face::Cli
|
349
|
+
Face = Skylab::Face
|
350
|
+
extend Face::Command::TreeDefiner
|
351
|
+
include Face::Colors
|
352
|
+
include Face::Command::Nodeish
|
353
|
+
include Face::Command::Treeish
|
338
354
|
|
339
355
|
def initialize
|
340
356
|
@out = $stdout
|
@@ -362,7 +378,6 @@ class Tmx::Face::Cli
|
|
362
378
|
end while (cmd and cmd.respond_to?(:find_command) and runner = cmd)
|
363
379
|
cmd and req = cmd.parse(argv) and
|
364
380
|
begin
|
365
|
-
req.command = cmd
|
366
381
|
runner.send(cmd.method_symbol, req, * req.method_parameters)
|
367
382
|
rescue ArgumentError => e
|
368
383
|
argument_error e, cmd
|
data/myterm.gemspec
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
2
|
$:.push File.expand_path("../lib", __FILE__)
|
3
|
-
require
|
3
|
+
require 'myterm/api'
|
4
4
|
|
5
5
|
Gem::Specification.new do |s|
|
6
6
|
s.name = "myterm"
|
7
|
-
s.version = Skylab::Myterm
|
7
|
+
s.version = Skylab::Myterm.version
|
8
8
|
s.authors = ["Mark Meves"]
|
9
9
|
s.email = ["mark.meves@gmail.com"]
|
10
10
|
s.homepage = "http://botnoise.org"
|
@@ -14,8 +14,11 @@ Gem::Specification.new do |s|
|
|
14
14
|
|
15
15
|
s.rubyforge_project = "myterm"
|
16
16
|
|
17
|
-
s.add_dependency 'rb-appscript'
|
18
17
|
s.add_dependency 'highline'
|
18
|
+
s.add_dependency 'rb-appscript'
|
19
|
+
s.add_dependency 'rmagick' # i had 2.13.1
|
20
|
+
|
21
|
+
s.add_development_dependency 'rake'
|
19
22
|
s.add_development_dependency "ruby-debug19"
|
20
23
|
|
21
24
|
s.files = `git ls-files`.split("\n")
|
@@ -23,3 +26,4 @@ Gem::Specification.new do |s|
|
|
23
26
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
24
27
|
s.require_paths = ["lib"]
|
25
28
|
end
|
29
|
+
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: myterm
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,12 +9,23 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2012-01-24 00:00:00.000000000 -08:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: highline
|
17
|
+
requirement: &70123357981900 !ruby/object:Gem::Requirement
|
18
|
+
none: false
|
19
|
+
requirements:
|
20
|
+
- - ! '>='
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '0'
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: *70123357981900
|
15
26
|
- !ruby/object:Gem::Dependency
|
16
27
|
name: rb-appscript
|
17
|
-
requirement: &
|
28
|
+
requirement: &70123357981460 !ruby/object:Gem::Requirement
|
18
29
|
none: false
|
19
30
|
requirements:
|
20
31
|
- - ! '>='
|
@@ -22,10 +33,10 @@ dependencies:
|
|
22
33
|
version: '0'
|
23
34
|
type: :runtime
|
24
35
|
prerelease: false
|
25
|
-
version_requirements: *
|
36
|
+
version_requirements: *70123357981460
|
26
37
|
- !ruby/object:Gem::Dependency
|
27
|
-
name:
|
28
|
-
requirement: &
|
38
|
+
name: rmagick
|
39
|
+
requirement: &70123357981040 !ruby/object:Gem::Requirement
|
29
40
|
none: false
|
30
41
|
requirements:
|
31
42
|
- - ! '>='
|
@@ -33,10 +44,21 @@ dependencies:
|
|
33
44
|
version: '0'
|
34
45
|
type: :runtime
|
35
46
|
prerelease: false
|
36
|
-
version_requirements: *
|
47
|
+
version_requirements: *70123357981040
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
name: rake
|
50
|
+
requirement: &70123357980620 !ruby/object:Gem::Requirement
|
51
|
+
none: false
|
52
|
+
requirements:
|
53
|
+
- - ! '>='
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
type: :development
|
57
|
+
prerelease: false
|
58
|
+
version_requirements: *70123357980620
|
37
59
|
- !ruby/object:Gem::Dependency
|
38
60
|
name: ruby-debug19
|
39
|
-
requirement: &
|
61
|
+
requirement: &70123357980200 !ruby/object:Gem::Requirement
|
40
62
|
none: false
|
41
63
|
requirements:
|
42
64
|
- - ! '>='
|
@@ -44,7 +66,7 @@ dependencies:
|
|
44
66
|
version: '0'
|
45
67
|
type: :development
|
46
68
|
prerelease: false
|
47
|
-
version_requirements: *
|
69
|
+
version_requirements: *70123357980200
|
48
70
|
description: Command line interface for customizing iTerm using AppleScript. Creates
|
49
71
|
meaningful, salient, eye-catching background images that help to discern between
|
50
72
|
iTerm windows.
|
@@ -59,16 +81,17 @@ files:
|
|
59
81
|
- .gitignore
|
60
82
|
- .repath.sh
|
61
83
|
- .rvmrc.example
|
84
|
+
- CHANGELOG.md
|
62
85
|
- Gemfile
|
63
86
|
- Gemfile.lock
|
64
87
|
- README.md
|
88
|
+
- VERSION
|
65
89
|
- bin/myterm
|
66
90
|
- doc/HISTORY.md
|
67
91
|
- lib-iterm.sh
|
68
92
|
- lib/myterm/api.rb
|
69
93
|
- lib/myterm/cli.rb
|
70
|
-
- lib/myterm/vendor/face/cli.rb
|
71
|
-
- lib/myterm/version.rb
|
94
|
+
- lib/myterm/vendor/skylab/face/cli.rb
|
72
95
|
- myterm.gemspec
|
73
96
|
has_rdoc: true
|
74
97
|
homepage: http://botnoise.org
|
data/lib/myterm/version.rb
DELETED