11
11
//===----------------------------------------------------------------------===//
12
12
13
13
import SwiftSyntax
14
+ import Foundation
14
15
15
16
/// PrettyPrinter takes a Syntax node and outputs a well-formatted, re-indented reproduction of the
16
17
/// code as a String.
@@ -66,6 +67,17 @@ public class PrettyPrinter {
66
67
private var configuration : Configuration { return context. configuration }
67
68
private let maxLineLength : Int
68
69
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
+
69
81
private var outputBuffer : String = " "
70
82
71
83
/// The number of spaces remaining on the current line.
@@ -172,11 +184,14 @@ public class PrettyPrinter {
172
184
/// - printTokenStream: Indicates whether debug information about the token stream should be
173
185
/// printed to standard output.
174
186
/// - 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 ) {
176
188
self . context = context
189
+ self . source = source
177
190
let configuration = context. configuration
178
191
self . tokens = node. makeTokenStream (
179
- configuration: configuration, operatorTable: context. operatorTable)
192
+ configuration: configuration,
193
+ selection: context. selection,
194
+ operatorTable: context. operatorTable)
180
195
self . maxLineLength = configuration. lineLength
181
196
self . spaceRemaining = self . maxLineLength
182
197
self . printTokenStream = printTokenStream
@@ -187,7 +202,9 @@ public class PrettyPrinter {
187
202
///
188
203
/// No further processing is performed on the string.
189
204
private func writeRaw< S: StringProtocol > ( _ str: S ) {
190
- outputBuffer. append ( String ( str) )
205
+ if disabledPosition == nil {
206
+ outputBuffer. append ( String ( str) )
207
+ }
191
208
}
192
209
193
210
/// Writes newlines into the output stream, taking into account any preexisting consecutive
@@ -241,7 +258,7 @@ public class PrettyPrinter {
241
258
writeRaw ( currentIndentation. indentation ( ) )
242
259
spaceRemaining = maxLineLength - currentIndentation. length ( in: configuration)
243
260
isAtStartOfLine = false
244
- } else if pendingSpaces > 0 {
261
+ } else if pendingSpaces > 0 {
245
262
writeRaw ( String ( repeating: " " , count: pendingSpaces) )
246
263
}
247
264
writeRaw ( text)
@@ -569,6 +586,39 @@ public class PrettyPrinter {
569
586
write ( " , " )
570
587
spaceRemaining -= 1
571
588
}
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
572
622
}
573
623
}
574
624
@@ -673,6 +723,10 @@ public class PrettyPrinter {
673
723
let length = isSingleElement ? 0 : 1
674
724
total += length
675
725
lengths. append ( length)
726
+
727
+ case . enableFormatting, . disableFormatting:
728
+ // no effect on length calculations
729
+ lengths. append ( 0 )
676
730
}
677
731
}
678
732
@@ -775,6 +829,14 @@ public class PrettyPrinter {
775
829
case . contextualBreakingEnd:
776
830
printDebugIndent ( )
777
831
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) ] " )
778
840
}
779
841
}
780
842
0 commit comments