Class: Riff

Inherits:
Object
  • Object
show all
Includes:
Colors
Defined in:
lib/riff.rb

Overview

Call do_stream() with the output of some diff-like tool (diff, diff3, git diff, …) and it will highlight that output for you.

Constant Summary collapse

DIFF_HEADER =
/^diff /
DIFF_HUNK_HEADER =
/^@@ /
DIFF_REPLACED_FILE_HEADER =
/^---/
DIFF_ADDED =
/^\+(.*)/
DIFF_REMOVED =
/^-(.*)/
DIFF_CONTEXT =
/^ /
DIFF_NO_ENDING_NEWLINE =
/^\\/
LINE_PREFIX =
{
  initial:          '',
  diff_header:      BOLD,
  diff_hunk_header: CYAN,
  diff_hunk:        '',
  diff_added:       GREEN,
  diff_removed:     RED,
  diff_context:     '',
  diff_no_ending_newline: ''
}

Constants included from Colors

Colors::BOLD, Colors::CYAN, Colors::DEFAULT_COLOR, Colors::ESC, Colors::GREEN, Colors::NOT_REVERSE, Colors::RED, Colors::RESET, Colors::REVERSE

Instance Method Summary collapse

Methods included from Colors

#reversed, #uncolor

Constructor Details

#initializeRiff

Returns a new instance of Riff.



30
31
32
33
34
35
# File 'lib/riff.rb', line 30

def initialize()
  @state = :initial

  @replace_old = ''
  @replace_new = ''
end

Instance Method Details

#consume_replacementObject

If we have stored adds / removes, calling this method will flush those.



109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/riff.rb', line 109

def consume_replacement()
  return '' if @replace_old.empty? && @replace_new.empty?

  refiner = Refiner.new(@replace_old, @replace_new)
  return_me = refiner.refined_old
  return_me += refiner.refined_new

  @replace_old = ''
  @replace_new = ''

  return return_me
end

#do_stream(diff_stream) ⇒ Object

Read diff from a stream and output a highlighted version to stdout



156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/riff.rb', line 156

def do_stream(diff_stream)
  output = ''
  current_line = nil

  begin
    diff_stream.each do |line|
      current_line = line
      output += handle_diff_line(line)
    end
    output += consume_replacement()
  rescue
    STDERR.puts "State: <#{@state}>"
    STDERR.puts "Current line: <#{current_line}>"
    raise
  end

  return output
end

#handle_diff_added_line(line) ⇒ Object



91
92
93
# File 'lib/riff.rb', line 91

def handle_diff_added_line(line)
  handle_diff_hunk_line(line)
end

#handle_diff_context_line(line) ⇒ Object



99
100
101
# File 'lib/riff.rb', line 99

def handle_diff_context_line(line)
  handle_diff_hunk_line(line)
end

#handle_diff_header_line(line) ⇒ Object



45
46
47
48
49
# File 'lib/riff.rb', line 45

def handle_diff_header_line(line)
  if line =~ DIFF_HUNK_HEADER
    @state = :diff_hunk_header
  end
end

#handle_diff_hunk_header_line(line) ⇒ Object



51
52
53
# File 'lib/riff.rb', line 51

def handle_diff_hunk_header_line(line)
  handle_diff_hunk_line(line)
end

#handle_diff_hunk_line(line) ⇒ Object



72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/riff.rb', line 72

def handle_diff_hunk_line(line)
  case line
  when DIFF_HUNK_HEADER
    @state = :diff_hunk_header
  when DIFF_HEADER
    @state = :diff_header
  when DIFF_ADDED
    @state = :diff_added
  when DIFF_REMOVED
    @state = :diff_removed
  when DIFF_CONTEXT
    @state = :diff_context
  when DIFF_NO_ENDING_NEWLINE
    handle_no_ending_newline(line)
  else
    fail NotImplementedError, "Can't handle <#{@state}> line: <#{line}>"
  end
end

#handle_diff_line(line) ⇒ Object



131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/riff.rb', line 131

def handle_diff_line(line)
  line.chomp!
  line = uncolor(line)

  handle_line_for_state(@state, line)

  case @state
  when :diff_added
    @replace_new += DIFF_ADDED.match(line)[1] + "\n"
    return ''
  when :diff_removed
    @replace_old += DIFF_REMOVED.match(line)[1] + "\n"
    return ''
  when :diff_no_ending_newline
    return ''
  else
    refined = consume_replacement()

    color = LINE_PREFIX.fetch(@state)

    return refined + DiffString.decorate_string('', color, line + "\n")
  end
end

#handle_diff_no_ending_newline_line(line) ⇒ Object



103
104
105
# File 'lib/riff.rb', line 103

def handle_diff_no_ending_newline_line(line)
  handle_diff_hunk_line(line)
end

#handle_diff_removed_line(line) ⇒ Object



95
96
97
# File 'lib/riff.rb', line 95

def handle_diff_removed_line(line)
  handle_diff_hunk_line(line)
end

#handle_initial_line(line) ⇒ Object



37
38
39
40
41
42
43
# File 'lib/riff.rb', line 37

def handle_initial_line(line)
  if line =~ DIFF_HEADER
    @state = :diff_header
  elsif line =~ DIFF_REPLACED_FILE_HEADER
    @state = :diff_header
  end
end

#handle_line_for_state(state, line) ⇒ Object

Call handle_<state>_line() for the given state and line



123
124
125
126
127
128
129
# File 'lib/riff.rb', line 123

def handle_line_for_state(state, line)
  method_name = "handle_#{state}_line"
  fail "Unknown state: <:#{state}>" unless
    self.respond_to? method_name

  send(method_name, line)
end

#handle_no_ending_newline(line) ⇒ Object



55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/riff.rb', line 55

def handle_no_ending_newline(line)
  case @state
  when :diff_added
    @replace_new.sub!(/\n$/, '')
  when :diff_removed
    @replace_old.sub!(/\n$/, '')
  when :diff_context
    # Intentionally ignored
    return
  else
    fail NotImplementedError,
         "Can't handle no-ending-newline in <#{@state}> line: <#{line}>"
  end

  @state = :diff_no_ending_newline
end