class DeadEnd::CaptureCodeContext

Given a block, this method will capture surrounding code to give the user more context for the location of the problem.

Return is an array of CodeLines to be rendered.

Surrounding code is captured regardless of visible state

puts block.to_s # => "def bark"

context = CaptureCodeContext.new(
  blocks: block,
  code_lines: code_lines
)

puts context.call.join
# =>
  class Dog
    def bark
  end

Attributes

code_lines[R]

Public Class Methods

new(blocks: , code_lines:) click to toggle source
# File lib/dead_end/capture_code_context.rb, line 29
def initialize(blocks: , code_lines:)
  @blocks = Array(blocks)
  @code_lines = code_lines
  @visible_lines = @blocks.map(&:visible_lines).flatten
  @lines_to_output = @visible_lines.dup
end

Public Instance Methods

call() click to toggle source
# File lib/dead_end/capture_code_context.rb, line 36
def call
  @blocks.each do |block|
    capture_last_end_same_indent(block)
    capture_before_after_kws(block)
    capture_falling_indent(block)
  end

  @lines_to_output.select!(&:not_empty?)
  @lines_to_output.select!(&:not_comment?)
  @lines_to_output.uniq!
  @lines_to_output.sort!

  return @lines_to_output
end
capture_before_after_kws(block) click to toggle source
# File lib/dead_end/capture_code_context.rb, line 60
def capture_before_after_kws(block)
  around_lines = AroundBlockScan.new(code_lines: @code_lines, block: block)
    .start_at_next_line
    .capture_neighbor_context

  around_lines -= block.lines

  @lines_to_output.concat(around_lines)
end
capture_falling_indent(block) click to toggle source
# File lib/dead_end/capture_code_context.rb, line 51
def capture_falling_indent(block)
  AroundBlockScan.new(
    block: block,
    code_lines: @code_lines,
  ).on_falling_indent do |line|
    @lines_to_output << line
  end
end
capture_last_end_same_indent(block) click to toggle source

Problems heredocs are back in play

# File lib/dead_end/capture_code_context.rb, line 71
def capture_last_end_same_indent(block)
  start_index = block.visible_lines.first.index
  lines = @code_lines[start_index..block.lines.last.index]
  kw_end_lines = lines.select {|line| line.indent == block.current_indent && (line.is_end? || line.is_kw?) }


  # TODO handle case of heredocs showing up here
  #
  # Due to https://github.com/zombocom/dead_end/issues/32
  # There's a special case where a keyword right before the last
  # end of a valid block accidentally ends up identifying that the problem
  # was with the block instead of before it. To handle that
  # special case, we can re-parse back through the internals of blocks
  # and if they have mis-matched keywords and ends show the last one
  end_lines = kw_end_lines.select(&:is_end?)
  end_lines.each_with_index  do |end_line, i|
    start_index = i.zero? ? 0 : end_lines[i-1].index
    end_index = end_line.index - 1
    lines = @code_lines[start_index..end_index]

    stop_next = false
    kw_count = 0
    end_count = 0
    lines = lines.reverse.take_while do |line|
      next false if stop_next

      end_count += 1 if line.is_end?
      kw_count += 1 if line.is_kw?

      stop_next = true if !kw_count.zero? && kw_count >= end_count
      true
    end.reverse

    next unless kw_count > end_count

    lines = lines.select {|line| line.is_kw? || line.is_end? }

    next if lines.empty?

    @lines_to_output << end_line
    @lines_to_output << lines.first
    @lines_to_output << lines.last
  end
end