Spec-Zone.ru › Ruby 3.4
  1. SyntaxSuggest::
  2. AroundBlockScan

class SyntaxSuggest::AroundBlockScan

Родитель:
Object

Этот класс полезен для изучения содержимого до и после блока

Он выполняет поиск выше и ниже переданного блока в соответствии с заданными критериями:

Пример:

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 lib/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 () Показать исходный код
# File lib/syntax_suggest/around_block_scan.rb, line 217
def code_block
  CodeBlock.new(lines: lines)
end

Возвращает текущие совпавшие строки в виде «CodeBlock`

При создании «CodeBlock` собирает метаданные о себе, поэтому такое преобразование не бесплатно. Не создавайте больше объектов CodeBlock, чем необходимо

force_add_empty () Показать исходный код
# File lib/syntax_suggest/around_block_scan.rb, line 60
def force_add_empty
  @force_add_empty = true
  self
end

При использовании этого флага `scan_while` проигнорирует переданный ему блок и всегда добавит строку, для которой `CodeLine#empty?` возвращает истинное значение

Пустые строки не содержат кода, только пробельные символы, например начальные пробелы и символ новой строки.

force_add_hidden () Показать исходный код
# File lib/syntax_suggest/around_block_scan.rb, line 49
def force_add_hidden
  @force_add_hidden = true
  self
end

При использовании этого флага `scan_while` проигнорирует переданный ему блок и всегда добавит строку, для которой `CodeLine#hidden?` возвращает истинное значение

Строки считаются скрытыми, если парсер проверил их как часть блока и обнаружил в них допустимый код.

inspect () Показать исходный код
# File lib/syntax_suggest/around_block_scan.rb, line 228
def inspect
  "#<#{self.class}:0x0000123843lol >"
end

Управляемые ошибки rspec

lines () Показать исходный код
# File lib/syntax_suggest/around_block_scan.rb, line 223
def lines
  @scanner.lines
end

Возвращает строки, найденные при текущем сканировании, в виде массива CodeLine

lookahead_balance_one_line () Показать исходный код
# File lib/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

Сканирование намеренно выполняется консервативно, поскольку сейчас у нас нет возможности отменить агрессивное расширение блока

Если сканирование блока остановилось по незначительной причине (например, из-за пустой строки), но следующая строка позволила бы сбалансировать блок, мы можем проверить это условие и захватить ещё одну строку сверху или снизу.

Например, при сканировании вверх, как показано ниже, строка 2 может привести к остановке сканирования. Это происходит потому, что пустые строки могут обозначать логические разрывы, в которых пользователь намеревался разделить код на фрагменты; это удобное место, чтобы остановиться и проверить корректность. К сожалению, это также означает, что у нас может остаться «висячее» ключевое слово или end.

1 def bark
2
3 end

Если строки 2 и 3 входят в блок, то при вызове этого метода он обнаружит, что блок несбалансирован, но добавление строки 1 сделает его сбалансированным, поэтому метод добавит её.

scan_adjacent_indent () Показать исходный код
# File lib/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

Сканирует блоки на основе отступа следующей строки выше или ниже блока

Определяет отступ следующей строки выше или ниже текущего блока.

Обычно этот метод вызывается, когда блок расширился и захватил всех «соседей» с таким же или большим отступом и ему нужно расшириться дальше. Например, строки `def/end`, окружающие метод.

scan_neighbors_not_empty () Показать исходный код
# File lib/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| ... } Показать исходный код
# File lib/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

Основной рабочий метод

Метод scan_while принимает блок, который передаёт ему строки выше и ниже блока. Если блок возвращает true, значения @before_index или @after_index изменяются, чтобы включить найденную строку.

Помимо передачи отдельных строк, внутренняя реализация этого объекта предоставляет мини-язык DSL для обработки распространённых ситуаций, например остановки при обнаружении несовпадения keyword/end в одном из направлений.

stop_after_kw () Показать исходный код
# File lib/syntax_suggest/around_block_scan.rb, line 73
def stop_after_kw
  @stop_after_kw = true
  self
end

Указывает `scan_while` искать несоответствия между keyword и end

При сканировании вверх метод остановится, если обнаружит больше ключевых слов, чем end. Это может произойти при сканировании за пределами тела метода: первая строка при сканировании вверх будет ключевым словом, и эта настройка вызовет остановку.

При сканировании вниз метод остановится, если end больше, чем ключевых слов.

Ruby Core © 1993–2025 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API