Class: GitLabBuildOutput::Ansi2html::Converter

Inherits:
Object
  • Object
show all
Defined in:
lib/gitlab_build_output/ansi2html.rb

Constant Summary collapse

STATE_PARAMS =
[:offset, :n_open_tags, :fg_color, :bg_color, :style_mask].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#bg_colorObject

Returns the value of attribute bg_color.



134
135
136
# File 'lib/gitlab_build_output/ansi2html.rb', line 134

def bg_color
  @bg_color
end

#fg_colorObject

Returns the value of attribute fg_color.



134
135
136
# File 'lib/gitlab_build_output/ansi2html.rb', line 134

def fg_color
  @fg_color
end

#n_open_tagsObject

Returns the value of attribute n_open_tags.



134
135
136
# File 'lib/gitlab_build_output/ansi2html.rb', line 134

def n_open_tags
  @n_open_tags
end

#offsetObject

Returns the value of attribute offset.



134
135
136
# File 'lib/gitlab_build_output/ansi2html.rb', line 134

def offset
  @offset
end

#style_maskObject

Returns the value of attribute style_mask.



134
135
136
# File 'lib/gitlab_build_output/ansi2html.rb', line 134

def style_mask
  @style_mask
end

Instance Method Details

#close_open_tagsObject



255
256
257
258
259
260
# File 'lib/gitlab_build_output/ansi2html.rb', line 255

def close_open_tags
  while @n_open_tags > 0
    @out << %{</span>}
    @n_open_tags -= 1
  end
end

#convert(stream, new_state) ⇒ Object



138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
# File 'lib/gitlab_build_output/ansi2html.rb', line 138

def convert(stream, new_state)
  reset_state
  restore_state(new_state, stream) if new_state.present?

  append = false
  truncated = false

  cur_offset = stream.tell
  if cur_offset > @offset
    @offset = cur_offset
    truncated = true
  else
    stream.seek(@offset)
    append = @offset > 0
  end
  start_offset = @offset

  open_new_tag

  stream.each_line do |line|
    s = StringScanner.new(line)
    until s.eos?
      if s.scan(TRACE_SECTION_REGEX)
        handle_section(s)
      elsif s.scan(/\e([@-_])(.*?)([@-~])/)
        handle_sequence(s)
      elsif s.scan(/\e(([@-_])(.*?)?)?$/)
        break
      elsif s.scan(/</)
        @out << '&lt;'
      elsif s.scan(/\r?\n/)
        @out << '<br>'
      else
        @out << s.scan(/./m)
      end
      @offset += s.matched_size
    end
  end

  close_open_tags()

  OpenStruct.new(
    html: @out.force_encoding(Encoding.default_external),
    state: state,
    append: append,
    truncated: truncated,
    offset: start_offset,
    size: stream.tell - start_offset,
    total: stream.size
  )
end

#disable(flag) ⇒ Object



297
298
299
# File 'lib/gitlab_build_output/ansi2html.rb', line 297

def disable(flag)
  @style_mask &= ~flag
end

#enable(flag) ⇒ Object



293
294
295
# File 'lib/gitlab_build_output/ansi2html.rb', line 293

def enable(flag)
  @style_mask |= flag
end

#evaluate_command_stack(stack) ⇒ Object



221
222
223
224
225
226
227
228
229
# File 'lib/gitlab_build_output/ansi2html.rb', line 221

def evaluate_command_stack(stack)
  return unless command = stack.shift()

  if self.respond_to?("on_#{command}", true)
    self.__send__("on_#{command}", stack) # rubocop:disable GitlabSecurity/PublicSend
  end

  evaluate_command_stack(stack)
end

#get_color_class(segments) ⇒ Object



340
341
342
# File 'lib/gitlab_build_output/ansi2html.rb', line 340

def get_color_class(segments)
  [segments].flatten.compact.join('-')
end

#get_term_color_class(color_index, prefix) ⇒ Object



309
310
311
312
313
314
# File 'lib/gitlab_build_output/ansi2html.rb', line 309

def get_term_color_class(color_index, prefix)
  color_name = COLOR[color_index]
  return nil if color_name.nil?

  get_color_class(["term", prefix, color_name])
end

#get_xterm_color_class(command_stack, prefix) ⇒ Object



326
327
328
329
330
331
332
333
334
335
336
337
338
# File 'lib/gitlab_build_output/ansi2html.rb', line 326

def get_xterm_color_class(command_stack, prefix)
  # the 38 and 48 commands have to be followed by "5" and the color index
  return unless command_stack.length >= 2
  return unless command_stack[0] == "5"

  command_stack.shift() # ignore the "5" command
  color_index = command_stack.shift().to_i

  return unless color_index >= 0
  return unless color_index <= 255

  get_color_class(["xterm", prefix, color_index])
end

#handle_section(s) ⇒ Object



190
191
192
193
194
195
196
197
# File 'lib/gitlab_build_output/ansi2html.rb', line 190

def handle_section(s)
  action = s[1]
  timestamp = s[2]
  section = s[3]
  line = s.matched()[0...-5] # strips \r\033[0K

  @out << %{<div class="hidden" data-action="#{action}" data-timestamp="#{timestamp}" data-section="#{section}">#{line}</div>}
end

#handle_sequence(s) ⇒ Object



199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
# File 'lib/gitlab_build_output/ansi2html.rb', line 199

def handle_sequence(s)
  indicator = s[1]
  commands = s[2].split ';'
  terminator = s[3]

  # We are only interested in color and text style changes - triggered by
  # sequences starting with '\e[' and ending with 'm'. Any other control
  # sequence gets stripped (including stuff like "delete last line")
  return unless indicator == '[' && terminator == 'm'

  close_open_tags()

  if commands.empty?()
    reset()
    return
  end

  evaluate_command_stack(commands)

  open_new_tag
end

#on_0(s) ⇒ Object



34
# File 'lib/gitlab_build_output/ansi2html.rb', line 34

def on_0(s) reset()                            end

#on_1(s) ⇒ Object



36
# File 'lib/gitlab_build_output/ansi2html.rb', line 36

def on_1(s) enable(STYLE_SWITCHES[:bold])      end

#on_100(s) ⇒ Object



116
# File 'lib/gitlab_build_output/ansi2html.rb', line 116

def on_100(s) set_bg_color(0, 'l') end

#on_101(s) ⇒ Object



118
# File 'lib/gitlab_build_output/ansi2html.rb', line 118

def on_101(s) set_bg_color(1, 'l') end

#on_102(s) ⇒ Object



120
# File 'lib/gitlab_build_output/ansi2html.rb', line 120

def on_102(s) set_bg_color(2, 'l') end

#on_103(s) ⇒ Object



122
# File 'lib/gitlab_build_output/ansi2html.rb', line 122

def on_103(s) set_bg_color(3, 'l') end

#on_104(s) ⇒ Object



124
# File 'lib/gitlab_build_output/ansi2html.rb', line 124

def on_104(s) set_bg_color(4, 'l') end

#on_105(s) ⇒ Object



126
# File 'lib/gitlab_build_output/ansi2html.rb', line 126

def on_105(s) set_bg_color(5, 'l') end

#on_106(s) ⇒ Object



128
# File 'lib/gitlab_build_output/ansi2html.rb', line 128

def on_106(s) set_bg_color(6, 'l') end

#on_107(s) ⇒ Object



130
# File 'lib/gitlab_build_output/ansi2html.rb', line 130

def on_107(s) set_bg_color(7, 'l') end

#on_109(s) ⇒ Object



132
# File 'lib/gitlab_build_output/ansi2html.rb', line 132

def on_109(s) set_bg_color(9, 'l') end

#on_21(s) ⇒ Object



46
# File 'lib/gitlab_build_output/ansi2html.rb', line 46

def on_21(s) disable(STYLE_SWITCHES[:bold])      end

#on_22(s) ⇒ Object



48
# File 'lib/gitlab_build_output/ansi2html.rb', line 48

def on_22(s) disable(STYLE_SWITCHES[:bold])      end

#on_23(s) ⇒ Object



50
# File 'lib/gitlab_build_output/ansi2html.rb', line 50

def on_23(s) disable(STYLE_SWITCHES[:italic])    end

#on_24(s) ⇒ Object



52
# File 'lib/gitlab_build_output/ansi2html.rb', line 52

def on_24(s) disable(STYLE_SWITCHES[:underline]) end

#on_28(s) ⇒ Object



54
# File 'lib/gitlab_build_output/ansi2html.rb', line 54

def on_28(s) disable(STYLE_SWITCHES[:conceal])   end

#on_29(s) ⇒ Object



56
# File 'lib/gitlab_build_output/ansi2html.rb', line 56

def on_29(s) disable(STYLE_SWITCHES[:cross])     end

#on_3(s) ⇒ Object



38
# File 'lib/gitlab_build_output/ansi2html.rb', line 38

def on_3(s) enable(STYLE_SWITCHES[:italic])    end

#on_30(s) ⇒ Object



58
# File 'lib/gitlab_build_output/ansi2html.rb', line 58

def on_30(s) set_fg_color(0) end

#on_31(s) ⇒ Object



60
# File 'lib/gitlab_build_output/ansi2html.rb', line 60

def on_31(s) set_fg_color(1) end

#on_32(s) ⇒ Object



62
# File 'lib/gitlab_build_output/ansi2html.rb', line 62

def on_32(s) set_fg_color(2) end

#on_33(s) ⇒ Object



64
# File 'lib/gitlab_build_output/ansi2html.rb', line 64

def on_33(s) set_fg_color(3) end

#on_34(s) ⇒ Object



66
# File 'lib/gitlab_build_output/ansi2html.rb', line 66

def on_34(s) set_fg_color(4) end

#on_35(s) ⇒ Object



68
# File 'lib/gitlab_build_output/ansi2html.rb', line 68

def on_35(s) set_fg_color(5) end

#on_36(s) ⇒ Object



70
# File 'lib/gitlab_build_output/ansi2html.rb', line 70

def on_36(s) set_fg_color(6) end

#on_37(s) ⇒ Object



72
# File 'lib/gitlab_build_output/ansi2html.rb', line 72

def on_37(s) set_fg_color(7) end

#on_38(s) ⇒ Object



74
# File 'lib/gitlab_build_output/ansi2html.rb', line 74

def on_38(s) set_fg_color_256(s) end

#on_39(s) ⇒ Object



76
# File 'lib/gitlab_build_output/ansi2html.rb', line 76

def on_39(s) set_fg_color(9) end

#on_4(s) ⇒ Object



40
# File 'lib/gitlab_build_output/ansi2html.rb', line 40

def on_4(s) enable(STYLE_SWITCHES[:underline]) end

#on_40(s) ⇒ Object



78
# File 'lib/gitlab_build_output/ansi2html.rb', line 78

def on_40(s) set_bg_color(0) end

#on_41(s) ⇒ Object



80
# File 'lib/gitlab_build_output/ansi2html.rb', line 80

def on_41(s) set_bg_color(1) end

#on_42(s) ⇒ Object



82
# File 'lib/gitlab_build_output/ansi2html.rb', line 82

def on_42(s) set_bg_color(2) end

#on_43(s) ⇒ Object



84
# File 'lib/gitlab_build_output/ansi2html.rb', line 84

def on_43(s) set_bg_color(3) end

#on_44(s) ⇒ Object



86
# File 'lib/gitlab_build_output/ansi2html.rb', line 86

def on_44(s) set_bg_color(4) end

#on_45(s) ⇒ Object



88
# File 'lib/gitlab_build_output/ansi2html.rb', line 88

def on_45(s) set_bg_color(5) end

#on_46(s) ⇒ Object



90
# File 'lib/gitlab_build_output/ansi2html.rb', line 90

def on_46(s) set_bg_color(6) end

#on_47(s) ⇒ Object



92
# File 'lib/gitlab_build_output/ansi2html.rb', line 92

def on_47(s) set_bg_color(7) end

#on_48(s) ⇒ Object



94
# File 'lib/gitlab_build_output/ansi2html.rb', line 94

def on_48(s) set_bg_color_256(s) end

#on_49(s) ⇒ Object



96
# File 'lib/gitlab_build_output/ansi2html.rb', line 96

def on_49(s) set_bg_color(9) end

#on_8(s) ⇒ Object



42
# File 'lib/gitlab_build_output/ansi2html.rb', line 42

def on_8(s) enable(STYLE_SWITCHES[:conceal])   end

#on_9(s) ⇒ Object



44
# File 'lib/gitlab_build_output/ansi2html.rb', line 44

def on_9(s) enable(STYLE_SWITCHES[:cross])     end

#on_90(s) ⇒ Object



98
# File 'lib/gitlab_build_output/ansi2html.rb', line 98

def on_90(s) set_fg_color(0, 'l') end

#on_91(s) ⇒ Object



100
# File 'lib/gitlab_build_output/ansi2html.rb', line 100

def on_91(s) set_fg_color(1, 'l') end

#on_92(s) ⇒ Object



102
# File 'lib/gitlab_build_output/ansi2html.rb', line 102

def on_92(s) set_fg_color(2, 'l') end

#on_93(s) ⇒ Object



104
# File 'lib/gitlab_build_output/ansi2html.rb', line 104

def on_93(s) set_fg_color(3, 'l') end

#on_94(s) ⇒ Object



106
# File 'lib/gitlab_build_output/ansi2html.rb', line 106

def on_94(s) set_fg_color(4, 'l') end

#on_95(s) ⇒ Object



108
# File 'lib/gitlab_build_output/ansi2html.rb', line 108

def on_95(s) set_fg_color(5, 'l') end

#on_96(s) ⇒ Object



110
# File 'lib/gitlab_build_output/ansi2html.rb', line 110

def on_96(s) set_fg_color(6, 'l') end

#on_97(s) ⇒ Object



112
# File 'lib/gitlab_build_output/ansi2html.rb', line 112

def on_97(s) set_fg_color(7, 'l') end

#on_99(s) ⇒ Object



114
# File 'lib/gitlab_build_output/ansi2html.rb', line 114

def on_99(s) set_fg_color(9, 'l') end

#open_new_tagObject



231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
# File 'lib/gitlab_build_output/ansi2html.rb', line 231

def open_new_tag
  css_classes = []

  unless @fg_color.nil?
    fg_color = @fg_color
    # Most terminals show bold colored text in the light color variant
    # Let's mimic that here
    if @style_mask & STYLE_SWITCHES[:bold] != 0
      fg_color.sub!(/fg-(\w{2,}+)/, 'fg-l-\1')
    end
    css_classes << fg_color
  end
  css_classes << @bg_color unless @bg_color.nil?

  STYLE_SWITCHES.each do |css_class, flag|
    css_classes << "term-#{css_class}" if @style_mask & flag != 0
  end

  return if css_classes.empty?

  @out << %{<span class="#{css_classes.join(' ')}">}
  @n_open_tags += 1
end

#resetObject



287
288
289
290
291
# File 'lib/gitlab_build_output/ansi2html.rb', line 287

def reset
  @fg_color = nil
  @bg_color = nil
  @style_mask = 0
end

#reset_stateObject



262
263
264
265
266
267
# File 'lib/gitlab_build_output/ansi2html.rb', line 262

def reset_state
  @offset = 0
  @n_open_tags = 0
  @out = ''
  reset
end

#restore_state(new_state, stream) ⇒ Object



277
278
279
280
281
282
283
284
285
# File 'lib/gitlab_build_output/ansi2html.rb', line 277

def restore_state(new_state, stream)
  state = Base64.urlsafe_decode64(new_state)
  state = JSON.parse(state, symbolize_names: true)
  return if state[:offset].to_i > stream.size

  STATE_PARAMS.each do |param|
    send("#{param}=".to_sym, state[param]) # rubocop:disable GitlabSecurity/PublicSend
  end
end

#set_bg_color(color_index, prefix = nil) ⇒ Object



305
306
307
# File 'lib/gitlab_build_output/ansi2html.rb', line 305

def set_bg_color(color_index, prefix = nil)
  @bg_color = get_term_color_class(color_index, ["bg", prefix])
end

#set_bg_color_256(command_stack) ⇒ Object



321
322
323
324
# File 'lib/gitlab_build_output/ansi2html.rb', line 321

def set_bg_color_256(command_stack)
  css_class = get_xterm_color_class(command_stack, "bg")
  @bg_color = css_class unless css_class.nil?
end

#set_fg_color(color_index, prefix = nil) ⇒ Object



301
302
303
# File 'lib/gitlab_build_output/ansi2html.rb', line 301

def set_fg_color(color_index, prefix = nil)
  @fg_color = get_term_color_class(color_index, ["fg", prefix])
end

#set_fg_color_256(command_stack) ⇒ Object



316
317
318
319
# File 'lib/gitlab_build_output/ansi2html.rb', line 316

def set_fg_color_256(command_stack)
  css_class = get_xterm_color_class(command_stack, "fg")
  @fg_color = css_class unless css_class.nil?
end

#stateObject



269
270
271
272
273
274
275
# File 'lib/gitlab_build_output/ansi2html.rb', line 269

def state
  state = STATE_PARAMS.inject({}) do |h, param|
    h[param] = send(param) # rubocop:disable GitlabSecurity/PublicSend
    h
  end
  Base64.urlsafe_encode64(state.to_json)
end