@@ -3,7 +3,7 @@ import SourceKittenFramework
33import SwiftSyntax
44
55@DisabledWithoutSourceKit
6- struct IndentationWidthRule : OptInRule {
6+ struct IndentationWidthRule : OptInRule , CorrectableRule {
77 // MARK: - Subtypes
88 private enum Indentation : Equatable {
99 case tabs( Int )
@@ -279,6 +279,103 @@ struct IndentationWidthRule: OptInRule {
279279 ) // Allow unindent if it stays in the grid
280280 )
281281 }
282+
283+ // MARK: - Methods: Correction
284+ func correct( file: SwiftLintFile ) -> Int {
285+ var corrections = 0
286+ var previousLineIndentations : [ Indentation ] = [ ]
287+ var correctedLines = file. lines. map ( \. content)
288+
289+ for (lineIndex, line) in file. lines. enumerated ( ) {
290+ corrections += correctLine (
291+ at: lineIndex,
292+ line: line,
293+ file: file,
294+ in: & correctedLines,
295+ trackingIndentations: & previousLineIndentations
296+ )
297+ }
298+
299+ if corrections > 0 {
300+ let correctedContent = correctedLines. joined ( separator: " \n " )
301+ file. write ( correctedContent)
302+ }
303+
304+ return corrections
305+ }
306+
307+ private func correctLine(
308+ at lineIndex: Int ,
309+ line: Line ,
310+ file: SwiftLintFile ,
311+ in correctedLines: inout [ String ] ,
312+ trackingIndentations previousLineIndentations: inout [ Indentation ]
313+ ) -> Int {
314+ if ignoreCompilerDirective ( line: line, in: file) { return 0 }
315+ let indentationCharacterCount = line. content. countOfLeadingCharacters ( in: CharacterSet ( charactersIn: " \t " ) )
316+ if line. content. count == indentationCharacterCount { return 0 }
317+ if ignoreComment ( line: line, in: file) || ignoreMultilineStrings ( line: line, in: file) { return 0 }
318+
319+ let prefix = String ( line. content. prefix ( indentationCharacterCount) )
320+ let tabCount = prefix. filter { $0 == " \t " } . count
321+ let spaceCount = prefix. filter { $0 == " " } . count
322+
323+ if tabCount != 0 , spaceCount != 0 { return 0 }
324+
325+ let indentation : Indentation = tabCount != 0 ? . tabs( tabCount) : . spaces( spaceCount)
326+
327+ guard previousLineIndentations. isNotEmpty else {
328+ previousLineIndentations = [ indentation]
329+ if indentation != . spaces( 0 ) {
330+ correctedLines [ lineIndex] = String ( line. content. dropFirst ( indentationCharacterCount) )
331+ return 1
332+ }
333+ return 0
334+ }
335+
336+ let linesValidationResult = previousLineIndentations. map {
337+ validate ( indentation: indentation, comparingTo: $0)
338+ }
339+
340+ if linesValidationResult. contains ( true ) {
341+ if linesValidationResult. first == true {
342+ previousLineIndentations = [ indentation]
343+ } else {
344+ previousLineIndentations. append ( indentation)
345+ }
346+ return 0
347+ }
348+
349+ guard let lastValidIndentation = previousLineIndentations. first else { return 0 }
350+
351+ let correctIndentLevel = lastValidIndentation. spacesEquivalent ( indentationWidth: configuration. indentationWidth)
352+ let shouldUseTabs = tabCount > 0
353+ let correctIndent = generateIndentation ( spaceCount: correctIndentLevel, usesTabs: shouldUseTabs)
354+ let lineContent = String ( line. content. dropFirst ( indentationCharacterCount) )
355+ correctedLines [ lineIndex] = correctIndent + lineContent
356+
357+ let correctedIndentation : Indentation = shouldUseTabs
358+ ? . tabs( correctIndent. filter { $0 == " \t " } . count)
359+ : . spaces( correctIndent. filter { $0 == " " } . count)
360+ previousLineIndentations = [ correctedIndentation]
361+
362+ return 1
363+ }
364+
365+ /// Generates an indentation string based on the number of spaces and whether tabs should be used.
366+ ///
367+ /// - parameter spaceCount: The number of space-equivalents needed.
368+ /// - parameter usesTabs: Whether the indentation should use tabs.
369+ ///
370+ /// - returns: The generated indentation string.
371+ private func generateIndentation( spaceCount: Int , usesTabs: Bool ) -> String {
372+ if usesTabs {
373+ let tabCount = spaceCount / configuration. indentationWidth
374+ let remainingSpaces = spaceCount % configuration. indentationWidth
375+ return String ( repeating: " \t " , count: tabCount) + String( repeating: " " , count: remainingSpaces)
376+ }
377+ return String ( repeating: " " , count: spaceCount)
378+ }
282379}
283380
284381private final class MultilineConditionLineVisitor : SyntaxVisitor {
0 commit comments