class SyntaxSuggest::CaptureCodeContext
Преобразует «недопустимые блоки» в полезный контекст
Алгоритм состоит из трёх основных этапов:
-
Очистка и форматирование исходного кода
-
Поиск недопустимых блоков
-
Преобразование недопустимых блоков в нечто осмысленное
Этот класс выполняет третий этап.
На втором этапе алгоритм очень хорошо захватывает всю синтаксическую ошибку в одном блоке, однако результаты могут быть неоднозначными. Люди хорошо распознают шаблоны и отфильтровывают лишнее, мысленно удаляя ненужные данные, но не могут добавить отсутствующие данные.
В известных неоднозначных случаях этот класс добавляет контекст, устраняющий неоднозначность, чтобы программист располагал всей необходимой информацией.
Помимо устранения неоднозначностей, он также захватывает контекст окружающего кода:
puts block.to_s # => "def bark"
context = CaptureCodeContext.new(
blocks: block,
code_lines: code_lines
)
lines = context.call.map(&:original)
puts lines.join
# =>
class Dog
def bark
end Атрибуты
Общедоступные методы класса
# File lib/syntax_suggest/capture_code_context.rb, line 51 def initialize(blocks:, code_lines:) @blocks = Array(blocks) @code_lines = code_lines @visible_lines = @blocks.map(&:visible_lines).flatten @lines_to_output = @visible_lines.dup end
Общедоступные методы экземпляра
# File lib/syntax_suggest/capture_code_context.rb, line 58
def call
@blocks.each do |block|
capture_first_kw_end_same_indent(block)
capture_last_end_same_indent(block)
capture_before_after_kws(block)
capture_falling_indent(block)
end
sorted_lines
end # File lib/syntax_suggest/capture_code_context.rb, line 127
def capture_before_after_kws(block)
return unless block.visible_lines.count == 1
around_lines = Capture::BeforeAfterKeywordEnds.new(
code_lines: @code_lines,
block: block
).call
around_lines -= block.lines
@lines_to_output.concat(around_lines)
end Показывает окружающие пары kw/end
Эти дополнительные пары показываются для устранения неоднозначности в случаях, когда совпадает только одна видимая строка.
Например:
1 class Dog 2 def bark 4 def eat 5 end 6 end
В этом случае во второй строке может отсутствовать `end` или четвёртая строка могла быть добавлена по ошибке (такое случается).
При обнаружении описанной выше проблемы указывается, что она затрагивает только строку 2
2 def bark
Показ соседних пар ключевых слов даёт дополнительный контекст:
2 def bark 4 def eat 5 end
# File lib/syntax_suggest/capture_code_context.rb, line 91
def capture_falling_indent(block)
Capture::FallingIndentLines.new(
block: block,
code_lines: @code_lines
).call do |line|
@lines_to_output << line
end
end Показывает контекст вокруг кода, полученного благодаря «снижающемуся» отступу
Преобразует:
it "foo" do
в:
class OH
def hello
it "foo" do
end
end # File lib/syntax_suggest/capture_code_context.rb, line 221
def capture_first_kw_end_same_indent(block)
return if block.visible_lines.length != 1
return unless block.visible_lines.first.is_end?
visible_line = block.visible_lines.first
lines = @code_lines[block.lines.first.index..visible_line.index]
matching_kw = lines.reverse.detect { |line| line.indent == block.current_indent && line.is_kw? }
return unless matching_kw
@lines_to_output << matching_kw
kw_count = 0
end_count = 0
orphan_end = @code_lines[matching_kw.index..visible_line.index].detect do |line|
kw_count += 1 if line.is_kw?
end_count += 1 if line.is_end?
end_count >= kw_count
end
return unless orphan_end
@lines_to_output << orphan_end
end Логическая противоположность `capture_last_end_same_indent`
Если в недопустимом блоке после `end` без ключевого слова сразу идёт ещё один `end`, непонятно, у какого из них отсутствует ключевое слово.
Рассмотрим пример:
class Dog # 1
puts "woof" # 2
end # 3
end # 4 проблемная строка будет определена как:
> end # 4
Это происходит потому, что строки 1, 2 и 3 технически являются допустимым кодом: сначала они разворачиваются, признаются допустимыми и скрываются. Необходимо снова показать соответствующее ключевое слово в строке 1. Также нужно двигаться в обратном направлении и показывать несовпадающий `end`, если он есть.
# File lib/syntax_suggest/capture_code_context.rb, line 161
def capture_last_end_same_indent(block)
return if block.visible_lines.length != 1
return unless block.visible_lines.first.is_kw?
visible_line = block.visible_lines.first
lines = @code_lines[visible_line.index..block.lines.last.index]
# Find first end with same indent
# (this would return line 4)
#
# end # 4
matching_end = lines.detect { |line| line.indent == block.current_indent && line.is_end? }
return unless matching_end
@lines_to_output << matching_end
# Work backwards from the end to
# see if there are mis-matched
# keyword/end pairs
#
# Return the first mis-matched keyword
# this would find line 2
#
# def bark # 2
# puts "woof" # 3
# end # 4
end_count = 0
kw_count = 0
kw_line = @code_lines[visible_line.index..matching_end.index].reverse.detect do |line|
end_count += 1 if line.is_end?
kw_count += 1 if line.is_kw?
!kw_count.zero? && kw_count >= end_count
end
return unless kw_line
@lines_to_output << kw_line
end Если в недопустимом блоке перед `end` без ключевого слова сразу идёт ещё один `end`, непонятно, у какого ключевого слова отсутствует `end`.
Рассмотрим пример:
class Dog # 1
def bark # 2
puts "woof" # 3
end # 4 Однако из-за github.com/ruby/syntax_suggest/issues/32 проблемная строка будет определена как:
> class Dog # 1
Это происходит потому, что строки 2, 3 и 4 технически являются допустимым кодом: сначала они разворачиваются, признаются допустимыми и скрываются. Необходимо снова показать соответствующую строку с `end` — строку 4. Также нужно двигаться в обратном направлении и показывать несовпадающее ключевое слово, если оно есть.
# File lib/syntax_suggest/capture_code_context.rb, line 69 def sorted_lines @lines_to_output.select!(&:not_empty?) @lines_to_output.uniq! @lines_to_output.sort! @lines_to_output end
Ruby Core © 1993–2025 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.