ghi 0.9.0.dev → 0.9.0.dev1

Sign up to get free protection for your applications and to get access to all the features.
data/lib/ghi.rb CHANGED
@@ -4,6 +4,7 @@ module GHI
4
4
  autoload :Authorization, 'ghi/authorization'
5
5
  autoload :Client, 'ghi/client'
6
6
  autoload :Commands, 'ghi/commands'
7
+ autoload :Editor, 'ghi/editor'
7
8
  autoload :Formatting, 'ghi/formatting'
8
9
 
9
10
  class << self
@@ -22,7 +22,7 @@ module GHI
22
22
  :note_url => 'https://github.com/stephencelis/ghi'
23
23
  )
24
24
  }
25
- @token = res['token']
25
+ @token = res.body['token']
26
26
 
27
27
  run = []
28
28
  unless username
data/lib/ghi/client.rb CHANGED
@@ -2,9 +2,8 @@ require 'cgi'
2
2
  require 'net/https'
3
3
 
4
4
  unless defined? Net::HTTP::Patch
5
- Net::HTTP::Patch = Class.new Net::HTTP::Post do
6
- METHOD = 'PATCH'
7
- end
5
+ # PATCH support for 1.8.7.
6
+ Net::HTTP::Patch = Class.new(Net::HTTP::Post) { METHOD = 'PATCH' }
8
7
  end
9
8
 
10
9
  module GHI
@@ -12,7 +12,7 @@ EOF
12
12
  end
13
13
  opts.separator ''
14
14
  opts.separator 'Issue modification options'
15
- opts.on '-m', '--message <text>', 'close with message' do |text|
15
+ opts.on '-m', '--message [<text>]', 'close with message' do |text|
16
16
  assigns[:comment] = text
17
17
  end
18
18
  opts.separator ''
@@ -27,11 +27,12 @@ EOF
27
27
  List.execute %W(-sc -- #{repo})
28
28
  else
29
29
  require_issue
30
- Edit.execute %W(-sc #{issue} -- #{repo})
31
- puts 'Closed.'
32
- if assigns[:comment]
33
- Comment.execute %W(#{issue} -m #{assigns[:comment]} -- #{repo})
30
+ if assigns.key? :comment
31
+ Comment.execute [
32
+ issue, '-m', assigns[:comment], '--', repo
33
+ ].compact
34
34
  end
35
+ Edit.execute %W(-sc #{issue} -- #{repo})
35
36
  end
36
37
  end
37
38
 
@@ -94,21 +94,6 @@ module GHI
94
94
  def any_or_none_or input
95
95
  input ? input : { nil => '*', false => 'none' }[input]
96
96
  end
97
-
98
- def page? message = 'Load more?'
99
- return unless STDIN.tty?
100
-
101
- STDOUT.print "#{message} [Yn] "
102
- begin
103
- system 'stty raw -echo'
104
- # Continue on y, j, <ENTER>, <DOWN>...
105
- exit unless [?y, ?Y, ?j, 13, 27].include? STDIN.getc
106
- STDOUT.print "\r" + ' ' * columns
107
- STDOUT.print "\r Loading..."
108
- ensure
109
- system 'stty -raw echo'
110
- end
111
- end
112
97
  end
113
98
  end
114
99
  end
@@ -15,7 +15,7 @@ EOF
15
15
  # opts.on '-v', '--verbose', 'list events, too'
16
16
  opts.separator ''
17
17
  opts.separator 'Comment modification options'
18
- opts.on '-m', '--message <text>', 'comment body' do |text|
18
+ opts.on '-m', '--message [<text>]', 'comment body' do |text|
19
19
  assigns[:body] = text
20
20
  end
21
21
  opts.on '--amend', 'amend previous comment' do
@@ -40,10 +40,9 @@ EOF
40
40
  case action
41
41
  when 'list'
42
42
  res = index
43
- loop do
43
+ page do
44
44
  puts format_comments(res.body)
45
45
  break unless res.next_page
46
- page?
47
46
  res = throb { api.get res.next_page }
48
47
  end
49
48
  when 'create'
@@ -60,7 +59,7 @@ EOF
60
59
  abort 'No recent comment found.'
61
60
  end
62
61
  when 'close'
63
- Close.execute %W(-m #{assigns[:body]} #{issue} -- #{repo})
62
+ Close.execute [issue, '-m', assigns[:body], '--', repo].compact
64
63
  end
65
64
  end
66
65
 
@@ -72,19 +71,21 @@ EOF
72
71
 
73
72
  def create
74
73
  require_body
75
- throb { api.post uri, assigns }
76
- puts 'Comment created.'
74
+ c = throb { api.post uri, assigns }.body
75
+ puts format_comment c
76
+ puts 'Commented.'
77
77
  end
78
78
 
79
79
  def update
80
80
  require_body
81
- throb { api.patch uri, assigns }
82
- puts 'Comment updated.'
81
+ c = throb { api.patch uri, assigns }.body
82
+ puts format_comment c
83
+ puts 'Updated.'
83
84
  end
84
85
 
85
86
  def destroy
86
87
  throb { api.delete uri }
87
- puts 'Comment deleted.'
88
+ puts 'Deleted.'
88
89
  end
89
90
 
90
91
  private
@@ -94,10 +95,11 @@ EOF
94
95
  end
95
96
 
96
97
  def require_body
97
- if assigns[:body].nil?
98
- warn 'Missing argument: -m'
99
- abort options.to_s
100
- end
98
+ return if assigns[:body]
99
+ message = Editor.gets format_comment_editor(issue, comment)
100
+ abort 'No comment.' if message.nil? || message.empty?
101
+ abort 'No change.' if comment && message.strip == comment['body'].strip
102
+ assigns[:body] = message if message
101
103
  end
102
104
  end
103
105
  end
@@ -1,6 +1,8 @@
1
1
  module GHI
2
2
  module Commands
3
3
  class Edit < Command
4
+ attr_accessor :edit
5
+
4
6
  def options
5
7
  OptionParser.new do |opts|
6
8
  opts.banner = <<EOF
@@ -8,8 +10,9 @@ usage: ghi edit [options] <issueno>
8
10
  EOF
9
11
  opts.separator ''
10
12
  opts.on(
11
- '-m', '--message <text>', 'change issue description'
13
+ '-m', '--message [<text>]', 'change issue description'
12
14
  ) do |text|
15
+ next self.edit = true if text.nil?
13
16
  assigns[:title], assigns[:body] = text.split(/\n+/, 2)
14
17
  end
15
18
  opts.on(
@@ -41,8 +44,26 @@ EOF
41
44
  require_issue
42
45
  require_repo
43
46
  options.parse! args
47
+ if edit || assigns.empty?
48
+ i = throb { api.get "/repos/#{repo}/issues/#{issue}" }.body
49
+ message = Editor.gets format_editor(i)
50
+ abort "There's no issue." if message.nil? || message.empty?
51
+ assigns[:title], assigns[:body] = message.split(/\n+/, 2)
52
+ end
53
+ if assigns[:title]
54
+ titles_match = assigns[:title].strip == i['title'].strip
55
+ if assigns[:body]
56
+ bodies_match = assigns[:body].to_s.strip == i['body'].to_s.strip
57
+ end
58
+ end
59
+ if titles_match && bodies_match
60
+ abort 'No change.' if assigns.dup.delete_if { |k, v|
61
+ [:title, :body].include? k
62
+ }
63
+ end
44
64
  i = throb { api.patch "/repos/#{repo}/issues/#{issue}", assigns }.body
45
65
  puts format_issue(i)
66
+ puts 'Updated.'
46
67
  end
47
68
  end
48
69
  end
@@ -61,6 +61,7 @@ EOF
61
61
  if issue
62
62
  self.action ||= 'add'
63
63
  self.name = args.shift.to_s.split ','
64
+ self.name.concat args
64
65
  else
65
66
  self.action ||= 'create'
66
67
  self.name ||= args.shift
@@ -117,7 +118,6 @@ EOF
117
118
  labels = throb {
118
119
  api.post "/repos/#{repo}/issues/#{issue}/labels", name
119
120
  }.body
120
- labels.delete_if { |l| !name.include?(l['name']) }
121
121
  puts "Issue #%d labeled %s." % [issue, format_labels(labels)]
122
122
  end
123
123
 
@@ -128,7 +128,11 @@ EOF
128
128
  puts "Labels removed."
129
129
  when 1
130
130
  labels = throb { api.delete "#{base_uri}/#{name.join}" }.body
131
- puts "Issue #%d labeled %s." % [issue, format_labels(labels)]
131
+ if labels.empty?
132
+ puts "Issue #%d unlabeled." % issue
133
+ else
134
+ puts "Issue #%d labeled %s." % [issue, format_labels(labels)]
135
+ end
132
136
  else
133
137
  labels = throb {
134
138
  api.get "/repos/#{repo}/issues/#{issue}/labels"
@@ -140,7 +144,11 @@ EOF
140
144
 
141
145
  def replace
142
146
  labels = throb { api.put base_uri, name }.body
143
- puts "Issue #%d labeled %s." % [issue, format_labels(labels)]
147
+ if labels.empty?
148
+ puts "Issue #%d unlabeled." % issue
149
+ else
150
+ puts "Issue #%d labeled %s." % [issue, format_labels(labels)]
151
+ end
144
152
  end
145
153
 
146
154
  private
@@ -90,20 +90,18 @@ module GHI
90
90
  fallback.parse! e.args
91
91
  retry
92
92
  end
93
-
94
93
  if reverse
95
94
  assigns[:sort] ||= 'created'
96
95
  assigns[:direction] = 'asc'
97
96
  end
98
-
99
97
  unless quiet
100
- print format_issues_header
98
+ print header = format_issues_header
101
99
  print "\n" unless STDOUT.tty?
102
100
  end
103
101
  res = throb(
104
102
  0, format_state(assigns[:state], quiet ? CURSOR[:up][1] : '#')
105
103
  ) { api.get uri, assigns }
106
- loop do
104
+ page header && "\r#{CURSOR[:up][1]}#{header}" do
107
105
  issues = res.body
108
106
  if verbose
109
107
  puts issues.map { |i| format_issue i }
@@ -111,9 +109,16 @@ module GHI
111
109
  puts format_issues(issues, repo.nil?)
112
110
  end
113
111
  break unless res.next_page
114
- page?
115
112
  res = throb { api.get res.next_page }
116
113
  end
114
+ rescue Client::Error => e
115
+ if e.response.code == '422'
116
+ e.errors.any? { |err|
117
+ err['code'] == 'missing' && err['field'] == 'milestone'
118
+ } and abort 'No such milestone.'
119
+ end
120
+
121
+ raise
117
122
  end
118
123
 
119
124
  private
@@ -99,8 +99,8 @@ EOF
99
99
  state = assigns[:state] || 'open'
100
100
  print format_state state, "# #{repo} #{state} milestones"
101
101
  print "\n" unless STDOUT.tty?
102
- res = throb(0, format_state(state, '#')) { api.get uri }
103
- loop do
102
+ res = throb(0, format_state(state, '#')) { api.get uri, assigns }
103
+ page do
104
104
  milestones = res.body
105
105
  if verbose
106
106
  puts milestones.map { |m| format_milestone m }
@@ -108,7 +108,6 @@ EOF
108
108
  puts format_milestones(milestones)
109
109
  end
110
110
  break unless res.next_page
111
- page?
112
111
  res = throb { api.get res.next_page }
113
112
  end
114
113
  when 'show'
@@ -18,11 +18,11 @@ EOF
18
18
  end
19
19
  opts.separator ''
20
20
  opts.separator 'Issue modification options'
21
- opts.on '-m', '--message <text>', 'describe issue' do |text|
21
+ opts.on '-m', '--message [<text>]', 'describe issue' do |text|
22
22
  assigns[:title], assigns[:body] = text.split(/\n+/, 2)
23
23
  end
24
24
  opts.on(
25
- '-u', '--[no-]assign <user>', 'assign to specified user'
25
+ '-u', '--[no-]assign [<user>]', 'assign to specified user'
26
26
  ) do |assignee|
27
27
  assigns[:assignee] = assignee
28
28
  end
@@ -41,11 +41,11 @@ EOF
41
41
  end
42
42
 
43
43
  def execute
44
+ require_repo
44
45
  self.action = 'create'
45
46
 
46
47
  if extract_issue
47
- Edit.new(args.unshift('-so', issue)).execute
48
- puts 'Reopened.'
48
+ Edit.execute args.push('-so', issue, '--', repo)
49
49
  exit
50
50
  end
51
51
 
@@ -53,11 +53,16 @@ EOF
53
53
 
54
54
  case action
55
55
  when 'index'
56
- List.new(args).execute
56
+ if assigns.key? :assignee
57
+ args.unshift assigns[:assignee] if assigns[:assignee]
58
+ args.unshift '-u'
59
+ end
60
+ List.execute args.push('--', repo)
57
61
  when 'create'
58
- if assigns[:title].nil? # FIXME: Open $EDITOR
59
- warn "Missing argument: -m"
60
- abort options.to_s
62
+ if assigns[:title].nil?
63
+ message = Editor.gets format_editor
64
+ abort "There's no issue." if message.nil? || message.empty?
65
+ assigns[:title], assigns[:body] = message.split(/\n+/, 2)
61
66
  end
62
67
  i = throb { api.post "/repos/#{repo}/issues", assigns }.body
63
68
  puts format_issue(i)
@@ -3,7 +3,7 @@ module GHI
3
3
  class Show < Command
4
4
  def options
5
5
  OptionParser.new do |opts|
6
- opts.banner = 'usage: ghi show <issueno> [[<user>/]<repo>]'
6
+ opts.banner = 'usage: ghi show <issueno>'
7
7
  opts.separator ''
8
8
  end
9
9
  end
@@ -12,9 +12,15 @@ module GHI
12
12
  require_issue
13
13
  require_repo
14
14
  i = throb { api.get "/repos/#{repo}/issues/#{issue}" }.body
15
- puts format_issue(i)
16
- page? 'Load comments?'
17
- Comment.execute %W(-l #{issue} -- #{repo})
15
+ page do
16
+ puts format_issue(i)
17
+ n = i['comments']
18
+ if n > 0
19
+ puts "#{n} Comment#{'s' unless n == 1}:\n\n"
20
+ Comment.execute %W(-l #{issue} -- #{repo})
21
+ end
22
+ break
23
+ end
18
24
  end
19
25
  end
20
26
  end
@@ -4,7 +4,7 @@ module GHI
4
4
  MAJOR = 0
5
5
  MINOR = 9
6
6
  PATCH = 0
7
- PRE = 'dev'
7
+ PRE = 'dev1'
8
8
 
9
9
  VERSION = [MAJOR, MINOR, PATCH, PRE].compact.join '.'
10
10
 
data/lib/ghi/editor.rb ADDED
@@ -0,0 +1,26 @@
1
+ require 'tempfile'
2
+
3
+ module GHI
4
+ class Editor
5
+ class << self
6
+ def gets prefill
7
+ Tempfile.open 'GHI_ISSUE' do |f|
8
+ f << prefill
9
+ f.rewind
10
+ system "#{editor} #{f.path}"
11
+ return File.read(f.path).gsub(/^#.*$\n?/, '').strip
12
+ end
13
+ end
14
+
15
+ private
16
+
17
+ def editor
18
+ editor = ENV['GHI_EDITOR']
19
+ editor ||= ENV['GIT_EDITOR']
20
+ editor ||= `git config core.editor`.split.first
21
+ editor ||= ENV['EDITOR']
22
+ editor ||= 'vi'
23
+ end
24
+ end
25
+ end
26
+ end
@@ -34,13 +34,25 @@ module GHI
34
34
  end
35
35
  }
36
36
  }
37
- stdout, $stdout = $stdout, IO.popen('less -ErX', 'w')
38
37
  super strings
38
+ end
39
+
40
+ def page header = nil, throttle = 1
41
+ $stdout = IO.popen('less -EKrX -b1', 'w') if $stdout == STDOUT
42
+ puts header if header
43
+ loop do
44
+ yield
45
+ sleep throttle
46
+ end
39
47
  rescue Errno::EPIPE
48
+ exit
40
49
  ensure
41
- $stdout.close_write
42
- $stdout = stdout
43
- print CURSOR[:show]
50
+ unless $stdout == STDOUT
51
+ $stdout.close_write
52
+ $stdout = STDOUT
53
+ print CURSOR[:show]
54
+ exit
55
+ end
44
56
  end
45
57
 
46
58
  def truncate string, reserved
@@ -49,11 +61,11 @@ module GHI
49
61
  result
50
62
  end
51
63
 
52
- def indent string, level = 4
64
+ def indent string, level = 4, maxwidth = columns
53
65
  string = string.gsub(/\r/, '')
54
66
  string.gsub!(/[\t ]+$/, '')
55
67
  string.gsub!(/\n{3,}/, "\n\n")
56
- width = columns - level - 1
68
+ width = maxwidth - level - 1
57
69
  lines = string.scan(
58
70
  /.{0,#{width}}(?:\s|\Z)|[\S]{#{width},}/ # TODO: Test long lines.
59
71
  ).map { |line| " " * level + line.chomp }
@@ -77,7 +89,12 @@ module GHI
77
89
  header = "# #{repo || 'Global,'} #{state} issues"
78
90
  if repo
79
91
  if milestone = assigns[:milestone]
80
- header.sub! repo, "#{repo} milestone ##{milestone}"
92
+ case milestone
93
+ when '*' then header << ' with a milestone'
94
+ when 'none' then header << ' without a milestone'
95
+ else
96
+ header.sub! repo, "#{repo} milestone ##{milestone}"
97
+ end
81
98
  end
82
99
  if assignee = assigns[:assignee]
83
100
  header << case assignee
@@ -143,11 +160,11 @@ module GHI
143
160
  end
144
161
 
145
162
  # TODO: Show milestone, number of comments, pull request attached.
146
- def format_issue i
163
+ def format_issue i, width = columns
147
164
  ERB.new(<<EOF).result binding
148
- <% p = i['pull_request'].key? 'html_url' %>\
149
- <%= bright { \
150
- indent '%s%s: %s' % [p ? '↑' : '#', *i.values_at('number', 'title')], 0 } %>
165
+ <% p = i['pull_request']['html_url'] %>\
166
+ <%= bright { no_color { indent '%s%s: %s' % [p ? '↑' : '#', \
167
+ *i.values_at('number', 'title')], 0, width } } %>
151
168
  @<%= i['user']['login'] %> opened this <%= p ? 'pull request' : 'issue' %> \
152
169
  <%= format_date DateTime.parse(i['created_at']) %>. \
153
170
  <%= format_state i['state'], format_tag(i['state']), :bg %>\
@@ -155,7 +172,7 @@ indent '%s%s: %s' % [p ? '↑' : '#', *i.values_at('number', 'title')], 0 } %>
155
172
  <% if i['assignee'] %>@<%= i['assignee']['login'] %> is assigned. <% end %>\
156
173
  <% unless i['labels'].empty? %><%= format_labels(i['labels']) %><% end %>\
157
174
  <% end %>
158
- <% if i['body'] && !i['body'].empty? %>\n<%= indent i['body'] %>
175
+ <% if i['body'] && !i['body'].empty? %>\n<%= indent i['body'], 4, width %>
159
176
  <% end %>
160
177
 
161
178
  EOF
@@ -166,11 +183,11 @@ EOF
166
183
  comments.map { |comment| format_comment comment }
167
184
  end
168
185
 
169
- def format_comment c
186
+ def format_comment c, width = columns
170
187
  <<EOF
171
188
  @#{c['user']['login']} commented \
172
189
  #{format_date DateTime.parse(c['created_at'])}:
173
- #{indent c['body']}
190
+ #{indent c['body'], 4, width}
174
191
 
175
192
 
176
193
  EOF
@@ -196,7 +213,8 @@ EOF
196
213
 
197
214
  def format_milestone m
198
215
  ERB.new(<<EOF).result binding
199
- <%= bright { indent '#%s: %s' % m.values_at('number', 'title'), 0 } %>
216
+ <%= bright { no_color { \
217
+ indent '#%s: %s' % m.values_at('number', 'title'), 0 } } %>
200
218
  @<%= m['creator']['login'] %> created this milestone <%= m['created_at'] %>. \
201
219
  <%= format_state m['state'], format_tag(m['state']), :bg %>
202
220
  <% if m['due_on'] %>\
@@ -204,13 +222,14 @@ EOF
204
222
  Due <%= fg((:red if due_on <= DateTime.now)) { format_date due_on } %>.
205
223
  <% end %>\
206
224
  <% if m['description'] && !m['description'].empty? %>
207
- <%= indent m['description'] %>\
225
+ <%= indent m['description'] %>
208
226
  <% end %>
227
+
209
228
  EOF
210
229
  end
211
230
 
212
231
  def format_state state, string = state, layer = :fg
213
- send(layer, state == 'closed' ? :red : :green) { string }
232
+ send(layer, state == 'closed' ? 'ff0000' : '2cc200') { string }
214
233
  end
215
234
 
216
235
  def format_labels labels
@@ -222,12 +241,62 @@ EOF
222
241
  (colorize? ? ' %s ' : '[%s]') % tag
223
242
  end
224
243
 
244
+ #--
245
+ # Helpers:
246
+ #++
247
+
248
+ def format_editor issue = nil
249
+ message = ERB.new(<<EOF).result binding
250
+
251
+ Please explain the issue. The first line will become the title. Lines
252
+ starting with '#' will be ignored, and empty messages will not be filed.
253
+ Issues are formatted with GitHub Flavored Markdown (GFM):
254
+
255
+ http://github.github.com/github-flavored-markdown
256
+
257
+ On <%= repo %>
258
+
259
+ <%= no_color { format_issue issue, columns - 2 if issue } %>
260
+ EOF
261
+ message.rstrip!
262
+ message.gsub!(/(?!\A)^.*$/) { |line| "# #{line}".rstrip }
263
+ message.insert 0, [issue['title'], issue['body']].join("\n\n") if issue
264
+ message
265
+ end
266
+
267
+ def format_comment_editor issue, comment = nil
268
+ message = ERB.new(<<EOF).result binding
269
+
270
+ Leave a comment. Lines starting with '#' will be ignored, and empty messages
271
+ will not be sent. Comments are formatted with GitHub Flavored Markdown (GFM):
272
+
273
+ http://github.github.com/github-flavored-markdown
274
+
275
+ On <%= repo %> issue #<%= issue %>
276
+
277
+ <%= no_color { format_comment comment, columns - 2 } if comment %>
278
+ EOF
279
+ message.rstrip!
280
+ message.gsub!(/(?!\A)^.*$/) { |line| "# #{line}".rstrip }
281
+ message.insert 0, comment['body'] if comment
282
+ message
283
+ end
284
+
225
285
  def format_markdown string, indent = 4
286
+ c = '268bd2'
287
+
226
288
  # Headers.
227
289
  string.gsub!(/^( {#{indent}}\#{1,6} .+)$/, bright{'\1'})
228
290
  string.gsub!(
229
291
  /(^ {#{indent}}.+$\n^ {#{indent}}[-=]+$)/, bright{'\1'}
230
292
  )
293
+ # Strong.
294
+ string.gsub!(
295
+ /(^|\s)(\*{2}\w(?:[^*]*\w)?\*{2})(\s|$)/m, '\1' + bright{'\2'} + '\3'
296
+ )
297
+ string.gsub!(
298
+ /(^|\s)(_{2}\w(?:[^_]*\w)?_{2})(\s|$)/m, '\1' + bright {'\2'} + '\3'
299
+ )
231
300
  # Emphasis.
232
301
  string.gsub!(
233
302
  /(^|\s)(\*\w(?:[^*]*\w)?\*)(\s|$)/m, '\1' + underline{'\2'} + '\3'
@@ -235,33 +304,32 @@ EOF
235
304
  string.gsub!(
236
305
  /(^|\s)(_\w(?:[^_]*\w)?_)(\s|$)/m, '\1' + underline{'\2'} + '\3'
237
306
  )
238
- # Strong.
239
- string.gsub!(
240
- /(^|\s)(\*{2}\w(?:[^*]*\w)?\*{2})(\s|$)/m, '\1' + bright{'\2'} + '\3'
241
- )
307
+ # Bullets/Blockquotes.
308
+ string.gsub!(/(^ {#{indent}}(?:[*>-]|\d+\.) )/, fg(c){'\1'})
309
+ # URIs.
242
310
  string.gsub!(
243
- /(^|\s)(_{2}\w(?:[^_]*\w)?_{2})(\s|$)/m, '\1' + bright {'\2'} + '\3'
311
+ %r{\b(<)?(https?://\S+|[^@\s]+@[^@\s]+)(>)?\b},
312
+ fg(c){'\1' + underline{'\2'} + '\3'}
244
313
  )
245
314
  # Code.
246
315
  string.gsub!(
247
316
  /
248
317
  (^\ {#{indent}}```.*?$)(.+?^\ {#{indent}}```$)|
249
318
  (^|[^`])(`[^`]+`)([^`]|$)
250
- /mx,
251
- fg(c = '268bd2'){'\1'} + bg(c){'\2'} + '\3' + fg(c){'\4'} + '\5'
252
- )
253
- # URI.
254
- string.gsub!(
255
- %r{\b(<)?(https?://[\s]+|\w+@\w+)(>)?\b},
256
- '\1' + underline{'\2'} + '\3'
257
- )
319
+ /mx
320
+ ) {
321
+ post = $5
322
+ fg(c){"#$1#$2#$3#$4".gsub(/\e\[[\d;]+m/, '')} + "#{post}"
323
+ }
258
324
  string
259
325
  end
260
326
 
261
327
  def format_date date
262
328
  days = (interval = DateTime.now - date).to_i.abs
263
329
  string = if days.zero?
264
- hours, minutes, seconds = DateTime.day_fraction_to_time interval.abs
330
+ seconds, _ = interval.divmod Rational(1, 86400)
331
+ hours, seconds = seconds.divmod 3600
332
+ minutes, seconds = seconds.divmod 60
265
333
  if hours > 0
266
334
  "#{hours} hour#{'s' unless hours == 1}"
267
335
  elsif minutes > 0
@@ -276,7 +344,7 @@ EOF
276
344
  end
277
345
 
278
346
  def throb position = 0, redraw = CURSOR[:up][1]
279
- return yield unless STDOUT.tty?
347
+ return yield unless $stdout.tty?
280
348
 
281
349
  throb = THROBBERS[rand(THROBBERS.length)]
282
350
  throb.reverse! if rand > 0.5
metadata CHANGED
@@ -1,14 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ghi
3
3
  version: !ruby/object:Gem::Version
4
- hash: -1101288818
4
+ hash: -4071658224
5
5
  prerelease: 6
6
6
  segments:
7
7
  - 0
8
8
  - 9
9
9
  - 0
10
10
  - dev
11
- version: 0.9.0.dev
11
+ - 1
12
+ version: 0.9.0.dev1
12
13
  platform: ruby
13
14
  authors:
14
15
  - Stephen Celis
@@ -16,7 +17,7 @@ autorequire:
16
17
  bindir: bin
17
18
  cert_chain: []
18
19
 
19
- date: 2012-04-04 00:00:00 -07:00
20
+ date: 2012-04-05 00:00:00 -07:00
20
21
  default_executable:
21
22
  dependencies: []
22
23
 
@@ -47,6 +48,7 @@ files:
47
48
  - lib/ghi/commands/show.rb
48
49
  - lib/ghi/commands/version.rb
49
50
  - lib/ghi/commands.rb
51
+ - lib/ghi/editor.rb
50
52
  - lib/ghi/formatting/colors.rb
51
53
  - lib/ghi/formatting.rb
52
54
  - lib/ghi/json.rb