класс SyntaxSuggest::CaptureCodeContext
Преобразует «некорректный(ые) блок(и)» в полезный контекст
В алгоритме есть три основных этапа:
-
Сантизация/форматирование исходного кода
-
Поиск некорректных блоков
-
Форматирование некорректных блоков в осмысленную форму
Этот класс обрабатывает третью часть.
Алгоритм хорошо справляется с тем, чтобы охватить все ошибки синтаксиса в одном блоке на этапе 2, однако результаты могут содержать неоднозначности. Люди хорошо умеют находить закономерности и фильтровать данные, а также могут мысленно удалять лишние данные, но они не могут добавлять отсутствующие.
В случае известных неоднозначных случаев этот класс добавляет обратно контекст неоднозначности, чтобы программист имел полную информацию.
Помимо обработки этих неоднозначностей, он также собирает информацию о контексте окружающего кода:
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 43 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 50
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
@lines_to_output.select!(&:not_empty?)
@lines_to_output.uniq!
@lines_to_output.sort!
@lines_to_output
end # File lib/syntax_suggest/capture_code_context.rb, line 116
def capture_before_after_kws(block)
return unless block.visible_lines.count == 1
around_lines = AroundBlockScan.new(code_lines: @code_lines, block: block)
.start_at_next_line
.capture_neighbor_context
around_lines -= block.lines
@lines_to_output.concat(around_lines)
end Отображает окружающие пары ключевых слов/end
Цель отображения этих дополнительных пар связана с неоднозначными случаями, когда соответствует только одна видимая строка.
Например:
1 class Dog 2 def bark 4 def eat 5 end 6 end
В этом случае либо строка 2 может отсутствовать `end`, либо строка 4 была добавлена ошибочно (такое бывает).
Когда мы обнаруживаем вышеуказанную проблему, она отображается только для строки 2
2 def bark
Отображение «соседних» пар ключевых слов предоставляет дополнительный контекст:
2 def bark 4 def eat 5 end
# File lib/syntax_suggest/capture_code_context.rb, line 80
def capture_falling_indent(block)
AroundBlockScan.new(
block: block,
code_lines: @code_lines
).on_falling_indent 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 209
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 149
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`, неясно, какое ключевое слово отсутствует.
Рассмотрим этот пример:
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. Также двигаться назад и, если есть несоответствующее ключевое слово, отображать его тоже.
Ruby Core © 1993–2022 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.