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 Открытые методы класса
# 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
Открытые методы экземпляра
# File lib/syntax_suggest/around_block_scan.rb, line 217 def code_block CodeBlock.new(lines: lines) end
Возвращает текущие совпавшие строки в виде «CodeBlock`
При создании «CodeBlock` собирает метаданные о себе, поэтому такое преобразование не бесплатно. Не создавайте больше объектов CodeBlock, чем необходимо
# File lib/syntax_suggest/around_block_scan.rb, line 60 def force_add_empty @force_add_empty = true self end
При использовании этого флага `scan_while` проигнорирует переданный ему блок и всегда добавит строку, для которой `CodeLine#empty?` возвращает истинное значение
Пустые строки не содержат кода, только пробельные символы, например начальные пробелы и символ новой строки.
При использовании этого флага `scan_while` проигнорирует переданный ему блок и всегда добавит строку, для которой `CodeLine#hidden?` возвращает истинное значение
Строки считаются скрытыми, если парсер проверил их как часть блока и обнаружил в них допустимый код.
# File lib/syntax_suggest/around_block_scan.rb, line 228
def inspect
"#<#{self.class}:0x0000123843lol >"
end Управляемые ошибки rspec
# File lib/syntax_suggest/around_block_scan.rb, line 223 def lines @scanner.lines end
Возвращает строки, найденные при текущем сканировании, в виде массива CodeLine
# 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 сделает его сбалансированным, поэтому метод добавит её.
# 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`, окружающие метод.
# 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 Находит строки с кодом, имеющие такой же или больший отступ, и добавляет их в блок
# 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 в одном из направлений.
# 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.