類別 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 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
new(line:, index:, lex:) 按一下以切換原始碼
# 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

公開實例方法

<=>(other) 按一下以切換來源

比較運算子,等號和排序需要

# File lib/syntax_suggest/code_line.rb, line 150
def <=>(other)
  index <=> other.index
end
empty?() 按一下以切換來源

「empty?」列是原始程式碼中原本留空的列,而「隱藏」列則是我们標記為「不可見」的列

# File lib/syntax_suggest/code_line.rb, line 115
def empty?
  @empty
end
hidden?() 按一下以切換來源

與「visible?」相反(注意:與「empty?」不同)

# File lib/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 lib/syntax_suggest/code_line.rb, line 172
def ignore_newline_not_beg?
  @ignore_newline_not_beg
end
indent_index() 按一下以切換來源

用於透過縮排層級進行穩定排序

Ruby 的排序並不「穩定」,表示當多個元素具有相同值時,無法保證它們會以放入的順序返回。

因此,當多個程式碼列具有相同的縮排層級時,它們會按其唯一且一致的索引值排序。

這主要是為了測試套件的一致性

# File lib/syntax_suggest/code_line.rb, line 72
def indent_index
  @indent_index ||= [indent, index]
end
is_end?() 按一下以切換來源

如果確定程式碼列包含「end」關鍵字,則傳回 true

# File lib/syntax_suggest/code_line.rb, line 87
def is_end?
  @is_end
end
is_kw?() 按一下以切換來源

如果確定程式碼列包含與「end」相符的關鍵字,則傳回 true

例如:「def」、「do」、「begin」、「ensure」等。

# File lib/syntax_suggest/code_line.rb, line 81
def is_kw?
  @is_kw
end
mark_invisible() 按一下以切換來源

用於隱藏行

搜尋演算法會將行分組成區塊,然後如果判定這些區塊代表有效的程式碼,就會將它們隱藏

# File lib/syntax_suggest/code_line.rb, line 96
def mark_invisible
  @line = ""
end
not_empty?() 按一下以切換來源

與「empty?」相反(注意:與「visible?」不同)

# File lib/syntax_suggest/code_line.rb, line 120
def not_empty?
  !empty?
end
to_s() 按一下以切換來源

呈現指定的行

也允許我們將原始碼表示為程式碼行的陣列。

當我們有一個程式碼行元素的陣列時,在陣列上呼叫「join」會在每個元素上呼叫「to_s」,這基本上將它轉換回其原始來源字串。

# File lib/syntax_suggest/code_line.rb, line 133
def to_s
  line
end
trailing_slash?() 按一下以切換來源
# File lib/syntax_suggest/code_line.rb, line 184
def trailing_slash?
  last = @lex.last
  last&.type == :on_tstring_end
end
visible?() 按一下以切換來源

表示該行已標記為「invisible」。令人困惑的是,「empty」行是可見的…它們只是不包含任何原始碼,只包含換行符號(「n」)。

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

私有實例方法

set_kw_end() 按一下以切換來源

無限方法偵測

來自 github.com/ruby/irb/commit/826ae909c9c93a2ddca6f9cfcd9c94dbf53d44ab 偵測「oneliner」似乎需要一個狀態機。這可以透過主要查看「狀態」(最後一個值)來完成

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