Spec-Zone.ru › Ruby 3.4

класс 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 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

Возвращает строки, соответствующие текущему сканированию, в виде массива CodeLines

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 может привести к остановке сканирования. Это происходит потому, что пустые строки могут обозначать логические разрывы, где пользователь намеревался разбить код на части, что является хорошим местом для остановки и проверки валидности. К сожалению, это также означает, что у нас может быть «висящий» ключевой символ или конец.

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 принимает блок, который возвращает строки выше и ниже блока. Если возвращаемое значение истинно, @before_index или @after_index изменяются, чтобы включить сопоставленную строку.

Помимо возвращения отдельных строк, внутренние компоненты этого объекта предоставляют мини-DSL для обработки распространённых ситуаций, таких как остановка, если мы обнаружили несоответствие ключевого слова/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` искать несоответствия ключевых слов/end

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

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

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

Spec-Zone.ru

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