ghi 0.9.0.dev → 0.9.0.dev1

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/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