類別 SyntaxSuggest::CaptureCodeContext

將「無效區塊」轉換為有用的內容

演算法有三個主要階段

  1. 清除/格式化輸入來源

  2. 搜尋無效區塊

  3. 將無效區塊格式化為有意義的內容

此類別處理第三部分。

演算法很擅長在步驟 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

屬性

code_lines[R]

公開類別方法

new(blocks:, code_lines:) 按一下以切換來源
# File lib/syntax_suggest/capture_code_context.rb, line 51
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

公開實例方法

call() 按一下以切換來源
# File lib/syntax_suggest/capture_code_context.rb, line 58
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

  sorted_lines
end
capture_before_after_kws(block) 按一下以切換來源

顯示周圍的 kw/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 127
def capture_before_after_kws(block)
  return unless block.visible_lines.count == 1

  around_lines = Capture::BeforeAfterKeywordEnds.new(
    code_lines: @code_lines,
    block: block
  ).call

  around_lines -= block.lines

  @lines_to_output.concat(around_lines)
end
capture_falling_indent(block) 按一下以切換來源

顯示「遞減」縮排所提供的程式碼周圍內容

轉換

it "foo" do

class OH
  def hello
    it "foo" do
  end
end
# File lib/syntax_suggest/capture_code_context.rb, line 91
def capture_falling_indent(block)
  Capture::FallingIndentLines.new(
    block: block,
    code_lines: @code_lines
  ).call do |line|
    @lines_to_output << line
  end
end
capture_first_kw_end_same_indent(block) 按一下以切換來源

「capture_last_end_same_indent」的邏輯反向

當有一個無效區塊,其 `end` 遺漏一個關鍵字,且緊接在另一個 `end` 之後時,就無法清楚判斷哪個 `end` 遺漏關鍵字。

請看這個範例

class Dog       # 1
    puts "woof" # 2
  end           # 3
end             # 4

問題行將被識別為

> end            # 4

這發生是因為第 1、2 和 3 行在技術上是有效的程式碼,且會先擴充,視為有效並隱藏。我們需要取消隱藏第 1 行上的相符關鍵字。同時向後運作,如果有一個不匹配的結尾,也顯示它

# File lib/syntax_suggest/capture_code_context.rb, line 221
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(block) 按一下以切換來源

當有一個無效區塊,其關鍵字在另一個結尾之前遺失結尾時,不清楚哪個關鍵字遺失結尾

請看這個範例

class Dog       # 1
  def bark      # 2
    puts "woof" # 3
end             # 4

然而,由於 github.com/ruby/syntax_suggest/issues/32 問題行將被識別為

> class Dog       # 1

因為第 2、3 和 4 行在技術上是有效的程式碼,且會先擴充,視為有效並隱藏。我們需要取消隱藏相符的結尾第 4 行。同時向後運作,如果有一個不匹配的關鍵字,也顯示它

# File lib/syntax_suggest/capture_code_context.rb, line 161
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
sorted_lines() 按一下以切換來源
# File lib/syntax_suggest/capture_code_context.rb, line 69
def sorted_lines
  @lines_to_output.select!(&:not_empty?)
  @lines_to_output.uniq!
  @lines_to_output.sort!

  @lines_to_output
end