Skip to content

Instantly share code, notes, and snippets.

@jamesrochabrun
Created July 5, 2024 18:10
Show Gist options
  • Save jamesrochabrun/d5f4892b0b84134051158b3a628117ca to your computer and use it in GitHub Desktop.
Save jamesrochabrun/d5f4892b0b84134051158b3a628117ca to your computer and use it in GitHub Desktop.
import AppKit
import Down
import Highlightr
struct MarkdownTextView: NSViewRepresentable {
let markdown: String
func makeNSView(context: Context) -> NSScrollView {
let scrollView = NSScrollView()
scrollView.hasVerticalScroller = true
scrollView.autoresizingMask = [.width, .height]
let textView = context.coordinator.textView
textView.minSize = NSSize(width: 0, height: 0)
textView.maxSize = NSSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude)
textView.isVerticallyResizable = true
textView.isHorizontallyResizable = false
textView.autoresizingMask = [.width]
textView.textContainer?.widthTracksTextView = true
scrollView.documentView = textView
return scrollView
}
func updateNSView(_ nsView: NSScrollView, context: Context) {
context.coordinator.updateText(markdown: markdown)
}
func makeCoordinator() -> Coordinator {
Coordinator()
}
class Coordinator: NSObject {
let textView = NSTextView()
let highlightr = Highlightr()
override init() {
super.init()
textView.isEditable = false
textView.isSelectable = true
textView.textContainerInset = NSSize(width: 10, height: 10)
highlightr?.setTheme(to: "atom-one-dark")
}
func updateText(markdown: String) {
DispatchQueue.global(qos: .userInitiated).async {
do {
let down = Down(markdownString: markdown)
let attributedString = try down.toAttributedString()
let highlightedAttributedString = NSMutableAttributedString(attributedString: attributedString)
self.applySyntaxHighlighting(to: highlightedAttributedString)
DispatchQueue.main.async {
self.textView.textStorage?.setAttributedString(highlightedAttributedString)
self.textView.scrollRangeToVisible(NSRange(location: self.textView.string.count, length: 0))
}
} catch {
DispatchQueue.main.async {
self.textView.string = "Failed to parse markdown: \(error)"
}
}
}
}
func applySyntaxHighlighting(to attributedString: NSMutableAttributedString) {
let fullRange = NSRange(location: 0, length: attributedString.length)
attributedString.enumerateAttribute(.font, in: fullRange, options: []) { value, range, _ in
if let font = value as? NSFont, font.fontDescriptor.symbolicTraits.contains(.monoSpace) {
let code = attributedString.attributedSubstring(from: range).string
if let highlightedCode = highlightr?.highlight(code, as: "swift") {
attributedString.replaceCharacters(in: range, with: highlightedCode)
}
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment