класс 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!
@indent = if (@empty = strip_line.empty?)
line.length - 1 # Newline removed from strip_line is not "whitespace"
else
line.length - strip_line.length
end
set_kw_end
end Публичные методы экземпляра
# File lib/syntax_suggest/code_line.rb, line 150 def <=>(other) index <=> other.index end
Оператор сравнения, необходимый для проверки равенства и сортировки
# File lib/syntax_suggest/code_line.rb, line 115 def empty? @empty end
Строка `empty?` изначально была пустой в исходном коде, тогда как «скрытая» строка — это строка, которую мы впоследствии пометили как «невидимую».
Противоположность `visible?` (обратите внимание: это не то же самое, что `empty?`)
# File lib/syntax_suggest/code_line.rb, line 172 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 72 def indent_index @indent_index ||= [indent, index] end
Используется для стабильной сортировки по уровню отступа
Сортировка в Ruby не является «стабильной»: если у нескольких элементов одинаковое значение, нет гарантии, что они будут возвращены в том же порядке, в котором были добавлены.
Поэтому строки кода с одинаковым уровнем отступа сортируются по значению индекса, которое уникально и неизменно.
В основном это необходимо для согласованности набора тестов.
# File lib/syntax_suggest/code_line.rb, line 87 def is_end? @is_end end
Возвращает true, если считается, что строка кода содержит ключевое слово `end`.
# File lib/syntax_suggest/code_line.rb, line 81 def is_kw? @is_kw end
Возвращает true, если считается, что строка кода содержит ключевое слово, которому соответствует `end`.
Например: `def`, `do`, `begin`, `ensure` и т. д.
# File lib/syntax_suggest/code_line.rb, line 96 def mark_invisible @line = "" end
Используется для скрытия строк
Алгоритм поиска группирует строки в блоки, а затем скрывает те блоки, которые считаются корректным кодом.
# File lib/syntax_suggest/code_line.rb, line 120 def not_empty? !empty? end
Противоположность `empty?` (обратите внимание: это не то же самое, что `visible?`)
# File lib/syntax_suggest/code_line.rb, line 133 def to_s line end
Возвращает строку в исходном виде
Также позволяет представлять исходный код как массив строк кода.
Если у нас есть массив элементов-строк кода, вызов `join` для массива вызовет `to_s` для каждого элемента, что по сути преобразует его обратно в исходную строку.
# File lib/syntax_suggest/code_line.rb, line 184 def trailing_slash? last = @lex.last last&.type == :on_tstring_end end
# File lib/syntax_suggest/code_line.rb, line 103 def visible? !line.empty? end
Означает, что строка была помечена как «невидимая». Как ни странно, «пустые» строки видимы… в них просто нет исходного кода, кроме символа новой строки («n»).
Приватные методы экземпляра
# File lib/syntax_suggest/code_line.rb, line 206
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–2025 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.