twig 1.2.1 → 1.3

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/HISTORY.md CHANGED
@@ -1,9 +1,22 @@
1
1
  Twig
2
2
  ====
3
3
 
4
+ 1.3 (2013-05-22)
5
+ ----------------
6
+ * ENHANCEMENT: Add `--branch-width` and `--<property>-width` options for setting
7
+ custom column widths.
8
+ * ENHANCEMENT: Add `--reverse` option for listing least recently updated
9
+ branches first. This can be used in a config file as `reverse: true`.
10
+ * ENHANCEMENT: Make `gh-open` and `gh-open-issue` work cross-platform.
11
+ (GH-18. Thanks [ixti](https://github.com/ixti)!)
12
+ * FIX: Allow getting, setting, and unsetting properties for branches older than
13
+ the `max-days-old` option, if given.
14
+ * FIX: Abort `twig gh-*` subcommands early if working in a non-Github
15
+ repository.
16
+
4
17
  1.2.1 (2013-05-04)
5
18
  ------------------
6
- * FIX: Add User-Agent string to `twig-gh-update` GitHub requests to comply with
19
+ * FIX: Add User-Agent string to `twig gh-update` GitHub requests to comply with
7
20
  GitHub API v3.
8
21
 
9
22
  1.2 (2013-03-21)
data/README.md CHANGED
@@ -43,13 +43,21 @@ chronologically with their properties.
43
43
  * `twig <property> -b <branch>`: Get property for any branch
44
44
  * `twig <property> <value> -b <branch>`: Set property for any branch
45
45
  * `twig --unset <property> -b <branch>`: Unset property for any branch
46
- * `twig --header-style <format>`: Change the header style, e.g., "red", "green bold"
47
46
  * `twig init-completion`: Set up tab completion for `-b` and `--branch`
48
47
  * `twig --help`: More info
49
48
 
50
49
 
51
- Filtering branches
52
- ------------------
50
+ Display options
51
+ ---------------
52
+
53
+ * `twig --header-style <format>`: Change the header style, e.g., "red", "green bold"
54
+ * `twig --branch-width <number>`: Set the character width for the `branch` column
55
+ * `twig --<property>-width <number>`: Set the character width for a specific property column
56
+ * `twig --reverse`: List oldest branches first
57
+
58
+
59
+ Filtering options
60
+ -----------------
53
61
 
54
62
  Twig lists all of your branches by default (newest first), but you can filter
55
63
  them by age, name, and custom properties:
@@ -154,7 +162,6 @@ a browser window if possible:
154
162
 
155
163
  $ twig gh-open
156
164
  GitHub URL: https://github.com/myname/myproject
157
- # Also opens a browser window (OS X only).
158
165
 
159
166
  If you're working on an issue for a GitHub repository, the `gh-update`
160
167
  subcommand syncs issue statuses with GitHub:
@@ -193,7 +200,6 @@ subcommand to view that issue on GitHub:
193
200
  # Current branch:
194
201
  $ twig gh-open-issue
195
202
  GitHub issue URL: https://github.com/myname/myproject/issues/111
196
- # Also opens a browser window (OS X only).
197
203
 
198
204
  # Any branch:
199
205
  $ twig gh-open-issue -b <branch name>
data/bin/twig CHANGED
@@ -2,13 +2,13 @@
2
2
 
3
3
  require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'twig'))
4
4
 
5
- twig = Twig.new
6
5
  repo_required = ARGV != ['--help'] && ARGV != ['help'] && ARGV != ['--version']
7
6
 
8
- if repo_required && !twig.repo?
7
+ if repo_required && !Twig.repo?
9
8
  abort "Current directory is not a git repository."
10
9
  end
11
10
 
12
11
  # Gettin' twiggy wit' it.
12
+ twig = Twig.new
13
13
  twig.read_config_file!
14
14
  twig.read_cli_args!(ARGV)
data/bin/twig-gh-open CHANGED
@@ -5,10 +5,11 @@
5
5
  # Author: Ron DeVera <http://rondevera.com>
6
6
 
7
7
  require 'rubygems'
8
- require 'twig/github'
8
+ require 'twig'
9
+ require 'launchy'
9
10
 
10
11
  Twig::GithubRepo.new do |gh_repo|
11
12
  url = "https://github.com/#{gh_repo.username}/#{gh_repo.repository}"
12
13
  puts "GitHub URL: #{url}"
13
- `which open && open #{url}`
14
+ Launchy.open(url) rescue nil
14
15
  end
@@ -10,6 +10,7 @@
10
10
 
11
11
  require 'rubygems'
12
12
  require 'twig'
13
+ require 'launchy'
13
14
 
14
15
  Twig::GithubRepo.new do |gh_repo|
15
16
  twig = Twig.new
@@ -25,5 +26,5 @@ Twig::GithubRepo.new do |gh_repo|
25
26
  url << "/issues/#{issue_id}"
26
27
 
27
28
  puts "GitHub issue URL: #{url}"
28
- `which open && open #{url}`
29
+ Launchy.open(url) rescue nil
29
30
  end
data/lib/twig.rb CHANGED
@@ -18,6 +18,11 @@ class Twig
18
18
  `#{command}`.strip
19
19
  end
20
20
 
21
+ def self.repo?
22
+ Twig.run('git rev-parse 2>&1')
23
+ $?.success?
24
+ end
25
+
21
26
  def initialize
22
27
  self.options = {}
23
28
 
@@ -25,14 +30,8 @@ class Twig
25
30
  set_option(:header_style, DEFAULT_HEADER_COLOR.to_s)
26
31
  end
27
32
 
28
- def repo?
29
- Twig.run('git rev-parse 2>&1')
30
- $?.success?
31
- end
32
-
33
33
  def current_branch_name
34
- @_current_branch_name ||=
35
- Twig.run('git symbolic-ref -q HEAD').sub(%r{^#{ REF_PREFIX }}, '')
34
+ @_current_branch_name ||= Twig.run('git rev-parse --abbrev-ref HEAD')
36
35
  end
37
36
 
38
37
  def all_branches
@@ -84,8 +83,8 @@ class Twig
84
83
  end
85
84
  end
86
85
 
87
- def branch_names
88
- branches.map { |branch| branch.name }
86
+ def all_branch_names
87
+ all_branches.map { |branch| branch.name }
89
88
  end
90
89
 
91
90
 
@@ -103,9 +102,11 @@ class Twig
103
102
 
104
103
  out = "\n" << branch_list_headers(options)
105
104
 
106
- # List most recently modified branches first
107
- listable_branches =
108
- branches.sort_by { |branch| branch.last_commit_time }.reverse
105
+ # List least recently modified branches first
106
+ listable_branches = branches.sort_by { |branch| branch.last_commit_time }
107
+ if options[:reverse] != true
108
+ listable_branches.reverse! # List most recently modified branches first
109
+ end
109
110
 
110
111
  branch_lines = listable_branches.inject([]) do |result, branch|
111
112
  result << branch_list_line(branch)
data/lib/twig/cli.rb CHANGED
@@ -12,7 +12,7 @@ class Twig
12
12
  for your Git branches.
13
13
  })
14
14
 
15
- <<-BANNER.gsub(/^[ ]+/, '')
15
+ intro = <<-BANNER.gsub(/^[ ]+/, '')
16
16
 
17
17
  #{version_string}
18
18
  #{'-' * version_string.size}
@@ -20,8 +20,9 @@ class Twig
20
20
  #{intro}
21
21
 
22
22
  #{Twig::HOMEPAGE}
23
-
24
23
  BANNER
24
+
25
+ intro + ' ' # Force extra blank line
25
26
  end
26
27
 
27
28
  def help_separator(option_parser, text, options={})
@@ -54,11 +55,47 @@ class Twig
54
55
  lines
55
56
  end
56
57
 
58
+ def help_description_for_custom_property(option_parser, desc_lines)
59
+ indent = ' '
60
+ left_column_width = 29
61
+
62
+ help_desc = desc_lines.inject('') do |desc, (left_column, right_column)|
63
+ desc + indent +
64
+ sprintf("%-#{left_column_width}s", left_column) + right_column + "\n"
65
+ end
66
+
67
+ help_separator(option_parser, help_desc, :trailing => "\n")
68
+ end
69
+
57
70
  def help_paragraph(text)
58
71
  help_description(text, :width => 80).join("\n")
59
72
  end
60
73
 
74
+ def help_line_for_custom_property?(line)
75
+ is_custom_property_except = (
76
+ line.include?('--except-') &&
77
+ !line.include?('--except-branch') &&
78
+ !line.include?('--except-PROPERTY')
79
+ )
80
+ is_custom_property_only = (
81
+ line.include?('--only-') &&
82
+ !line.include?('--only-branch') &&
83
+ !line.include?('--only-PROPERTY')
84
+ )
85
+ is_custom_property_width = (
86
+ line =~ /--.+-width/ &&
87
+ !line.include?('--branch-width') &&
88
+ !line.include?('--PROPERTY-width')
89
+ )
90
+
91
+ is_custom_property_except ||
92
+ is_custom_property_only ||
93
+ is_custom_property_width
94
+ end
95
+
61
96
  def read_cli_options!(args)
97
+ custom_properties = Twig::Branch.all_properties
98
+
62
99
  option_parser = OptionParser.new do |opts|
63
100
  opts.banner = help_intro
64
101
  opts.summary_indent = ' ' * 2
@@ -82,22 +119,17 @@ class Twig
82
119
 
83
120
  desc = 'Show this help content.'
84
121
  opts.on('--help', *help_description(desc)) do
85
- summary_lines = opts.to_a
122
+ summary_lines = opts.to_s.split("\n")
86
123
 
87
- # Filter out `--only-PROPERTY` lines
124
+ # Filter out custom property lines
125
+ prev_line = nil
88
126
  summary_lines.each do |line|
89
- is_custom_property_only = (
90
- line.include?('--only-') &&
91
- !line.include?('--only-branch') &&
92
- !line.include?('--only-PROPERTY')
93
- )
94
- is_custom_property_except = (
95
- line.include?('--except-') &&
96
- !line.include?('--except-branch') &&
97
- !line.include?('--except-PROPERTY')
98
- )
99
- unless is_custom_property_only || is_custom_property_except
127
+ # Squash successive blank lines
128
+ next if line == "\n" && prev_line == "\n"
129
+
130
+ unless help_line_for_custom_property?(line)
100
131
  puts line
132
+ prev_line = line
101
133
  end
102
134
  end
103
135
 
@@ -129,16 +161,10 @@ class Twig
129
161
  end
130
162
 
131
163
  desc = 'Do not list branches whose name matches a given pattern.'
132
- opts.on(
133
- '--except-branch PATTERN',
134
- *help_description(desc, :add_separator => false)
135
- # `:add_separator => false` skips the extra line generated by the
136
- # custom property filters below.
137
- ) do |pattern|
164
+ opts.on('--except-branch PATTERN', *help_description(desc)) do |pattern|
138
165
  set_option(:property_except, :branch => pattern)
139
166
  end
140
167
 
141
- custom_properties = Twig::Branch.all_properties
142
168
  custom_properties.each do |property_name|
143
169
  opts.on("--only-#{property_name} PATTERN") do |pattern|
144
170
  set_option(:property_only, property_name.to_sym => pattern)
@@ -148,18 +174,14 @@ class Twig
148
174
  set_option(:property_except, property_name.to_sym => pattern)
149
175
  end
150
176
  end
151
-
152
- custom_properties_desc_lines = [
177
+ help_description_for_custom_property(opts, [
153
178
  ['--only-PROPERTY PATTERN', 'Only list branches with a given property'],
154
179
  ['', 'that matches a given pattern.'],
155
- ['', ''],
180
+ ])
181
+ help_description_for_custom_property(opts, [
156
182
  ['--except-PROPERTY PATTERN', 'Do not list branches with a given property'],
157
183
  ['', 'that matches a given pattern.']
158
- ]
159
- custom_properties_desc = custom_properties_desc_lines.inject('') do |desc, line_parts|
160
- desc + sprintf(' %-29s', line_parts[0]) + line_parts[1] + "\n"
161
- end
162
- help_separator(opts, custom_properties_desc, :trailing => "\n")
184
+ ])
163
185
 
164
186
  desc =
165
187
  'Lists all branches regardless of other filtering options. ' +
@@ -175,11 +197,25 @@ class Twig
175
197
 
176
198
  help_separator(opts, 'Listing branches:')
177
199
 
200
+ desc = 'Set the width for the `branch` column.'
201
+ opts.on('--branch-width NUMBER', *help_description(desc)) do |width|
202
+ set_option(:property_width, :branch => width)
203
+ end
204
+
205
+ custom_properties.each do |property_name|
206
+ opts.on("--#{property_name}-width NUMBER") do |width|
207
+ set_option(:property_width, property_name.to_sym => width)
208
+ end
209
+ end
210
+ help_description_for_custom_property(opts, [
211
+ ['--PROPERTY-width NUMBER', "Set the width for a given property's column."]
212
+ ])
213
+
178
214
  colors = Twig::Display::COLORS.keys.map do |value|
179
- format_string(value, { :color => value })
215
+ format_string(value, :color => value)
180
216
  end.join(', ')
181
217
  weights = Twig::Display::WEIGHTS.keys.map do |value|
182
- format_string(value, { :weight => value })
218
+ format_string(value, :weight => value)
183
219
  end.join(' and ')
184
220
  default_color = format_string(
185
221
  Twig::DEFAULT_HEADER_COLOR.to_s,
@@ -190,10 +226,18 @@ class Twig
190
226
  Valid colors are #{colors}. Valid weights are #{weights}.
191
227
  The default is "#{default_color}".
192
228
  DESC
193
- opts.on('--header-style "STYLE"', *help_description(desc)) do |style|
229
+ opts.on(
230
+ '--header-style "STYLE"',
231
+ *help_description(desc, :add_separator => true)
232
+ ) do |style|
194
233
  set_option(:header_style, style)
195
234
  end
196
235
 
236
+ desc = 'Show oldest branches first.'
237
+ opts.on('--reverse', *help_description(desc)) do
238
+ set_option(:reverse, true)
239
+ end
240
+
197
241
 
198
242
 
199
243
  help_separator(opts, help_paragraph(%{
data/lib/twig/display.rb CHANGED
@@ -14,41 +14,58 @@ class Twig
14
14
  :normal => 0,
15
15
  :bold => 1
16
16
  }
17
+ DEFAULT_PROPERTY_COLUMN_WIDTH = 16
18
+ DEFAULT_BRANCH_COLUMN_WIDTH = 48
17
19
  CURRENT_BRANCH_INDICATOR = '* '
18
20
  EMPTY_BRANCH_PROPERTY_INDICATOR = '-'
19
21
 
20
- def column(string = ' ', num_columns = 1, column_options = {})
22
+ def column(string, options = {})
21
23
  # Returns `string` with an exact fixed width. If `string` is too wide, it
22
24
  # is truncated with an ellipsis and a trailing space to separate columns.
23
25
  #
24
- # `column_options`:
26
+ # `options`:
25
27
  # - `:color`: `nil` by default. Accepts a key from `COLORS`.
26
28
  # - `:weight`: `nil` by default. Accepts a key from `WEIGHTS`.
27
29
  # - `:width`: 8 (characters) by default.
28
30
 
29
- width_per_column = column_options[:width] || 8
30
- total_width = num_columns * width_per_column
31
- new_string = string[0, total_width]
32
- omission = '... '
31
+ string ||= ' '
32
+ width = options[:width] || 8
33
+ new_string = string[0, width]
34
+ omission = '...'
33
35
 
34
- if string.size >= total_width
36
+ if string.size > width
35
37
  new_string[-omission.size, omission.size] = omission
36
38
  else
37
- new_string = ' ' * total_width
39
+ new_string = ' ' * width
38
40
  new_string[0, string.size] = string
39
41
  end
40
42
 
41
43
  new_string = format_string(
42
44
  new_string,
43
- column_options.reject { |k, v| ![:color, :weight].include?(k) }
45
+ options.reject { |k, v| ![:color, :weight].include?(k) }
44
46
  )
45
47
 
46
48
  new_string
47
49
  end
48
50
 
51
+ def date_time_column_width; 35; end
52
+ def column_gutter; ' '; end
53
+
54
+ def property_column_width(property_name = nil)
55
+ if property_name && options[:property_width]
56
+ width = options[:property_width][property_name.to_sym]
57
+ end
58
+
59
+ if width
60
+ width
61
+ elsif property_name == :branch
62
+ Twig::Display::DEFAULT_BRANCH_COLUMN_WIDTH
63
+ else
64
+ Twig::Display::DEFAULT_PROPERTY_COLUMN_WIDTH
65
+ end
66
+ end
67
+
49
68
  def branch_list_headers(header_options = {})
50
- columns_for_date_time = 5
51
- columns_per_property = 2
52
69
  branch_indicator_padding = ' ' * CURRENT_BRANCH_INDICATOR.size
53
70
 
54
71
  header_options.merge!(
@@ -62,22 +79,22 @@ class Twig
62
79
  end
63
80
  )
64
81
 
65
- out =
66
- column(' ', columns_for_date_time) <<
67
- Twig::Branch.all_properties.map do |property|
68
- column(property, columns_per_property, header_options)
69
- end.join <<
70
- column(branch_indicator_padding + 'branch',
71
- columns_per_property, header_options) <<
72
- "\n"
73
- out <<
74
- column(' ', columns_for_date_time) <<
75
- Twig::Branch.all_properties.map do |property|
76
- column('-' * property.size, columns_per_property, header_options)
77
- end.join <<
78
- column(branch_indicator_padding + '------',
79
- columns_per_property, header_options) <<
80
- "\n"
82
+ out = column(' ', :width => date_time_column_width) << column_gutter
83
+ out << Twig::Branch.all_properties.map do |property|
84
+ width = property_column_width(property)
85
+ column(property, header_options.merge(:width => width)) << column_gutter
86
+ end.join
87
+ out << column(branch_indicator_padding + 'branch', header_options)
88
+ out << "\n"
89
+
90
+ out << column(' ', :width => date_time_column_width) << column_gutter
91
+ out << Twig::Branch.all_properties.map do |property|
92
+ width = property_column_width(property)
93
+ underline = '-' * property.size
94
+ column(underline, header_options.merge(:width => width)) << column_gutter
95
+ end.join
96
+ out << column(branch_indicator_padding + '------', header_options)
97
+ out << "\n"
81
98
 
82
99
  out
83
100
  end
@@ -87,24 +104,29 @@ class Twig
87
104
 
88
105
  properties = Twig::Branch.all_properties.inject({}) do |result, property_name|
89
106
  property = (get_branch_property(branch.name, property_name) || '').strip
90
- property = column(EMPTY_BRANCH_PROPERTY_INDICATOR) if property.empty?
107
+ property = EMPTY_BRANCH_PROPERTY_INDICATOR if property.empty?
91
108
  property.gsub!(/[\n\r]+/, ' ')
92
109
  result.merge(property_name => property)
93
110
  end
94
111
 
95
- line = column(branch.last_commit_time.to_s, 5)
112
+ line = column(branch.last_commit_time.to_s, :width => date_time_column_width)
113
+ line << column_gutter
96
114
 
97
115
  line <<
98
116
  Twig::Branch.all_properties.map do |property_name|
99
- property = properties[property_name] || ''
100
- column(property, 2)
117
+ property_value = properties[property_name] || ''
118
+ width = property_column_width(property_name)
119
+ column(property_value, :width => width) << column_gutter
101
120
  end.join
102
121
 
122
+ branch_column_width = property_column_width(:branch)
123
+ branch_column = column(branch.to_s, :width => branch_column_width)
124
+ branch_column.strip! # Strip final column
103
125
  line <<
104
126
  if is_current_branch
105
- CURRENT_BRANCH_INDICATOR + branch.to_s
127
+ CURRENT_BRANCH_INDICATOR + branch_column
106
128
  else
107
- (' ' * CURRENT_BRANCH_INDICATOR.size) + branch.to_s
129
+ (' ' * CURRENT_BRANCH_INDICATOR.size) + branch_column
108
130
  end
109
131
 
110
132
  line = format_string(line, :weight => :bold) if is_current_branch