forked from realm/SwiftLint
-
Notifications
You must be signed in to change notification settings - Fork 0
/
VerticalParameterAlignmentRule.swift
95 lines (84 loc) · 4.51 KB
/
VerticalParameterAlignmentRule.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
//
// 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.",
kind: .style,
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",
"func regex(_ pattern: String,\n" +
" options: NSRegularExpression.Options = [.anchorsMatchLines,\n" +
" .dotMatchesLineSeparators]) -> NSRegularExpression\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 validate(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.nameOffset,
let nameLength = dictionary.nameLength,
let nameRange = contents.byteRangeToNSRange(start: nameOffset, length: nameLength),
let paramStart = regex(pattern).firstMatch(in: file.contents,
options: [], range: nameRange)?.range(at: 1).location,
let (startLine, startCharacter) = contents.lineAndCharacter(forCharacterOffset: paramStart),
let (endLine, _) = contents.lineAndCharacter(forByteOffset: nameOffset + nameLength - 1),
endLine > startLine else {
return []
}
let paramRegex = regex("^\\s*(.*?):", options: [.anchorsMatchLines])
let linesRange = (startLine + 1)...endLine
let violationLocations = linesRange.flatMap { lineIndex -> Int? in
let line = file.lines[lineIndex - 1]
guard let paramRange = paramRegex.firstMatch(in: file.contents, options: [],
range: line.range)?.range(at: 1),
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))
}
}
}