class SyntaxSuggest::CodeFrontier
Es gibt drei Hauptphasen im Algorithmus
-
Bereinigung/Formatierung des Eingabequellcodes
-
Suche nach ungültigen Blöcken
-
Formatierung ungültiger Blöcke in etwas Sinnvolles
Der Code-Frontier ist ein kritischer Teil des zweiten Schritts.
## Wissen, wo wir waren
Sobald ein Codeblock generiert wurde, wird er dem Frontier hinzugefügt. Dann wird er nach Einrückung sortiert und der Frontier kann gefiltert werden. Große Blöcke, die einen kleineren Block vollständig umschließen, führen dazu, dass der kleinere Block verdrängt wird.
CodeFrontier#<<(block) # Adds block to frontier CodeFrontier#pop # Removes block from frontier
## Wissen, wohin wir gehen können
Intern verfolgt der Frontier „unbesuchte“ Zeilen, die über `next_indent_line` verfügbar gemacht werden. Wenn diese Methode aufgerufen wird, gibt sie eine Codezeile mit der höchsten Einrückung zurück.
Die zurückgegebene Codezeile kann verwendet werden, um einen CodeBlock zu erstellen. Dann wird dieser Codeblock wieder zum Frontier hinzugefügt. Anschließend werden die Zeilen aus dem „unbesuchten“ Bereich entfernt, damit wir nicht denselben Block doppelt erstellen.
CodeFrontier#next_indent_line # Shows next line CodeFrontier#register_indent_block(block) # Removes lines from unvisited
## Wissen, wann man aufhören muss
Der Frontier weiß, wie das gesamte Dokument auf Syntaxfehler überprüft werden kann. Wenn Blöcke dem Frontier hinzugefügt werden, werden sie aus dem Dokument entfernt. Wenn der gesamte Code, der Syntaxfehler enthält, zum Frontier hinzugefügt wurde, ist das Dokument ohne Syntaxfehler analysierbar und die Suche kann beendet werden.
CodeFrontier#holds_all_syntax_errors? # Returns true when frontier holds all syntax errors
## Filtern von falsch positiven Ergebnissen
Sobald die Suche abgeschlossen ist, kann der Frontier mehrere Blöcke enthalten, die den Syntaxfehler nicht enthalten. Um das Ergebnis auf die kleinste Teilmenge von „ungültigen Blöcken“ zu beschränken, rufen Sie auf:
CodeFrontier#detect_invalid_blocks
Öffentliche Klassenmethoden
Source
# 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
Beispiel
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]]
Source
# 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
Öffentliche Instanzmethoden
Source
# 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
Fügt einen Block zum Frontier hinzu.
Diese Methode stellt sicher, dass der Frontier immer sortiert bleibt (in Einrückungsreihenfolge) und dass die Zeilen jedes Codeblocks aus dem Einrückungs-Hash entfernt werden, damit wir dieselbe Zeile nicht mehrmals auswerten.
Source
# 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
Da wir wissen, dass unser Syntaxfehler irgendwo in unserem Frontier existiert, wollen wir die kleinstmögliche Menge an Blöcken finden, die alle Syntaxfehler enthalten.
Source
# 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
Source
# 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
Gibt `true` zurück, wenn das Dokument mit entfernten Zeilen gültig ist. Standardmäßig werden alle im Frontier-Array vorhandenen Blöcke überprüft, kann aber auch für beliebige Arrays von Codeblöcken verwendet werden.
Source
# File lib/syntax_suggest/code_frontier.rb, line 107 def next_indent_line @unvisited.peek end
Source
# File lib/syntax_suggest/code_frontier.rb, line 103 def pop @queue.pop end
Gibt einen Codeblock mit der größtmöglichen Einrückung zurück.
Source
# File lib/syntax_suggest/code_frontier.rb, line 140 def register_engulf_block(block) end
Wenn ein Element ein anderes vollständig umschließt, entfernen wir den kleineren Block aus dem Frontier. Dies verhindert doppelte Expansionen und allseitig seltsames Verhalten. Diese Garantie ist jedoch sehr teuer aufrechtzuerhalten.
Source
# File lib/syntax_suggest/code_frontier.rb, line 132 def register_indent_block(block) @unvisited.visit_block(block) self end
Verfolgt, welche Zeilen zu Blöcken hinzugefügt wurden und welche noch nicht besucht wurden.
Private Instanzmethoden
Source
# 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
Performance-Optimierung.
Das Parsen mit `ripper` ist teuer. Wenn wir wissen, dass wir keine Blöcke mit ungültiger Syntax haben, dann wissen wir auch, dass wir die fehlerhafte Syntax noch nicht gefunden haben können.
Wenn ein ungültiger Block dem Frontier hinzugefügt wird, überprüfen Sie den Dokumentstatus.