Files
SwiftLint/Source/SwiftLintFramework/Rules/VerticalParameterAlignmentRule.swift
T
2016-12-23 12:46:22 -02:00

92 lines
4.3 KiB
Swift

//
// VerticalParameterAlignmentRule.swift
// SwiftLint
//
// Created by Marcelo Fabri on 12/22/16.
// Copyright © 2016 Realm. All rights reserved.
//
import Foundation
import SourceKittenFramework
public struct VerticalParameterAlignmentRule: ASTRule, ConfigurationProviderRule {
public var configuration = SeverityConfiguration(.warning)
public init() {}
public static let description = RuleDescription(
identifier: "vertical_parameter_alignment",
name: "Vertical Parameter Alignment",
description: "Function parameters should be aligned vertically if they're in multiple lines in a declaration.",
nonTriggeringExamples: [
"func validateFunction(_ file: File, kind: SwiftDeclarationKind,\n" +
" dictionary: [String: SourceKitRepresentable]) { }\n",
"func validateFunction(_ file: File, kind: SwiftDeclarationKind,\n" +
" dictionary: [String: SourceKitRepresentable]) -> [StyleViolation]\n",
"func foo(bar: Int)\n",
"func foo(bar: Int) -> String \n",
"func validateFunction(_ file: File, kind: SwiftDeclarationKind,\n" +
" dictionary: [String: SourceKitRepresentable])\n" +
" -> [StyleViolation]\n",
"func validateFunction(\n" +
" _ file: File, kind: SwiftDeclarationKind,\n" +
" dictionary: [String: SourceKitRepresentable]) -> [StyleViolation]\n",
"func validateFunction(\n" +
" _ file: File, kind: SwiftDeclarationKind,\n" +
" dictionary: [String: SourceKitRepresentable]\n" +
") -> [StyleViolation]\n"
],
triggeringExamples: [
"func validateFunction(_ file: File, kind: SwiftDeclarationKind,\n" +
" ↓dictionary: [String: SourceKitRepresentable]) { }\n",
"func validateFunction(_ file: File, kind: SwiftDeclarationKind,\n" +
" ↓dictionary: [String: SourceKitRepresentable]) { }\n",
"func validateFunction(_ file: File,\n" +
" ↓kind: SwiftDeclarationKind,\n" +
" ↓dictionary: [String: SourceKitRepresentable]) { }\n"
]
)
public func validateFile(_ file: File,
kind: SwiftDeclarationKind,
dictionary: [String: SourceKitRepresentable]) -> [StyleViolation] {
guard SwiftDeclarationKind.functionKinds().contains(kind) else {
return []
}
let contents = file.contents.bridge()
let pattern = "\\(\\s*(\\S)"
guard let nameOffset = (dictionary["key.nameoffset"] as? Int64).flatMap({ Int($0) }),
let nameLength = (dictionary["key.namelength"] as? Int64).flatMap({ Int($0) }),
let nameRange = contents.byteRangeToNSRange(start: nameOffset, length: nameLength),
let paramStart = regex(pattern).firstMatch(in: file.contents,
options: [], range: nameRange)?.rangeAt(1).location,
let (startLine, startCharacter) = contents.lineAndCharacter(forCharacterOffset: paramStart),
let (endLine, _) = contents.lineAndCharacter(forByteOffset: nameOffset + nameLength - 1),
endLine > startLine else {
return []
}
let linesRange = (startLine + 1)...endLine
let violationLocations = linesRange.flatMap { lineIndex -> Int? in
let line = file.lines[lineIndex - 1]
guard let paramRange = regex("\\S").firstMatch(in: file.contents, options: [],
range: line.range)?.range,
contents.substring(with: paramRange) != ")",
let (_, paramCharacter) = contents.lineAndCharacter(forCharacterOffset: paramRange.location),
paramCharacter != startCharacter else {
return nil
}
return paramRange.location
}
return violationLocations.map {
StyleViolation(ruleDescription: type(of: self).description,
severity: configuration.severity,
location: Location(file: file, characterOffset: $0))
}
}
}