feat: hide rendered markdown formatting markers

This commit is contained in:
2026-10-19 20:22:17 +02:00
parent 6f37910797
commit 04bfd785e7
4 changed files with 164 additions and 30 deletions
@@ -0,0 +1,90 @@
import AppKit
import ConjureKit
import Testing
@testable import Conjure
@Test @MainActor
func noteFormattingHidesSyntaxButPreservesMarkdownAndLiteralCode() throws {
_ = NSApplication.shared
let editor = NotesTextView(frame: NSRect(x: 0, y: 0, width: 600, height: 800))
let source = "# Heading\n## Subheading\n*italic* **bold** _emphasis_ __strong__ ~~gone~~ `*literal*`\n> Quote\n[Label](https://example.com)\n```text\n# Code *literal*\n```\nUnmatched * and \\*escaped*"
editor.setMarkdownContents(NSAttributedString(string: source))
MarkdownHighlighting.apply(to: editor)
editor.refreshTasks()
let layout = try #require(editor.layoutManager)
let container = try #require(editor.textContainer)
layout.ensureLayout(for: container)
func visibleText() -> String {
var result = ""
let text = editor.string as NSString
for index in 0..<text.length {
let glyph = layout.glyphIndexForCharacter(at: index)
if !layout.propertyForGlyph(at: glyph).contains(.null) {
result += text.substring(with: NSRange(location: index, length: 1))
}
}
return result
}
#expect(visibleText() == "Heading\nSubheading\nitalic bold emphasis strong gone *literal*\nQuote\nLabel\n\n# Code *literal*\n\nUnmatched * and \\*escaped*")
#expect(editor.markdownString == source)
editor.setSelectedRange(NSRange(location: 0, length: editor.string.utf16.count))
let pasteboard = NSPasteboard.withUniqueName()
defer { pasteboard.releaseGlobally() }
#expect(editor.writeSelection(to: pasteboard, types: editor.writablePasteboardTypes))
#expect(pasteboard.string(forType: .string) == source)
let directory = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)
defer { try? FileManager.default.removeItem(at: directory) }
let store = try NotesStore(directory: directory)
let id = try store.create()
try store.update(id: id, contents: editor.markdownContents)
let reloaded = try #require(NotesStore(directory: directory).notes.first)
#expect(reloaded.contents.string == source)
}
@Test @MainActor
func hiddenNoteMarkupKeepsTypingDeletionFormattingAndUndoAtVisibleBoundaries() throws {
_ = NSApplication.shared
let suite = "NotesMarkup.\(UUID().uuidString)"
let defaults = try #require(UserDefaults(suiteName: suite))
let directory = FileManager.default.temporaryDirectory.appendingPathComponent(suite)
defer {
defaults.removePersistentDomain(forName: suite)
try? FileManager.default.removeItem(at: directory)
}
let controller = NotesController(defaults: defaults, directory: directory)
let window = NSWindow(contentRect: NSRect(x: 0, y: 0, width: 570, height: 404),
styleMask: .borderless, backing: .buffered, defer: false)
window.contentViewController = controller
defer { _ = controller.close(); window.orderOut(nil) }
func descendants(_ view: NSView) -> [NSView] { [view] + view.subviews.flatMap(descendants) }
let editor = try #require(descendants(controller.view).compactMap { $0 as? NotesTextView }.first)
window.makeFirstResponder(editor)
editor.insertText("# Title\n*word*", replacementRange: NSRange(location: 0, length: 0))
editor.setSelectedRange(NSRange(location: 0, length: 0))
editor.insertText("A", replacementRange: editor.selectedRange())
#expect(editor.markdownString == "# ATitle\n*word*")
editor.breakUndoCoalescing()
editor.undoManager?.removeAllActions()
editor.undoManager?.beginUndoGrouping()
editor.setSelectedRange(NSRange(location: editor.string.utf16.count, length: 0))
editor.deleteBackward(nil)
editor.undoManager?.endUndoGrouping()
#expect(editor.markdownString == "# ATitle\n*wor*")
editor.undoManager?.undo()
#expect(editor.markdownString == "# ATitle\n*word*")
let word = (editor.string as NSString).range(of: "word")
editor.setSelectedRange(NSRange(location: word.location, length: 0))
editor.moveRight(nil)
#expect(editor.selectedRange().location == word.location + 1)
editor.setSelectedRange(NSRange(location: NSMaxRange(word), length: 0))
editor.moveRight(nil)
#expect(editor.selectedRange().location == editor.string.utf16.count)
editor.setSelectedRange(NSRange(location: word.location, length: 0))
let italic = try #require(descendants(controller.view).compactMap { $0 as? NSButton }.first { $0.toolTip == "Italic" })
// Selecting the body and toggling formatting must remove its hidden delimiters.
editor.setSelectedRange(word)
italic.performClick(nil)
#expect(editor.markdownString == "# ATitle\nword")
#expect(controller.save())
#expect(try NotesStore(directory: directory).notes.first?.contents.string == "# ATitle\nword")
}