1111//===----------------------------------------------------------------------===//
1212
1313import SwiftSyntax
14+ import Foundation
1415
1516/// PrettyPrinter takes a Syntax node and outputs a well-formatted, re-indented reproduction of the
1617/// code as a String.
@@ -66,6 +67,17 @@ public class PrettyPrinter {
6667 private var configuration : Configuration { return context. configuration }
6768 private let maxLineLength : Int
6869 private var tokens : [ Token ]
70+ private var source : String
71+
72+ /// Keep track of where formatting was disabled in the original source
73+ ///
74+ /// To format a selection, we insert `enableFormatting`/`disableFormatting` tokens into the
75+ /// stream when entering/exiting a selection range. Those tokens include utf8 offsets into the
76+ /// original source. When enabling formatting, we copy the text between `disabledPosition` and the
77+ /// current position to `outputBuffer`. From then on, we continue to format until the next
78+ /// `disableFormatting` token.
79+ private var disabledPosition : AbsolutePosition ? = nil
80+
6981 private var outputBuffer : String = " "
7082
7183 /// The number of spaces remaining on the current line.
@@ -172,11 +184,14 @@ public class PrettyPrinter {
172184 /// - printTokenStream: Indicates whether debug information about the token stream should be
173185 /// printed to standard output.
174186 /// - whitespaceOnly: Whether only whitespace changes should be made.
175- public init ( context: Context , node: Syntax , printTokenStream: Bool , whitespaceOnly: Bool ) {
187+ public init ( context: Context , source : String , node: Syntax , printTokenStream: Bool , whitespaceOnly: Bool ) {
176188 self . context = context
189+ self . source = source
177190 let configuration = context. configuration
178191 self . tokens = node. makeTokenStream (
179- configuration: configuration, operatorTable: context. operatorTable)
192+ configuration: configuration,
193+ selection: context. selection,
194+ operatorTable: context. operatorTable)
180195 self . maxLineLength = configuration. lineLength
181196 self . spaceRemaining = self . maxLineLength
182197 self . printTokenStream = printTokenStream
@@ -187,7 +202,9 @@ public class PrettyPrinter {
187202 ///
188203 /// No further processing is performed on the string.
189204 private func writeRaw< S: StringProtocol > ( _ str: S ) {
190- outputBuffer. append ( String ( str) )
205+ if disabledPosition == nil {
206+ outputBuffer. append ( String ( str) )
207+ }
191208 }
192209
193210 /// Writes newlines into the output stream, taking into account any preexisting consecutive
@@ -241,7 +258,7 @@ public class PrettyPrinter {
241258 writeRaw ( currentIndentation. indentation ( ) )
242259 spaceRemaining = maxLineLength - currentIndentation. length ( in: configuration)
243260 isAtStartOfLine = false
244- } else if pendingSpaces > 0 {
261+ } else if pendingSpaces > 0 {
245262 writeRaw ( String ( repeating: " " , count: pendingSpaces) )
246263 }
247264 writeRaw ( text)
@@ -569,6 +586,39 @@ public class PrettyPrinter {
569586 write ( " , " )
570587 spaceRemaining -= 1
571588 }
589+
590+ case . enableFormatting( let enabledPosition) :
591+ guard let disabledPosition else {
592+ // if we're not disabled, we ignore the token
593+ break
594+ }
595+ let start = source. utf8. index ( source. utf8. startIndex, offsetBy: disabledPosition. utf8Offset)
596+ let end : String . Index
597+ if let enabledPosition {
598+ end = source. utf8. index ( source. utf8. startIndex, offsetBy: enabledPosition. utf8Offset)
599+ } else {
600+ end = source. endIndex
601+ }
602+ var text = String ( source [ start..< end] )
603+ // strip trailing whitespace so that the next formatting can add the right amount
604+ if let nonWhitespace = text. rangeOfCharacter (
605+ from: CharacterSet . whitespaces. inverted, options: . backwards) {
606+ text = String ( text [ ..< nonWhitespace. upperBound] )
607+ }
608+
609+ self . disabledPosition = nil
610+ writeRaw ( text)
611+ if text. hasSuffix ( " \n " ) {
612+ isAtStartOfLine = true
613+ consecutiveNewlineCount = 1
614+ } else {
615+ isAtStartOfLine = false
616+ consecutiveNewlineCount = 0
617+ }
618+
619+ case . disableFormatting( let newPosition) :
620+ assert ( disabledPosition == nil )
621+ disabledPosition = newPosition
572622 }
573623 }
574624
@@ -673,6 +723,10 @@ public class PrettyPrinter {
673723 let length = isSingleElement ? 0 : 1
674724 total += length
675725 lengths. append ( length)
726+
727+ case . enableFormatting, . disableFormatting:
728+ // no effect on length calculations
729+ lengths. append ( 0 )
676730 }
677731 }
678732
@@ -775,6 +829,14 @@ public class PrettyPrinter {
775829 case . contextualBreakingEnd:
776830 printDebugIndent ( )
777831 print ( " [END BREAKING CONTEXT Idx: \( idx) ] " )
832+
833+ case . enableFormatting( let pos) :
834+ printDebugIndent ( )
835+ print ( " [ENABLE FORMATTING utf8 offset: \( String ( describing: pos) ) ] " )
836+
837+ case . disableFormatting( let pos) :
838+ printDebugIndent ( )
839+ print ( " [DISABLE FORMATTING utf8 offset: \( pos) ] " )
778840 }
779841 }
780842
0 commit comments