myterm 0.0.3 → 0.0.4
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.
- 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