類別 SyntaxSuggest::CodeFrontier

演算法中有三個主要階段

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

  2. 搜尋無效區塊

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

Code frontier 是第二個步驟中至關重要的部分

## 了解我們去過哪裡

一旦產生一個程式區塊,就會將其加入 frontier。然後會依照縮排進行排序,並且可以過濾 frontier。完全包住較小區塊的大區塊會導致較小區塊被驅逐。

CodeFrontier#<<(block) # Adds block to frontier
CodeFrontier#pop # Removes block from frontier

## 了解我們可以去哪裡

在內部,frontier 會追蹤「未拜訪」的行,這些行會透過 `next_indent_line` 顯示,當呼叫此方法時,它會傳回縮排程度最高的程式碼行。

傳回的程式碼行可以用來建構 CodeBlock,然後將該程式區塊加回 frontier。接著,會從「未拜訪」中移除這些行,這樣我們就不會重複建立同一個區塊。

CodeFrontier#next_indent_line # Shows next line
CodeFrontier#register_indent_block(block) # Removes lines from unvisited

## 了解何時停止

frontier 知道如何檢查整個文件是否有語法錯誤。當區塊加入 frontier 時,它們會從文件中移除。當所有包含語法錯誤的程式碼都已加入 frontier 時,文件就可以在沒有語法錯誤的情況下進行解析,並且可以停止搜尋。

CodeFrontier#holds_all_syntax_errors? # Returns true when frontier holds all syntax errors

## 過濾誤報

搜尋完成後,frontier 可能有多個不包含語法錯誤的區塊。若要將結果限制在「無效區塊」的最小子集,請呼叫

CodeFrontier#detect_invalid_blocks

公開類別方法

combination(array) 按一下以切換來源

範例

combination([:a, :b, :c, :d])
# => [[:a], [:b], [:c], [:d], [:a, :b], [:a, :c], [:a, :d], [:b, :c], [:b, :d], [:c, :d], [:a, :b, :c], [:a, :b, :d], [:a, :c, :d], [:b, :c, :d], [:a, :b, :c, :d]]
# File lib/syntax_suggest/code_frontier.rb, line 162
def self.combination(array)
  guesses = []
  1.upto(array.length).each do |size|
    guesses.concat(array.combination(size).to_a)
  end
  guesses
end
new(code_lines:, unvisited: UnvisitedLines.new(code_lines: code_lines)) 按一下以切換來源
# File lib/syntax_suggest/code_frontier.rb, line 53
def initialize(code_lines:, unvisited: UnvisitedLines.new(code_lines: code_lines))
  @code_lines = code_lines
  @unvisited = unvisited
  @queue = PriorityEngulfQueue.new

  @check_next = true
end

公開實例方法

<<(block) 按一下以切換來源

將區塊加入 frontier

此方法確保邊界始終保持排序(依縮排順序),且每個程式區塊的行都會從縮排雜湊中移除,因此我們不會重複評估同一行多次。

# File lib/syntax_suggest/code_frontier.rb, line 148
def <<(block)
  @unvisited.visit_block(block)

  @queue.push(block)

  @check_next = true if block.invalid?

  self
end
count() 按一下以切換來源
# File lib/syntax_suggest/code_frontier.rb, line 61
def count
  @queue.length
end
detect_invalid_blocks() 按一下以切換來源

由於我們知道我們的語法錯誤存在於邊界中的某個地方,我們想要找出包含所有語法錯誤的最小區塊組。

# File lib/syntax_suggest/code_frontier.rb, line 172
def detect_invalid_blocks
  self.class.combination(@queue.to_a.select(&:invalid?)).detect do |block_array|
    holds_all_syntax_errors?(block_array, can_cache: false)
  end || []
end
expand?() 按一下以切換來源
# File lib/syntax_suggest/code_frontier.rb, line 111
def expand?
  return false if @queue.empty?
  return true if @unvisited.empty?

  frontier_indent = @queue.peek.current_indent
  unvisited_indent = next_indent_line.indent

  if ENV["SYNTAX_SUGGEST_DEBUG"]
    puts "```"
    puts @queue.peek
    puts "```"
    puts "  @frontier indent:  #{frontier_indent}"
    puts "  @unvisited indent: #{unvisited_indent}"
  end

  # Expand all blocks before moving to unvisited lines
  frontier_indent >= unvisited_indent
end
holds_all_syntax_errors?(block_array = @queue, can_cache: true) 按一下以切換來源

如果移除所有行後文件有效,則傳回 true。預設會檢查邊界陣列中存在的所有區塊,但也可將其用於任意程式區塊陣列

# File lib/syntax_suggest/code_frontier.rb, line 89
def holds_all_syntax_errors?(block_array = @queue, can_cache: true)
  return false if can_cache && can_skip_check?

  without_lines = block_array.to_a.flat_map do |block|
    block.lines
  end

  SyntaxSuggest.valid_without?(
    without_lines: without_lines,
    code_lines: @code_lines
  )
end
next_indent_line() 按一下以切換來源
# File lib/syntax_suggest/code_frontier.rb, line 107
def next_indent_line
  @unvisited.peek
end
pop() 按一下以切換來源

傳回縮排最大的程式區塊

# File lib/syntax_suggest/code_frontier.rb, line 103
def pop
  @queue.pop
end
register_engulf_block(block) 按一下以切換來源

當一個元素完全封裝另一個元素時,我們會從邊界中移除較小的區塊。這可以防止重複擴充和各種奇怪的行為。但是,要維持此保證的成本很高

# File lib/syntax_suggest/code_frontier.rb, line 140
def register_engulf_block(block)
end
register_indent_block(block) 按一下以切換來源

追蹤已新增至區塊的行,以及尚未拜訪的行。

# File lib/syntax_suggest/code_frontier.rb, line 132
def register_indent_block(block)
  @unvisited.visit_block(block)
  self
end

私人實例方法

can_skip_check?() 按一下以切換來源

效能最佳化

使用 ripper 進行剖析的成本很高。如果我們知道沒有任何區塊具有無效語法,則我們知道我們尚未找到不正確的語法。

當無效區塊新增至邊界時,檢查文件狀態

# File lib/syntax_suggest/code_frontier.rb, line 74
        def can_skip_check?
  check_next = @check_next
  @check_next = false

  if check_next
    false
  else
    true
  end
end