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

属性

indent[R]
index[R]
lex[R]
line[R]
line_number[R]
number[R]
original[R]

当代码行被标记为不可见时,我们会保留其行的原始值,这对于调试和显示额外的上下文很有用

DisplayCodeWithLineNumbers 将渲染给它的所有行,而不仅仅是可见行,它使用原始方法来获取它们。

公共类方法

from_source(source, lines: nil) 单击以切换源代码

从源字符串返回 CodeLine 对象数组

# File 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
new(line:, index:, lex:) 单击以切换源代码
# File 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

公共实例方法

<=>(other) 单击以切换源代码

比较运算符,用于相等和排序

# File syntax_suggest/code_line.rb, line 150
def <=>(other)
  index <=> other.index
end
empty?() 单击以切换源代码

“empty?” 行是在源代码中最初留空的行,而“hidden” 行是我们后来标记为“不可见”的行

# File syntax_suggest/code_line.rb, line 115
def empty?
  @empty
end
hidden?() 单击以切换源代码

与“visible?”相反(注意:与“empty?”不同)

# File syntax_suggest/code_line.rb, line 108
def hidden?
  !visible?
end
ignore_newline_not_beg?() 单击以切换源代码
不稳定的 API

具有“on_ignored_nl”类型标记且不是“BEG”类型的行似乎是能够将多行合并为一行的良好代理。

此谓词方法用于确定何时满足这两个条件。

已知不处理这种情况的唯一情况是

Ripper.lex <<~EOM
  a &&
   b ||
   c
EOM

出于某种原因,这引入了“on_ignore_newline”,但具有 BEG 类型

# File syntax_suggest/code_line.rb, line 172
def ignore_newline_not_beg?
  @ignore_newline_not_beg
end
indent_index() 单击以切换源代码

用于通过缩进级别进行稳定排序

Ruby 的排序不是“稳定”的,这意味着当多个元素具有相同的值时,不能保证它们以放入时的相同顺序返回。

因此,当多个代码行具有相同的缩进级别时,它们会按其索引值排序,该值是唯一且一致的。

这主要用于测试套件的一致性

# File syntax_suggest/code_line.rb, line 72
def indent_index
  @indent_index ||= [indent, index]
end
is_end?() 单击以切换源代码

如果代码行被确定为包含“end”关键字,则返回 true

# File syntax_suggest/code_line.rb, line 87
def is_end?
  @is_end
end
is_kw?() 单击以切换源代码

如果代码行被确定为包含与“end”匹配的关键字,则返回 true

例如:“def”、“do”、“begin”、“ensure”等。

# File syntax_suggest/code_line.rb, line 81
def is_kw?
  @is_kw
end
mark_invisible() 单击以切换源代码

用于隐藏行

搜索算法会将行分组为块,然后如果这些块被确定为表示有效的代码,它们将被隐藏

# File syntax_suggest/code_line.rb, line 96
def mark_invisible
  @line = ""
end
not_empty?() 单击以切换源代码

与“empty?”相反(注意:与“visible?”不同)

# File syntax_suggest/code_line.rb, line 120
def not_empty?
  !empty?
end
to_s() 单击以切换源代码

渲染给定的行

还允许我们将源代码表示为代码行数组。

当我们有一个代码行元素数组时,在数组上调用“join”将对每个元素调用“to_s”,这基本上将其转换回原始源字符串。

# File syntax_suggest/code_line.rb, line 133
def to_s
  line
end
trailing_slash?() 单击以切换源代码
# File syntax_suggest/code_line.rb, line 184
def trailing_slash?
  last = @lex.last
  last&.type == :on_tstring_end
end
visible?() 单击以切换源代码

表示该行被标记为“不可见”。令人困惑的是,“空”行是可见的……它们除了换行符 (“n”) 之外不包含任何源代码。

# File syntax_suggest/code_line.rb, line 103
def visible?
  !line.empty?
end

私有实例方法

set_kw_end() 单击以切换源代码

无限方法检测

来自 github.com/ruby/irb/commit/826ae909c9c93a2ddca6f9cfcd9c94dbf53d44ab 检测“单行”似乎需要一个状态机。这可以通过主要查看“状态”(最后一个值)来完成

ENDFN -> BEG (token = '=' ) -> END
# File 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