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 +1 -0
- data/lib/ghi/authorization.rb +1 -1
- data/lib/ghi/client.rb +2 -3
- data/lib/ghi/commands/close.rb +6 -5
- data/lib/ghi/commands/command.rb +0 -15
- data/lib/ghi/commands/comment.rb +15 -13
- data/lib/ghi/commands/edit.rb +22 -1
- data/lib/ghi/commands/label.rb +11 -3
- data/lib/ghi/commands/list.rb +10 -5
- data/lib/ghi/commands/milestone.rb +2 -3
- data/lib/ghi/commands/open.rb +13 -8
- data/lib/ghi/commands/show.rb +10 -4
- data/lib/ghi/commands/version.rb +1 -1
- data/lib/ghi/editor.rb +26 -0
- data/lib/ghi/formatting.rb +100 -32
- metadata +5 -3
data/lib/ghi.rb
CHANGED
data/lib/ghi/authorization.rb
CHANGED
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
|
-
|
6
|
-
|
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
|
data/lib/ghi/commands/close.rb
CHANGED
@@ -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
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
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
|
|
data/lib/ghi/commands/command.rb
CHANGED
@@ -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
|
data/lib/ghi/commands/comment.rb
CHANGED
@@ -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
|
-
|
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
|
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
|
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
|
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 '
|
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]
|
98
|
-
|
99
|
-
|
100
|
-
|
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
|
data/lib/ghi/commands/edit.rb
CHANGED
@@ -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
|
data/lib/ghi/commands/label.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
data/lib/ghi/commands/list.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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'
|
data/lib/ghi/commands/open.rb
CHANGED
@@ -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.
|
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
|
-
|
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?
|
59
|
-
|
60
|
-
abort
|
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)
|
data/lib/ghi/commands/show.rb
CHANGED
@@ -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>
|
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
|
-
|
16
|
-
|
17
|
-
|
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
|
data/lib/ghi/commands/version.rb
CHANGED
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
|
data/lib/ghi/formatting.rb
CHANGED
@@ -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
|
42
|
-
|
43
|
-
|
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 =
|
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
|
-
|
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']
|
149
|
-
<%= bright { \
|
150
|
-
|
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 {
|
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' ?
|
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
|
-
#
|
239
|
-
string.gsub!(
|
240
|
-
|
241
|
-
)
|
307
|
+
# Bullets/Blockquotes.
|
308
|
+
string.gsub!(/(^ {#{indent}}(?:[*>-]|\d+\.) )/, fg(c){'\1'})
|
309
|
+
# URIs.
|
242
310
|
string.gsub!(
|
243
|
-
|
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
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
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
|
-
|
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
|
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: -
|
4
|
+
hash: -4071658224
|
5
5
|
prerelease: 6
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 9
|
9
9
|
- 0
|
10
10
|
- dev
|
11
|
-
|
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-
|
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
|