colora 0.1.240114 → 1.0.250401

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a3760588ff62affbf6a8f1c58a95e9d76415d77db9fd9d214a783e51dad1d177
4
- data.tar.gz: 6422c39bc7dc14d01575da4af643f5029a06f8e7db15a22fff72655ba468b199
3
+ metadata.gz: aaeff7f365a7ed911d0a4e43ba1dc6f1db993a779d6fc5c7228581580f823979
4
+ data.tar.gz: 127b845d20c7380072a5f2bd8d8b542d403efc7713b0992ae00da3445e58b00b
5
5
  SHA512:
6
- metadata.gz: 87dcb061cc8f77823edda88f80ad60f3c5abc3bccd199504cbab391c7615853b58e682b04d97b9fa2cee5665dfb9e46b2a8fe35e05184724c984d71526ac5b9b
7
- data.tar.gz: c17e22167482923f6900ba3e614bdf25cee992496fe789d6a57cc344b0821653b9585cf22ed106960b483c0bac451780bff7035b929b82cdb5d83636cf2b43cd
6
+ metadata.gz: d618d434df2e56d75c50f94a6a26cb656c0b148a400f5b50feb04b2934f2f3f990718b9c219c582e6d2c048ea4b8e145172b5f52485346be15a78ec3b62905ba
7
+ data.tar.gz: 4f20a7ea99b7e1b4ed5641fe7f6810b0c789ccb51af5a78e1e7d1d8887e839cff492c4a5dfc542490d64cf038b2045b57efec520c253c40daa15ab0bf132398b
data/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # Colora
2
2
 
3
- * [VERSION 0.1.240114](https://github.com/carlosjhr64/colora/releases)
3
+ * [VERSION 1.0.250401](https://github.com/carlosjhr64/colora/releases)
4
4
  * [github](https://www.github.com/carlosjhr64/colora)
5
- * [rubygems](https://rubygems.org/carlosjhr64/colora)
5
+ * [rubygems](https://rubygems.org/gems/colora)
6
6
 
7
7
  ## DESCRIPTION
8
8
 
@@ -26,26 +26,24 @@ $ colora # Git-diff default
26
26
 
27
27
  ![Demo](img/demo.png)
28
28
 
29
- Colora will decorate your git-diff output with the following additional flags:
29
+ Colora will color code your git-diff output:
30
30
 
31
- * Absent: `*`
32
- * Deleted: `<`
33
- * Inserted: `>`
34
- * Moved/Touched: `t`
35
- * Edited: `e`
36
- * Duplicated: `d`
31
+ * Deleted: `:red`
32
+ * Replaced: `:magenta`
33
+ * Inserted: `:green`
34
+ * Edited: `:black`
35
+ * Moved/Touched: `:blue`
36
+ * Duplicated: `:cyan`
37
+ * Context: `:gray`
37
38
 
38
- The flags are the first three characters of the diff line,
39
- the first coming from git.
40
- The second and third refer to the code and comment of the diff line
41
- (Colora assumes comments as in Ruby).
42
39
  One can choose to filter(via command line options) the diff output to:
43
40
 
44
41
  * Filter out context lines(quiet)
45
- * Just view green(inserted lines)
46
- * Just view red(deleted lines)
42
+ * Just view in(`+>`) lines
43
+ * Just view out(`-<`) lines
47
44
  * Just view edited code or comments
48
45
  * Just view duplicated code or comments
46
+ * Toggle on/off lines on regexp
49
47
 
50
48
  Additional features:
51
49
 
@@ -59,8 +57,8 @@ Usage:
59
57
  colora [:options+] [<file=FILE>]
60
58
  Options:
61
59
  -q --quiet
62
- -g --green Skip red: /^[-<]/
63
- -r --red Skip green: /^[+>]/
60
+ --in in lines, skip out lines
61
+ --out out lines, skip in lines
64
62
  -c --code Show only new(changed) code
65
63
  -C --comment Show only new(changed) comments
66
64
  -d --dupcode Show only duplicate code
@@ -69,11 +67,17 @@ Options:
69
67
  -t --tab Swap tab with ⇥
70
68
  --theme=NAME Rouge theme(default: github)
71
69
  --lang=NAME Language being diffed(default: ruby)
70
+ --on=REGEXP Start showing lines at this regexp
71
+ --off=REGEXP Stop showing lines at this regexp
72
+ --fuzzy=FUZZY Fuzzy match threshold (default: 0.618)
72
73
  Types:
73
- FILE /^[-\w\.\/]+$/
74
- NAME /^[\d.a-z_]+$/
74
+ FILE /^[-\w\.\/]+$/
75
+ NAME /^[\d.a-z_]+$/
76
+ REGEXP /^\S+$/
77
+ FUZZY /^0.\d+$/
75
78
  Exclusive:
76
- green red
79
+ in out
80
+ code comment dupcode dupcomment
77
81
  # Notes: #
78
82
  When no FILE is given and STDIN in a TTY, git-diff is run.
79
83
  Known themes:
@@ -87,7 +91,7 @@ Known themes:
87
91
  ```
88
92
  ## LICENSE
89
93
 
90
- Copyright (c) 2024 CarlosJHR64
94
+ Copyright (c) 2025 CarlosJHR64
91
95
 
92
96
  Permission is hereby granted, free of charge,
93
97
  to any person obtaining a copy of this software and
data/bin/colora CHANGED
@@ -1,44 +1,46 @@
1
1
  #!/usr/bin/env ruby
2
- require 'colora'
2
+ # frozen_string_literal: true
3
3
 
4
- def parse_argv
5
- return nil if ARGV.empty?
4
+ require 'colora'
6
5
 
7
- require 'help_parser'
8
- HelpParser[Colora::VERSION, <<~HELP]
9
- Usage:
10
- colora [:options+] [<file=FILE>]
11
- Options:
12
- -q --quiet
13
- -g --green \t Skip red: /^[-<]/
14
- -r --red \t Skip green: /^[+>]/
15
- -c --code \t Show only new(changed) code
16
- -C --comment \t Show only new(changed) comments
17
- -d --dupcode \t Show only duplicate code
18
- -D --dupcomment\t Show only duplicate comments
19
- -G --git \t Run git-diff
20
- -t --tab \t Swap tab with ⇥
21
- --theme=NAME \t Rouge theme(default: github)
22
- --lang=NAME \t Language being diffed(default: ruby)
23
- Types:
24
- FILE /^[-\\w\\.\\/]+$/
25
- NAME /^[\\d.a-z_]+$/
26
- Exclusive:
27
- green red
28
- # Notes: #
29
- When no FILE is given and STDIN in a TTY, git-diff is run.
30
- Known themes:
31
- base16 base16.monokai base16.solarized bw
32
- colorful
33
- github gruvbox
34
- igorpro
35
- magritte molokai monokai monokai.sublime
36
- pastie
37
- thankful_eyes tulip
38
- HELP
39
- end
6
+ options = ARGV.empty? ? nil : Colora.parse_argv(<<~HELP)
7
+ Usage:
8
+ colora [:options+] [<file=FILE>]
9
+ Options:
10
+ -q --quiet
11
+ --in \t in lines, skip out lines
12
+ --out \t out lines, skip in lines
13
+ -c --code \t Show only new(changed) code
14
+ -C --comment \t Show only new(changed) comments
15
+ -d --dupcode \t Show only duplicate code
16
+ -D --dupcomment\t Show only duplicate comments
17
+ -G --git \t Run git-diff
18
+ -t --tab \t Swap tab with ⇥
19
+ --theme=NAME \t Rouge theme(default: github)
20
+ --lang=NAME \t Language being diffed(default: ruby)
21
+ --on=REGEXP \t Start showing lines at this regexp
22
+ --off=REGEXP \t Stop showing lines at this regexp
23
+ --fuzzy=FUZZY \t Fuzzy match threshold (default: 0.618)
24
+ Types:
25
+ FILE /^[-\\w\\.\\/]+$/
26
+ NAME /^[\\d.a-z_]+$/
27
+ REGEXP /^\\S+$/
28
+ FUZZY /^0.\\d+$/
29
+ Exclusive:
30
+ in out
31
+ code comment dupcode dupcomment
32
+ # Notes: #
33
+ When no FILE is given and STDIN in a TTY, git-diff is run.
34
+ Known themes:
35
+ base16 base16.monokai base16.solarized bw
36
+ colorful
37
+ github gruvbox
38
+ igorpro
39
+ magritte molokai monokai monokai.sublime
40
+ pastie
41
+ thankful_eyes tulip
42
+ HELP
40
43
 
41
- options = parse_argv
42
44
  begin
43
45
  Colora.run(options)
44
46
  rescue Colora::Error => e
@@ -1,40 +1,123 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Colora namespace.
4
+ # Configuration file.
1
5
  module Colora
2
- Config = OpenStruct.new
6
+ # Filter keys:
7
+ OPTIONS = %i[git file lang theme tab on off fuzzy].freeze
8
+ FILTERS = %i[quiet in out code comment dupcode dupcomment].freeze
9
+ CODES = %i[context deleted replaced duplicated edited inserted touched].freeze
3
10
 
4
- # Options:
5
- Config.file = nil
6
- Config.git = false
7
- Config.lang = 'ruby'
8
- Config.theme = 'github'
9
- Config.tab = false
11
+ Struct.new('Config', *OPTIONS, *CODES, *FILTERS) do
12
+ # rubocop:disable Metrics
13
+ # :reek:TooManyStatements
14
+ def get(*keys)
15
+ keys.each do |key|
16
+ next unless (value = self[key])
10
17
 
11
- # Filter keys:
12
- FILTERS = %i[quiet green red code comment dupcode dupcomment]
13
- FILTERS.each{Config[_1]=false}
14
-
15
- # Flags colors:
16
- Config.duplicated_flag = [:default, '#E0FFFF'] # LightCyan
17
- Config.inserted_flag = [:default, '#ADD8E6'] # LightBlue
18
- Config.edited_flag = [:default, '#90EE90'] # LightGreen
19
-
20
- # Comments colors:
21
- Config.moved_comment = ['#A9A9A9', :default] # DarkGray
22
- Config.duplicated_comment = ['#008B8B', :default] # DarkCyan
23
- Config.inserted_comment = ['#00008B', :default] # DarkBlue
24
- Config.edited_comment = ['#006400', :default] # DarkGreen
25
-
26
- def self.configure(options)
27
- # FILE:
28
- Config.file = options.file if options.file
29
-
30
- # Options:
31
- Config.theme = options.theme if options.theme?
32
- Config.lang = options.lang if options.lang?
33
- Config.git = options.git?
34
- Config.tab = options.tab?
35
-
36
- # Filters:
37
- # Config.quiet=options.quiet? ...
38
- FILTERS.each{Config[_1]=options.send("#{_1}?")}
18
+ return value
19
+ end
20
+ nil
21
+ end
22
+
23
+ # :reek:TooManyStatements
24
+ def reset
25
+ FILTERS.each { self[it] = false }
26
+
27
+ # Options:
28
+ self.file = nil
29
+ self.git = false
30
+ self.lang = 'ruby'
31
+ self.theme = 'github'
32
+ self.tab = false
33
+ self.fuzzy = 0.618034
34
+
35
+ # Colors:
36
+ # rubocop:disable Style/SymbolArray
37
+ self.context = [:gray, :default]
38
+ self.deleted = [:red, :default]
39
+ self.replaced = [:magenta, :default]
40
+ self.duplicated = [:cyan, :default]
41
+ self.edited = [:black, :default]
42
+ self.inserted = [:green, :default]
43
+ self.touched = [:blue, :default]
44
+ # rubocop:enable Style/SymbolArray
45
+ end
46
+
47
+ # :reek:TooManyStatements
48
+ def configure(options)
49
+ # FILE:
50
+ self.file = options.file if options.file?
51
+
52
+ # Options:
53
+ self.theme = options.theme if options.theme?
54
+ self.lang = options.lang if options.lang?
55
+ self.git = options.git?
56
+ self.tab = options.tab?
57
+ self.on = Regexp.new(options.on) if options.on?
58
+ self.off = Regexp.new(options.off) if options.off?
59
+ self.fuzzy = options.fuzzy.to_f if options.fuzzy?
60
+
61
+ FILTERS.each { self[it] = options.send("#{it}?") }
62
+ self.code = context if code
63
+ self.comment = context if comment
64
+ self.dupcode = context if dupcode
65
+ self.dupcomment = context if dupcomment
66
+
67
+ file_check
68
+ end
69
+
70
+ private
71
+
72
+ # :reek:UncommunicativeVariableName
73
+ def efd
74
+ e = File.exist?(file)
75
+ f = e && File.file?(file)
76
+ d = e && !f && File.directory?(file)
77
+ [e, f, d]
78
+ end
79
+
80
+ # :reek:UncommunicativeVariableName :reek:TooManyStatements
81
+ def file_check
82
+ return unless file
83
+
84
+ e, f, d = efd
85
+ raise Colora::Error, "Not a file or directory: #{file}" if e && !(f || d)
86
+ return if f # It is a file.
87
+
88
+ if git
89
+ return if d # It is a diff on directory.
90
+ return if gitlog
91
+ end
92
+ raise Colora::Error, "Does not exist: #{file}" unless e
93
+ raise Colora::Error, "Is a directory: #{file}" if d
94
+
95
+ raise "Unexpected error: #{file}" # Should not happen.
96
+ end
97
+
98
+ # :reek:UncommunicativeVariableName :reek:NilCheck :reek:TooManyStatements
99
+ def gitlog
100
+ return false unless (mdt = /^(\h+)(\.\.(\h+))?$/.match(file))
101
+
102
+ a, b = mdt.values_at(1, 3)
103
+ if a != b && /^\d$/.match?(a) && (b.nil? || /^\d$/.match?(b))
104
+ h = `git log --format='%h %s' -n 10`.lines
105
+ .map { it.strip.split(' ', 2) }
106
+ a, ma = h[a.to_i]
107
+ puts Paint["a: #{ma}", :bold]
108
+ if b
109
+ b, mb = h[b.to_i]
110
+ puts Paint["b: #{mb}", :bold]
111
+ self.file = "#{a}..#{b}"
112
+ else
113
+ self.file = a
114
+ end
115
+ end
116
+ true
117
+ end
118
+ # rubocop:enable Metrics
39
119
  end
120
+
121
+ Config = Struct::Config.new
122
+ Config.reset # Resets to default values.
40
123
  end
data/lib/colora/data.rb CHANGED
@@ -1,74 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Colora namespace.
1
4
  module Colora
5
+ # Data class for processing git-diff lines.
6
+ # :reek:DuplicateMethodCall :reek:UncommunicativeVariableName
7
+ # :reek:TooManyStatements
2
8
  class Data
3
- SPLIT = lambda do |line|
4
- flag = line[0]
5
- code, pounds, comment = line[1..].split(/(?<!['"])(\s*#+)(?!{)/, 2)
6
- code = nil if code.empty?
7
- comment ? [flag, code, pounds+comment] : [flag, code, nil]
9
+ def self.split(line)
10
+ case line[1..]
11
+ when /^\s*#/
12
+ [line[0], nil, line[1..]]
13
+ when %r{^(.*?)(\s*#[^`'")/\}\]]*)$}
14
+ [line[0], $LAST_MATCH_INFO[1], $LAST_MATCH_INFO[2]]
15
+ else
16
+ [line[0], line[1..], nil]
17
+ end
18
+ # [flag, code, comment]
8
19
  end
9
- UPDATE = lambda do |hash, key, flag|
20
+
21
+ # :reek:NilCheck
22
+ def self.update(hash, key, flag)
10
23
  k = key.strip
11
24
  hash[k] = case hash[k]
12
25
  when nil
13
- flag # added(+>) or removed(-<)
26
+ flag # added or removed
14
27
  when 'd', 't', flag
15
- 'd' # duplicate
28
+ 'd' # duplicate
16
29
  else
17
- 't' # touched
30
+ 't' # touched
18
31
  end
19
32
  end
20
33
 
21
- def pre_process(line)
22
- # rubocop:disable Lint/DuplicateBranch
23
- case line.rstrip
24
- when '', '+', '-', '---', /^[-+][-+][-+] [ab]/
25
- line
26
- when /^[-+<>]/
27
- flag, code, comment = SPLIT[line]
28
- f = flag=='-' ? '<' : flag=='+' ? '>' : flag
29
- UPDATE[@codes, code, f] if code
30
- UPDATE[@comments, comment, f] if comment
31
- [flag, code, comment]
34
+ def self.reflag(flag)
35
+ if flag == '-'
36
+ '-'
32
37
  else
33
- line
38
+ flag == '+' ? '+' : flag
34
39
  end
35
- # rubocop:enable Lint/DuplicateBranch
36
40
  end
37
41
 
38
42
  attr_reader :lines
39
43
 
40
44
  def initialize(lines)
41
- @lines,@codes,@comments,@edits = [],{},{},Set.new
42
- while (line = lines.shift)
43
- @lines << pre_process(line)
44
- end
45
+ @lines = []
46
+ @codes = {}
47
+ @comments = {}
48
+ @edits = Set.new
49
+ populate_lines(lines)
45
50
  populate_edits
46
51
  @lines.each do |line|
47
52
  next unless line.is_a?(Array)
53
+
48
54
  post_process line
49
55
  end
50
- @codes = @comments = @edits = nil # GC
51
56
  end
52
57
 
53
- def populate_edits
54
- partners = []
55
- jarrow = FuzzyStringMatch::JaroWinkler.create(:pure) # Need pure for UTF-8
56
- removed = @codes.select{|_,flag| '-<'.include?flag}.keys
57
- added = @codes.select{|_,flag| '+>'.include?flag}.keys
58
- short, long = [removed, added].sort_by(&:length)
59
- short.each do |a|
60
- long.each do |b|
61
- d = jarrow.getDistance(a, b)
62
- partners.push([a, b, d]) if d > 0.618034
63
- end
64
- end
65
- partners.sort_by(&:last).reverse.each do |a, b, _|
66
- next if @edits.include?(a) || @edits.include?(b)
67
- @edits.add(a)
68
- @edits.add(b)
58
+ private
59
+
60
+ def flag_code_comment(line)
61
+ flag, code, comment = Data.split(line)
62
+ f = Data.reflag(flag)
63
+ Data.update(@codes, code, f) if code
64
+ Data.update(@comments, comment, f) if comment
65
+ [flag, code, comment]
66
+ end
67
+
68
+ def populate_lines(lines)
69
+ while (line = lines.shift)
70
+ @lines << pre_process(line)
69
71
  end
70
72
  end
71
73
 
74
+ # :reek:FeatureEnvy
72
75
  def post_process(line)
73
76
  if (code = line[1]&.strip)
74
77
  flag = @edits.include?(code) ? 'e' : @codes[code]
@@ -79,5 +82,48 @@ module Colora
79
82
  line[2] = [flag, line[2]]
80
83
  end
81
84
  end
85
+
86
+ def pre_process(line)
87
+ # rubocop:disable Lint/DuplicateBranch
88
+ case line.rstrip
89
+ when '', '+', '-', '---', %r{^[-+][-+][-+] [\w./]}
90
+ line
91
+ when /^[-+<>]/
92
+ flag_code_comment(line)
93
+ else
94
+ line
95
+ end
96
+ # rubocop:enable Lint/DuplicateBranch
97
+ end
98
+
99
+ def populate_edits
100
+ partners.sort_by(&:last).reverse.each do |a, b, _|
101
+ next if @edits.include?(a) || @edits.include?(b)
102
+
103
+ @edits.add(a)
104
+ @edits.add(b)
105
+ end
106
+ end
107
+
108
+ # :reek:NestedIterators
109
+ def partners
110
+ partners = []
111
+ short, long, jarrow = short_long_jarrow
112
+ short.each do |a|
113
+ long.each do |b|
114
+ d = jarrow.getDistance(a, b)
115
+ partners.push([a, b, d]) if d > Config.fuzzy
116
+ end
117
+ end
118
+ partners
119
+ end
120
+
121
+ def short_long_jarrow
122
+ jarrow = FuzzyStringMatch::JaroWinkler.create(:pure) # Need pure for UTF-8
123
+ removed = @codes.select { |_, flag| '-<'.include? flag }.keys
124
+ added = @codes.select { |_, flag| '+>'.include? flag }.keys
125
+ short, long = [removed, added].sort_by(&:length)
126
+ [short, long, jarrow]
127
+ end
82
128
  end
83
129
  end
data/lib/colora/lines.rb CHANGED
@@ -1,69 +1,148 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Colora namespace
1
4
  module Colora
5
+ # Here we read lines and color each to be yielded out.
6
+ # rubocop:disable Metrics/ClassLength, Style/ClassVars
7
+ # :reek:TooManyInstanceVariables :reek:TooManyMethods :reek:ClassVariable
2
8
  class Lines
9
+ # :reek:DuplicateMethodCall :reek:UtilityFunction
10
+ def filehandle
11
+ if Config.git
12
+ IO.popen("git diff #{Config.file}")
13
+ elsif Config.file
14
+ File.open(Config.file)
15
+ else
16
+ $stdin
17
+ end
18
+ end
19
+
20
+ # :reek:FeatureEnvy
21
+ def get_lines(getter = filehandle)
22
+ getter.readlines.map(&:chomp)
23
+ ensure
24
+ getter.close
25
+ end
26
+
27
+ # :reek:DuplicateMethodCall
3
28
  def formatter
4
29
  theme = Rouge::Theme.find(Config.theme)
5
30
  raise Error, "Unrecognized theme: #{Config.theme}" unless theme
31
+
6
32
  Rouge::Formatters::Terminal256.new(theme.new)
7
33
  end
8
34
 
9
- def get_lexer_by_file(file = Config.file)
35
+ def initialize
36
+ @formatter = formatter
37
+ @lines = get_lines
38
+ @lexer = @orig_lexer = guess_lexer
39
+ @tag = @lexer.tag.to_sym
40
+ @lines = @tag == :diff ? Data.new(@lines).lines : @lines
41
+ @lang = @orig_lang = Rouge::Lexer.find_fancy(Config.lang)
42
+ # `@on` is `true` unless there is a `Config.on` condition to be met
43
+ @on = Config.on ? false : true
44
+ end
45
+
46
+ def guess_lexer
47
+ return Rouge::Lexers::Diff if Config.git
48
+
49
+ guess_lexer_by_file || guess_lexer_by_source
50
+ end
51
+
52
+ # :reek:UtilityFunction
53
+ def guess_lexer_by_file(file = Config.file)
10
54
  return nil unless file && !File.extname(file).empty?
55
+
11
56
  Rouge::Lexer.guess_by_filename(file)
12
57
  end
13
58
 
14
- def get_lexer_by_source(source)
59
+ def guess_lexer_by_source(source = @lines[0])
15
60
  case source
16
- when /^---/, /^# /
17
- Rouge::Lexer.guess_by_filename('*.md')
61
+ when /^---( #.*)?$/, /^# /
62
+ Rouge::Lexers::Markdown
63
+ when %r{^--- [\w./]}
64
+ Rouge::Lexers::Diff
18
65
  else
19
66
  Rouge::Lexer.guess_by_source(source)
20
67
  end
21
68
  end
22
69
 
23
- def filehandle
24
- Config.git ? IO.popen("git diff #{Config.file}") :
25
- Config.file ? File.open(Config.file) :
26
- $stdin
70
+ # :reek:ControlParameter
71
+ def toggle(line)
72
+ if @on
73
+ @on = false if Config.off&.match?(line)
74
+ elsif Config.on&.match?(line)
75
+ @on = true
76
+ end
27
77
  end
28
78
 
29
- def get_lines(fh = filehandle)
30
- fh.readlines.map(&:chomp)
79
+ # rubocop:disable Metrics
80
+ # :reek:LongParameterList :reek:UncommunicativeParameterName
81
+ # :reek:UtilityFunction
82
+ def by_filters?(ary, ar0 = ary[0], dg1 = ary.dig(1, 0), dg2 = ary.dig(2, 0))
83
+ (Config.in && '-<'.include?(ar0)) ||
84
+ (Config.out && '+>'.include?(ar0)) ||
85
+ (Config.quiet && ((Config.code && [nil, 't'].include?(dg1)) ||
86
+ (Config.comment && [nil, 't'].include?(dg2)) ||
87
+ (Config.dupcode && dg1 == 'd') ||
88
+ (Config.dupcomment && dg2 == 'd'))) ||
89
+ false
31
90
  end
91
+ # rubocop:enable Metrics
32
92
 
33
- def initialize
34
- @formatter = formatter
35
- lines = get_lines
36
- @lexer = @orig_lexer = get_lexer_by_file || get_lexer_by_source(lines[0])
37
- @tag = @lexer.tag
38
- @lines = @tag=='diff' ? Data.new(lines).lines : lines
39
- @lang = @orig_lang = Rouge::Lexer.find_fancy(Config.lang)
40
- @pad0 = ' '
41
- @pad1 = ' '
93
+ # :reek:ControlParameter
94
+ def filtered?(line, str = line.is_a?(String))
95
+ toggle(line) if str
96
+ return true unless @on
97
+ return false if str
98
+
99
+ by_filters?(line)
42
100
  end
43
101
 
44
- def to_a = @lines
102
+ @@plugins = []
103
+ def self.plugins = @@plugins
45
104
 
46
- def filtered?(line)
47
- return false if line.is_a?(String)
105
+ def txt_formatter(line)
106
+ # Is there a plugin for @tag? If so, use it: Else use the lexer.
107
+ if Lines.plugins.include?(@tag)
108
+ send(@tag, line)
109
+ else
110
+ @formatter.format(@lexer.lex(line))
111
+ end
112
+ end
48
113
 
49
- (Config.green && '-<'.include?(line[0])) ||
50
- (Config.red && '+>'.include?(line[0])) ||
51
- (Config.code && [nil,'t'].include?(line.dig 1,0)) ||
52
- (Config.comment && [nil, 't'].include?(line.dig 2,0)) ||
53
- (Config.dupcode && line.dig(1,0)=='d') ||
54
- (Config.dupcomment && line.dig(2,0)=='d') ||
55
- false
114
+ # :reek:TooManyStatements
115
+ def each
116
+ @lines.each do |line|
117
+ next if filtered?(line)
118
+
119
+ txt = txt_formatter(line)
120
+ yield txt if txt
121
+ end
122
+ reset_lexer
123
+ reset_lang
56
124
  end
57
125
 
58
- def pad(line)
59
- @pad0+line
126
+ # :reek:NilCheck
127
+ def reset_lexer(lang = nil)
128
+ @lexer = if lang.nil?
129
+ @orig_lexer
130
+ else
131
+ Rouge::Lexer.find_fancy(lang) || @orig_lexer
132
+ end
60
133
  end
61
134
 
62
- def flags(line)
63
- line[0] + (line.dig(1,0)||'*') + (line.dig(2,0)||'*') + @pad1
135
+ # :reek:NilCheck
136
+ def reset_lang(lang = nil)
137
+ @lang = if lang.nil?
138
+ @orig_lang
139
+ else
140
+ Rouge::Lexer.find_fancy(lang) || @orig_lang
141
+ end
64
142
  end
65
143
 
66
- def format(line, color=nil)
144
+ # :reek:NilCheck
145
+ def format(line, color = nil)
67
146
  case color
68
147
  when nil
69
148
  @formatter.format(@lexer.lex(line))
@@ -74,35 +153,15 @@ module Colora
74
153
  end
75
154
  end
76
155
 
77
- def reset_lang_by_source(source)
78
- @lang = Rouge::Lexer.guess_by_source(source)
79
- end
80
-
81
156
  def reset_lang_by_filename(file)
82
157
  @lang = Rouge::Lexer.guess_by_filename(file)
83
158
  end
84
159
 
85
- def reset_lexer(lang=nil)
86
- @lexer = lang.nil? ? @orig_lexer :
87
- (Rouge::Lexer.find_fancy(lang) || @orig_lexer)
88
- end
89
-
90
- def reset_lang(lang=nil)
91
- @lang = lang.nil? ? @orig_lang :
92
- (Rouge::Lexer.find_fancy(lang) || @orig_lang)
160
+ def reset_lang_by_source(source)
161
+ @lang = Rouge::Lexer.guess_by_source(source)
93
162
  end
94
163
 
95
- def each
96
- @lines.each do |line|
97
- next if filtered?(line)
98
-
99
- # Is there a plugin for @tag? If so, use it: Else use the lexer.
100
- txt = respond_to?(@tag) ? send(@tag, line) :
101
- @formatter.format(@lexer.lex(line))
102
- yield txt if txt
103
- end
104
- reset_lexer
105
- reset_lang
106
- end
164
+ def to_a = @lines
107
165
  end
166
+ # rubocop:enable Metrics/ClassLength, Style/ClassVars
108
167
  end
@@ -1,67 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Colora namespace
1
4
  module Colora
5
+ # Lines namespace
6
+ # :reek:DuplicateMethodCall :reek:NilCheck :reek:RepeatedConditional
2
7
  class Lines
8
+ plugins.push :diff
9
+
10
+ # Diff plugin
11
+ module Diff
12
+ # :reek:TooManyStatements
13
+ def self.flags(line)
14
+ case line[0]
15
+ when '-', '<'
16
+ ldg = line.dig(1, 0)
17
+ ['-', nil].any? { ldg == it } ? '- ' : '< '
18
+ when '+', '>'
19
+ ldg = line.dig(1, 0)
20
+ ['+', nil].any? { ldg == it } ? '+ ' : '> '
21
+ else
22
+ '* '
23
+ end
24
+ end
25
+
26
+ def self.pad(line)
27
+ " #{line}"
28
+ end
29
+ end
30
+
31
+ # category: method
32
+ # :reek:TooManyStatements
33
+ # rubocop:disable Metrics, Layout/LineLength
3
34
  def diff(line)
4
35
  case line
5
36
  when String
6
37
  case line
7
- when /^[-+][-+][-+] [ab]\/(.*)$/
8
- reset_lang_by_filename($~[1])
9
- format(line)
38
+ when '-', '<'
39
+ format(line, Config.deleted)
40
+ when '+', '>'
41
+ format(line, Config.inserted)
42
+ when %r{^[-+][-+][-+] ([\w./].*)$}
43
+ reset_lang_by_filename($LAST_MATCH_INFO[1].strip.split.first)
44
+ format(line, :bold)
10
45
  when /^\s*#!/
11
46
  reset_lang_by_source(line)
12
47
  format(line) unless Config.quiet
13
48
  when /^ /
14
- format(pad(line), :lang) unless Config.quiet
49
+ format(Diff.pad(line), Config.context) unless Config.quiet
15
50
  else
16
51
  format(line) unless Config.quiet
17
52
  end
18
53
  else
19
54
  # Initialized text variables
20
- txt = ''
21
- flags = flags(line)
22
- code = line.dig(1,1)||''
23
- comment = line.dig(2,1)||''
24
- # txt << flags+code
55
+ txt = String.new
56
+ flags = Diff.flags(line)
57
+ code = line.dig(1, 1) || ''
58
+ comment = line.dig(2, 1) || ''
25
59
  case line[0]
26
60
  when '-', '<'
27
- txt << format(flags+code+comment)
28
- comment = '' # will skip commenting below
29
- when '+', '>'
30
- case line.dig(1,0)
31
- when nil, 't'
32
- txt << format(flags+code)
33
- when 'd'
34
- txt << format(flags, Config.dupplicated_flag)
35
- txt << format(code, :lang)
36
- when '>'
37
- txt << format(flags, Config.inserted_flag)
38
- txt << format(code, :lang)
39
- when 'e'
40
- txt << format(flags, Config.edited_flag)
41
- txt << format(code, :lang)
42
- else
43
- warn "Unknown code type: #{line[0]}"
61
+ txt << format(flags, Config.deleted)
62
+ txt << case line.dig(1, 0)
63
+ when '-'
64
+ format(code, Config.get(:comment, :deleted))
65
+ else
66
+ format(code, Config.get(:comment, :replaced))
67
+ end
68
+ unless comment.empty?
69
+ txt << case line.dig(2, 0)
70
+ when '-'
71
+ format(comment, Config.get(:code, :deleted))
72
+ else
73
+ format(comment, Config.get(:code, :replaced))
74
+ end
44
75
  end
45
- else
46
- warn "Unknown line type: #{line[0]}"
47
- end
48
- # txt << comment
49
- unless comment.empty?
50
- case line.dig(2,0)
51
- when 't'
52
- txt << format(comment, Config.moved_comment)
53
- when 'd'
54
- txt << format(comment, Config.dupplicated_comment)
55
- when '>'
56
- txt << format(comment, Config.inserted_comment)
57
- when 'e'
58
- txt << format(comment, Config.edited_comment)
59
- else
60
- warn "Unknown comment type: #{line[0]}"
76
+ txt
77
+ when '+', '>'
78
+ txt << format(flags, Config.inserted)
79
+ txt << case line.dig(1, 0)
80
+ when 'd'
81
+ format(code, Config.get(:code, :comment, :dupcomment, :duplicated))
82
+ when '+'
83
+ format(code, Config.get(:comment, :dupcode, :dupcomment, :inserted))
84
+ when 'e'
85
+ format(code, Config.get(:comment, :dupcode, :dupcomment, :edited))
86
+ else # t
87
+ format(code, Config.get(:code, :comment, :dupcode, :dupcomment, :touched))
88
+ end
89
+ unless comment.empty?
90
+ txt << case line.dig(2, 0)
91
+ when 'd'
92
+ format(comment, Config.get(:code, :comment, :dupcode, :duplicated))
93
+ when '+'
94
+ format(comment, Config.get(:code, :dupcode, :dupcomment, :inserted))
95
+ when 'e'
96
+ format(comment, Config.get(:code, :dupcode, :dupcomment, :edited))
97
+ else # t
98
+ format(comment, Config.get(:code, :comment, :dupcode, :dupcomment, :touched))
99
+ end
61
100
  end
101
+ txt
62
102
  end
63
- txt
64
103
  end
104
+ # rubocop:enable Metrics, Layout/LineLength
65
105
  end
66
106
  end
67
107
  end
@@ -1,11 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Colora namespace
1
4
  module Colora
5
+ # Lines namespace
6
+ # :reek:DuplicateMethodCall
2
7
  class Lines
8
+ plugins.push :markdown
9
+
10
+ # Markdown plug
11
+ # :reek:TooManyStatements
12
+ # rubocop:disable Metrics
3
13
  def markdown(line)
4
14
  txt = nil
5
15
  case line
6
16
  when /^```(\w+)$/
7
17
  txt = format(line)
8
- reset_lexer($~[1])
18
+ reset_lexer($LAST_MATCH_INFO[1])
9
19
  when /^```$/
10
20
  reset_lexer
11
21
  txt = format(line)
@@ -14,5 +24,6 @@ module Colora
14
24
  end
15
25
  txt
16
26
  end
27
+ # rubocop:enable Metrics
17
28
  end
18
29
  end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Standard libraries:
4
+ require 'English'
5
+ require 'ostruct'
6
+ # Gems:
7
+ require 'fuzzystringmatch'
8
+ require 'paint'
9
+ require 'rouge'
10
+ # Colora:
11
+ require_relative 'configure'
12
+ require_relative 'data'
13
+ require_relative 'lines'
14
+ # Plugs:
15
+ require_relative 'plugs/diff'
16
+ require_relative 'plugs/markdown'
data/lib/colora.rb CHANGED
@@ -1,32 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Colora colorizes output to the terminal.
1
4
  module Colora
2
- class Error < RuntimeError; end
3
- VERSION = '0.1.240114'
5
+ VERSION = '1.0.250401'
4
6
 
5
- # Colora.run(options)
6
- def self.run(options=nil)
7
- # Standard libraries:
8
- require 'ostruct'
9
- # Gems:
10
- require 'fuzzystringmatch'
11
- require 'paint'
12
- require 'rouge'
13
- # Colora:
14
- require_relative 'colora/configure'
15
- require_relative 'colora/data'
16
- require_relative 'colora/lines'
17
- # Plugs:
18
- require_relative 'colora/plugs/diff'
19
- require_relative 'colora/plugs/markdown'
7
+ # Colora::Error for raised exceptions in Colora.
8
+ # Anything else is a bug.
9
+ class Error < RuntimeError
10
+ end
20
11
 
21
- # Configure Colora:
22
- Colora.configure(options) if options
23
- # By default, run git-diff:
24
- Config.git = true if $stdin.tty? && !Config.file
12
+ def self.parse_argv(help)
13
+ require 'help_parser'
14
+ HelpParser[VERSION, help]
15
+ end
25
16
 
26
- # Puts Colora::Lines
17
+ # Colora.run(options)
18
+ # :reek:TooManyStatements
19
+ def self.run(options = nil)
20
+ require_relative 'colora/requires'
21
+ Config.configure(options) if options # Configure Colora:
22
+ # If stdin is a tty(we're not in a pipe) and no file is specified,
23
+ # then default to running git-diff.
24
+ Config.git = true if $stdin.tty? && !Config.file
27
25
  Lines.new.each do |line|
28
26
  line.gsub!("\t", '⇥') if Config.tab
29
- puts line
27
+ puts line # Puts Colora::Lines
30
28
  end
31
29
  end
32
30
  end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: colora
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.240114
4
+ version: 1.0.250401
5
5
  platform: ruby
6
6
  authors:
7
7
  - CarlosJHR64
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2024-01-14 00:00:00.000000000 Z
10
+ date: 2025-04-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: fuzzy-string-match
@@ -36,20 +35,20 @@ dependencies:
36
35
  requirements:
37
36
  - - "~>"
38
37
  - !ruby/object:Gem::Version
39
- version: '8.2'
38
+ version: '9.0'
40
39
  - - ">="
41
40
  - !ruby/object:Gem::Version
42
- version: 8.2.230210
41
+ version: 9.0.240926
43
42
  type: :runtime
44
43
  prerelease: false
45
44
  version_requirements: !ruby/object:Gem::Requirement
46
45
  requirements:
47
46
  - - "~>"
48
47
  - !ruby/object:Gem::Version
49
- version: '8.2'
48
+ version: '9.0'
50
49
  - - ">="
51
50
  - !ruby/object:Gem::Version
52
- version: 8.2.230210
51
+ version: 9.0.240926
53
52
  - !ruby/object:Gem::Dependency
54
53
  name: paint
55
54
  requirement: !ruby/object:Gem::Requirement
@@ -76,120 +75,20 @@ dependencies:
76
75
  requirements:
77
76
  - - "~>"
78
77
  - !ruby/object:Gem::Version
79
- version: '4.2'
78
+ version: '4.5'
80
79
  - - ">="
81
80
  - !ruby/object:Gem::Version
82
- version: 4.2.0
81
+ version: 4.5.1
83
82
  type: :runtime
84
83
  prerelease: false
85
84
  version_requirements: !ruby/object:Gem::Requirement
86
85
  requirements:
87
86
  - - "~>"
88
87
  - !ruby/object:Gem::Version
89
- version: '4.2'
88
+ version: '4.5'
90
89
  - - ">="
91
90
  - !ruby/object:Gem::Version
92
- version: 4.2.0
93
- - !ruby/object:Gem::Dependency
94
- name: colorize
95
- requirement: !ruby/object:Gem::Requirement
96
- requirements:
97
- - - "~>"
98
- - !ruby/object:Gem::Version
99
- version: '1.1'
100
- - - ">="
101
- - !ruby/object:Gem::Version
102
- version: 1.1.0
103
- type: :development
104
- prerelease: false
105
- version_requirements: !ruby/object:Gem::Requirement
106
- requirements:
107
- - - "~>"
108
- - !ruby/object:Gem::Version
109
- version: '1.1'
110
- - - ">="
111
- - !ruby/object:Gem::Version
112
- version: 1.1.0
113
- - !ruby/object:Gem::Dependency
114
- name: cucumber
115
- requirement: !ruby/object:Gem::Requirement
116
- requirements:
117
- - - "~>"
118
- - !ruby/object:Gem::Version
119
- version: '9.1'
120
- - - ">="
121
- - !ruby/object:Gem::Version
122
- version: 9.1.1
123
- type: :development
124
- prerelease: false
125
- version_requirements: !ruby/object:Gem::Requirement
126
- requirements:
127
- - - "~>"
128
- - !ruby/object:Gem::Version
129
- version: '9.1'
130
- - - ">="
131
- - !ruby/object:Gem::Version
132
- version: 9.1.1
133
- - !ruby/object:Gem::Dependency
134
- name: parser
135
- requirement: !ruby/object:Gem::Requirement
136
- requirements:
137
- - - "~>"
138
- - !ruby/object:Gem::Version
139
- version: '3.3'
140
- - - ">="
141
- - !ruby/object:Gem::Version
142
- version: 3.3.0
143
- type: :development
144
- prerelease: false
145
- version_requirements: !ruby/object:Gem::Requirement
146
- requirements:
147
- - - "~>"
148
- - !ruby/object:Gem::Version
149
- version: '3.3'
150
- - - ">="
151
- - !ruby/object:Gem::Version
152
- version: 3.3.0
153
- - !ruby/object:Gem::Dependency
154
- name: rubocop
155
- requirement: !ruby/object:Gem::Requirement
156
- requirements:
157
- - - "~>"
158
- - !ruby/object:Gem::Version
159
- version: '1.59'
160
- - - ">="
161
- - !ruby/object:Gem::Version
162
- version: 1.59.0
163
- type: :development
164
- prerelease: false
165
- version_requirements: !ruby/object:Gem::Requirement
166
- requirements:
167
- - - "~>"
168
- - !ruby/object:Gem::Version
169
- version: '1.59'
170
- - - ">="
171
- - !ruby/object:Gem::Version
172
- version: 1.59.0
173
- - !ruby/object:Gem::Dependency
174
- name: test-unit
175
- requirement: !ruby/object:Gem::Requirement
176
- requirements:
177
- - - "~>"
178
- - !ruby/object:Gem::Version
179
- version: '3.6'
180
- - - ">="
181
- - !ruby/object:Gem::Version
182
- version: 3.6.1
183
- type: :development
184
- prerelease: false
185
- version_requirements: !ruby/object:Gem::Requirement
186
- requirements:
187
- - - "~>"
188
- - !ruby/object:Gem::Version
189
- version: '3.6'
190
- - - ">="
191
- - !ruby/object:Gem::Version
192
- version: 3.6.1
91
+ version: 4.5.1
193
92
  description: |
194
93
  Colorizes terminal outputs.
195
94
 
@@ -209,11 +108,11 @@ files:
209
108
  - lib/colora/lines.rb
210
109
  - lib/colora/plugs/diff.rb
211
110
  - lib/colora/plugs/markdown.rb
111
+ - lib/colora/requires.rb
212
112
  homepage: https://github.com/carlosjhr64/colora
213
113
  licenses:
214
114
  - MIT
215
115
  metadata: {}
216
- post_install_message:
217
116
  rdoc_options: []
218
117
  require_paths:
219
118
  - lib
@@ -221,7 +120,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
221
120
  requirements:
222
121
  - - ">="
223
122
  - !ruby/object:Gem::Version
224
- version: '0'
123
+ version: '3.4'
225
124
  required_rubygems_version: !ruby/object:Gem::Requirement
226
125
  requirements:
227
126
  - - ">="
@@ -229,8 +128,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
229
128
  version: '0'
230
129
  requirements:
231
130
  - 'git: 2.30'
232
- rubygems_version: 3.5.4
233
- signing_key:
131
+ rubygems_version: 3.6.6
234
132
  specification_version: 4
235
133
  summary: Colorizes terminal outputs.
236
134
  test_files: []