Files
Outpost/Vendor/swift-markdown-engine/Tests/MarkdownEngineTests/RebuildSelectionReplayTests.swift
T
Puranjay Savar Mattas 6e1ffb96fd chore: vendor swift-markdown-engine as plain tracked source
Convert from git submodule to plain vendored copy. No push access to
upstream nodes-app/swift-markdown-engine meant our local fix commit
(05c1720) was stranded and unreachable from any remote on fresh
clones/CI. Vendoring as plain files folds it into normal repo history
instead.
2026-08-20 13:26:13 +01:00

97 lines
4.4 KiB
Swift

//
// RebuildSelectionReplayTests.swift
// MarkdownEngineTests
//
// Created by Luca Chen on 26.07.26.
//
// A document rebuild suppresses the re-entrant textViewDidChangeSelection
// (isRebuildingDocument) that `textView.string =` and the styled-string transfer
// fire synchronously. These prove the rebuild still leaves the selection-derived
// state that suppressed handler used to produce: the spell/grammar toggles for the
// loaded caret, and the previous* bookkeeping the next selection change diffs
// against.
//
import AppKit
import SwiftUI
import Testing
@testable import MarkdownEngine
@MainActor
@Suite("Rebuild replays selection-derived state")
struct RebuildSelectionReplayTests {
private func makeEditor() -> (NativeTextViewCoordinator, NativeTextView) {
_ = NSApplication.shared // selection path reads NSApp.currentEvent
let coordinator = NativeTextViewCoordinator(
text: .constant(""), fontName: "SF Pro Text", fontSize: 14,
isWikiLinkActive: .constant(false), onLinkClick: nil, onInlineSelectionChange: nil
)
let textView = NativeTextView(frame: NSRect(x: 0, y: 0, width: 600, height: 400))
textView.isEditable = true
textView.configuration = .default
textView.delegate = coordinator
coordinator.textView = textView
return (coordinator, textView)
}
// After a rebuild the previous* fields must describe the LOADED document, not the
// one open before — the first real selection change diffs against them, and a
// stale caret mis-scopes its restyle. Poisoned here as a prior document would.
@Test func rebuildSeedsSelectionBookkeeping() {
let (coord, tv) = makeEditor()
coord.previousCaretLocation = 9_999
coord.previousSelectedRange = NSRange(location: 9_999, length: 42)
coord.previousActiveTokenIndices = [7, 8, 9]
coord.rebuildTextStorageAndStyle(tv, from: "Hello **world** and `code`.\n")
#expect(coord.isRebuildingDocument == false) // defer cleared it
#expect(coord.previousCaretLocation == tv.selectedRange().location)
#expect(coord.previousSelectedRange == tv.selectedRange())
#expect(coord.previousActiveTokenIndices == coord.activeTokenIndices)
}
// updateAutocorrectSettings is the one side effect nothing else runs after a
// rebuild; the replay must apply the toggles for the loaded caret. Starting the
// cache at nil proves the call ran — it only early-returns when the cache already
// matches the decision. Expectations derive from the handler's own logic against
// the actual final caret, so this tracks the replay's inputs exactly.
@Test func rebuildAppliesAutocorrectForLoadedCaret() {
let (coord, tv) = makeEditor()
coord.cachedSpellingDisabled = nil
coord.rebuildTextStorageAndStyle(tv, from: "```\nlet x = 1\n```\nprose here\n")
let caret = tv.selectedRange().location
let parsed = coord.parsedDocument(for: tv.string)
let inCode = MarkdownDetection.isInsideCodeBlock(location: caret, codeTokens: parsed.codeTokens)
let inLatex = MarkdownDetection.isInsideLatex(location: caret, latexTokens: parsed.latexTokens)
let inLink = parsed.tokens.contains {
($0.kind == .wikiLink || $0.kind == .link || $0.kind == .imageEmbed)
&& NSLocationInRange(caret, $0.range)
}
let expectedDisabled = inCode || inLatex || inLink
#expect(coord.cachedSpellingDisabled == expectedDisabled) // the replay ran
#expect(tv.isContinuousSpellCheckingEnabled
== (expectedDisabled ? false : coord.userPrefersContinuousSpellChecking))
#expect(tv.isAutomaticQuoteSubstitutionEnabled == !expectedDisabled)
}
// The guard is scoped to the rebuild only — a genuine caret move afterwards must
// still run the full handler (here: disabling spelling inside a code block).
@Test func selectionChangeAfterRebuildStillRuns() {
let (coord, tv) = makeEditor()
coord.rebuildTextStorageAndStyle(tv, from: "prose\n```\ncode line\n```\n")
#expect(coord.isRebuildingDocument == false)
let ns = tv.string as NSString
let codeInner = ns.range(of: "code line")
#expect(codeInner.location != NSNotFound)
tv.setSelectedRange(NSRange(location: codeInner.location + 1, length: 0))
#expect(tv.isContinuousSpellCheckingEnabled == false) // handler ran, caret is in code
}
}