Skip to content

Commit

Permalink
Allow customizing soft break mode (#342)
Browse files Browse the repository at this point in the history
* Allow customizing soft break mode

Resolved #341

* Attributed renderer

* Tests

* Update snapshots
  • Loading branch information
freak4pc authored Aug 12, 2024
1 parent 9a8119b commit 5544181
Show file tree
Hide file tree
Showing 8 changed files with 104 additions and 5 deletions.
10 changes: 10 additions & 0 deletions Sources/MarkdownUI/DSL/Inlines/SoftBreak.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,13 @@ public struct SoftBreak: InlineContentProtocol {
.init(inlines: [.softBreak])
}
}

extension SoftBreak {
public enum Mode {
/// Treat a soft break as a space
case space

/// Treat a soft break as a line break
case lineBreak
}
}
18 changes: 15 additions & 3 deletions Sources/MarkdownUI/Renderer/AttributedStringInlineRenderer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ extension InlineNode {
func renderAttributedString(
baseURL: URL?,
textStyles: InlineTextStyles,
softBreakMode: SoftBreak.Mode,
attributes: AttributeContainer
) -> AttributedString {
var renderer = AttributedStringInlineRenderer(
baseURL: baseURL,
textStyles: textStyles,
softBreakMode: softBreakMode,
attributes: attributes
)
renderer.render(self)
Expand All @@ -21,12 +23,19 @@ private struct AttributedStringInlineRenderer {

private let baseURL: URL?
private let textStyles: InlineTextStyles
private let softBreakMode: SoftBreak.Mode
private var attributes: AttributeContainer
private var shouldSkipNextWhitespace = false

init(baseURL: URL?, textStyles: InlineTextStyles, attributes: AttributeContainer) {
init(
baseURL: URL?,
textStyles: InlineTextStyles,
softBreakMode: SoftBreak.Mode,
attributes: AttributeContainer
) {
self.baseURL = baseURL
self.textStyles = textStyles
self.softBreakMode = softBreakMode
self.attributes = attributes
}

Expand Down Expand Up @@ -67,10 +76,13 @@ private struct AttributedStringInlineRenderer {
}

private mutating func renderSoftBreak() {
if self.shouldSkipNextWhitespace {
switch softBreakMode {
case .space where self.shouldSkipNextWhitespace:
self.shouldSkipNextWhitespace = false
} else {
case .space:
self.result += .init(" ", attributes: self.attributes)
case .lineBreak:
self.renderLineBreak()
}
}

Expand Down
14 changes: 12 additions & 2 deletions Sources/MarkdownUI/Renderer/TextInlineRenderer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ extension Sequence where Element == InlineNode {
baseURL: URL?,
textStyles: InlineTextStyles,
images: [String: Image],
softBreakMode: SoftBreak.Mode,
attributes: AttributeContainer
) -> Text {
var renderer = TextInlineRenderer(
baseURL: baseURL,
textStyles: textStyles,
images: images,
softBreakMode: softBreakMode,
attributes: attributes
)
renderer.render(self)
Expand All @@ -24,18 +26,21 @@ private struct TextInlineRenderer {
private let baseURL: URL?
private let textStyles: InlineTextStyles
private let images: [String: Image]
private let softBreakMode: SoftBreak.Mode
private let attributes: AttributeContainer
private var shouldSkipNextWhitespace = false

init(
baseURL: URL?,
textStyles: InlineTextStyles,
images: [String: Image],
softBreakMode: SoftBreak.Mode,
attributes: AttributeContainer
) {
self.baseURL = baseURL
self.textStyles = textStyles
self.images = images
self.softBreakMode = softBreakMode
self.attributes = attributes
}

Expand Down Expand Up @@ -72,10 +77,14 @@ private struct TextInlineRenderer {
}

private mutating func renderSoftBreak() {
if self.shouldSkipNextWhitespace {
switch self.softBreakMode {
case .space where self.shouldSkipNextWhitespace:
self.shouldSkipNextWhitespace = false
} else {
case .space:
self.defaultRender(.softBreak)
case .lineBreak:
self.shouldSkipNextWhitespace = true
self.defaultRender(.lineBreak)
}
}

Expand Down Expand Up @@ -104,6 +113,7 @@ private struct TextInlineRenderer {
inline.renderAttributedString(
baseURL: self.baseURL,
textStyles: self.textStyles,
softBreakMode: self.softBreakMode,
attributes: self.attributes
)
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import SwiftUI

extension View {
/// Sets the soft break mode for inline texts in a view hierarchy.
///
/// - parameter softBreakMode: If set to `space`, treats all soft breaks as spaces, keeping sentences whole. If set to `lineBreak`, treats soft breaks as full line breaks
///
/// - Returns: A view that uses the specified soft break mode for itself and its child views.
public func markdownSoftBreakMode(_ softBreakMode: SoftBreak.Mode) -> some View {
self.environment(\.softBreakMode, softBreakMode)
}
}

extension EnvironmentValues {
var softBreakMode: SoftBreak.Mode {
get { self[SoftBreakModeKey.self] }
set { self[SoftBreakModeKey.self] = newValue }
}
}

private struct SoftBreakModeKey: EnvironmentKey {
static let defaultValue: SoftBreak.Mode = .space
}
2 changes: 2 additions & 0 deletions Sources/MarkdownUI/Views/Inlines/InlineText.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ struct InlineText: View {
@Environment(\.inlineImageProvider) private var inlineImageProvider
@Environment(\.baseURL) private var baseURL
@Environment(\.imageBaseURL) private var imageBaseURL
@Environment(\.softBreakMode) private var softBreakMode
@Environment(\.theme) private var theme

@State private var inlineImages: [String: Image] = [:]
Expand All @@ -26,6 +27,7 @@ struct InlineText: View {
link: self.theme.link
),
images: self.inlineImages,
softBreakMode: self.softBreakMode,
attributes: attributes
)
}
Expand Down
42 changes: 42 additions & 0 deletions Tests/MarkdownUITests/MarkdownTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -300,5 +300,47 @@

assertSnapshot(of: view, as: .image(layout: layout))
}

func testSoftBreakModeSpace() {
let view = Markdown {
#"""
# This is a heading
Item 1
Item 2
Item 3
Item 4
I would **very much** like to write
A long paragraph that spans _multiple lines_
But should ~~render differently~~ based on
soft break mode
"""#
}
.markdownSoftBreakMode(.space)

assertSnapshot(of: view, as: .image(layout: layout))
}

func testSoftBreakModeLineBreak() {
let view = Markdown {
#"""
# This is a heading
Item 1
Item 2
Item 3
Item 4
I would **very much** like to write
A long paragraph that spans _multiple lines_
But should ~~render differently~~ based on
soft break mode
"""#
}
.markdownSoftBreakMode(.lineBreak)

assertSnapshot(of: view, as: .image(layout: layout))
}
}
#endif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 5544181

Please sign in to comment.