/
html_formatter.rb
132 lines (104 loc) · 3.68 KB
/
html_formatter.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# frozen_string_literal: true
require 'cgi'
require 'erb'
require 'ostruct'
require 'base64'
module RuboCop
module Formatter
# This formatter saves the output as an html file.
class HTMLFormatter < BaseFormatter
ELLIPSES = '<span class="extra-code">...</span>'
TEMPLATE_PATH = File.expand_path('../../../assets/output.html.erb', __dir__)
Color = Struct.new(:red, :green, :blue, :alpha) do
def to_s
"rgba(#{values.join(', ')})"
end
def fade_out(amount)
dup.tap { |color| color.alpha -= amount }
end
end
Summary = Struct.new(:offense_count, :inspected_files, :target_files, keyword_init: true)
FileOffenses = Struct.new(:path, :offenses, keyword_init: true)
attr_reader :files, :summary
def initialize(output, options = {})
super
@files = []
@summary = Summary.new(offense_count: 0)
end
def started(target_files)
summary.target_files = target_files
end
def file_finished(file, offenses)
files << FileOffenses.new(path: file, offenses: offenses)
summary.offense_count += offenses.count
end
def finished(inspected_files)
summary.inspected_files = inspected_files
render_html
end
def render_html
context = ERBContext.new(files, summary)
template = File.read(TEMPLATE_PATH, encoding: Encoding::UTF_8)
erb = ERB.new(template, trim_mode: '-')
html = erb.result(context.binding)
output.write html
end
# This class provides helper methods used in the ERB template.
class ERBContext
include PathUtil
include TextUtil
SEVERITY_COLORS = {
refactor: Color.new(0xED, 0x9C, 0x28, 1.0),
convention: Color.new(0xED, 0x9C, 0x28, 1.0),
warning: Color.new(0x96, 0x28, 0xEF, 1.0),
error: Color.new(0xD2, 0x32, 0x2D, 1.0),
fatal: Color.new(0xD2, 0x32, 0x2D, 1.0)
}.freeze
LOGO_IMAGE_PATH = File.expand_path('../../../assets/logo.png', __dir__)
attr_reader :files, :summary
def initialize(files, summary)
@files = files.sort_by(&:path)
@summary = summary
end
# Make Kernel#binding public.
# rubocop:disable Lint/UselessMethodDefinition
def binding
super
end
# rubocop:enable Lint/UselessMethodDefinition
def decorated_message(offense)
offense.message.gsub(/`(.+?)`/) { "<code>#{Regexp.last_match(1)}</code>" }
end
def highlighted_source_line(offense)
source_before_highlight(offense) +
hightlight_source_tag(offense) +
source_after_highlight(offense) +
possible_ellipses(offense.location)
end
def hightlight_source_tag(offense)
"<span class=\"highlight #{offense.severity}\">" \
"#{escape(offense.highlighted_area.source)}" \
'</span>'
end
def source_before_highlight(offense)
source_line = offense.location.source_line
escape(source_line[0...offense.highlighted_area.begin_pos])
end
def source_after_highlight(offense)
source_line = offense.location.source_line
escape(source_line[offense.highlighted_area.end_pos..])
end
def possible_ellipses(location)
location.single_line? ? '' : " #{ELLIPSES}"
end
def escape(string)
CGI.escapeHTML(string)
end
def base64_encoded_logo_image
image = File.read(LOGO_IMAGE_PATH, binmode: true)
Base64.encode64(image)
end
end
end
end
end