1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
|
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2021-2022 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
//
//===----------------------------------------------------------------------===//
// Useful for testing, debugging, etc.
extension AST.Node {
func _postOrder() -> Array<AST.Node> {
var nodes = Array<AST.Node>()
_postOrder(into: &nodes)
return nodes
}
func _postOrder(into array: inout Array<AST.Node>) {
children?.forEach { $0._postOrder(into: &array) }
array.append(self)
}
// Produce a textually "rendered" range
//
// NOTE: `input` must be the string from which a
// source range was derived.
func _renderRange(
count: Int, into output: inout String
) {
guard count > 0 else { return }
let repl = String(repeating: "-", count: count-1) + "^"
output.replaceSubrange(location.range, with: repl)
}
// We render from top-to-bottom, coalescing siblings
public func _render(in input: String) -> [String] {
let base = String(repeating: " ", count: input.count)
var lines = [base]
// TODO: drop the filtering when fake-ness is taken out of
// this module
let nodes = _postOrder().filter(\.location.isReal)
nodes.forEach { node in
let loc = node.location
let count = input[loc.range].count
for idx in lines.indices {
if lines[idx][loc.range].all(\.isWhitespace) {
node._renderRange(count: count, into: &lines[idx])
return
}
}
var nextLine = base
node._renderRange(count: count, into: &nextLine)
lines.append(nextLine)
}
return lines.first!.all(\.isWhitespace) ? [] : lines
}
}
extension AST {
// We render from top-to-bottom, coalescing siblings
public func _render(in input: String) -> [String] {
root._render(in: input)
}
}
|