@@ -2,7 +2,7 @@ import Foundation
22import SourceKittenFramework
33
44@DisabledWithoutSourceKit
5- struct IndentationWidthRule : OptInRule {
5+ struct IndentationWidthRule : OptInRule , CorrectableRule {
66 // MARK: - Subtypes
77 private enum Indentation : Equatable {
88 case tabs( Int )
@@ -202,4 +202,101 @@ struct IndentationWidthRule: OptInRule {
202202 ) // Allow unindent if it stays in the grid
203203 )
204204 }
205+
206+ // MARK: - Methods: Correction
207+ func correct( file: SwiftLintFile ) -> Int {
208+ var corrections = 0
209+ var previousLineIndentations : [ Indentation ] = [ ]
210+ var correctedLines = file. lines. map ( \. content)
211+
212+ for (lineIndex, line) in file. lines. enumerated ( ) {
213+ corrections += correctLine (
214+ at: lineIndex,
215+ line: line,
216+ file: file,
217+ in: & correctedLines,
218+ trackingIndentations: & previousLineIndentations
219+ )
220+ }
221+
222+ if corrections > 0 {
223+ let correctedContent = correctedLines. joined ( separator: " \n " )
224+ file. write ( correctedContent)
225+ }
226+
227+ return corrections
228+ }
229+
230+ private func correctLine(
231+ at lineIndex: Int ,
232+ line: Line ,
233+ file: SwiftLintFile ,
234+ in correctedLines: inout [ String ] ,
235+ trackingIndentations previousLineIndentations: inout [ Indentation ]
236+ ) -> Int {
237+ if ignoreCompilerDirective ( line: line, in: file) { return 0 }
238+ let indentationCharacterCount = line. content. countOfLeadingCharacters ( in: CharacterSet ( charactersIn: " \t " ) )
239+ if line. content. count == indentationCharacterCount { return 0 }
240+ if ignoreComment ( line: line, in: file) || ignoreMultilineStrings ( line: line, in: file) { return 0 }
241+
242+ let prefix = String ( line. content. prefix ( indentationCharacterCount) )
243+ let tabCount = prefix. filter { $0 == " \t " } . count
244+ let spaceCount = prefix. filter { $0 == " " } . count
245+
246+ if tabCount != 0 , spaceCount != 0 { return 0 }
247+
248+ let indentation : Indentation = tabCount != 0 ? . tabs( tabCount) : . spaces( spaceCount)
249+
250+ guard previousLineIndentations. isNotEmpty else {
251+ previousLineIndentations = [ indentation]
252+ if indentation != . spaces( 0 ) {
253+ correctedLines [ lineIndex] = String ( line. content. dropFirst ( indentationCharacterCount) )
254+ return 1
255+ }
256+ return 0
257+ }
258+
259+ let linesValidationResult = previousLineIndentations. map {
260+ validate ( indentation: indentation, comparingTo: $0)
261+ }
262+
263+ if linesValidationResult. contains ( true ) {
264+ if linesValidationResult. first == true {
265+ previousLineIndentations = [ indentation]
266+ } else {
267+ previousLineIndentations. append ( indentation)
268+ }
269+ return 0
270+ }
271+
272+ guard let lastValidIndentation = previousLineIndentations. first else { return 0 }
273+
274+ let correctIndentLevel = lastValidIndentation. spacesEquivalent ( indentationWidth: configuration. indentationWidth)
275+ let shouldUseTabs = tabCount > 0
276+ let correctIndent = generateIndentation ( spaceCount: correctIndentLevel, usesTabs: shouldUseTabs)
277+ let lineContent = String ( line. content. dropFirst ( indentationCharacterCount) )
278+ correctedLines [ lineIndex] = correctIndent + lineContent
279+
280+ let correctedIndentation : Indentation = shouldUseTabs
281+ ? . tabs( correctIndent. filter { $0 == " \t " } . count)
282+ : . spaces( correctIndent. filter { $0 == " " } . count)
283+ previousLineIndentations = [ correctedIndentation]
284+
285+ return 1
286+ }
287+
288+ /// Generates an indentation string based on the number of spaces and whether tabs should be used.
289+ ///
290+ /// - parameter spaceCount: The number of space-equivalents needed.
291+ /// - parameter usesTabs: Whether the indentation should use tabs.
292+ ///
293+ /// - returns: The generated indentation string.
294+ private func generateIndentation( spaceCount: Int , usesTabs: Bool ) -> String {
295+ if usesTabs {
296+ let tabCount = spaceCount / configuration. indentationWidth
297+ let remainingSpaces = spaceCount % configuration. indentationWidth
298+ return String ( repeating: " \t " , count: tabCount) + String( repeating: " " , count: remainingSpaces)
299+ }
300+ return String ( repeating: " " , count: spaceCount)
301+ }
205302}
0 commit comments