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 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



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

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



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

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



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

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



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

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



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

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



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

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

#display_modeSymbol

Get display mode

Returns:

  • (Symbol)

    :separate, :inline, :mixed



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

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)


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

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



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

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



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

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



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

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

#structure_stylesHash

Get structure styles

Returns:

  • (Hash)

    Keys: :line_number, :pipe, :context



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

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



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

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



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

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



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

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



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

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



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

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

#visualization_charsHash

Get visualization characters

Returns:

  • (Hash)

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



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

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