fix: highlight fenced code by language
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import AppKit
|
||||
import ConjureKit
|
||||
import UniformTypeIdentifiers
|
||||
import Highlighter
|
||||
|
||||
@MainActor
|
||||
final class NotesController: NSViewController, NSTextViewDelegate {
|
||||
@@ -208,9 +209,36 @@ enum MarkdownHighlighting {
|
||||
(try! NSRegularExpression(pattern: "\\*\\*[^\\n]+?\\*\\*|__[^\\n]+?__"), [.font: NSFont.boldSystemFont(ofSize: 14)]),
|
||||
(try! NSRegularExpression(pattern: "(?<!\\*)\\*(?!\\*)[^*\\n]+\\*(?!\\*)|(?<!_)_(?!_)[^_\\n]+_(?!_)"), [.font: NSFontManager.shared.convert(.systemFont(ofSize: 14), toHaveTrait: .italicFontMask)]),
|
||||
(try! NSRegularExpression(pattern: "!?\\[[^\\]\\n]+\\]\\([^\\n)]+\\)|(?m)^\\s*(?:[-+*]|[0-9]+\\.)\\s"), [.foregroundColor: SunsetPalette.accent]),
|
||||
(try! NSRegularExpression(pattern: "`[^`\\n]+`|(?ms)^```[^\\n]*\\n.*?(?:^```[^\\n]*$|\\z)"), [.font: NSFont.monospacedSystemFont(ofSize: 14, weight: .regular), .foregroundColor: NSColor.systemGreen]),
|
||||
(try! NSRegularExpression(pattern: "`[^`\\n]+`"), [.font: NSFont.monospacedSystemFont(ofSize: 14, weight: .regular), .foregroundColor: NSColor.systemGreen]),
|
||||
]
|
||||
|
||||
private static let highlighter: Highlighter = {
|
||||
guard let value = Highlighter() else { preconditionFailure("Missing bundled syntax highlighter") }
|
||||
value.setTheme("atom-one-dark", withFont: "Menlo-Regular", ofSize: 14)
|
||||
return value
|
||||
}()
|
||||
private static let openingFence = try! NSRegularExpression(pattern: "(?m)^ {0,3}(`{3,}|~{3,})([^\\r\\n]*)\\r?\\n")
|
||||
|
||||
private static func codeBlocks(in text: String) -> [(range: NSRange, body: NSRange, language: String?)] {
|
||||
let source = text as NSString
|
||||
var blocks: [(NSRange, NSRange, String?)] = []
|
||||
var cursor = 0
|
||||
while let opening = openingFence.firstMatch(in: text, range: NSRange(location: cursor, length: source.length - cursor)) {
|
||||
let fence = source.substring(with: opening.range(at: 1))
|
||||
let marker = NSRegularExpression.escapedPattern(for: String(fence.first!))
|
||||
let closing = try! NSRegularExpression(pattern: "(?m)^ {0,3}\(marker){\(fence.count),}[ \\t]*\\r?$")
|
||||
let start = NSMaxRange(opening.range)
|
||||
let end = closing.firstMatch(in: text, range: NSRange(location: start, length: source.length - start))
|
||||
let bodyEnd = end?.range.location ?? source.length
|
||||
cursor = end.map { NSMaxRange($0.range) } ?? source.length
|
||||
let language = source.substring(with: opening.range(at: 2)).split(whereSeparator: { $0.isWhitespace }).first.map(String.init)
|
||||
blocks.append((NSRange(location: opening.range.location, length: cursor - opening.range.location),
|
||||
NSRange(location: start, length: bodyEnd - start), language))
|
||||
if cursor == source.length { break }
|
||||
}
|
||||
return blocks
|
||||
}
|
||||
|
||||
static func apply(to editor: NSTextView) {
|
||||
guard let storage = editor.textStorage else { return }
|
||||
let text = editor.string
|
||||
@@ -226,11 +254,27 @@ enum MarkdownHighlighting {
|
||||
if scale < 1 { image.size = NSSize(width: size.width * scale, height: size.height * scale) }
|
||||
}
|
||||
storage.addAttributes([.font: NSFont.systemFont(ofSize: 14), .foregroundColor: SunsetPalette.text], range: range)
|
||||
let blocks = codeBlocks(in: text)
|
||||
for (expression, attributes) in rules {
|
||||
for match in expression.matches(in: text, range: range) {
|
||||
guard !blocks.contains(where: { NSIntersectionRange($0.range, match.range).length > 0 }) else { continue }
|
||||
storage.addAttributes(attributes, range: match.range)
|
||||
}
|
||||
}
|
||||
for block in blocks {
|
||||
storage.addAttributes([.font: NSFont.monospacedSystemFont(ofSize: 14, weight: .regular),
|
||||
.foregroundColor: SunsetPalette.secondaryText], range: block.range)
|
||||
storage.addAttribute(.foregroundColor, value: SunsetPalette.text, range: block.body)
|
||||
let code = (text as NSString).substring(with: block.body)
|
||||
guard let highlighted = highlighter.highlight(code, as: block.language),
|
||||
highlighted.string == code else { continue }
|
||||
highlighted.enumerateAttribute(.foregroundColor, in: NSRange(location: 0, length: highlighted.length)) { color, range, _ in
|
||||
if let color {
|
||||
storage.addAttribute(.foregroundColor, value: color,
|
||||
range: NSRange(location: block.body.location + range.location, length: range.length))
|
||||
}
|
||||
}
|
||||
}
|
||||
editor.typingAttributes = [.font: NSFont.systemFont(ofSize: 14), .foregroundColor: SunsetPalette.text]
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user