Class: Canon::Diff::DiffCharRange

Inherits:
Object
  • Object
show all
Defined in:
lib/canon/diff/diff_char_range.rb

Overview

Represents a character range within a source line, linked to a DiffNode.

DiffCharRange is the core abstraction for character-level diff rendering. Each DiffNode produces one or more DiffCharRanges (before-text, changed-text, after-text) that tell the formatter exactly which characters to highlight.

The formatter reads DiffCharRanges and applies colors — no computation needed.

Examples:

Text change “Hello World” → “Hello Universe”

# Before-text (unchanged):
DiffCharRange.new(line_number: 0, start_col: 9, end_col: 15,
                  side: :old, status: :unchanged, role: :before, diff_node: dn)
# Changed-text (old side):
DiffCharRange.new(line_number: 0, start_col: 15, end_col: 20,
                  side: :old, status: :changed_old, role: :changed, diff_node: dn)
# Changed-text (new side):
DiffCharRange.new(line_number: 0, start_col: 15, end_col: 23,
                  side: :new, status: :changed_new, role: :changed, diff_node: dn)

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(line_number:, start_col:, end_col:, side:, status:, role: nil, diff_node: nil) ⇒ DiffCharRange

Returns a new instance of DiffCharRange.

Parameters:

  • line_number (Integer)

    0-based line index

  • start_col (Integer)

    0-based column (inclusive)

  • end_col (Integer)

    exclusive end

  • side (Symbol)

    :old or :new

  • status (Symbol)

    :unchanged, :removed, :added, :changed_old, :changed_new

  • role (Symbol) (defaults to: nil)

    :before, :changed, :after

  • diff_node (DiffNode, nil) (defaults to: nil)

    originating DiffNode



52
53
54
55
56
57
58
59
60
61
# File 'lib/canon/diff/diff_char_range.rb', line 52

def initialize(line_number:, start_col:, end_col:, side:, status:,
               role: nil, diff_node: nil)
  @line_number = line_number
  @start_col = start_col
  @end_col = end_col
  @side = side
  @status = status
  @role = role
  @diff_node = diff_node
end

Instance Attribute Details

#diff_nodeDiffNode? (readonly)

Returns the originating DiffNode.

Returns:

  • (DiffNode, nil)

    the originating DiffNode



43
44
45
# File 'lib/canon/diff/diff_char_range.rb', line 43

def diff_node
  @diff_node
end

#end_colInteger (readonly)

Returns exclusive end (half-open [start_col, end_col)).

Returns:

  • (Integer)

    exclusive end (half-open [start_col, end_col))



31
32
33
# File 'lib/canon/diff/diff_char_range.rb', line 31

def end_col
  @end_col
end

#line_numberInteger (readonly)

Returns 0-based line index in text1 or text2.

Returns:

  • (Integer)

    0-based line index in text1 or text2



25
26
27
# File 'lib/canon/diff/diff_char_range.rb', line 25

def line_number
  @line_number
end

#roleSymbol (readonly)

Returns :before, :changed, :after.

Returns:

  • (Symbol)

    :before, :changed, :after



40
41
42
# File 'lib/canon/diff/diff_char_range.rb', line 40

def role
  @role
end

#sideSymbol (readonly)

Returns :old (text1) or :new (text2).

Returns:

  • (Symbol)

    :old (text1) or :new (text2)



34
35
36
# File 'lib/canon/diff/diff_char_range.rb', line 34

def side
  @side
end

#start_colInteger (readonly)

Returns 0-based column offset (inclusive start).

Returns:

  • (Integer)

    0-based column offset (inclusive start)



28
29
30
# File 'lib/canon/diff/diff_char_range.rb', line 28

def start_col
  @start_col
end

#statusSymbol (readonly)

Returns :unchanged, :removed, :added, :changed_old, :changed_new.

Returns:

  • (Symbol)

    :unchanged, :removed, :added, :changed_old, :changed_new



37
38
39
# File 'lib/canon/diff/diff_char_range.rb', line 37

def status
  @status
end

Instance Method Details

#==(other) ⇒ Object



129
130
131
132
133
134
135
136
137
# File 'lib/canon/diff/diff_char_range.rb', line 129

def ==(other)
  other.is_a?(DiffCharRange) &&
    line_number == other.line_number &&
    start_col == other.start_col &&
    end_col == other.end_col &&
    side == other.side &&
    status == other.status &&
    role == other.role
end

#changed_new?Boolean

Returns true if this range represents a change on the new side.

Returns:

  • (Boolean)

    true if this range represents a change on the new side



109
110
111
# File 'lib/canon/diff/diff_char_range.rb', line 109

def changed_new?
  status == :changed_new
end

#changed_old?Boolean

Returns true if this range represents a change on the old side.

Returns:

  • (Boolean)

    true if this range represents a change on the old side



104
105
106
# File 'lib/canon/diff/diff_char_range.rb', line 104

def changed_old?
  status == :changed_old
end

#covers_entire_line?(line_length) ⇒ Boolean

Returns true if this range covers the entire line.

Parameters:

  • line_length (Integer)

    total length of the line

Returns:

  • (Boolean)

    true if this range covers the entire line



75
76
77
# File 'lib/canon/diff/diff_char_range.rb', line 75

def covers_entire_line?(line_length)
  start_col.zero? && end_col >= line_length
end

#empty?Boolean

Returns true if this range has zero length.

Returns:

  • (Boolean)

    true if this range has zero length



64
65
66
# File 'lib/canon/diff/diff_char_range.rb', line 64

def empty?
  start_col >= end_col
end

#extract_from(line_text) ⇒ String

Extract the substring this range covers from a line

Parameters:

  • line_text (String)

    the full line text

Returns:

  • (String)

    the substring, or “” if out of bounds



82
83
84
85
86
# File 'lib/canon/diff/diff_char_range.rb', line 82

def extract_from(line_text)
  return "" if line_text.nil? || empty?

  line_text[start_col...end_col] || ""
end

#highlighted?Boolean

Returns true if this range should be highlighted.

Returns:

  • (Boolean)

    true if this range should be highlighted



114
115
116
# File 'lib/canon/diff/diff_char_range.rb', line 114

def highlighted?
  %i[changed_old changed_new removed added].include?(status)
end

#lengthInteger

Returns number of characters in this range.

Returns:

  • (Integer)

    number of characters in this range



69
70
71
# File 'lib/canon/diff/diff_char_range.rb', line 69

def length
  end_col - start_col
end

#new_side?Boolean

Returns true if this is a new-side (text2) range.

Returns:

  • (Boolean)

    true if this is a new-side (text2) range



94
95
96
# File 'lib/canon/diff/diff_char_range.rb', line 94

def new_side?
  side == :new
end

#old_side?Boolean

Returns true if this is an old-side (text1) range.

Returns:

  • (Boolean)

    true if this is an old-side (text1) range



89
90
91
# File 'lib/canon/diff/diff_char_range.rb', line 89

def old_side?
  side == :old
end

#to_hObject



118
119
120
121
122
123
124
125
126
127
# File 'lib/canon/diff/diff_char_range.rb', line 118

def to_h
  {
    line_number: line_number,
    start_col: start_col,
    end_col: end_col,
    side: side,
    status: status,
    role: role,
  }
end

#unchanged?Boolean

Returns true if this range represents unchanged content.

Returns:

  • (Boolean)

    true if this range represents unchanged content



99
100
101
# File 'lib/canon/diff/diff_char_range.rb', line 99

def unchanged?
  status == :unchanged
end