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) ⇒ BaseFormatter

Returns a new instance of BaseFormatter.



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/canon/diff_formatter/by_line/base_formatter.rb', line 48

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)
  @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
end

Instance Attribute Details

#context_linesObject (readonly)

Returns the value of attribute context_lines.



14
15
16
# File 'lib/canon/diff_formatter/by_line/base_formatter.rb', line 14

def context_lines
  @context_lines
end

#diff_grouping_linesObject (readonly)

Returns the value of attribute diff_grouping_lines.



14
15
16
# File 'lib/canon/diff_formatter/by_line/base_formatter.rb', line 14

def diff_grouping_lines
  @diff_grouping_lines
end

#diff_modeObject (readonly)

Returns the value of attribute diff_mode.



14
15
16
# File 'lib/canon/diff_formatter/by_line/base_formatter.rb', line 14

def diff_mode
  @diff_mode
end

#legacy_terminalObject (readonly)

Returns the value of attribute legacy_terminal.



14
15
16
# File 'lib/canon/diff_formatter/by_line/base_formatter.rb', line 14

def legacy_terminal
  @legacy_terminal
end

#show_diffsObject (readonly)

Returns the value of attribute show_diffs.



14
15
16
# File 'lib/canon/diff_formatter/by_line/base_formatter.rb', line 14

def show_diffs
  @show_diffs
end

#use_colorObject (readonly)

Returns the value of attribute use_color.



14
15
16
# File 'lib/canon/diff_formatter/by_line/base_formatter.rb', line 14

def use_color
  @use_color
end

#visualization_mapObject (readonly)

Returns the value of attribute visualization_map.



14
15
16
# File 'lib/canon/diff_formatter/by_line/base_formatter.rb', line 14

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:



22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/canon/diff_formatter/by_line/base_formatter.rb', line 22

def self.for_format(format, **options)
  case format
  when :xml
    require_relative "xml_formatter"
    XmlFormatter.new(**options)
  when :html, :html4, :html5
    require_relative "html_formatter"
    # 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
    require_relative "json_formatter"
    JsonFormatter.new(**options)
  when :yaml
    require_relative "yaml_formatter"
    YamlFormatter.new(**options)
  else
    require_relative "simple_formatter"
    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



240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
# File 'lib/canon/diff_formatter/by_line/base_formatter.rb', line 240

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



230
231
232
233
234
# File 'lib/canon/diff_formatter/by_line/base_formatter.rb', line 230

def apply_color(presenter, color)
  valid_colors = normalize_color_for_rainbow(color)
  valid_colors.each { |c| presenter = presenter.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



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
111
112
113
114
# File 'lib/canon/diff_formatter/by_line/base_formatter.rb', line 85

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



143
144
145
146
147
148
# File 'lib/canon/diff_formatter/by_line/base_formatter.rb', line 143

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



117
118
119
120
# File 'lib/canon/diff_formatter/by_line/base_formatter.rb', line 117

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



137
138
139
# File 'lib/canon/diff_formatter/by_line/base_formatter.rb', line 137

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

#display_modeSymbol

Get display mode

Returns:

  • (Symbol)

    :separate, :inline, :mixed



170
171
172
# File 'lib/canon/diff_formatter/by_line/base_formatter.rb', line 170

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)


262
263
264
265
# File 'lib/canon/diff_formatter/by_line/base_formatter.rb', line 262

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



130
131
132
# File 'lib/canon/diff_formatter/by_line/base_formatter.rb', line 130

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



207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
# File 'lib/canon/diff_formatter/by_line/base_formatter.rb', line 207

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



196
197
198
# File 'lib/canon/diff_formatter/by_line/base_formatter.rb', line 196

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

#structure_stylesHash

Get structure styles

Returns:

  • (Hash)

    Keys: :line_number, :pipe, :context



158
159
160
# File 'lib/canon/diff_formatter/by_line/base_formatter.rb', line 158

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



178
179
180
181
182
183
# File 'lib/canon/diff_formatter/by_line/base_formatter.rb', line 178

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



68
69
70
# File 'lib/canon/diff_formatter/by_line/base_formatter.rb', line 68

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



189
190
191
# File 'lib/canon/diff_formatter/by_line/base_formatter.rb', line 189

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



77
78
79
# File 'lib/canon/diff_formatter/by_line/base_formatter.rb', line 77

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



152
153
154
# File 'lib/canon/diff_formatter/by_line/base_formatter.rb', line 152

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

#visualization_charsHash

Get visualization characters

Returns:

  • (Hash)

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



164
165
166
# File 'lib/canon/diff_formatter/by_line/base_formatter.rb', line 164

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