diff --git a/CHANGELOG.md b/CHANGELOG.md
index b42f7fb..13718da 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -31,9 +31,14 @@ If needed, pluralize to `Tasks`, `PRs` or `Authors` and list multiple entries se
### Security
- None.
+## [0.2.0] - 2020-04-10
+### Added
+- Added new `-x` / `--xcode` option to print out warnings & errors in an Xcode-compatible manner to improve user experience when used with an Xcode build script. Requires `arguments: CommandLine.arguments` as parameters to `logSummary` in config file.
+ Issue: [#4](https://github.com/Flinesoft/AnyLint/issues/4) | PR: [#8](https://github.com/Flinesoft/AnyLint/pull/8) | Author: [Cihat Gündüz](https://github.com/Jeehut)
+
## [0.1.1] - 2020-03-23
### Added
-- Added two simple lint check examples in first code sample in README. (Thanks for the pointer, [Dave Verwer](https://github.com/daveverwer)!)
+- Added two simple lint check examples in first code sample in README. (Thanks for the pointer, [Dave Verwer](https://github.com/daveverwer)!)
Author: [Cihat Gündüz](https://github.com/Jeehut)
### Changed
- Changed `CheckInfo` id casing convention from snake_case to UpperCamelCase in `blank` template.
diff --git a/Formula/anylint.rb b/Formula/anylint.rb
index 9ba0049..c5deb69 100644
--- a/Formula/anylint.rb
+++ b/Formula/anylint.rb
@@ -1,7 +1,7 @@
class Anylint < Formula
desc "Lint anything by combining the power of Swift & regular expressions"
homepage "https://github.com/Flinesoft/AnyLint"
- url "https://github.com/Flinesoft/AnyLint.git", :tag => "0.1.0", :revision => "25fec7dd29d86f0ef97fc2dddcd41d2576d9570c"
+ url "https://github.com/Flinesoft/AnyLint.git", :tag => "0.1.1", :revision => "e80ac907d160a0e8f359dc84fabfbd1cc80a8b50"
head "https://github.com/Flinesoft/AnyLint.git"
depends_on :xcode => ["11.3", :build]
diff --git a/README.md b/README.md
index ced2f50..876749d 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
+ width=562 />
@@ -17,8 +17,8 @@
alt="Coverage"/>
-
+
Installation
• Getting Started
• Configuration
+ • Xcode Build Script
• Donation
• Issues
• Contributing
@@ -86,11 +87,11 @@ To initialize AnyLint in a project, run:
anylint --init blank
```
-This will create the Swift script file `lint.swift` with something like following contents:
+This will create the Swift script file `lint.swift` with something like the following contents:
```swift
#!/usr/local/bin/swift-sh
-import AnyLint // @Flinesoft ~> 0.1.1
+import AnyLint // @Flinesoft ~> 0.2.0
// MARK: - Variables
let readmeFile: Regex = #"README\.md"#
@@ -120,7 +121,7 @@ try Lint.checkFileContents(
)
// MARK: - Log Summary & Exit
-Lint.logSummaryAndExit()
+Lint.logSummaryAndExit(arguments: CommandLine.arguments)
```
The most important thing to note is that the **first two lines and the last line are required** for AnyLint to work properly.
@@ -161,7 +162,7 @@ Many parameters in the above mentioned lint check methods are of `Regex` type. A
1. Using a **String**:
```swift
- let regex = Regex(#"(foo|bar)[0-9]+"#) // => /(foo|bar)[0-9]+/`
+ let regex = Regex(#"(foo|bar)[0-9]+"#) // => /(foo|bar)[0-9]+/
```
2. Using a **String Literal**:
```swift
@@ -188,6 +189,7 @@ While there is an initializer available, we recommend using a String Literal ins
```swift
// accepted structure: (@):
let checkInfo: CheckInfo = "ReadmePath: The README file should be named exactly `README.md`."
+let checkInfoCustomSeverity: CheckInfo = "ReadmePath@warning: The README file should be named exactly `README.md`."
```
### Check File Contents
@@ -197,7 +199,7 @@ AnyLint has rich support for checking the contents of a file using a regex. The
In its simplest form, the method just requires a `checkInfo` and a `regex`:
```swift
-// MARK: empty_todo
+// MARK: EmptyTodo
try Lint.checkFileContents(
checkInfo: "EmptyTodo: TODO comments should not be empty.",
regex: #"// TODO: *\n"#
@@ -308,7 +310,7 @@ When using the `customCheck`, you might want to also include some Swift packages
```swift
#!/usr/local/bin/swift-sh
-import AnyLint // @Flinesoft ~> 0.1.1
+import AnyLint // @Flinesoft ~> 0.2.0
import Files // @JohnSundell ~> 4.1.1
import ShellOut // @JohnSundell ~> 2.3.0
@@ -329,9 +331,23 @@ try Lint.customCheck(checkInfo: "Echo: Always say hello to the world.") {
}
// MARK: - Log Summary & Exit
-Lint.logSummaryAndExit()
+Lint.logSummaryAndExit(arguments: CommandLine.arguments)
+```
+
+## Xcode Build Script
+
+If you are using AnyLint for a project in Xcode, you can configure a build script to run it on each build. In order to do this select your target, choose the `Build Phases` tab and click the + button on the top left corner of that pane. Select `New Run Script Phase` and copy the following into the text box below the `Shell: /bin/sh` of your new run script phase:
+
+```shell
+if which anylint > /dev/null; then
+ anylint -x
+else
+ echo "warning: AnyLint not installed, download it from https://github.com/Flinesoft/AnyLint"
+fi
```
+Next, make sure the AnyLint script runs before the steps `Compiling Sources` by moving it per drag & drop, for example right after `Dependencies`. You probably also want to rename it to somethng like `AnyLint`.
+
## Donation
AnyLint was brought to you by [Cihat Gündüz](https://github.com/Jeehut) in his free time. If you want to thank me and support the development of this project, please **make a small donation on [PayPal](https://paypal.me/Dschee/5EUR)**. In case you also like my other [open source contributions](https://github.com/Flinesoft) and [articles](https://medium.com/@Jeehut), please consider motivating me by **becoming a sponsor on [GitHub](https://github.com/sponsors/Jeehut)** or a **patron on [Patreon](https://www.patreon.com/Jeehut)**.
diff --git a/Sources/AnyLint/Lint.swift b/Sources/AnyLint/Lint.swift
index c6f2d27..16af2e4 100644
--- a/Sources/AnyLint/Lint.swift
+++ b/Sources/AnyLint/Lint.swift
@@ -125,12 +125,17 @@ public enum Lint {
}
/// Logs the summary of all detected violations and exits successfully on no violations or with a failure, if any violations.
- public static func logSummaryAndExit(failOnWarnings: Bool = false) {
+ public static func logSummaryAndExit(failOnWarnings: Bool = false, arguments: [String] = []) {
+ let targetIsXcode = arguments.contains(Logger.OutputType.xcode.rawValue)
+ if targetIsXcode {
+ log = Logger(outputType: .xcode)
+ }
+
Statistics.shared.logSummary()
- if Statistics.shared.violationsBySeverity[.error]!.isFilled {
+ if Statistics.shared.violations(severity: .error, excludeAutocorrected: targetIsXcode).isFilled {
log.exit(status: .failure)
- } else if failOnWarnings && Statistics.shared.violationsBySeverity[.warning]!.isFilled {
+ } else if failOnWarnings && Statistics.shared.violations(severity: .warning, excludeAutocorrected: targetIsXcode).isFilled {
log.exit(status: .failure)
} else {
log.exit(status: .success)
diff --git a/Sources/AnyLint/Severity.swift b/Sources/AnyLint/Severity.swift
index 2ece7ea..195c8a0 100644
--- a/Sources/AnyLint/Severity.swift
+++ b/Sources/AnyLint/Severity.swift
@@ -41,3 +41,9 @@ public enum Severity: Int, CaseIterable {
}
}
}
+
+extension Severity: Comparable {
+ public static func < (lhs: Severity, rhs: Severity) -> Bool {
+ lhs.rawValue < rhs.rawValue
+ }
+}
diff --git a/Sources/AnyLint/Statistics.swift b/Sources/AnyLint/Statistics.swift
index 3ef9dfa..d917703 100644
--- a/Sources/AnyLint/Statistics.swift
+++ b/Sources/AnyLint/Statistics.swift
@@ -31,55 +31,85 @@ final class Statistics {
if executedChecks.isEmpty {
log.message("No checks found to perform.", level: .warning)
} else if violationsBySeverity.values.contains(where: { $0.isFilled }) {
- for check in executedChecks {
- if let checkViolations = violationsPerCheck[check], checkViolations.isFilled {
- let violationsWithLocationMessage = checkViolations.filter { $0.locationMessage() != nil }
-
- if violationsWithLocationMessage.isFilled {
- log.message(
- "\("[\(check.id)]".bold) Found \(checkViolations.count) violation(s) at:",
- level: check.severity.logLevel
- )
- let numerationDigits = String(violationsWithLocationMessage.count).count
-
- for (index, violation) in violationsWithLocationMessage.enumerated() {
- let violationNumString = String(format: "%0\(numerationDigits)d", index + 1)
- let prefix = "> \(violationNumString). "
- log.message(prefix + violation.locationMessage()!, level: check.severity.logLevel)
-
- let prefixLengthWhitespaces = (0 ..< prefix.count).map { _ in " " }.joined()
- if let appliedAutoCorrection = violation.appliedAutoCorrection {
- for messageLine in appliedAutoCorrection.appliedMessageLines {
- log.message(prefixLengthWhitespaces + messageLine, level: .info)
- }
- } else if let matchedString = violation.matchedString {
- log.message(prefixLengthWhitespaces + "Matching string:".bold + " (trimmed & reduced whitespaces)", level: .info)
- let matchedStringOutput = matchedString
- .showNewlines()
- .trimmingCharacters(in: .whitespacesAndNewlines)
- .replacingOccurrences(of: " ", with: " ")
- .replacingOccurrences(of: " ", with: " ")
- .replacingOccurrences(of: " ", with: " ")
- log.message(prefixLengthWhitespaces + "> " + matchedStringOutput, level: .info)
+ switch log.outputType {
+ case .console, .test:
+ logViolationsToConsole()
+
+ case .xcode:
+ showViolationsInXcode()
+ }
+ } else {
+ log.message("Performed \(executedChecks.count) check(s) without any violations.", level: .success)
+ }
+ }
+
+ func violations(severity: Severity, excludeAutocorrected: Bool) -> [Violation] {
+ let violations: [Violation] = violationsBySeverity[severity]!
+ guard excludeAutocorrected else { return violations }
+ return violations.filter { $0.appliedAutoCorrection == nil }
+ }
+
+ private func logViolationsToConsole() {
+ for check in executedChecks {
+ if let checkViolations = violationsPerCheck[check], checkViolations.isFilled {
+ let violationsWithLocationMessage = checkViolations.filter { $0.locationMessage(pathType: .relative) != nil }
+
+ if violationsWithLocationMessage.isFilled {
+ log.message(
+ "\("[\(check.id)]".bold) Found \(checkViolations.count) violation(s) at:",
+ level: check.severity.logLevel
+ )
+ let numerationDigits = String(violationsWithLocationMessage.count).count
+
+ for (index, violation) in violationsWithLocationMessage.enumerated() {
+ let violationNumString = String(format: "%0\(numerationDigits)d", index + 1)
+ let prefix = "> \(violationNumString). "
+ log.message(prefix + violation.locationMessage(pathType: .relative)!, level: check.severity.logLevel)
+
+ let prefixLengthWhitespaces = (0 ..< prefix.count).map { _ in " " }.joined()
+ if let appliedAutoCorrection = violation.appliedAutoCorrection {
+ for messageLine in appliedAutoCorrection.appliedMessageLines {
+ log.message(prefixLengthWhitespaces + messageLine, level: .info)
}
+ } else if let matchedString = violation.matchedString {
+ log.message(prefixLengthWhitespaces + "Matching string:".bold + " (trimmed & reduced whitespaces)", level: .info)
+ let matchedStringOutput = matchedString
+ .showNewlines()
+ .trimmingCharacters(in: .whitespacesAndNewlines)
+ .replacingOccurrences(of: " ", with: " ")
+ .replacingOccurrences(of: " ", with: " ")
+ .replacingOccurrences(of: " ", with: " ")
+ log.message(prefixLengthWhitespaces + "> " + matchedStringOutput, level: .info)
}
- } else {
- log.message("\("[\(check.id)]".bold) Found \(checkViolations.count) violation(s).", level: check.severity.logLevel)
}
-
- log.message(">> Hint: \(check.hint)".bold.italic, level: check.severity.logLevel)
+ } else {
+ log.message("\("[\(check.id)]".bold) Found \(checkViolations.count) violation(s).", level: check.severity.logLevel)
}
+
+ log.message(">> Hint: \(check.hint)".bold.italic, level: check.severity.logLevel)
}
+ }
- let errors = "\(violationsBySeverity[.error]!.count) error(s)"
- let warnings = "\(violationsBySeverity[.warning]!.count) warning(s)"
+ let errors = "\(violationsBySeverity[.error]!.count) error(s)"
+ let warnings = "\(violationsBySeverity[.warning]!.count) warning(s)"
- log.message(
- "Performed \(executedChecks.count) check(s) and found \(errors) & \(warnings).",
- level: maxViolationSeverity!.logLevel
- )
- } else {
- log.message("Performed \(executedChecks.count) check(s) without any violations.", level: .success)
+ log.message(
+ "Performed \(executedChecks.count) check(s) and found \(errors) & \(warnings).",
+ level: maxViolationSeverity!.logLevel
+ )
+ }
+
+ private func showViolationsInXcode() {
+ for severity in violationsBySeverity.keys.sorted().reversed() {
+ let severityViolations = violationsBySeverity[severity]!
+ for violation in severityViolations where violation.appliedAutoCorrection == nil {
+ let check = violation.checkInfo
+ log.xcodeMessage(
+ "[\(check.id)] \(check.hint)",
+ level: check.severity.logLevel,
+ location: violation.locationMessage(pathType: .absolute)
+ )
+ }
}
}
}
diff --git a/Sources/AnyLint/Violation.swift b/Sources/AnyLint/Violation.swift
index b1f9c64..5105a51 100644
--- a/Sources/AnyLint/Violation.swift
+++ b/Sources/AnyLint/Violation.swift
@@ -33,9 +33,9 @@ public struct Violation {
self.appliedAutoCorrection = appliedAutoCorrection
}
- func locationMessage() -> String? {
+ func locationMessage(pathType: String.PathType) -> String? {
guard let filePath = filePath else { return nil }
- guard let locationInfo = locationInfo else { return filePath }
- return "\(filePath):\(locationInfo.line):\(locationInfo.charInLine)"
+ guard let locationInfo = locationInfo else { return filePath.path(type: pathType) }
+ return "\(filePath.path(type: pathType)):\(locationInfo.line):\(locationInfo.charInLine):"
}
}
diff --git a/Sources/AnyLintCLI/Commands/SingleCommand.swift b/Sources/AnyLintCLI/Commands/SingleCommand.swift
index 160f2d5..9483cfd 100644
--- a/Sources/AnyLintCLI/Commands/SingleCommand.swift
+++ b/Sources/AnyLintCLI/Commands/SingleCommand.swift
@@ -11,6 +11,9 @@ class SingleCommand: Command {
@Flag("-v", "--version", description: "Print the current tool version")
var version: Bool
+ @Flag("-x", "--xcode", description: "Print warnings & errors in a format to be reported right within Xcodes left sidebar")
+ var xcode: Bool
+
@Key("-i", "--init", description: "Configure AnyLint with a default template. Has to be one of: [\(CLIConstants.initTemplateCases)]")
var initTemplateName: String?
@@ -20,6 +23,10 @@ class SingleCommand: Command {
// MARK: - Execution
func execute() throws {
+ if xcode {
+ log = Logger(outputType: .xcode)
+ }
+
// version subcommand
if version {
try VersionTask().perform()
diff --git a/Sources/AnyLintCLI/ConfigurationTemplates/ConfigurationTemplate.swift b/Sources/AnyLintCLI/ConfigurationTemplates/ConfigurationTemplate.swift
index 9860e90..352a165 100644
--- a/Sources/AnyLintCLI/ConfigurationTemplates/ConfigurationTemplate.swift
+++ b/Sources/AnyLintCLI/ConfigurationTemplates/ConfigurationTemplate.swift
@@ -11,6 +11,6 @@ extension ConfigurationTemplate {
}
static var commonSuffix: String {
- "\n\n// MARK: - Log Summary & Exit\nLint.logSummaryAndExit()\n"
+ "\n\n// MARK: - Log Summary & Exit\nLint.logSummaryAndExit(arguments: CommandLine.arguments)\n"
}
}
diff --git a/Sources/AnyLintCLI/Tasks/LintTask.swift b/Sources/AnyLintCLI/Tasks/LintTask.swift
index c4f6703..a77f541 100644
--- a/Sources/AnyLintCLI/Tasks/LintTask.swift
+++ b/Sources/AnyLintCLI/Tasks/LintTask.swift
@@ -23,10 +23,13 @@ extension LintTask: TaskHandler {
do {
log.message("Start linting using config file at \(configFilePath) ...", level: .info)
- try Task.run(bash: "\(configFilePath.absolutePath)")
+ try Task.run(bash: "\(configFilePath.absolutePath) \(log.outputType.rawValue)")
log.message("Linting successful using config file at \(configFilePath). Congrats! 🎉", level: .success)
} catch is RunError {
- log.message("Linting failed using config file at \(configFilePath).", level: .error)
+ if log.outputType != .xcode {
+ log.message("Linting failed using config file at \(configFilePath).", level: .error)
+ }
+
throw LintError.configFileFailed
}
}
diff --git a/Sources/Utility/Constants.swift b/Sources/Utility/Constants.swift
index f1adc14..347b371 100644
--- a/Sources/Utility/Constants.swift
+++ b/Sources/Utility/Constants.swift
@@ -9,7 +9,7 @@ public var log = Logger(outputType: .console)
/// Constants to reference across the project.
public enum Constants {
/// The current tool version string. Conforms to SemVer 2.0.
- public static let currentVersion: String = "0.1.1"
+ public static let currentVersion: String = "0.2.0"
/// The name of this tool.
public static let toolName: String = "AnyLint"
diff --git a/Sources/Utility/Extensions/StringExt.swift b/Sources/Utility/Extensions/StringExt.swift
index 8be934a..d1ec81a 100644
--- a/Sources/Utility/Extensions/StringExt.swift
+++ b/Sources/Utility/Extensions/StringExt.swift
@@ -1,15 +1,25 @@
import Foundation
extension String {
+ /// The type of a given file path.
+ public enum PathType {
+ /// The relative path.
+ case relative
+
+ /// The absolute path.
+ case absolute
+ }
+
/// Returns the absolute path for a path given relative to the current directory.
public var absolutePath: String {
- guard let url = URL(string: self, relativeTo: fileManager.currentDirectoryUrl) else {
- log.message("Could not convert path '\(self)' to type URL.", level: .error)
- log.exit(status: .failure)
- return "" // only reachable in unit tests
- }
+ guard !self.starts(with: fileManager.currentDirectoryUrl.path) else { return self }
+ return fileManager.currentDirectoryUrl.appendingPathComponent(self).path
+ }
- return url.absoluteString
+ /// Returns the relative path for a path given relative to the current directory.
+ public var relativePath: String {
+ guard self.starts(with: fileManager.currentDirectoryUrl.path) else { return self }
+ return replacingOccurrences(of: fileManager.currentDirectoryUrl.path, with: "")
}
/// Returns the parent directory path.
@@ -23,6 +33,17 @@ extension String {
return url.deletingLastPathComponent().absoluteString
}
+ /// Returns the path with the given type related to the current directory.
+ public func path(type: PathType) -> String {
+ switch type {
+ case .absolute:
+ return absolutePath
+
+ case .relative:
+ return relativePath
+ }
+ }
+
/// Returns the path with a components appended at it.
public func appendingPathComponent(_ pathComponent: String) -> String {
guard let pathUrl = URL(string: self) else {
diff --git a/Sources/Utility/Logger.swift b/Sources/Utility/Logger.swift
index d8a42ab..e970074 100644
--- a/Sources/Utility/Logger.swift
+++ b/Sources/Utility/Logger.swift
@@ -35,10 +35,13 @@ public final class Logger {
}
/// The output type.
- public enum OutputType {
+ public enum OutputType: String {
/// Output is targeted to a console to be read by developers.
case console
+ /// Output is targeted to Xcodes left pane to be interpreted by it to mark errors & warnings.
+ case xcode
+
/// Output is targeted for unit tests. Collect into globally accessible TestHelper.
case test
}
@@ -62,9 +65,11 @@ public final class Logger {
}
}
- let outputType: OutputType
+ /// The output type of the logger.
+ public let outputType: OutputType
- init(outputType: OutputType) {
+ /// Initializes a new Logger object with a given output type.
+ public init(outputType: OutputType) {
self.outputType = outputType
}
@@ -78,6 +83,9 @@ public final class Logger {
case .console:
consoleMessage(message, level: level)
+ case .xcode:
+ xcodeMessage(message, level: level)
+
case .test:
TestHelper.shared.consoleOutputs.append((message, level))
}
@@ -86,7 +94,7 @@ public final class Logger {
/// Exits the current program with the given status.
public func exit(status: ExitStatus) {
switch outputType {
- case .console:
+ case .console, .xcode:
Darwin.exit(status.statusCode)
case .test:
@@ -110,6 +118,20 @@ public final class Logger {
}
}
+ /// Reports a message in an Xcode compatible format to be shown in the left pane.
+ ///
+ /// - Parameters:
+ /// - message: The message to be printed. Don't include `Error!`, `Warning!` or similar information at the beginning.
+ /// - level: The level of the print statement.
+ /// - location: The file, line and char in line location string.
+ public func xcodeMessage(_ message: String, level: PrintLevel, location: String? = nil) {
+ if let location = location {
+ print("\(location) \(level.rawValue): \(Constants.toolName): \(message)")
+ } else {
+ print("\(level.rawValue): \(Constants.toolName): \(message)")
+ }
+ }
+
private func formattedCurrentTime() -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "HH:mm:ss.SSS"
diff --git a/Tests/AnyLintTests/StatisticsTests.swift b/Tests/AnyLintTests/StatisticsTests.swift
index b85df9d..5a44bcb 100644
--- a/Tests/AnyLintTests/StatisticsTests.swift
+++ b/Tests/AnyLintTests/StatisticsTests.swift
@@ -102,9 +102,9 @@ final class StatisticsTests: XCTestCase {
"> 2. Hogwarts/Albus.swift",
">> Hint: hint2".bold.italic,
"\("[id3]".bold) Found 3 violation(s) at:",
- "> 1. Hogwarts/Harry.swift:10:30",
- "> 2. Hogwarts/Harry.swift:72:17",
- "> 3. Hogwarts/Albus.swift:40:4",
+ "> 1. Hogwarts/Harry.swift:10:30:",
+ "> 2. Hogwarts/Harry.swift:72:17:",
+ "> 3. Hogwarts/Albus.swift:40:4:",
">> Hint: hint3".bold.italic,
"Performed 3 check(s) and found 3 error(s) & 2 warning(s).",
]
diff --git a/Tests/AnyLintTests/ViolationTests.swift b/Tests/AnyLintTests/ViolationTests.swift
index 96e4a7d..c9d0ebd 100644
--- a/Tests/AnyLintTests/ViolationTests.swift
+++ b/Tests/AnyLintTests/ViolationTests.swift
@@ -12,10 +12,10 @@ final class ViolationTests: XCTestCase {
func testLocationMessage() {
let checkInfo = CheckInfo(id: "demo_check", hint: "Make sure to always check the demo.", severity: .warning)
- XCTAssertNil(Violation(checkInfo: checkInfo).locationMessage())
+ XCTAssertNil(Violation(checkInfo: checkInfo).locationMessage(pathType: .relative))
let fileViolation = Violation(checkInfo: checkInfo, filePath: "Temp/Souces/Hello.swift")
- XCTAssertEqual(fileViolation.locationMessage(), "Temp/Souces/Hello.swift")
+ XCTAssertEqual(fileViolation.locationMessage(pathType: .relative), "Temp/Souces/Hello.swift")
let locationInfoViolation = Violation(
checkInfo: checkInfo,
@@ -23,6 +23,6 @@ final class ViolationTests: XCTestCase {
locationInfo: String.LocationInfo(line: 5, charInLine: 15)
)
- XCTAssertEqual(locationInfoViolation.locationMessage(), "Temp/Souces/World.swift:5:15")
+ XCTAssertEqual(locationInfoViolation.locationMessage(pathType: .relative), "Temp/Souces/World.swift:5:15:")
}
}