class SyntaxSuggest::AroundBlockScan

这个类用于探索代码块之前和之后的内容

它在传入的代码块的上方和下方搜索,以匹配你给定的任何条件

示例

def dog         # 1
  puts "bark"   # 2
  puts "bark"   # 3
end             # 4

scan = AroundBlockScan.new(
  code_lines: code_lines
  block: CodeBlock.new(lines: code_lines[1])
)

scan.scan_while { true }

puts scan.before_index # => 0
puts scan.after_index  # => 3

公共类方法

new(code_lines:, block:) 点击切换源代码
# File syntax_suggest/around_block_scan.rb, line 30
def initialize(code_lines:, block:)
  @code_lines = code_lines
  @orig_indent = block.current_indent

  @stop_after_kw = false
  @force_add_empty = false
  @force_add_hidden = false
  @target_indent = nil

  @scanner = ScanHistory.new(code_lines: code_lines, block: block)
end

公共实例方法

code_block() 点击切换源代码

返回当前匹配的行作为 `CodeBlock`

当创建一个 `CodeBlock` 时,它会收集自身的元数据,因此这不是一个免费的转换。避免分配超过需要的 CodeBlock

# File syntax_suggest/around_block_scan.rb, line 217
def code_block
  CodeBlock.new(lines: lines)
end
force_add_empty() 点击切换源代码

当使用此标志时,`scan_while` 将绕过它给定的代码块,并始终添加一个对 `CodeLine#empty?` 响应为真的行

空行不包含代码,只包含空格,例如前导空格和换行符。

# File syntax_suggest/around_block_scan.rb, line 60
def force_add_empty
  @force_add_empty = true
  self
end
force_add_hidden() 点击切换源代码

当使用此标志时,`scan_while` 将绕过它给定的代码块,并始终添加一个对 `CodeLine#hidden?` 响应为真的行

当行被解析器评估为代码块的一部分并发现包含有效代码时,这些行将被隐藏。

# File syntax_suggest/around_block_scan.rb, line 49
def force_add_hidden
  @force_add_hidden = true
  self
end
inspect() 点击切换源代码

可管理的 rspec 错误

# File syntax_suggest/around_block_scan.rb, line 228
def inspect
  "#<#{self.class}:0x0000123843lol >"
end
lines() 点击切换源代码

以 CodeLines 数组的形式返回当前扫描匹配的行

# File syntax_suggest/around_block_scan.rb, line 223
def lines
  @scanner.lines
end
lookahead_balance_one_line() 点击切换源代码

扫描是有意保守的,因为我们没有办法回滚激进的代码块(目前是这样)

如果一个代码块由于一些微不足道的原因(例如空行)而停止,但下一行会导致它平衡,那么我们可以检查这种情况,并在向上或向下方向获取多一行。

例如,在下面,如果我们向上扫描,第 2 行可能会导致扫描停止。这是因为空行可能表示用户打算分块代码的逻辑中断,这是一个很好的停止并检查有效性的位置。不幸的是,这也意味着我们可能有一个“悬空”的关键字或 `end`。

1 def bark
2
3 end

如果第 2 行和第 3 行在代码块中,那么当此方法运行时,它会发现它是未平衡的,但是获取第 1 行会使其平衡,所以它会这样做。

# File syntax_suggest/around_block_scan.rb, line 141
def lookahead_balance_one_line
  kw_count = 0
  end_count = 0
  lines.each do |line|
    kw_count += 1 if line.is_kw?
    end_count += 1 if line.is_end?
  end

  return self if kw_count == end_count # nothing to balance

  @scanner.commit_if_changed # Rollback point if we don't find anything to optimize

  # Try to eat up empty lines
  @scanner.scan(
    up: ->(line, _, _) { line.hidden? || line.empty? },
    down: ->(line, _, _) { line.hidden? || line.empty? }
  )

  # More ends than keywords, check if we can balance expanding up
  next_up = @scanner.next_up
  next_down = @scanner.next_down
  case end_count - kw_count
  when 1
    if next_up&.is_kw? && next_up.indent >= @target_indent
      @scanner.scan(
        up: ->(line, _, _) { line == next_up },
        down: ->(line, _, _) { false }
      )
      @scanner.commit_if_changed
    end
  when -1
    if next_down&.is_end? && next_down.indent >= @target_indent
      @scanner.scan(
        up: ->(line, _, _) { false },
        down: ->(line, _, _) { line == next_down }
      )
      @scanner.commit_if_changed
    end
  end
  # Rollback any uncommitted changes
  @scanner.stash_changes

  self
end
scan_adjacent_indent() 点击切换源代码

根据代码块上方/下方下一行的缩进扫描代码块

确定当前代码块上方/下方下一行的缩进。

通常,当一个代码块已经扩展到捕获所有相同(或更大)缩进的“邻居”,并且需要向外扩展时,会调用此方法。例如,围绕方法的 `def/end` 行。

# File syntax_suggest/around_block_scan.rb, line 200
def scan_adjacent_indent
  before_after_indent = []

  before_after_indent << (@scanner.next_up&.indent || 0)
  before_after_indent << (@scanner.next_down&.indent || 0)

  @target_indent = before_after_indent.min
  scan_while { |line| line.not_empty? && line.indent >= @target_indent }

  self
end
scan_neighbors_not_empty() 点击切换源代码

查找相同或更大缩进的代码行,并将它们添加到代码块中

# File syntax_suggest/around_block_scan.rb, line 188
def scan_neighbors_not_empty
  @target_indent = @orig_indent
  scan_while { |line| line.not_empty? && line.indent >= @target_indent }
end
scan_while() { |line| ... } 点击切换源代码

主要工作方法

scan_while 方法接收一个代码块,该代码块会产生代码块上方和下方的行。如果 yield 返回 true,则会修改 @before_index 或 @after_index 以包含匹配的行。

除了产生单个行之外,此对象的内部结构还提供了一个迷你 DSL 来处理常见情况,例如如果我们在一个方向或另一个方向发现关键字/end 不匹配则停止。

# File syntax_suggest/around_block_scan.rb, line 88
def scan_while
  stop_next_up = false
  stop_next_down = false

  @scanner.scan(
    up: ->(line, kw_count, end_count) {
      next false if stop_next_up
      next true if @force_add_hidden && line.hidden?
      next true if @force_add_empty && line.empty?

      if @stop_after_kw && kw_count > end_count
        stop_next_up = true
      end

      yield line
    },
    down: ->(line, kw_count, end_count) {
      next false if stop_next_down
      next true if @force_add_hidden && line.hidden?
      next true if @force_add_empty && line.empty?

      if @stop_after_kw && end_count > kw_count
        stop_next_down = true
      end

      yield line
    }
  )

  self
end
stop_after_kw() 点击切换源代码

告诉 `scan_while` 查找不匹配的关键字/end

向上扫描时,如果我们看到的关键字比 end 多,它会停止。当在方法体外部扫描时,可能会发生这种情况。向上扫描的第一行将是一个关键字,此设置将触发停止。

向下扫描时,如果 end 比关键字多,则停止。

# File syntax_suggest/around_block_scan.rb, line 73
def stop_after_kw
  @stop_after_kw = true
  self
end