diff --git a/Sources/SwiftDocC/Model/Rendering/Content/RenderBlockContent+Capitalization.swift b/Sources/SwiftDocC/Model/Rendering/Content/RenderBlockContent+Capitalization.swift index 06614e5621..e18b3e3d3f 100644 --- a/Sources/SwiftDocC/Model/Rendering/Content/RenderBlockContent+Capitalization.swift +++ b/Sources/SwiftDocC/Model/Rendering/Content/RenderBlockContent+Capitalization.swift @@ -8,85 +8,73 @@ See https://swift.org/CONTRIBUTORS.txt for Swift project authors */ -/// For auto capitalizing the first letter of a sentence following a colon (e.g. asides, sections such as parameters, returns). -protocol AutoCapitalizable { - - /// Any type that conforms to the AutoCapitalizable protocol will have the first letter of the first word capitalized (if applicable). - var withFirstWordCapitalized: Self { - get - } - -} - -extension AutoCapitalizable { - var withFirstWordCapitalized: Self { return self } -} -extension RenderInlineContent: AutoCapitalizable { +extension RenderInlineContent { /// Capitalize the first word for normal text content, as well as content that has emphasis or strong applied. - var withFirstWordCapitalized: Self { + func capitalizingFirstWord() -> Self { switch self { case .text(let text): - return .text(text.capitalizeFirstWord()) + return .text(text.capitalizingFirstWord()) case .emphasis(inlineContent: let embeddedContent): - return .emphasis(inlineContent: [embeddedContent[0].withFirstWordCapitalized] + embeddedContent[1...]) + return .emphasis(inlineContent: embeddedContent.capitalizingFirstWord()) case .strong(inlineContent: let embeddedContent): - return .strong(inlineContent: [embeddedContent[0].withFirstWordCapitalized] + embeddedContent[1...]) + return .strong(inlineContent: embeddedContent.capitalizingFirstWord()) default: return self } } } +extension [RenderBlockContent] { + func capitalizingFirstWord() -> Self { + guard let first else { return [] } + + return [first.capitalizingFirstWord()] + dropFirst() + } +} + +extension [RenderInlineContent] { + func capitalizingFirstWord() -> Self { + guard let first else { return [] } + + return [first.capitalizingFirstWord()] + dropFirst() + } +} -extension RenderBlockContent: AutoCapitalizable { + +extension RenderBlockContent { /// Capitalize the first word for paragraphs, asides, headings, and small content. - var withFirstWordCapitalized: Self { + func capitalizingFirstWord() -> Self { switch self { case .paragraph(let paragraph): - return .paragraph(paragraph.withFirstWordCapitalized) + return .paragraph(paragraph.capitalizingFirstWord()) case .aside(let aside): - return .aside(aside.withFirstWordCapitalized) + return .aside(aside.capitalizingFirstWord()) case .small(let small): - return .small(small.withFirstWordCapitalized) + return .small(small.capitalizingFirstWord()) case .heading(let heading): - return .heading(.init(level: heading.level, text: heading.text.capitalizeFirstWord(), anchor: heading.anchor)) + return .heading(.init(level: heading.level, text: heading.text.capitalizingFirstWord(), anchor: heading.anchor)) default: return self } } } -extension RenderBlockContent.Paragraph: AutoCapitalizable { - var withFirstWordCapitalized: RenderBlockContent.Paragraph { - guard !self.inlineContent.isEmpty else { - return self - } - - let inlineContent = [self.inlineContent[0].withFirstWordCapitalized] + self.inlineContent[1...] - return .init(inlineContent: inlineContent) +extension RenderBlockContent.Paragraph { + func capitalizingFirstWord() -> RenderBlockContent.Paragraph { + return .init(inlineContent: inlineContent.capitalizingFirstWord()) } } -extension RenderBlockContent.Aside: AutoCapitalizable { - var withFirstWordCapitalized: RenderBlockContent.Aside { - guard !self.content.isEmpty else { - return self - } - - let content = [self.content[0].withFirstWordCapitalized] + self.content[1...] - return .init(style: self.style, content: content) +extension RenderBlockContent.Aside { + func capitalizingFirstWord() -> RenderBlockContent.Aside { + return .init(style: self.style, content: self.content.capitalizingFirstWord()) } } -extension RenderBlockContent.Small: AutoCapitalizable { - var withFirstWordCapitalized: RenderBlockContent.Small { - guard !self.inlineContent.isEmpty else { - return self - } - - let inlineContent = [self.inlineContent[0].withFirstWordCapitalized] + self.inlineContent[1...] - return .init(inlineContent: inlineContent) +extension RenderBlockContent.Small { + func capitalizingFirstWord() -> RenderBlockContent.Small { + return .init(inlineContent: self.inlineContent.capitalizingFirstWord()) } } diff --git a/Sources/SwiftDocC/Model/Rendering/RenderContentCompiler.swift b/Sources/SwiftDocC/Model/Rendering/RenderContentCompiler.swift index 8d70847b26..253527b9a1 100644 --- a/Sources/SwiftDocC/Model/Rendering/RenderContentCompiler.swift +++ b/Sources/SwiftDocC/Model/Rendering/RenderContentCompiler.swift @@ -42,7 +42,7 @@ struct RenderContentCompiler: MarkupVisitor { content: aside.content.reduce(into: [], { result, child in result.append(contentsOf: visit(child))}) as! [RenderBlockContent] ) - return [RenderBlockContent.aside(newAside.withFirstWordCapitalized)] + return [RenderBlockContent.aside(newAside.capitalizingFirstWord())] } mutating func visitCodeBlock(_ codeBlock: CodeBlock) -> [RenderContent] { diff --git a/Sources/SwiftDocC/Model/Rendering/RenderSectionTranslator/DiscussionSectionTranslator.swift b/Sources/SwiftDocC/Model/Rendering/RenderSectionTranslator/DiscussionSectionTranslator.swift index 5b6594a5b7..1c2ba22db6 100644 --- a/Sources/SwiftDocC/Model/Rendering/RenderSectionTranslator/DiscussionSectionTranslator.swift +++ b/Sources/SwiftDocC/Model/Rendering/RenderSectionTranslator/DiscussionSectionTranslator.swift @@ -27,8 +27,6 @@ struct DiscussionSectionTranslator: RenderSectionTranslator { return nil } - let capitalizedDiscussionContent = [discussionContent[0].withFirstWordCapitalized] + discussionContent[1...] - let title: String? if let first = discussionContent.first, case RenderBlockContent.heading = first { // There's already an authored heading. Don't add another heading. @@ -44,7 +42,7 @@ struct DiscussionSectionTranslator: RenderSectionTranslator { } } - return ContentRenderSection(kind: .content, content: capitalizedDiscussionContent, heading: title) + return ContentRenderSection(kind: .content, content: discussionContent.capitalizingFirstWord(), heading: title) } } } diff --git a/Sources/SwiftDocC/Model/Rendering/RenderSectionTranslator/ParametersSectionTranslator.swift b/Sources/SwiftDocC/Model/Rendering/RenderSectionTranslator/ParametersSectionTranslator.swift index 66c4238806..26503f0399 100644 --- a/Sources/SwiftDocC/Model/Rendering/RenderSectionTranslator/ParametersSectionTranslator.swift +++ b/Sources/SwiftDocC/Model/Rendering/RenderSectionTranslator/ParametersSectionTranslator.swift @@ -33,9 +33,7 @@ struct ParametersSectionTranslator: RenderSectionTranslator { return ParameterRenderSection(name: parameter.name, content: parameterContent) } - let capitalizedParameterContent = [parameterContent[0].withFirstWordCapitalized] + parameterContent[1...] - - return ParameterRenderSection(name: parameter.name, content: capitalizedParameterContent) + return ParameterRenderSection(name: parameter.name, content: parameterContent.capitalizingFirstWord()) } ) } diff --git a/Sources/SwiftDocC/Model/Rendering/RenderSectionTranslator/ReturnsSectionTranslator.swift b/Sources/SwiftDocC/Model/Rendering/RenderSectionTranslator/ReturnsSectionTranslator.swift index e10985f0b0..772b317fc1 100644 --- a/Sources/SwiftDocC/Model/Rendering/RenderSectionTranslator/ReturnsSectionTranslator.swift +++ b/Sources/SwiftDocC/Model/Rendering/RenderSectionTranslator/ReturnsSectionTranslator.swift @@ -28,9 +28,7 @@ struct ReturnsSectionTranslator: RenderSectionTranslator { return nil } - let capitalizedReturnsContent = [returnsContent[0].withFirstWordCapitalized] + returnsContent[1...] - - return ContentRenderSection(kind: .content, content: capitalizedReturnsContent, heading: "Return Value") + return ContentRenderSection(kind: .content, content: returnsContent.capitalizingFirstWord(), heading: "Return Value") } } } diff --git a/Sources/SwiftDocC/Utility/FoundationExtensions/String+Capitalization.swift b/Sources/SwiftDocC/Utility/FoundationExtensions/String+Capitalization.swift index 3f27074c22..47ae628163 100644 --- a/Sources/SwiftDocC/Utility/FoundationExtensions/String+Capitalization.swift +++ b/Sources/SwiftDocC/Utility/FoundationExtensions/String+Capitalization.swift @@ -12,13 +12,13 @@ import Foundation extension String { - // Precomputes the CharacterSet to use in capitalizeFirstWord(). + // Precomputes the CharacterSet to use in capitalizingFirstWord(). private static let charactersPreventingWordCapitalization = CharacterSet.lowercaseLetters.union(.punctuationCharacters).inverted /// Returns the string with the first letter capitalized. /// This auto-capitalization only occurs if the first word is all lowercase and contains only lowercase letters. /// The first word can also contain punctuation (e.g. a period, comma, hyphen, semi-colon, colon). - func capitalizeFirstWord() -> String { + func capitalizingFirstWord() -> String { guard let firstWordStartIndex = self.firstIndex(where: { !$0.isWhitespace && !$0.isNewline }) else { return self } let firstWord = self[firstWordStartIndex...].prefix(while: { !$0.isWhitespace && !$0.isNewline}) @@ -26,12 +26,9 @@ extension String { return self } - var resultString = String() - resultString.reserveCapacity(self.count) - resultString.append(contentsOf: self[..