rfix 2.0.4 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. checksums.yaml +4 -4
  2. data/exe/rfix +11 -90
  3. data/lib/rfix.rb +10 -9
  4. data/lib/rfix/branch/reference.rb +2 -2
  5. data/lib/rfix/branch/upstream.rb +2 -4
  6. data/lib/rfix/cli/command.rb +14 -1
  7. data/lib/rfix/cli/command/all.rb +21 -0
  8. data/lib/rfix/cli/command/base.rb +30 -19
  9. data/lib/rfix/cli/command/branch.rb +2 -0
  10. data/lib/rfix/cli/command/help.rb +2 -0
  11. data/lib/rfix/cli/command/info.rb +6 -1
  12. data/lib/rfix/cli/command/local.rb +2 -0
  13. data/lib/rfix/cli/command/origin.rb +2 -0
  14. data/lib/rfix/cli/command/setup.rb +2 -0
  15. data/lib/rfix/cli/command/status.rb +39 -0
  16. data/lib/rfix/collector.rb +69 -0
  17. data/lib/rfix/diff.rb +69 -0
  18. data/lib/rfix/extension/comment_config.rb +15 -0
  19. data/lib/rfix/extension/offense.rb +17 -14
  20. data/lib/rfix/extension/pastel.rb +7 -4
  21. data/lib/rfix/extension/progresbar.rb +15 -0
  22. data/lib/rfix/extension/strings.rb +10 -2
  23. data/lib/rfix/file.rb +5 -3
  24. data/lib/rfix/file/base.rb +21 -14
  25. data/lib/rfix/file/deleted.rb +2 -0
  26. data/lib/rfix/file/ignored.rb +2 -0
  27. data/lib/rfix/file/null.rb +17 -0
  28. data/lib/rfix/file/tracked.rb +39 -23
  29. data/lib/rfix/file/undefined.rb +17 -0
  30. data/lib/rfix/file/untracked.rb +3 -1
  31. data/lib/rfix/formatter.rb +67 -71
  32. data/lib/rfix/highlighter.rb +1 -3
  33. data/lib/rfix/rake/gemfile.rb +26 -23
  34. data/lib/rfix/repository.rb +59 -96
  35. data/lib/rfix/types.rb +24 -14
  36. data/lib/rfix/version.rb +1 -1
  37. data/rfix.gemspec +11 -3
  38. data/vendor/cli-ui/Gemfile +17 -0
  39. data/vendor/cli-ui/Gemfile.lock +60 -0
  40. data/vendor/cli-ui/LICENSE.txt +21 -0
  41. data/vendor/cli-ui/README.md +224 -0
  42. data/vendor/cli-ui/Rakefile +20 -0
  43. data/vendor/cli-ui/bin/console +14 -0
  44. data/vendor/cli-ui/cli-ui.gemspec +25 -0
  45. data/vendor/cli-ui/dev.yml +14 -0
  46. data/vendor/cli-ui/lib/cli/ui.rb +233 -0
  47. data/vendor/cli-ui/lib/cli/ui/ansi.rb +157 -0
  48. data/vendor/cli-ui/lib/cli/ui/color.rb +84 -0
  49. data/vendor/cli-ui/lib/cli/ui/formatter.rb +192 -0
  50. data/vendor/cli-ui/lib/cli/ui/frame.rb +269 -0
  51. data/vendor/cli-ui/lib/cli/ui/frame/frame_stack.rb +98 -0
  52. data/vendor/cli-ui/lib/cli/ui/frame/frame_style.rb +120 -0
  53. data/vendor/cli-ui/lib/cli/ui/frame/frame_style/box.rb +166 -0
  54. data/vendor/cli-ui/lib/cli/ui/frame/frame_style/bracket.rb +139 -0
  55. data/vendor/cli-ui/lib/cli/ui/glyph.rb +84 -0
  56. data/vendor/cli-ui/lib/cli/ui/os.rb +67 -0
  57. data/vendor/cli-ui/lib/cli/ui/printer.rb +59 -0
  58. data/vendor/cli-ui/lib/cli/ui/progress.rb +90 -0
  59. data/vendor/cli-ui/lib/cli/ui/prompt.rb +297 -0
  60. data/vendor/cli-ui/lib/cli/ui/prompt/interactive_options.rb +484 -0
  61. data/vendor/cli-ui/lib/cli/ui/prompt/options_handler.rb +29 -0
  62. data/vendor/cli-ui/lib/cli/ui/spinner.rb +66 -0
  63. data/vendor/cli-ui/lib/cli/ui/spinner/async.rb +40 -0
  64. data/vendor/cli-ui/lib/cli/ui/spinner/spin_group.rb +263 -0
  65. data/vendor/cli-ui/lib/cli/ui/stdout_router.rb +232 -0
  66. data/vendor/cli-ui/lib/cli/ui/terminal.rb +46 -0
  67. data/vendor/cli-ui/lib/cli/ui/truncater.rb +102 -0
  68. data/vendor/cli-ui/lib/cli/ui/version.rb +5 -0
  69. data/vendor/cli-ui/lib/cli/ui/widgets.rb +77 -0
  70. data/vendor/cli-ui/lib/cli/ui/widgets/base.rb +27 -0
  71. data/vendor/cli-ui/lib/cli/ui/widgets/status.rb +61 -0
  72. data/vendor/cli-ui/lib/cli/ui/wrap.rb +56 -0
  73. data/vendor/cli-ui/test/cli/ui/ansi_test.rb +32 -0
  74. data/vendor/cli-ui/test/cli/ui/cli_ui_test.rb +23 -0
  75. data/vendor/cli-ui/test/cli/ui/color_test.rb +40 -0
  76. data/vendor/cli-ui/test/cli/ui/formatter_test.rb +79 -0
  77. data/vendor/cli-ui/test/cli/ui/glyph_test.rb +68 -0
  78. data/vendor/cli-ui/test/cli/ui/printer_test.rb +103 -0
  79. data/vendor/cli-ui/test/cli/ui/progress_test.rb +46 -0
  80. data/vendor/cli-ui/test/cli/ui/prompt/options_handler_test.rb +39 -0
  81. data/vendor/cli-ui/test/cli/ui/prompt_test.rb +348 -0
  82. data/vendor/cli-ui/test/cli/ui/spinner/spin_group_test.rb +39 -0
  83. data/vendor/cli-ui/test/cli/ui/spinner_test.rb +141 -0
  84. data/vendor/cli-ui/test/cli/ui/stdout_router_test.rb +32 -0
  85. data/vendor/cli-ui/test/cli/ui/terminal_test.rb +26 -0
  86. data/vendor/cli-ui/test/cli/ui/truncater_test.rb +31 -0
  87. data/vendor/cli-ui/test/cli/ui/widgets/status_test.rb +49 -0
  88. data/vendor/cli-ui/test/cli/ui/widgets_test.rb +15 -0
  89. data/vendor/cli-ui/test/test_helper.rb +53 -0
  90. data/vendor/cli-ui/tmp/cache/bootsnap/compile-cache/d9/c036af0f3dc494 +0 -0
  91. data/vendor/cli-ui/tmp/cache/bootsnap/load-path-cache +0 -0
  92. data/vendor/dry-cli/lib/dry/cli/command.rb +2 -1
  93. data/vendor/dry-cli/tmp/cache/bootsnap/compile-cache/ff/a22a5daafbd74c +0 -0
  94. data/vendor/dry-cli/tmp/cache/bootsnap/load-path-cache +0 -0
  95. data/vendor/strings-ansi/tmp/cache/bootsnap/compile-cache/79/49cf49407b370e +0 -0
  96. data/vendor/strings-ansi/tmp/cache/bootsnap/load-path-cache +0 -0
  97. metadata +170 -9
  98. data/lib/rfix/extension/string.rb +0 -12
  99. data/lib/rfix/indicator.rb +0 -19
@@ -0,0 +1,46 @@
1
+ require 'cli/ui'
2
+ require 'io/console'
3
+
4
+ module CLI
5
+ module UI
6
+ module Terminal
7
+ DEFAULT_WIDTH = 80
8
+ DEFAULT_HEIGHT = 24
9
+
10
+ # Returns the width of the terminal, if possible
11
+ # Otherwise will return DEFAULT_WIDTH
12
+ #
13
+ def self.width
14
+ winsize[1]
15
+ end
16
+
17
+ # Returns the width of the terminal, if possible
18
+ # Otherwise, will return DEFAULT_HEIGHT
19
+ #
20
+ def self.height
21
+ winsize[0]
22
+ end
23
+
24
+ def self.winsize
25
+ @winsize ||= begin
26
+ winsize = IO.console.winsize
27
+ setup_winsize_trap
28
+
29
+ if winsize.any?(&:zero?)
30
+ [DEFAULT_HEIGHT, DEFAULT_WIDTH]
31
+ else
32
+ winsize
33
+ end
34
+ rescue
35
+ [DEFAULT_HEIGHT, DEFAULT_WIDTH]
36
+ end
37
+ end
38
+
39
+ def self.setup_winsize_trap
40
+ @winsize_trap ||= Signal.trap('WINCH') do
41
+ @winsize = nil
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cli/ui'
4
+
5
+ module CLI
6
+ module UI
7
+ # Truncater truncates a string to a provided printable width.
8
+ module Truncater
9
+ PARSE_ROOT = :root
10
+ PARSE_ANSI = :ansi
11
+ PARSE_ESC = :esc
12
+ PARSE_ZWJ = :zwj
13
+
14
+ ESC = 0x1b
15
+ LEFT_SQUARE_BRACKET = 0x5b
16
+ ZWJ = 0x200d # emojipedia.org/emoji-zwj-sequences
17
+ SEMICOLON = 0x3b
18
+
19
+ # EMOJI_RANGE in particular is super inaccurate. This is best-effort.
20
+ # If you need this to be more accurate, we'll almost certainly accept a
21
+ # PR improving it.
22
+ EMOJI_RANGE = 0x1f300..0x1f5ff
23
+ NUMERIC_RANGE = 0x30..0x39
24
+ LC_ALPHA_RANGE = 0x40..0x5a
25
+ UC_ALPHA_RANGE = 0x60..0x71
26
+
27
+ TRUNCATED = "\x1b[0m…"
28
+
29
+ class << self
30
+ def call(text, printing_width)
31
+ return text if text.size <= printing_width
32
+
33
+ width = 0
34
+ mode = PARSE_ROOT
35
+ truncation_index = nil
36
+
37
+ codepoints = text.codepoints
38
+ codepoints.each.with_index do |cp, index|
39
+ case mode
40
+ when PARSE_ROOT
41
+ case cp
42
+ when ESC # non-printable, followed by some more non-printables.
43
+ mode = PARSE_ESC
44
+ when ZWJ # non-printable, followed by another non-printable.
45
+ mode = PARSE_ZWJ
46
+ else
47
+ width += width(cp)
48
+ if width >= printing_width
49
+ truncation_index ||= index
50
+ # it looks like we could break here but we still want the
51
+ # width calculation for the rest of the characters.
52
+ end
53
+ end
54
+ when PARSE_ESC
55
+ mode = case cp
56
+ when LEFT_SQUARE_BRACKET
57
+ PARSE_ANSI
58
+ else
59
+ PARSE_ROOT
60
+ end
61
+ when PARSE_ANSI
62
+ # ANSI escape codes preeeetty much have the format of:
63
+ # \x1b[0-9;]+[A-Za-z]
64
+ case cp
65
+ when NUMERIC_RANGE, SEMICOLON
66
+ when LC_ALPHA_RANGE, UC_ALPHA_RANGE
67
+ mode = PARSE_ROOT
68
+ else
69
+ # unexpected. let's just go back to the root state I guess?
70
+ mode = PARSE_ROOT
71
+ end
72
+ when PARSE_ZWJ
73
+ # consume any character and consider it as having no width
74
+ # width(x+ZWJ+y) = width(x).
75
+ mode = PARSE_ROOT
76
+ end
77
+ end
78
+
79
+ # Without the `width <= printing_width` check, we truncate
80
+ # "foo\x1b[0m" for a width of 3, but it should not be truncated.
81
+ # It's specifically for the case where we decided "Yes, this is the
82
+ # point at which we'd have to add a truncation!" but it's actually
83
+ # the end of the string.
84
+ return text if !truncation_index || width <= printing_width
85
+
86
+ codepoints[0...truncation_index].pack('U*') + TRUNCATED
87
+ end
88
+
89
+ private
90
+
91
+ def width(printable_codepoint)
92
+ case printable_codepoint
93
+ when EMOJI_RANGE
94
+ 2
95
+ else
96
+ 1
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,5 @@
1
+ module CLI
2
+ module UI
3
+ VERSION = '1.5.1'
4
+ end
5
+ end
@@ -0,0 +1,77 @@
1
+ require('cli/ui')
2
+
3
+ module CLI
4
+ module UI
5
+ # Widgets are formatter objects with more custom implementations than the
6
+ # other features, which all center around formatting text with colours,
7
+ # etc.
8
+ #
9
+ # If you want to extend CLI::UI with your own widgets, you may want to do
10
+ # something like this:
11
+ #
12
+ # require('cli/ui')
13
+ # class MyWidget < CLI::UI::Widgets::Base
14
+ # # ...
15
+ # end
16
+ # CLI::UI::Widgets.register('my-widget') { MyWidget }
17
+ # puts(CLI::UI.fmt("{{@widget/my-widget:args}}"))
18
+ module Widgets
19
+ MAP = {}
20
+
21
+ autoload(:Base, 'cli/ui/widgets/base')
22
+
23
+ def self.register(name, &cb)
24
+ MAP[name] = cb
25
+ end
26
+
27
+ autoload(:Status, 'cli/ui/widgets/status')
28
+ register('status') { Widgets::Status }
29
+
30
+ # Looks up a widget by handle
31
+ #
32
+ # ==== Raises
33
+ # Raises InvalidWidgetHandle if the widget is not available.
34
+ #
35
+ # ==== Returns
36
+ # A callable widget, to be invoked like `.call(argstring)`
37
+ #
38
+ def self.lookup(handle)
39
+ MAP.fetch(handle.to_s).call
40
+ rescue KeyError, NameError
41
+ raise(InvalidWidgetHandle, handle)
42
+ end
43
+
44
+ # All available widgets by name
45
+ #
46
+ def self.available
47
+ MAP.keys
48
+ end
49
+
50
+ class InvalidWidgetHandle < ArgumentError
51
+ def initialize(handle)
52
+ super
53
+ @handle = handle
54
+ end
55
+
56
+ def message
57
+ keys = Widget.available.join(',')
58
+ "invalid widget handle: #{@handle} " \
59
+ "-- must be one of CLI::UI::Widgets.available (#{keys})"
60
+ end
61
+ end
62
+
63
+ class InvalidWidgetArguments < ArgumentError
64
+ def initialize(argstring, pattern)
65
+ super
66
+ @argstring = argstring
67
+ @pattern = pattern
68
+ end
69
+
70
+ def message
71
+ "invalid widget arguments: #{@argstring} " \
72
+ "-- must match pattern: #{@pattern.inspect}"
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,27 @@
1
+ require('cli/ui')
2
+
3
+ module CLI
4
+ module UI
5
+ module Widgets
6
+ class Base
7
+ def self.call(argstring)
8
+ new(argstring).render
9
+ end
10
+
11
+ def initialize(argstring)
12
+ pat = self.class.argparse_pattern
13
+ unless (@match_data = pat.match(argstring))
14
+ raise(Widgets::InvalidWidgetArguments.new(argstring, pat))
15
+ end
16
+ @match_data.names.each do |name|
17
+ instance_variable_set(:"@#{name}", @match_data[name])
18
+ end
19
+ end
20
+
21
+ def self.argparse_pattern
22
+ const_get(:ARGPARSE_PATTERN)
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,61 @@
1
+ # frozen-string-literal: true
2
+ require('cli/ui')
3
+
4
+ module CLI
5
+ module UI
6
+ module Widgets
7
+ class Status < Widgets::Base
8
+ ARGPARSE_PATTERN = %r{
9
+ \A (?<succeeded> \d+)
10
+ : (?<failed> \d+)
11
+ : (?<working> \d+)
12
+ : (?<pending> \d+) \z
13
+ }x # e.g. "1:23:3:404"
14
+ OPEN = Color::RESET.code + Color::BOLD.code + '[' + Color::RESET.code
15
+ CLOSE = Color::RESET.code + Color::BOLD.code + ']' + Color::RESET.code
16
+ ARROW = Color::RESET.code + Color::GRAY.code + '◂' + Color::RESET.code
17
+ COMMA = Color::RESET.code + Color::GRAY.code + ',' + Color::RESET.code
18
+
19
+ SPINNER_STOPPED = '⠿'
20
+ EMPTY_SET = '∅'
21
+
22
+ def render
23
+ if zero?(@succeeded) && zero?(@failed) && zero?(@working) && zero?(@pending)
24
+ Color::RESET.code + Color::BOLD.code + EMPTY_SET + Color::RESET.code
25
+ else
26
+ # [ 0✓ , 2✗ ◂ 3⠼ ◂ 4⌛︎ ]
27
+ "#{OPEN}#{succeeded_part}#{COMMA}#{failed_part}#{ARROW}#{working_part}#{ARROW}#{pending_part}#{CLOSE}"
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ def zero?(num_str)
34
+ num_str == '0'
35
+ end
36
+
37
+ def colorize_if_nonzero(num_str, rune, color)
38
+ color = Color::GRAY if zero?(num_str)
39
+ color.code + num_str + rune
40
+ end
41
+
42
+ def succeeded_part
43
+ colorize_if_nonzero(@succeeded, Glyph::CHECK.char, Color::GREEN)
44
+ end
45
+
46
+ def failed_part
47
+ colorize_if_nonzero(@failed, Glyph::X.char, Color::RED)
48
+ end
49
+
50
+ def working_part
51
+ rune = zero?(@working) ? SPINNER_STOPPED : Spinner.current_rune
52
+ colorize_if_nonzero(@working, rune, Color::BLUE)
53
+ end
54
+
55
+ def pending_part
56
+ colorize_if_nonzero(@pending, Glyph::HOURGLASS.char, Color::WHITE)
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,56 @@
1
+ # coding: utf-8
2
+ require 'cli/ui'
3
+ require 'cli/ui/frame/frame_stack'
4
+ require 'cli/ui/frame/frame_style'
5
+
6
+ module CLI
7
+ module UI
8
+ class Wrap
9
+ def initialize(input)
10
+ @input = input
11
+ end
12
+
13
+ def wrap
14
+ max_width = Terminal.width - Frame.prefix_width
15
+ width = 0
16
+ final = []
17
+ # Create an alternation of format codes of parameter lengths 1-20, since + and {1,n} not allowed in lookbehind
18
+ format_codes = (1..20).map { |n| /\x1b\[[\d;]{#{n}}m/ }.join('|')
19
+ codes = ''
20
+ @input.split(/(?=\s|\x1b\[[\d;]+m|\r)|(?<=\s|#{format_codes})/).each do |token|
21
+ case token
22
+ when '\x1B[0?m'
23
+ codes = ''
24
+ final << token
25
+ when /\x1b\[[\d;]+m/
26
+ codes += token # Track in use format codes so that they are resent after frame coloring
27
+ final << token
28
+ when "\n"
29
+ final << "\n#{codes}"
30
+ width = 0
31
+ when /\s/
32
+ token_width = ANSI.printing_width(token)
33
+ if width + token_width <= max_width
34
+ final << token
35
+ width += token_width
36
+ else
37
+ final << "\n#{codes}"
38
+ width = 0
39
+ end
40
+ else
41
+ token_width = ANSI.printing_width(token)
42
+ if width + token_width <= max_width
43
+ final << token
44
+ width += token_width
45
+ else
46
+ final << "\n#{codes}"
47
+ final << token
48
+ width = token_width
49
+ end
50
+ end
51
+ end
52
+ final.join
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,32 @@
1
+ require 'test_helper'
2
+
3
+ module CLI
4
+ module UI
5
+ class ANSITest < MiniTest::Test
6
+ def test_sgr
7
+ assert_equal("\x1b[1;34m", ANSI.sgr('1;34'))
8
+ end
9
+
10
+ def test_printing_width
11
+ assert_equal(4, ANSI.printing_width("\x1b[38;2;100;100;100mtest\x1b[0m"))
12
+ assert_equal(0, ANSI.printing_width(''))
13
+
14
+ assert_equal(3, ANSI.printing_width('>🔧<'))
15
+ assert_equal(1, ANSI.printing_width('👩‍💻'))
16
+ end
17
+
18
+ def test_line_skip_with_shift
19
+ next_line_expected = "\e[1B\e[1G"
20
+ previous_line_expected = "\e[1A\e[1G"
21
+
22
+ assert_equal(next_line_expected, ANSI.next_line)
23
+ assert_equal(previous_line_expected, ANSI.previous_line)
24
+
25
+ CLI::UI::OS.stubs(:current).returns(CLI::UI::OS::Windows)
26
+
27
+ assert_equal("#{next_line_expected}\e[1D", ANSI.next_line)
28
+ assert_equal("#{previous_line_expected}\e[1D", ANSI.previous_line)
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,23 @@
1
+ require 'test_helper'
2
+
3
+ module CLI
4
+ class UITest < MiniTest::Test
5
+ def test_resolve_test
6
+ input = 'a{{blue:b {{*}}{{bold:c {{red:d}}}}{{bold: e}}}} f'
7
+ expected = "\e[0ma\e[0;94mb \e[0;33m⭑\e[0;94;1mc \e[0;94;1;31md\e[0;94;1m e\e[0m f"
8
+ actual = CLI::UI.resolve_text(input)
9
+ assert_equal(expected, actual)
10
+ end
11
+
12
+ def test_color
13
+ prev = CLI::UI.enable_color?
14
+
15
+ CLI::UI.enable_color = true
16
+ assert_equal("\e[0;31ma\e[0m", CLI::UI.fmt('{{red:a}}'))
17
+ CLI::UI.enable_color = false
18
+ assert_equal('a', CLI::UI.fmt('{{red:a}}'))
19
+ ensure
20
+ CLI::UI.enable_color = prev
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,40 @@
1
+ require 'test_helper'
2
+
3
+ module CLI
4
+ module UI
5
+ class ColorTest < MiniTest::Test
6
+ def test_colors
7
+ assert_equal("\x1b[31m", Color::RED.code)
8
+ assert_equal("\x1b[32m", Color::GREEN.code)
9
+ assert_equal("\x1b[33m", Color::YELLOW.code)
10
+ assert_equal("\x1b[94m", Color::BLUE.code)
11
+ assert_equal("\x1b[35m", Color::MAGENTA.code)
12
+ assert_equal("\x1b[36m", Color::CYAN.code)
13
+ assert_equal("\x1b[0m", Color::RESET.code)
14
+ assert_equal("\x1b[1m", Color::BOLD.code)
15
+ assert_equal("\x1b[97m", Color::WHITE.code)
16
+
17
+ assert_equal('36', Color::CYAN.sgr)
18
+ assert_equal(:bold, Color::BOLD.name)
19
+
20
+ assert_equal(Color::BLUE, Color.lookup(:blue))
21
+ assert_equal(Color::RESET, Color.lookup(:reset))
22
+
23
+ assert_raises(Color::InvalidColorName) do
24
+ Color.lookup(:foobar)
25
+ end
26
+ end
27
+
28
+ def test_useful_exception
29
+ e = begin
30
+ Color.lookup(:foobar)
31
+ rescue => e
32
+ e
33
+ end
34
+ assert_match(/invalid color: :foobar/, e.message) # error
35
+ assert_match(/Color\.available/, e.message) # where to find colors
36
+ assert_match(/:green/, e.message) # list of valid colors
37
+ end
38
+ end
39
+ end
40
+ end