Skip to content

Commit 89823e4

Browse files
committed
Added Comment, Keyword
1 parent 06e9f18 commit 89823e4

File tree

10 files changed

+351
-340
lines changed

10 files changed

+351
-340
lines changed
Lines changed: 15 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// DeclComment.swift
1+
// Comment.swift
22
//
33
// Created by Iurii Khvorost <iurii.khvorost@gmail.com> on 11.02.2024.
44
// Copyright © 2022 Iurii Khvorost. All rights reserved.
@@ -24,10 +24,10 @@
2424
import SwiftSyntax
2525

2626

27-
struct DeclComment {
28-
let piece: TriviaPiece
27+
public struct Comment {
28+
private let piece: TriviaPiece
2929

30-
var text: String {
30+
public var text: String {
3131
switch piece {
3232
case .lineComment(let text): return text
3333
case .blockComment(let text): return text
@@ -37,47 +37,21 @@ struct DeclComment {
3737
}
3838
}
3939

40-
var isDoc: Bool {
40+
public var hasDoc: Bool {
4141
switch piece {
42-
case .docLineComment:
43-
fallthrough
44-
case .docBlockComment:
45-
return true
46-
default:
47-
return true
42+
case .docLineComment: fallthrough
43+
case .docBlockComment: return true
44+
default: return false
4845
}
4946
}
5047

51-
init(piece: TriviaPiece) {
52-
self.piece = piece
53-
}
54-
}
55-
56-
struct DeclComments {
57-
private let pieces: [DeclComment]
58-
59-
var count: Int { pieces.count }
60-
61-
var documented: Bool {
62-
pieces.first { $0.isDoc } != nil
63-
}
64-
65-
subscript(index: Int) -> DeclComment { pieces[index] }
66-
67-
init(trivia: Trivia) {
68-
pieces = trivia.compactMap {
69-
switch $0 {
70-
case .lineComment:
71-
fallthrough
72-
case .blockComment:
73-
fallthrough
74-
case .docLineComment:
75-
fallthrough
76-
case .docBlockComment:
77-
return DeclComment(piece: $0)
78-
default:
79-
return nil
80-
}
48+
init?(piece: TriviaPiece) {
49+
switch piece {
50+
case .lineComment: fallthrough
51+
case .blockComment: fallthrough
52+
case .docLineComment: fallthrough
53+
case .docBlockComment: self.piece = piece
54+
default: return nil
8155
}
8256
}
8357
}

Sources/SwiftDocCoverage/Coverage.swift

Lines changed: 64 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -28,56 +28,56 @@ extension String : LocalizedError {
2828
public var errorDescription: String? { self }
2929
}
3030

31-
fileprivate func findFiles(path: String, ext: String, skipsHiddenFiles: Bool, ignoreFilenameRegex: String) throws -> [URL] {
32-
var isDirectory: ObjCBool = false
33-
guard FileManager.default.fileExists(atPath: path, isDirectory: &isDirectory) else {
34-
throw "Path not found."
35-
}
31+
public struct Coverage {
32+
let sources: [Source]
33+
let minAccessLevel: AccessLevel
34+
let output: Output
3635

37-
if isDirectory.boolValue {
38-
var urls = [URL]()
39-
40-
let regex: NSRegularExpression? = ignoreFilenameRegex.isEmpty ? nil : try NSRegularExpression(pattern: ignoreFilenameRegex)
36+
private static func files(path: String, ext: String, skipsHiddenFiles: Bool, ignoreFilenameRegex: String) throws -> [URL] {
37+
var isDirectory: ObjCBool = false
38+
guard FileManager.default.fileExists(atPath: path, isDirectory: &isDirectory) else {
39+
throw "Path not found."
40+
}
4141

42-
let url = URL(fileURLWithPath: path)
43-
let resourceKeys = Set<URLResourceKey>([.nameKey, .isDirectoryKey])
44-
let options: FileManager.DirectoryEnumerationOptions = skipsHiddenFiles ? [.skipsHiddenFiles] : []
45-
if let enumerator = FileManager.default.enumerator(at: url, includingPropertiesForKeys: [.isDirectoryKey, .isHiddenKey], options: options) {
46-
for case let fileURL as URL in enumerator {
47-
guard let resourceValues = try? fileURL.resourceValues(forKeys: resourceKeys),
48-
let isDirectory = resourceValues.isDirectory, !isDirectory,
49-
let name = resourceValues.name,
50-
name.hasSuffix(ext)
51-
else {
52-
continue
53-
}
54-
55-
// Skip by regex
56-
if let regex = regex {
57-
let fileName = fileURL.lastPathComponent
58-
let range = NSRange(location: 0, length: fileName.utf16.count)
59-
if regex.firstMatch(in: fileName, range: range) != nil {
42+
if isDirectory.boolValue {
43+
var urls = [URL]()
44+
45+
let regex: NSRegularExpression? = ignoreFilenameRegex.isEmpty ? nil : try NSRegularExpression(pattern: ignoreFilenameRegex)
46+
47+
let url = URL(fileURLWithPath: path)
48+
let resourceKeys = Set<URLResourceKey>([.nameKey, .isDirectoryKey])
49+
let options: FileManager.DirectoryEnumerationOptions = skipsHiddenFiles ? [.skipsHiddenFiles] : []
50+
if let enumerator = FileManager.default.enumerator(at: url, includingPropertiesForKeys: [.isDirectoryKey, .isHiddenKey], options: options) {
51+
for case let fileURL as URL in enumerator {
52+
guard let resourceValues = try? fileURL.resourceValues(forKeys: resourceKeys),
53+
let isDirectory = resourceValues.isDirectory, !isDirectory,
54+
let name = resourceValues.name,
55+
name.hasSuffix(ext)
56+
else {
6057
continue
6158
}
59+
60+
// Skip by regex
61+
if let regex = regex {
62+
let fileName = fileURL.lastPathComponent
63+
let range = NSRange(location: 0, length: fileName.utf16.count)
64+
if regex.firstMatch(in: fileName, range: range) != nil {
65+
continue
66+
}
67+
}
68+
69+
urls.append(fileURL)
6270
}
63-
64-
urls.append(fileURL)
6571
}
72+
return urls
6673
}
67-
return urls
68-
}
69-
70-
guard path.hasSuffix(ext) else {
71-
throw "Not swift file."
74+
75+
guard path.hasSuffix(ext) else {
76+
throw "Not swift file."
77+
}
78+
let url = URL(fileURLWithPath: path)
79+
return [url]
7280
}
73-
let url = URL(fileURLWithPath: path)
74-
return [url]
75-
}
76-
77-
public struct Coverage {
78-
let urls: [URL]
79-
let minAccessLevel: AccessLevel
80-
let output: Output
8181

8282
private let dateComponentsFormatter: DateComponentsFormatter = {
8383
let formatter = DateComponentsFormatter()
@@ -87,12 +87,15 @@ public struct Coverage {
8787
}()
8888

8989
public init(paths: [String], skipsHiddenFiles: Bool = true, ignoreFilenameRegex: String = "", minAccessLevel: AccessLevel = .public, output: Output = TerminalOutput()) throws {
90-
self.urls = try paths.flatMap {
91-
try findFiles(path: $0, ext: ".swift", skipsHiddenFiles: skipsHiddenFiles, ignoreFilenameRegex: ignoreFilenameRegex)
90+
self.sources = try paths.flatMap {
91+
try Self.files(path: $0, ext: ".swift", skipsHiddenFiles: skipsHiddenFiles, ignoreFilenameRegex: ignoreFilenameRegex)
9292
}
93-
guard urls.count > 0 else {
93+
.map { try Source(url: $0) }
94+
95+
guard sources.count > 0 else {
9496
throw "Swift files not found."
9597
}
98+
9699
self.minAccessLevel = minAccessLevel
97100
self.output = output
98101
}
@@ -116,33 +119,37 @@ public struct Coverage {
116119
}
117120

118121
@discardableResult
119-
func report(_ body: ((SourceReport, TimeInterval) -> Void)? = nil) throws -> CoverageReport {
120-
precondition(urls.count > 0)
122+
public func report(_ body: ((SourceReport, TimeInterval) -> Void)? = nil) throws -> CoverageReport {
123+
precondition(sources.count > 0)
121124

122-
var sources = [SourceReport]()
125+
var sourceReports = [SourceReport]()
123126

124-
try urls.forEach { url in
127+
sources.forEach { source in
125128
let time = Date()
126129

127-
let source = try Source(fileURL: url, minAccessLevel: minAccessLevel)
128130
guard source.declarations.count > 0 else {
129131
return
130132
}
131133

132-
let undocumented = source.undocumented.map { DeclarationReport(line: $0.line, column: $0.column, name: $0.name) }
133-
let sourceReport = SourceReport(path: url.path,
134-
totalCount: source.declarations.count,
135-
undocumented: undocumented)
136-
sources.append(sourceReport)
134+
let declarations = source.declarations.filter { $0.accessLevel.rawValue <= self.minAccessLevel.rawValue }
135+
136+
let undocumented: [DeclarationReport] = declarations
137+
.filter {
138+
$0.comments.contains { $0.hasDoc == false }
139+
}
140+
.map { DeclarationReport(line: $0.line, column: $0.column, name: $0.name) }
141+
142+
let sourceReport = SourceReport(path: source.url!.absoluteString, totalCount: source.declarations.count, undocumented: undocumented)
143+
sourceReports.append(sourceReport)
137144

138145
body?(sourceReport, -time.timeIntervalSinceNow)
139146
}
140147

141-
guard sources.count > 0 else {
148+
guard sourceReports.count > 0 else {
142149
throw "Declarations not found."
143150
}
144151

145-
return CoverageReport(sources: sources)
152+
return CoverageReport(sources: sourceReports)
146153
}
147154

148155
public func reportStatistics() throws {

Sources/SwiftDocCoverage/DeclProtocol.swift

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ protocol DeclProtocol: SyntaxProtocol {
2828

2929
// var attributes: AttributeListSyntax
3030
var modifiers: DeclModifierListSyntax { get }
31-
var keyword: DeclKeyword { get }
31+
var keyword: Keyword { get }
3232
var name: TokenSyntax { get }
3333

3434
var genericParameterClause: GenericParameterClauseSyntax? { get }
@@ -43,48 +43,50 @@ extension DeclProtocol {
4343
var funcSignature: FunctionSignatureSyntax? { nil }
4444
var genericWhereClause: GenericWhereClauseSyntax? { nil }
4545

46-
var comments: DeclComments { DeclComments(trivia: leadingTrivia) }
46+
var comments: [Comment] {
47+
leadingTrivia.compactMap { Comment(piece: $0) }
48+
}
4749
var accessLevel: AccessLevel { AccessLevel(modifiers: modifiers) }
4850
}
4951

5052
// MARK: -
5153

5254
extension TypeAliasDeclSyntax: DeclProtocol {
53-
var keyword: DeclKeyword { .typealias }
55+
var keyword: Keyword { .typealias }
5456
}
5557

5658
extension AssociatedTypeDeclSyntax: DeclProtocol {
57-
var keyword: DeclKeyword { .associatedtype }
59+
var keyword: Keyword { .associatedtype }
5860
}
5961

6062
extension ClassDeclSyntax: DeclProtocol {
61-
var keyword: DeclKeyword { .class }
63+
var keyword: Keyword { .class }
6264
}
6365

6466
extension ActorDeclSyntax: DeclProtocol {
65-
var keyword: DeclKeyword { .actor }
67+
var keyword: Keyword { .actor }
6668
}
6769

6870
extension StructDeclSyntax: DeclProtocol {
69-
var keyword: DeclKeyword { .struct }
71+
var keyword: Keyword { .struct }
7072
}
7173

7274
extension ProtocolDeclSyntax: DeclProtocol {
73-
var keyword: DeclKeyword { .protocol }
75+
var keyword: Keyword { .protocol }
7476
}
7577

7678
extension ExtensionDeclSyntax: DeclProtocol {
77-
var keyword: DeclKeyword { .extension }
79+
var keyword: Keyword { .extension }
7880
var name: TokenSyntax { TokenSyntax(.identifier(extendedType.trimmedDescription), presence: .present)}
7981
}
8082

8183
extension FunctionDeclSyntax: DeclProtocol {
82-
var keyword: DeclKeyword { .func }
84+
var keyword: Keyword { .func }
8385
var funcSignature: FunctionSignatureSyntax? { signature }
8486
}
8587

8688
extension InitializerDeclSyntax: DeclProtocol {
87-
var keyword: DeclKeyword { .`init` }
89+
var keyword: Keyword { .`init` }
8890

8991
var name: TokenSyntax {
9092
let optionalMark = optionalMark?.trimmedDescription ?? ""
@@ -95,7 +97,7 @@ extension InitializerDeclSyntax: DeclProtocol {
9597
}
9698

9799
extension SubscriptDeclSyntax: DeclProtocol {
98-
var keyword: DeclKeyword { .subscript }
100+
var keyword: Keyword { .subscript }
99101

100102
var name: TokenSyntax {
101103
TokenSyntax(.identifier("subscript"), presence: .present)
@@ -107,7 +109,7 @@ extension SubscriptDeclSyntax: DeclProtocol {
107109
}
108110

109111
extension VariableDeclSyntax: DeclProtocol {
110-
var keyword: DeclKeyword { DeclKeyword(token: bindingSpecifier)! }
112+
var keyword: Keyword { Keyword(token: bindingSpecifier)! }
111113

112114
var name: TokenSyntax {
113115
let name = bindings.map { $0.pattern.trimmedDescription }.joined(separator: ",")
@@ -116,11 +118,11 @@ extension VariableDeclSyntax: DeclProtocol {
116118
}
117119

118120
extension EnumDeclSyntax: DeclProtocol {
119-
var keyword: DeclKeyword { .enum }
121+
var keyword: Keyword { .enum }
120122
}
121123

122124
extension EnumCaseDeclSyntax: DeclProtocol {
123-
var keyword: DeclKeyword { .case }
125+
var keyword: Keyword { .case }
124126

125127
var name: TokenSyntax {
126128
let name = elements.map {
@@ -133,10 +135,10 @@ extension EnumCaseDeclSyntax: DeclProtocol {
133135
}
134136

135137
extension PrecedenceGroupDeclSyntax: DeclProtocol {
136-
var keyword: DeclKeyword { .precedencegroup }
138+
var keyword: Keyword { .precedencegroup }
137139
}
138140

139141
extension MacroDeclSyntax: DeclProtocol {
140-
var keyword: DeclKeyword { .macro }
142+
var keyword: Keyword { .macro }
141143
var funcSignature: FunctionSignatureSyntax? { signature }
142144
}

Sources/SwiftDocCoverage/Declaration.swift

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,17 +35,18 @@ fileprivate struct StringBuilder {
3535
}
3636
}
3737

38-
class Declaration {
38+
public class Declaration {
3939
private let decl: DeclProtocol
4040
private let context: [DeclProtocol]
4141

42-
let line: Int
43-
let column: Int
42+
public let line: Int
43+
public let column: Int
4444

45-
private(set) lazy var accessLevel: AccessLevel = { decl.accessLevel }()
46-
private(set) lazy var comments: DeclComments = { decl.comments }()
45+
public var keyword: Keyword { decl.keyword }
4746

48-
private(set) lazy var name: String = { buildName() }()
47+
public private(set) lazy var accessLevel: AccessLevel = { decl.accessLevel }()
48+
public private(set) lazy var comments: [Comment] = { decl.comments }()
49+
public private(set) lazy var name: String = { buildName() }()
4950

5051
@StringBuilder
5152
private func buildName() -> String {

0 commit comments

Comments
 (0)