Permalink
Browse files

Read options from .yanagiba files

  • Loading branch information...
ryuichis committed Jun 18, 2017
1 parent 788ccda commit a0e33017d4434183fde23fd9f143e995621b50e0
Showing with 232 additions and 6 deletions.
  1. +1 −1 .travis.yml
  2. +8 −0 .yanagiba
  3. +170 −0 Sources/swift-lint/DotYanagibaLint.swift
  4. +51 −5 Sources/swift-lint/main.swift
  5. +2 −0 run.sh
View
@@ -9,6 +9,6 @@ install:
- eval "$(curl -sL https://gist.githubusercontent.com/kylef/5c0475ff02b7c7671d2a/raw/9f442512a46d7a2af7b850d65a7e9bd31edfb09b/swiftenv-install.sh)"
script:
- make test
- ./dogFooding.sh --rule-configure NESTED_CODE_BLOCK_DEPTH=6
- ./dogFooding.sh
after_success:
- eval "$(curl -sL https://swift.vapor.sh/codecov)"
View
@@ -0,0 +1,8 @@
lint:
rule-configurations:
- NESTED_CODE_BLOCK_DEPTH: 6
severity-thresholds:
- critical: 0
- major: 0
- minor: 0
- cosmetic: 0
@@ -0,0 +1,170 @@
/*
Copyright 2017 Ryuichi Saito, LLC and the Yanagiba project contributors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import Foundation
struct DotYanagibaLint {
var enableRules: [String]?
var disableRules: [String]?
var ruleConfigurations: [String: Any]?
var reportType: String?
var outputPath: String?
var severityThresholds: [String: Int]?
}
extension DotYanagibaLint {
mutating func merge(with other: DotYanagibaLint) {
if let otherEnableRules = other.enableRules {
var mergedEnableRules = enableRules ?? []
mergedEnableRules += otherEnableRules
enableRules = Array(Set(mergedEnableRules))
}
if let otherDisableRules = other.disableRules {
var mergedDisableRules = disableRules ?? []
mergedDisableRules += otherDisableRules
disableRules = Array(Set(mergedDisableRules))
}
if let otherRuleConfigurations = other.ruleConfigurations {
var mergedRuleConfigurations = ruleConfigurations ?? [:]
for (key, value) in otherRuleConfigurations {
mergedRuleConfigurations[key] = value
}
ruleConfigurations = mergedRuleConfigurations
}
if let otherReportType = other.reportType {
reportType = otherReportType
}
if let otherOutputPath = other.outputPath {
outputPath = otherOutputPath
}
if let otherSeverityThresholds = other.severityThresholds {
var mergedSeverityThresholds = severityThresholds ?? [:]
for (key, value) in otherSeverityThresholds {
mergedSeverityThresholds[key] = value
}
severityThresholds = mergedSeverityThresholds
}
}
}
struct DotYanagibaLintReader {
private static func extractLintLines(from content: String) -> [String] {
var lines = [String]()
var lintBlock = false
for line in content.components(separatedBy: "\n") {
if line.hasPrefix("lint:") {
lintBlock = true
} else if line.hasPrefix(" ") {
if lintBlock {
let whitespaceRemoved = line.components(separatedBy: .whitespaces).joined()
lines.append(whitespaceRemoved)
}
} else {
lintBlock = false
}
}
return lines
}
private static func extractLintOptions(from lines: [String]) -> [String: [String]] {
var lintOptions: [String: [String]] = [:]
var currentKey = ""
var currentOptions: [String] = []
for line in lines {
if line.hasPrefix("-") {
let option = String(line[line.index(after: line.startIndex)...])
currentOptions.append(option)
} else {
if !currentKey.isEmpty {
if !currentOptions.isEmpty {
lintOptions[currentKey] = currentOptions
currentOptions = []
}
currentKey = ""
}
let keyValuePair = line.components(separatedBy: ":")
if keyValuePair.count == 2 {
let key = keyValuePair[0]
let value = keyValuePair[1]
if value.isEmpty {
currentKey = key
} else {
lintOptions[key] = [value]
}
}
}
}
if !currentKey.isEmpty && !currentOptions.isEmpty {
lintOptions[currentKey] = currentOptions
}
return lintOptions
}
private static func extractKeyValuePair(from options: [String]) -> [String: Int] {
var dict: [String: Int] = [:]
for option in options {
let keyValuePair = option.components(separatedBy: ":")
if keyValuePair.count == 2, let value = Int(keyValuePair[1]) {
let key = keyValuePair[0]
dict[key] = value
}
}
return dict
}
static func read(from path: String) -> DotYanagibaLint? {
let fileManager = FileManager.default
guard fileManager.fileExists(atPath: path),
let content = try? String(contentsOfFile: path, encoding: .utf8)
else {
return nil
}
let lines = extractLintLines(from: content)
let lintOptions = extractLintOptions(from: lines)
var dotYanagibaLint = DotYanagibaLint()
for (key, option) in lintOptions {
switch key {
case "enable-rules":
dotYanagibaLint.enableRules = option
case "diable-rules":
dotYanagibaLint.disableRules = option
case "rule-configurations":
dotYanagibaLint.ruleConfigurations = extractKeyValuePair(from: option)
case "report-type":
dotYanagibaLint.reportType = option.first
case "output-path":
dotYanagibaLint.outputPath = option.first
case "severity-thresholds":
dotYanagibaLint.severityThresholds = extractKeyValuePair(from: option)
default:
break
}
}
return dotYanagibaLint
}
}
@@ -19,6 +19,24 @@ import Foundation
import Source
import Lint
// configurations from .yanagiba files
let fileManager = FileManager.default
var dotYanagibaLint: DotYanagibaLint?
if let homeEnv = getenv("HOME"), let homePath = String(utf8String: homeEnv) {
dotYanagibaLint = DotYanagibaLintReader.read(from: "\(homePath)/.yanagiba")
}
if let currentDotYanagibaLint = DotYanagibaLintReader.read(from: "\(fileManager.currentDirectoryPath)/.yanagiba") {
if dotYanagibaLint == nil {
dotYanagibaLint = currentDotYanagibaLint
} else {
dotYanagibaLint?.merge(with: currentDotYanagibaLint)
}
}
// merge configurations from command line options
var cliArgs = CommandLine.arguments
cliArgs.remove(at: 0)
@@ -80,7 +98,7 @@ if argumentsContain("help") {
Enable rules, default to all rules
--disable-rules <rule_identifier0>[,...,<rule_identifierN>]
Disable rules, default to empty
--rule-configure <parameter0>=<value0>[,...,<parameterN>=<valueN>]
--rule-configurations <parameter0>=<value0>[,...,<parameterN>=<valueN>]
Override the default rule configurations
--report-type <report_identifier>
@@ -131,24 +149,45 @@ var enabledRules = [
"redundant_variable_declaration_keyword",
"redundant_enumcase_string_value",
]
if let dotYanagibaLint = dotYanagibaLint, let enableRules = dotYanagibaLint.enableRules {
enabledRules = enableRules
}
if let enableRulesOption = readOption("-enable-rules") {
enabledRules = enableRulesOption.components(separatedBy: ",")
}
if let disableRulesOption = readOption("-disable-rules") {
let disabledRuleIdentifiers = disableRulesOption.components(separatedBy: ",")
enabledRules = enabledRules.filter({ !disabledRuleIdentifiers.contains($0) })
} else if let disabledRuleIdentifiers = dotYanagibaLint?.disableRules {
enabledRules = enabledRules.filter({ !disabledRuleIdentifiers.contains($0) })
}
var ruleConfigurations: [String: Any]?
if let customRuleConfigurations = readOptionAsDictionary("-rule-configure") {
if let dotYanagibaLint = dotYanagibaLint, let customRuleConfigurations = dotYanagibaLint.ruleConfigurations {
ruleConfigurations = customRuleConfigurations
}
if let customRuleConfigurations = readOptionAsDictionary("-rule-configurations") {
ruleConfigurations = customRuleConfigurations
}
let reportType = readOption("-report-type") ?? "text"
var reportType = "text"
if let dotYanagibaLint = dotYanagibaLint, let reportTypeOption = dotYanagibaLint.reportType {
reportType = reportTypeOption
}
if let reportTypeOption = readOption("-report-type") {
reportType = reportTypeOption
}
var outputPath: String?
if let dotYanagibaLint = dotYanagibaLint, let outputPathOption = dotYanagibaLint.outputPath {
outputPath = outputPathOption
}
if let outputPathOption = readOption("o") ?? readOption("-output") {
outputPath = outputPathOption
}
var outputHandle: FileHandle = .standardOutput
if let outputPath = readOption("o") ?? readOption("-output") {
let fileManager = FileManager.default
if let outputPath = outputPath {
if !fileManager.fileExists(atPath: outputPath) {
fileManager.createFile(atPath: outputPath, contents: nil)
}
@@ -158,6 +197,9 @@ if let outputPath = readOption("o") ?? readOption("-output") {
}
var severityThresholds: [String: Int] = [:]
if let dotYanagibaLint = dotYanagibaLint, let customSeverityThresholds = dotYanagibaLint.severityThresholds {
severityThresholds = customSeverityThresholds
}
if let customSeverityThresholds = readOptionAsDictionary("-severity-thresholds") {
for (key, value) in customSeverityThresholds {
if let intValue = value as? Int {
@@ -166,6 +208,8 @@ if let customSeverityThresholds = readOptionAsDictionary("-severity-thresholds")
}
}
// parse the source files
let filePaths = cliArgs
var sourceFiles = [SourceFile]()
for filePath in filePaths {
@@ -176,6 +220,8 @@ for filePath in filePaths {
sourceFiles.append(sourceFile)
}
// lint the source files
let driver = Driver(
ruleIdentifiers: enabledRules,
reportType: reportType,
View
2 run.sh
@@ -1,4 +1,6 @@
#!/bin/bash
set -e
make
.build/debug/swift-lint $@

0 comments on commit a0e3301

Please sign in to comment.