класс 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
Возвращает строки, сопоставленные текущим сканированием, как массив CodeLines
# 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 сделает его сбалансированным, поэтому он это и делает.
# 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 для обработки распространенных ситуаций, таких как остановка, если мы нашли несоответствие ключевое слово/конец в одном или другом направлении.
# File lib/syntax_suggest/around_block_scan.rb, line 73 def stop_after_kw @stop_after_kw = true self end
Указывает ‘scan_while` на поиск несоответствий ключевое слово/конец
При сканировании вверх, если мы видим больше ключевых слов, чем окончаний, он останавливается. Это может произойти при сканировании вне тела метода. Первая сканируемая строка вверх будет ключевым словом, и это значение запустит остановку.
При сканировании вниз, остановиться, если окончаний больше, чем ключевых слов.
Ruby Core © 1993–2022 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.