From 3ca204bb18c4a20bcb4402c22ddb8c6241c906f3 Mon Sep 17 00:00:00 2001 From: Ben Barham Date: Thu, 9 Jan 2025 20:32:55 -0800 Subject: [PATCH] Optimize pretty printing performance swift-format#883 fixed outputting incorrect line numbers, but introduced a performance regression. swift-format#901 improved this back to around the original, but had to be reverted as it introduced a an issue due to counting codepoints rather than characters. Introduce a similar optimization again, but only for the first portion of the string (prior to the last newline). Fixes swift-format#894 again. --- .../PrettyPrint/PrettyPrintBuffer.swift | 25 ++++++++------ .../PrettyPrint/LineNumbersTests.swift | 34 +++++++++++++++++++ 2 files changed, 48 insertions(+), 11 deletions(-) diff --git a/Sources/SwiftFormat/PrettyPrint/PrettyPrintBuffer.swift b/Sources/SwiftFormat/PrettyPrint/PrettyPrintBuffer.swift index 4b02d372e..9ea726206 100644 --- a/Sources/SwiftFormat/PrettyPrint/PrettyPrintBuffer.swift +++ b/Sources/SwiftFormat/PrettyPrint/PrettyPrintBuffer.swift @@ -119,18 +119,21 @@ struct PrettyPrintBuffer { consecutiveNewlineCount = 0 pendingSpaces = 0 - // In case of comments, we may get a multi-line string. - // To account for that case, we need to correct the lineNumber count. - // The new column is only the position within the last line. - let lines = text.split(separator: "\n") - lineNumber += lines.count - 1 - if lines.count > 1 { - // in case we have inserted new lines, we need to reset the column - column = lines.last?.count ?? 0 + // In case of comments, we may get a multi-line string. To account for that case, we need to correct the + // `lineNumber` count. The new `column` is the position within the last line. + + var lastNewlineIndex: String.Index? = nil + for i in text.utf8.indices { + if text.utf8[i] == UInt8(ascii: "\n") { + lastNewlineIndex = i + lineNumber += 1 + } + } + + if let lastNewlineIndex { + column = text.distance(from: text.utf8.index(after: lastNewlineIndex), to: text.endIndex) } else { - // in case it is an end of line comment or a single line comment, - // we just add to the current column - column += lines.last?.count ?? 0 + column += text.count } } diff --git a/Tests/SwiftFormatTests/PrettyPrint/LineNumbersTests.swift b/Tests/SwiftFormatTests/PrettyPrint/LineNumbersTests.swift index 1bb58f7ab..c377e7cad 100644 --- a/Tests/SwiftFormatTests/PrettyPrint/LineNumbersTests.swift +++ b/Tests/SwiftFormatTests/PrettyPrint/LineNumbersTests.swift @@ -81,4 +81,38 @@ final class LineNumbersTests: PrettyPrintTestCase { ] ) } + + func testCharacterVsCodepoint() { + let input = + """ + let fo = 1 // 🤥 + + """ + + assertPrettyPrintEqual( + input: input, + expected: input, + linelength: 16, + whitespaceOnly: true, + findings: [] + ) + } + + func testCharacterVsCodepointMultiline() { + let input = + #""" + /// This is a multiline + /// comment that is in 🤥 + /// fact perfectly sized + + """# + + assertPrettyPrintEqual( + input: input, + expected: input, + linelength: 25, + whitespaceOnly: true, + findings: [] + ) + } }