canon 0.1.11 → 0.1.12

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: e123b3a909e4ca7309ed6815282fd03c86e4514fa325d82f1e8973f1428ce83a
4
- data.tar.gz: '09e45b9a9f5e8721f4df5b41465b199d0a34824640745321f35cd7edd6d65a91'
3
+ metadata.gz: 0c7f014834dac890a353a1e85abe80dc834e81f20b853cfa00f51a06c423a699
4
+ data.tar.gz: 3b849925479fcec2777b22619bee9ab3d710fd33ce29b10bf2873c90d3095192
5
5
  SHA512:
6
- metadata.gz: c027b7abd237f7b0f3dc679c3b919c11dea6d02f8636aaca9ac95ba56c2d9e321ed3dc23691c33274fa1403f7147b0547011adebb4cb70f1073875cac557a394
7
- data.tar.gz: 75aea03c181390b0f927c5d7ba979a8ed6a5ab807e9ab40fa281257e3d165fa001e62fa7ad9bfff3c1d007e1d8157aa49c7218cb2a99bb07919efb1b7da6b120
6
+ metadata.gz: 7def7dca16c59b0e3f3952b12f814ad93b38cb108654903471320e0e3c7d55418af60068a114eb9d3fcd4e260bdc806c5cd65b9471b06a85b5c5a223f6373395
7
+ data.tar.gz: b36133cf1c3c0597e12b880057ebdc5af80689ab59bb8983ff631208d31db994151858521fb8897c88c00d1651e0ac25ad376584558a3b5b514cfd409c2f20be
data/.rubocop_todo.yml CHANGED
@@ -1,6 +1,6 @@
1
1
  # This configuration was generated by
2
2
  # `rubocop --auto-gen-config`
3
- # on 2026-01-21 06:10:55 UTC using RuboCop version 1.81.7.
3
+ # on 2026-01-21 09:17:44 UTC using RuboCop version 1.81.7.
4
4
  # The point is for the user to remove these configuration records
5
5
  # one by one as the offenses are removed from the code base.
6
6
  # Note that changes in the inspected code, or installation of new
@@ -12,30 +12,13 @@ Gemspec/RequiredRubyVersion:
12
12
  Exclude:
13
13
  - 'canon.gemspec'
14
14
 
15
- # Offense count: 4
16
- # This cop supports safe autocorrection (--autocorrect).
17
- # Configuration parameters: EnforcedStyle, IndentationWidth.
18
- # SupportedStyles: with_first_argument, with_fixed_indentation
19
- Layout/ArgumentAlignment:
20
- Exclude:
21
- - 'lib/canon/diff_formatter.rb'
22
- - 'spec/canon/diff_formatter_spec.rb'
23
-
24
- # Offense count: 705
15
+ # Offense count: 700
25
16
  # This cop supports safe autocorrection (--autocorrect).
26
17
  # Configuration parameters: Max, AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, IgnoreCopDirectives, AllowedPatterns, SplitStrings.
27
18
  # URISchemes: http, https
28
19
  Layout/LineLength:
29
20
  Enabled: false
30
21
 
31
- # Offense count: 5
32
- # This cop supports safe autocorrection (--autocorrect).
33
- # Configuration parameters: AllowInHeredoc.
34
- Layout/TrailingWhitespace:
35
- Exclude:
36
- - 'lib/canon/diff_formatter.rb'
37
- - 'spec/canon/diff_formatter_spec.rb'
38
-
39
22
  # Offense count: 48
40
23
  # Configuration parameters: IgnoreLiteralBranches, IgnoreConstantBranches, IgnoreDuplicateElseBranch.
41
24
  Lint/DuplicateBranch:
@@ -87,12 +70,12 @@ Metrics/AbcSize:
87
70
  Metrics/BlockLength:
88
71
  Max: 84
89
72
 
90
- # Offense count: 176
73
+ # Offense count: 177
91
74
  # Configuration parameters: AllowedMethods, AllowedPatterns, Max.
92
75
  Metrics/CyclomaticComplexity:
93
76
  Enabled: false
94
77
 
95
- # Offense count: 362
78
+ # Offense count: 363
96
79
  # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
97
80
  Metrics/MethodLength:
98
81
  Max: 110
@@ -102,7 +85,7 @@ Metrics/MethodLength:
102
85
  Metrics/ParameterLists:
103
86
  Max: 9
104
87
 
105
- # Offense count: 142
88
+ # Offense count: 143
106
89
  # Configuration parameters: AllowedMethods, AllowedPatterns, Max.
107
90
  Metrics/PerceivedComplexity:
108
91
  Enabled: false
@@ -143,7 +126,7 @@ Performance/CollectionLiteralInLoop:
143
126
  - 'lib/canon/comparison/html_comparator.rb'
144
127
  - 'lib/canon/xml/xml_base_handler.rb'
145
128
 
146
- # Offense count: 64
129
+ # Offense count: 68
147
130
  # Configuration parameters: Prefixes, AllowedPatterns.
148
131
  # Prefixes: when, with, without
149
132
  RSpec/ContextWording:
@@ -160,6 +143,12 @@ RSpec/DescribeMethod:
160
143
  - 'spec/canon/comparison/multiple_differences_spec.rb'
161
144
  - 'spec/canon/diff_formatter/character_map_customization_spec.rb'
162
145
 
146
+ # Offense count: 1
147
+ # This cop supports safe autocorrection (--autocorrect).
148
+ RSpec/EmptyHook:
149
+ Exclude:
150
+ - 'spec/canon/color_detector_spec.rb'
151
+
163
152
  # Offense count: 679
164
153
  # Configuration parameters: CountAsOne.
165
154
  RSpec/ExampleLength:
@@ -191,10 +180,11 @@ RSpec/IndexedLet:
191
180
  - 'spec/canon/tree_diff/matchers/universal_matcher_spec.rb'
192
181
  - 'spec/canon/tree_diff/operations/operation_detector_spec.rb'
193
182
 
194
- # Offense count: 4
183
+ # Offense count: 5
195
184
  # Configuration parameters: AssignmentOnly.
196
185
  RSpec/InstanceVariable:
197
186
  Exclude:
187
+ - 'spec/canon/color_detector_spec.rb'
198
188
  - 'spec/canon/rspec_matchers_spec.rb'
199
189
 
200
190
  # Offense count: 15
@@ -229,7 +219,7 @@ RSpec/NamedSubject:
229
219
  - 'spec/canon/pretty_printer/json_spec.rb'
230
220
  - 'spec/canon/pretty_printer/xml_spec.rb'
231
221
 
232
- # Offense count: 37
222
+ # Offense count: 40
233
223
  # Configuration parameters: AllowedGroups.
234
224
  RSpec/NestedGroups:
235
225
  Max: 4
@@ -457,59 +457,110 @@ The whitespace difference is informative because `structural_whitespace: :ignore
457
457
 
458
458
  == Color Control
459
459
 
460
+ === Automatic Color Detection
461
+
462
+ Canon automatically detects whether the terminal supports color output:
463
+
464
+ * **TTY detection**: Colors are only enabled when output is to a terminal (not when piped to a file)
465
+ * **NO_COLOR support**: If the `NO_COLOR` environment variable is set (per https://no-color.org/), colors are disabled
466
+ * **Terminal capability detection**: Checks environment variables like `TERM` and `COLORTERM`
467
+ * **CI environment awareness**: Detects and adapts to CI systems (GitHub Actions, Travis CI, etc.)
468
+
469
+ This means colors work automatically in most scenarios without manual configuration.
470
+
460
471
  === Enabling/Disabling Colors
461
472
 
462
- Colors are controlled by the `use_color` option:
473
+ Colors can be explicitly controlled using the `use_color` option:
474
+
475
+ .Using automatic detection (default)
476
+ [example]
477
+ ====
478
+ [source,bash]
479
+ ----
480
+ # Auto-detects color support (default)
481
+ canon diff file1.xml file2.xml
482
+ ----
483
+ ====
463
484
 
464
- .CLI
485
+ .Explicit control
465
486
  [example]
466
487
  ====
467
488
  [source,bash]
468
489
  ----
469
- # Enable colors (default)
490
+ # Force enable colors
470
491
  canon diff file1.xml file2.xml --color
471
492
 
472
- # Disable colors
493
+ # Force disable colors
473
494
  canon diff file1.xml file2.xml --no-color
474
495
  ----
475
496
  ====
476
497
 
477
498
  .Ruby API
478
- [example]
479
- ====
480
499
  [source,ruby]
481
500
  ----
482
- # Enable colors
483
- Canon.compare(file1, file2, format: :xml, color: true)
501
+ # Auto-detect (default)
502
+ Canon.compare(file1, file2, format: :xml)
484
503
 
485
- # Disable colors
486
- Canon.compare(file1, file2, format: :xml, color: false)
504
+ # Explicit control
505
+ Canon.compare(file1, file2, format: :xml, use_color: false)
487
506
  ----
488
507
  ====
489
508
 
490
- .RSpec
491
- [example]
492
- ====
493
- [source,ruby]
509
+ === NO_COLOR Environment Variable
510
+
511
+ Per the https://no-color.org/[NO_COLOR standard], setting the `NO_COLOR`
512
+ environment variable will disable colors:
513
+
514
+ [source,bash]
494
515
  ----
495
- RSpec.configure do |config|
496
- # Enable for local, disable for CI
497
- config.canon.xml.diff.use_color = !ENV['CI']
498
- end
516
+ # Disable colors (respected by Canon and other NO_COLOR-aware tools)
517
+ export NO_COLOR=1
518
+
519
+ # Run canon - colors will be disabled
520
+ canon diff file1.xml file2.xml
499
521
  ----
500
- ====
501
522
 
502
- === Environment Variable
523
+ The `NO_COLOR` variable always takes precedence over other color settings.
524
+
525
+ === Environment Variables
526
+
527
+ In addition to `NO_COLOR`, Canon supports these environment variables:
503
528
 
504
529
  [source,bash]
505
530
  ----
506
- # Disable colors globally
531
+ # Disable colors globally (CANON_USE_COLOR)
507
532
  export CANON_USE_COLOR=false
508
533
 
509
- # Enable colors globally
534
+ # Enable colors globally (CANON_USE_COLOR)
510
535
  export CANON_USE_COLOR=true
536
+
537
+ # Format-specific color control
538
+ export CANON_XML_DIFF_USE_COLOR=false
539
+ export CANON_HTML_DIFF_USE_COLOR=false
540
+ export CANON_JSON_DIFF_USE_COLOR=false
541
+ export CANON_YAML_DIFF_USE_COLOR=false
511
542
  ----
512
543
 
544
+ === Terminal Detection
545
+
546
+ Canon detects terminal capabilities through these environment variables:
547
+
548
+ `COLORTERM`::
549
+ Set to `24bit`, `truecolor`, or `true` indicates True Color support
550
+
551
+ `TERM`:: Terminal type:
552
+
553
+ 256-color support::: `xterm-256color`, `screen-256color`
554
+ Direct color support::: `xterm-direct`
555
+ No color support::: `dumb`, `emacs`
556
+ Assume basic ANSI color support::: Most other values
557
+
558
+ `CI`:: CI environment detection
559
+ (plus CI-specific variables)
560
+
561
+ If no terminal information is available, Canon assumes color support for modern
562
+ terminals.
563
+
513
564
 
514
565
  == Diff Type Display Options
515
566
 
data/lib/canon/cli.rb CHANGED
@@ -160,8 +160,7 @@ module Canon
160
160
  desc: "Format type for second file"
161
161
  method_option :color,
162
162
  type: :boolean,
163
- default: true,
164
- desc: "Colorize diff output"
163
+ desc: "Colorize diff output (auto-detected by default)"
165
164
  method_option :verbose,
166
165
  aliases: "-v",
167
166
  type: :boolean,
@@ -0,0 +1,141 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Canon
4
+ # Detects whether the current terminal supports color output.
5
+ #
6
+ # This class provides cross-platform detection of terminal color capabilities
7
+ # by checking environment variables and, on Unix-like systems, optionally
8
+ # querying the terminfo database.
9
+ #
10
+ # == Detection Logic
11
+ #
12
+ # The detection follows this priority order:
13
+ #
14
+ # 1. **NO_COLOR**: If set (regardless of value), colors are disabled
15
+ # (per https://no-color.org/)
16
+ # 2. **Explicit user choice**: If explicitly set, honor that choice
17
+ # 3. **Terminal capability detection**:
18
+ # - COLORTERM=24bit or truecolor → True color support
19
+ # - TERM ending with 256 or 256color → 256-color support
20
+ # - TERM=dumb, TERM containing "emacs" → No color support
21
+ # - CI environment → Check specific CI variables
22
+ # - TTY detection → Only enable colors if output is to a TTY
23
+ #
24
+ # == Usage
25
+ #
26
+ # # Auto-detect
27
+ # ColorDetector.supports_color? # => true or false
28
+ #
29
+ # # Explicit choice (bypass detection)
30
+ # ColorDetector.supports_color?(explicit: true) # => true
31
+ # ColorDetector.supports_color?(explicit: false) # => false
32
+ #
33
+ # # With output stream check
34
+ # ColorDetector.supports_color?(output: $stdout)
35
+ #
36
+ class ColorDetector
37
+ # Environment variables that indicate color support
38
+ COLOR_TERM_VALUES = %w[24bit truecolor true].freeze
39
+ COLOR_TERM_SUFFIXES = %w[256 256color direct].freeze
40
+ NO_COLOR_TERMS = %w[dumb emacs].freeze
41
+ CI_ENV_VARS = %w[CI GITHUB_ACTIONS TRAVIS GITLAB_CI JENKINS_HOME].freeze
42
+
43
+ class << self
44
+ # Detect whether the current environment supports color output.
45
+ #
46
+ # @param explicit [Boolean, nil] Explicit user choice to bypass detection
47
+ # @param output [IO, nil] Output stream to check (default: $stdout)
48
+ # @return [Boolean] true if colors are supported, false otherwise
49
+ def supports_color?(explicit: nil, output: $stdout)
50
+ # 1. NO_COLOR always wins (per https://no-color.org/)
51
+ return false if ENV.key?("NO_COLOR")
52
+
53
+ # 2. Explicit user choice bypasses detection
54
+ return explicit unless explicit.nil?
55
+
56
+ # 3. Check if output is a TTY (don't use colors for piped/file output)
57
+ return false unless tty?(output)
58
+
59
+ # 4. Check terminal capability indicators
60
+ detect_from_env
61
+ end
62
+
63
+ private
64
+
65
+ # Check if output stream is a TTY
66
+ #
67
+ # @param io [IO] Output stream
68
+ # @return [Boolean] true if the stream is a TTY
69
+ def tty?(io)
70
+ return false unless io.respond_to?(:tty?)
71
+ return false unless io.respond_to?(:isatty)
72
+
73
+ # Ruby 2.5+ uses tty?, older uses isatty
74
+ io.tty? || io.isatty
75
+ rescue ArgumentError, IOError
76
+ # Stream might be closed or invalid
77
+ false
78
+ end
79
+
80
+ # Detect color support from environment variables
81
+ #
82
+ # @return [Boolean] true if colors appear to be supported
83
+ def detect_from_env
84
+ # Check for known color-capable terminals
85
+ colorterm = ENV["COLORTERM"]
86
+ return true if COLOR_TERM_VALUES.include?(colorterm)
87
+
88
+ # Check TERM variable
89
+ term = ENV["TERM"]
90
+ if term
91
+ # Known no-color terminals
92
+ return false if NO_COLOR_TERMS.any? { |t| term.include?(t) }
93
+ # Known color-capable terminals
94
+ return true if COLOR_TERM_SUFFIXES.any? { |s| term.end_with?(s) }
95
+ # Most modern terminals support basic ANSI colors
96
+ return true unless term.empty? || term == "unknown"
97
+ end
98
+
99
+ # Check CI environments
100
+ # Some CI systems support colors, others don't
101
+ return detect_ci_colors if ci_environment?
102
+
103
+ # Default: assume colors are supported on modern terminals
104
+ # This is a safe default for most use cases
105
+ true
106
+ end
107
+
108
+ # Detect if we're in a CI environment
109
+ #
110
+ # @return [Boolean] true if in a CI environment
111
+ def ci_environment?
112
+ CI_ENV_VARS.any? { |var| ENV.key?(var) }
113
+ end
114
+
115
+ # Detect color support in CI environments
116
+ #
117
+ # Different CI systems have different color support:
118
+ # - GitHub Actions: supports colors (explicit CI env vars)
119
+ # - Travis CI: supports colors
120
+ # - GitLab CI: supports colors
121
+ # - Jenkins: supports colors
122
+ # - Generic CI: check for specific TeamCity/Terminal variables
123
+ #
124
+ # @return [Boolean] true if CI environment likely supports colors
125
+ def detect_ci_colors
126
+ # GitHub Actions explicitly supports colors
127
+ return true if ENV["GITHUB_ACTIONS"]
128
+
129
+ # TeamCity supports colors with specific env var
130
+ return true if ENV["TEAMCITY_VERSION"]
131
+
132
+ # Most modern CI systems support ANSI colors
133
+ # Only disable for explicitly known non-color CI
134
+ return false if ENV["TERM"] == "dumb"
135
+
136
+ # Default to supporting colors in CI
137
+ true
138
+ end
139
+ end
140
+ end
141
+ end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require_relative "../comparison"
4
4
  require_relative "../diff_formatter"
5
+ require_relative "../color_detector"
5
6
  require "json"
6
7
  require "yaml"
7
8
 
@@ -48,7 +49,7 @@ module Canon
48
49
 
49
50
  # Format and output results
50
51
  formatter = Canon::DiffFormatter.new(
51
- use_color: @options[:color],
52
+ use_color: resolve_color_option,
52
53
  mode: mode,
53
54
  context_lines: @options.fetch(:context_lines, 3),
54
55
  diff_grouping_lines: @options[:diff_grouping_lines],
@@ -64,7 +65,7 @@ module Canon
64
65
  config_output = Canon::DiffFormatter::DebugOutput.verbose_tables_only(
65
66
  result,
66
67
  {
67
- use_color: @options[:color],
68
+ use_color: resolve_color_option,
68
69
  mode: mode,
69
70
  context_lines: @options.fetch(:context_lines, 3),
70
71
  diff_grouping_lines: @options[:diff_grouping_lines],
@@ -278,6 +279,19 @@ module Canon
278
279
  5_242_880 # Default 5MB
279
280
  end
280
281
  end
282
+
283
+ # Resolve color option with auto-detection
284
+ #
285
+ # Returns the user's explicit choice if provided, otherwise
286
+ # auto-detects terminal color support.
287
+ #
288
+ # @return [Boolean] true if colors should be used
289
+ def resolve_color_option
290
+ return @options[:color] unless @options[:color].nil?
291
+
292
+ # Auto-detect when no explicit choice was made
293
+ ColorDetector.supports_color?
294
+ end
281
295
  end
282
296
  end
283
297
  end
data/lib/canon/config.rb CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  require_relative "config/env_provider"
4
4
  require_relative "config/override_resolver"
5
+ require_relative "color_detector"
5
6
 
6
7
  module Canon
7
8
  # Global configuration for Canon
@@ -304,7 +305,7 @@ module Canon
304
305
  def build_resolver(format)
305
306
  defaults = {
306
307
  mode: :by_line,
307
- use_color: true,
308
+ use_color: ColorDetector.supports_color?,
308
309
  context_lines: 3,
309
310
  grouping_lines: 10,
310
311
  show_diffs: :all,
data/lib/canon/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Canon
4
- VERSION = "0.1.11"
4
+ VERSION = "0.1.12"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: canon
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.11
4
+ version: 0.1.12
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
@@ -193,6 +193,7 @@ files:
193
193
  - lib/canon.rb
194
194
  - lib/canon/cache.rb
195
195
  - lib/canon/cli.rb
196
+ - lib/canon/color_detector.rb
196
197
  - lib/canon/commands/diff_command.rb
197
198
  - lib/canon/commands/format_command.rb
198
199
  - lib/canon/comparison.rb