Class: Canon::DiffFormatter::ByLine::BaseFormatter

Inherits:
Object
  • Object
show all
Defined in:
lib/canon/diff_formatter/by_line/base_formatter.rb

Overview

Base formatter for line-by-line diffs Provides common LCS diff logic and hunk building

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(use_color: true, context_lines: 3, diff_grouping_lines: nil, visualization_map: nil, show_diffs: :all, differences: [], diff_mode: :separate, legacy_terminal: false, equivalent: nil, theme: nil, character_visualization: true) ⇒ BaseFormatter

rubocop:disable-next Metrics/ParameterLists



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/canon/diff_formatter/by_line/base_formatter.rb', line 42

def initialize(use_color: true, context_lines: 3,
               diff_grouping_lines: nil, visualization_map: nil,
               show_diffs: :all, differences: [],
               diff_mode: :separate, legacy_terminal: false,
               equivalent: nil, theme: nil,
               character_visualization: true)
  @use_color = use_color
  @context_lines = context_lines
  @diff_grouping_lines = diff_grouping_lines
  @visualization_map = visualization_map
  @show_diffs = show_diffs
  @differences = differences
  @line_num_width = 4
  @diff_mode = legacy_terminal ? :separate : diff_mode
  @legacy_terminal = legacy_terminal
  @equivalent = equivalent
  @theme = theme
  @character_visualization = character_visualization
end

Instance Attribute Details

#context_linesObject (readonly)

Returns the value of attribute context_lines.



12
13
14
# File 'lib/canon/diff_formatter/by_line/base_formatter.rb', line 12

def context_lines
  @context_lines
end

#diff_grouping_linesObject (readonly)

Returns the value of attribute diff_grouping_lines.



12
13
14
# File 'lib/canon/diff_formatter/by_line/base_formatter.rb', line 12

def diff_grouping_lines
  @diff_grouping_lines
end

#diff_modeObject (readonly)

Returns the value of attribute diff_mode.



12
13
14
# File 'lib/canon/diff_formatter/by_line/base_formatter.rb', line 12

def diff_mode
  @diff_mode
end

#legacy_terminalObject (readonly)

Returns the value of attribute legacy_terminal.



12
13
14
# File 'lib/canon/diff_formatter/by_line/base_formatter.rb', line 12

def legacy_terminal
  @legacy_terminal
end

#show_diffsObject (readonly)

Returns the value of attribute show_diffs.



12
13
14
# File 'lib/canon/diff_formatter/by_line/base_formatter.rb', line 12

def show_diffs
  @show_diffs
end

#use_colorObject (readonly)

Returns the value of attribute use_color.



12
13
14
# File 'lib/canon/diff_formatter/by_line/base_formatter.rb', line 12

def use_color
  @use_color
end

#visualization_mapObject (readonly)

Returns the value of attribute visualization_map.



12
13
14
# File 'lib/canon/diff_formatter/by_line/base_formatter.rb', line 12

def visualization_map
  @visualization_map
end

Class Method Details

.for_format(format, **options) ⇒ BaseFormatter

Create a format-specific by-line formatter

Parameters:

  • format (Symbol)

    Format type (:xml, :html, :html4, :html5, :json, :yaml, :simple)

  • options (Hash)

    Formatting options

Returns:



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/canon/diff_formatter/by_line/base_formatter.rb', line 20

def self.for_format(format, **options)
  case format
  when :xml
    XmlFormatter.new(**options)
  when :html, :html4, :html5
    # Determine HTML version from format
    version = case format
              when :html5 then :html5
              when :html4 then :html4
              else :html4 # default to html4
              end
    HtmlFormatter.new(html_version: version, **options)
  when :json
    JsonFormatter.new(**options)
  when :yaml
    YamlFormatter.new(**options)
  else
    SimpleFormatter.new(**options)
  end
end

Instance Method Details

#apply_bg(presenter, bg_color) ⇒ Rainbow::Presenter

Apply a background color to a Rainbow presenter.

Parameters:

  • presenter (Rainbow::Presenter)

    The presenter to colorize

  • bg_color (Symbol)

    Background color like :red, :light_blue, etc.

Returns:

  • (Rainbow::Presenter)

    Colorized presenter



236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
# File 'lib/canon/diff_formatter/by_line/base_formatter.rb', line 236

def apply_bg(presenter, bg_color)
  return presenter unless bg_color

  case bg_color.to_s
  when /^light_(.+)$/
    # Rainbow doesn't support light_ backgrounds, use the base color
    base = $1.to_sym
    presenter.background(base)
  when "default", "black", "red", "green", "yellow", "blue", "magenta", "cyan", "white"
    presenter.background(bg_color)
  else
    # Try as-is and let Rainbow handle unknown colors
    presenter.background(bg_color)
  end
end

#apply_color(presenter, color) ⇒ Rainbow::Presenter

Apply a color to a Rainbow presenter, normalizing bright_/light_ colors.

Parameters:

  • presenter (Rainbow::Presenter)

    The presenter to colorize

  • color (Symbol)

    Color like :bright_blue, :red, etc.

Returns:

  • (Rainbow::Presenter)

    Colorized presenter



226
227
228
229
230
# File 'lib/canon/diff_formatter/by_line/base_formatter.rb', line 226

def apply_color(presenter, color)
  valid_colors = normalize_color_for_rainbow(color)
  valid_colors.each { |c| presenter = presenter.public_send(c) }
  presenter
end

#apply_theme_style(text, style) ⇒ String

Apply full theme styling to text

Parameters:

  • text (String)

    Text to style

  • style (Hash)

    Style properties from theme (color, bg, bold, underline, strikethrough)

Returns:

  • (String)

    Styled text



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/canon/diff_formatter/by_line/base_formatter.rb', line 81

def apply_theme_style(text, style)
  return text if style.empty? || !@use_color

  color = style[:color]
  bg = style[:bg]
  bold = style[:bold]
  underline = style[:underline]
  strikethrough = style[:strikethrough]

  # Apply visualization first
  visual = apply_visualization(text)

  return visual unless color || bg || bold || underline || strikethrough

  require "rainbow"
  rainbow = Rainbow.new
  rainbow.enabled = true
  presenter = rainbow.wrap(visual)

  if color && color != :default
    presenter = apply_color(presenter,
                            color)
  end
  presenter = apply_bg(presenter, bg) if bg
  presenter = presenter.bold if bold
  presenter = presenter.underline if underline
  presenter = presenter.cross_out if strikethrough

  presenter.to_s
end

#changed_content_stylesHash

Get changed content styles (old and new)

Returns:

  • (Hash)

    Keys: :content_old, :content_new



139
140
141
142
143
144
# File 'lib/canon/diff_formatter/by_line/base_formatter.rb', line 139

def changed_content_styles
  {
    content_old: theme_style(:diff, :changed, :content_old),
    content_new: theme_style(:diff, :changed, :content_new),
  }
end

#compute_line_num_width(doc1, doc2) ⇒ Object

Compute line number column width from document line counts



113
114
115
116
# File 'lib/canon/diff_formatter/by_line/base_formatter.rb', line 113

def compute_line_num_width(doc1, doc2)
  max_lines = [doc1.count("\n"), doc2.count("\n")].max
  @line_num_width = [max_lines.to_s.length, 4].max
end

#content_style(diff_type) ⇒ Hash

Get content style for a diff type

Parameters:

  • diff_type (Symbol)

    :removed, :added, :formatting, :informative

Returns:

  • (Hash)

    Style properties



133
134
135
# File 'lib/canon/diff_formatter/by_line/base_formatter.rb', line 133

def content_style(diff_type)
  theme_style(:diff, diff_type, :content)
end

#display_modeSymbol

Get display mode

Returns:

  • (Symbol)

    :separate, :inline, :mixed



166
167
168
# File 'lib/canon/diff_formatter/by_line/base_formatter.rb', line 166

def display_mode
  theme[:display_mode] || :separate
end

#format(doc1, doc2) ⇒ String

Format line-by-line diff Subclasses must implement this method

Parameters:

  • doc1 (String)

    First document

  • doc2 (String)

    Second document

Returns:

  • (String)

    Formatted diff

Raises:

  • (NotImplementedError)


258
259
260
261
# File 'lib/canon/diff_formatter/by_line/base_formatter.rb', line 258

def format(doc1, doc2)
  raise NotImplementedError,
        "Subclasses must implement the format method"
end

#marker_style(diff_type) ⇒ Hash

Get marker style for a diff type

Parameters:

  • diff_type (Symbol)

    :removed, :added, :changed, :formatting, :informative

Returns:

  • (Hash)

    Style properties



126
127
128
# File 'lib/canon/diff_formatter/by_line/base_formatter.rb', line 126

def marker_style(diff_type)
  theme_style(:diff, diff_type, :marker)
end

#normalize_color_for_rainbow(color) ⇒ Array<Symbol>

Normalize a color symbol for Rainbow presenter. Rainbow doesn't support :bright_blue directly - instead it uses chained methods like .blue.bright or .bright.blue. This returns an array of method symbols to chain.

Parameters:

  • color (Symbol)

    Color like :bright_blue, :light_red, etc.

Returns:

  • (Array<Symbol>)

    Method chain for Rainbow



203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
# File 'lib/canon/diff_formatter/by_line/base_formatter.rb', line 203

def normalize_color_for_rainbow(color)
  return [] if color.nil?

  case color.to_s
  when /^bright_(.+)$/
    # :bright_blue -> [:blue, :bright]
    base = $1.to_sym
    [base, :bright]
  when /^light_(.+)$/
    # :light_red -> Rainbow doesn't support light_, treat as white
    [:white]
  when "default", "black", "red", "green", "yellow", "blue", "magenta", "cyan", "white"
    [color]
  else
    # Unknown color, return as-is and let Rainbow raise
    [color]
  end
end

#structure_color(element) ⇒ Symbol?

Get structure color

Parameters:

  • element (Symbol)

    :line_number, :pipe, :context

Returns:

  • (Symbol, nil)

    Color value



192
193
194
# File 'lib/canon/diff_formatter/by_line/base_formatter.rb', line 192

def structure_color(element)
  theme.dig(:structure, element, :color)
end

#structure_stylesHash

Get structure styles

Returns:

  • (Hash)

    Keys: :line_number, :pipe, :context



154
155
156
# File 'lib/canon/diff_formatter/by_line/base_formatter.rb', line 154

def structure_styles
  theme[:structure] || {}
end

#styled_marker(text, diff_type) ⇒ String

Apply marker styling using theme

Parameters:

  • text (String)

    Marker text (e.g., "-", "+", "*")

  • diff_type (Symbol)

    Type of diff

Returns:

  • (String)

    Styled marker



174
175
176
177
178
179
# File 'lib/canon/diff_formatter/by_line/base_formatter.rb', line 174

def styled_marker(text, diff_type)
  style = marker_style(diff_type)
  return text unless @use_color && style[:color]

  apply_theme_style(text, style)
end

#themeHash

Get the resolved theme hash

Returns:

  • (Hash)

    Theme hash



64
65
66
# File 'lib/canon/diff_formatter/by_line/base_formatter.rb', line 64

def theme
  @theme ||= Theme.resolver(Canon::Config.instance).resolve
end

#theme_color(diff_type, element) ⇒ Symbol?

Get theme color for a specific diff type and element

Parameters:

  • diff_type (Symbol)

    :removed, :added, :changed, :formatting, :informative

  • element (Symbol)

    :marker, :content, :content_old, :content_new

Returns:

  • (Symbol, nil)

    Color value



185
186
187
# File 'lib/canon/diff_formatter/by_line/base_formatter.rb', line 185

def theme_color(diff_type, element)
  theme_style(:diff, diff_type, element)[:color]
end

#theme_style(section, diff_type, element) ⇒ Hash

Get theme by section and type

Parameters:

  • section (Symbol)

    e.g., :diff, :xml, :structure

  • diff_type (Symbol)

    e.g., :removed, :added, :changed

  • element (Symbol)

    e.g., :marker, :content

Returns:

  • (Hash)

    Style properties



73
74
75
# File 'lib/canon/diff_formatter/by_line/base_formatter.rb', line 73

def theme_style(section, diff_type, element)
  theme.dig(section, diff_type, element) || {}
end

#unchanged_content_styleHash

Get style for unchanged content

Returns:

  • (Hash)

    Style properties



148
149
150
# File 'lib/canon/diff_formatter/by_line/base_formatter.rb', line 148

def unchanged_content_style
  theme_style(:diff, :unchanged, :content)
end

#visualization_charsHash

Get visualization characters

Returns:

  • (Hash)

    Keys: :space, :tab, :newline, :nbsp



160
161
162
# File 'lib/canon/diff_formatter/by_line/base_formatter.rb', line 160

def visualization_chars
  theme[:visualization] || {}
end