class SyntaxSuggest::CodeLine
Представляет собой отдельную строку кода заданного исходного файла
Этот объект содержит метаданные о строке, такие как отступ, пуста ли она, а также лексические данные, такие как наличие ключевого слова «end» или ключевых слов.
Видимость строк может быть отключена. Отмеченная как невидимая строка не используется для проверки синтаксиса. Функционально это эквивалентно её комментарию.
Пример:
line = CodeLine.from_source("def foo\n").first
line.number => 1
line.empty? # => false
line.visible? # => true
line.mark_invisible
line.visible? # => false
Константы
- TRAILING_SLASH
Атрибуты
При маркировке строки кода как невидимой, мы сохраняем её исходное значение. Это полезно для отладки и для отображения дополнительного контекста.
DisplayCodeWithLineNumbers отобразит все предоставленные строки, а не только видимые, используя метод original для их получения.
Публичные Классовые Методы
# File lib/syntax_suggest/code_line.rb, line 29
def self.from_source(source, lines: nil)
lines ||= source.lines
lex_array_for_line = LexAll.new(source: source, source_lines: lines).each_with_object(Hash.new { |h, k| h[k] = [] }) { |lex, hash| hash[lex.line] << lex }
lines.map.with_index do |line, index|
CodeLine.new(
line: line,
index: index,
lex: lex_array_for_line[index + 1]
)
end
end Возвращает массив объектов CodeLine из строки исходного кода
# File lib/syntax_suggest/code_line.rb, line 42
def initialize(line:, index:, lex:)
@lex = lex
@line = line
@index = index
@original = line
@line_number = @index + 1
strip_line = line.dup
strip_line.lstrip!
if strip_line.empty?
@empty = true
@indent = 0
else
@empty = false
@indent = line.length - strip_line.length
end
set_kw_end
end Публичные Методы Объектов
# File lib/syntax_suggest/code_line.rb, line 152 def <=>(other) index <=> other.index end
Оператор сравнения, необходимый для проверки на равенство и сортировки
# File lib/syntax_suggest/code_line.rb, line 117 def empty? @empty end
Строка «empty?» — это строка, которая изначально была пустой в исходном коде, а «скрытая» строка — это строка, которую мы затем отметили как «невидимую»
Обратный или «visible?» (отличие от `empty?`)
# File lib/syntax_suggest/code_line.rb, line 174 def ignore_newline_not_beg? @ignore_newline_not_beg end
- Нестабильный API
-
Строки, имеющие токен типа «on_ignored_nl», но НЕ типа «BEG», являются хорошим приближением для возможности объединения нескольких строк в одну.
Этот предикат используется для определения выполнения этих двух условий.
Известный случай, который не обрабатывается:
Ripper.lex <<~EOM a && b || c EOM
По какой-то причине это вводит «on_ignore_newline» но с типом BEG
# File lib/syntax_suggest/code_line.rb, line 74 def indent_index @indent_index ||= [indent, index] end
Используется для стабильной сортировки по уровню отступа
Сортировка в Ruby не является «стабильной», т.е. при наличии нескольких элементов с одинаковым значением, нет гарантии, что они будут возвращены в том же порядке, в котором они были помещены.
Поэтому строки кода с одинаковым уровнем отступа сортируются по их индексу, который является уникальным и постоянным.
Это в основном нужно для стабильности набора тестов
# File lib/syntax_suggest/code_line.rb, line 89 def is_end? @is_end end
Возвращает true, если строка кода содержит ключевое слово «end»
# File lib/syntax_suggest/code_line.rb, line 83 def is_kw? @is_kw end
Возвращает true, если строка кода содержит ключевое слово, соответствующее ключевому слову «end»
Например: ‘def’, `do`, `begin`, `ensure` и т.д.
# File lib/syntax_suggest/code_line.rb, line 98 def mark_invisible @line = "" end
Используется для скрытия строк
Алгоритм поиска сгруппирует строки в блоки, а затем, если эти блоки определены как представляющие корректный код, они будут скрыты.
# File lib/syntax_suggest/code_line.rb, line 122 def not_empty? !empty? end
Обратное значение `empty?` (отличие от `visible?`)
# File lib/syntax_suggest/code_line.rb, line 135 def to_s line end
Отображает заданную строку
Также позволяет представлять исходный код в виде массива строк кода.
При вызове `join` на массиве строк кода, он вызовет `to_s` на каждом элементе, что по существу преобразует его обратно в исходную строку.
# File lib/syntax_suggest/code_line.rb, line 185 def trailing_slash? last = @lex.last return false unless last return false unless last.type == :on_sp last.token == TRAILING_SLASH end
Определяет, содержит ли заданная строка конечный слэш
lines = CodeLine.from_source(<<~EOM) it "foo" \ EOM expect(lines.first.trailing_slash?).to eq(true)
# File lib/syntax_suggest/code_line.rb, line 105 def visible? !line.empty? end
Означает, что строка была помечена как «невидимая». Неочевидно, что пустые строки видимы… они просто не содержат исходного кода, кроме новой строки (“n”).
Приватные Методы Объектов
# File lib/syntax_suggest/code_line.rb, line 201
def set_kw_end
oneliner_count = 0
in_oneliner_def = nil
kw_count = 0
end_count = 0
@ignore_newline_not_beg = false
@lex.each do |lex|
kw_count += 1 if lex.is_kw?
end_count += 1 if lex.is_end?
if lex.type == :on_ignored_nl
@ignore_newline_not_beg = !lex.expr_beg?
end
if in_oneliner_def.nil?
in_oneliner_def = :ENDFN if lex.state.allbits?(Ripper::EXPR_ENDFN)
elsif lex.state.allbits?(Ripper::EXPR_ENDFN)
# Continue
elsif lex.state.allbits?(Ripper::EXPR_BEG)
in_oneliner_def = :BODY if lex.token == "="
elsif lex.state.allbits?(Ripper::EXPR_END)
# We found an endless method, count it
oneliner_count += 1 if in_oneliner_def == :BODY
in_oneliner_def = nil
else
in_oneliner_def = nil
end
end
kw_count -= oneliner_count
@is_kw = (kw_count - end_count) > 0
@is_end = (end_count - kw_count) > 0
end Обнаружение методов бесконечного цикла
Из github.com/ruby/irb/commit/826ae909c9c93a2ddca6f9cfcd9c94dbf53d44ab Кажется, что для обнаружения «однострочников» нужен конечный автомат. Это можно сделать, в основном, посмотрев на «состояние» (последнее значение):
ENDFN -> BEG (token = '=' ) -> END
Ruby Core © 1993–2022 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.