at main 2.9 kB view raw
1// SPDX-FileCopyrightText: 2025 The Project Pterodactyl Developers 2// 3// SPDX-License-Identifier: MPL-2.0 4 5import Foundation 6 7fileprivate struct TokenLocation { 8 let startLine: Int 9 let startColumn: Int 10} 11 12fileprivate struct LocatedToken { 13 let token: Token 14 let location: TokenLocation 15 let lines: [String.SubSequence] 16 17 init(token: Token, location: TokenLocation) { 18 self.token = token 19 self.location = location 20 self.lines = token.text.split(separator: "\n", omittingEmptySubsequences: false) 21 } 22 23 var nextLocation: TokenLocation { 24 let startLine = location.startLine + lines.count - 1 25 let startColumn = if lines.count > 1 { lines.last!.utf16.count } else { lines.last?.utf16.count ?? location.startColumn } 26 return TokenLocation(startLine: startLine, startColumn: startColumn) 27 } 28} 29 30public struct BlockLayoutProcessor { 31 private let locatedTokens: [LocatedToken] 32 33 public init(tokens: [Token]) { 34 var locatedTokens: [LocatedToken] = [] 35 var location = TokenLocation(startLine: 0, startColumn: 0) 36 for token in tokens { 37 let locatedToken = LocatedToken(token: token, location: location) 38 locatedTokens.append(locatedToken) 39 location = locatedToken.nextLocation 40 } 41 42 self.locatedTokens = locatedTokens 43 } 44 45 46 public func layout() -> [Token] { 47 var result: [Token] = [] 48 var indentStack: [Int] = [0] 49 var previousLine = 0 50 var firstTokenInBlock = false 51 52 for (index, locatedToken) in locatedTokens.enumerated() { 53 guard locatedToken.token.kind != .eof else { break } 54 guard locatedToken.token.kind.canDetermineLayoutColumn else { 55 result.append(locatedToken.token) 56 continue 57 } 58 59 if locatedToken.location.startLine > previousLine { 60 while indentStack.count > 1 && locatedToken.location.startColumn < indentStack.last! { 61 indentStack.removeLast() 62 result.append(Token(kind: .blockEnd, text: "")) 63 } 64 65 if !firstTokenInBlock && indentStack.count > 1 && locatedToken.token.kind.isVisible && locatedToken.location.startColumn == indentStack.last! { 66 result.append(Token(kind: .blockSep, text: "")) 67 } 68 } 69 70 result.append(locatedToken.token) 71 72 if locatedToken.token.kind.isBlockHerald { 73 firstTokenInBlock = true 74 75 if let nextToken = locatedTokens[index...].first(where: { $0.location.startLine > locatedToken.location.startLine && $0.token.kind.canDetermineLayoutColumn }) { 76 result.append(Token(kind: .blockBegin, text: "")) 77 indentStack.append(nextToken.location.startColumn) 78 } else { 79 result.append(Token(kind: .blockBegin, text: "")) 80 result.append(Token(kind: .blockEnd, text: "")) 81 } 82 } else { 83 firstTokenInBlock = false 84 } 85 86 previousLine = locatedToken.location.startLine 87 } 88 89 while indentStack.count > 1 { 90 indentStack.removeLast() 91 result.append(Token(kind: .blockEnd, text: "")) 92 } 93 94 if let eof = locatedTokens.last, eof.token.kind == .eof { 95 result.append(eof.token) 96 } 97 98 return result 99 } 100}