krz/orgstar

A native macOS editor for org-mode files. editor org-mode swift

Tests/OrgCoreTests/BlockCommandTests.swift

191ddb506f3a91b93c755ba6e8123a4e8ac4f90d
orgstar/Tests/OrgCoreTests/BlockCommandTests.swift history · blame · raw

97 lines · 4671 bytes

 1import Foundation
 2import Testing
 3@testable import OrgCore
 4
 5struct BlockCommandTests {
 6    static let oracle = ProcessInfo.processInfo.environment["ORGSTAR_SKIP_ORACLE"] == nil
 7
 8    static let text = """
 9        * Heading
10        Some text here.
11          Indented line
12        * Starred
13        #+keyword
14        last
15        """
16
17    @Test(.enabled(if: oracle))
18    func insertsTemplatesLikeOrg() throws {
19        let ns = Self.text as NSString
20        let points = [ns.range(of: "Some").location, ns.range(of: "text").location, ns.range(of: "Indented").location, ns.length]
21        let regions = [("Some", "here."), ("Indented", "#+keyword"), ("Some", "line\n")]
22        let types = ["src", "quote", "EXAMPLE", "export html", "verse"]
23        var cases: [EmacsOracle.Case] = []
24        var ours: [(String, Int)] = []
25        for type in types {
26            for offset in points {
27                cases.append(EmacsOracle.Case(text: Self.text, point: EmacsOracle.point(offset, in: Self.text), form: "(org-insert-structure-template \(lispString(type)))"))
28                ours.append(Self.run(type, offset..<offset))
29            }
30            for (from, to) in regions {
31                let start = ns.range(of: from).location
32                let end = NSMaxRange(ns.range(of: to))
33                cases.append(EmacsOracle.Case(text: Self.text, point: EmacsOracle.point(end, in: Self.text), form: "(progn (transient-mark-mode 1) (set-mark \(EmacsOracle.point(start, in: Self.text))) (activate-mark) (org-insert-structure-template \(lispString(type))))"))
34                ours.append(Self.run(type, start..<end))
35            }
36        }
37        for (i, (mine, theirs)) in zip(ours, try EmacsOracle.run(cases)).enumerated() {
38            #expect(mine.0 == theirs.text, "\(cases[i].form)\nours:  \(mine.0.debugDescription)\nemacs: \(theirs.text.debugDescription)")
39            if mine.0 == theirs.text { #expect(mine.1 == EmacsOracle.offset(theirs.point, in: theirs.text), "\(cases[i].form) caret") }
40        }
41    }
42
43    static func run(_ type: String, _ selection: Range<Int>) -> (String, Int) {
44        let key = InsertStructureTemplate.templates.first { $0.type == type }.map { String($0.key) } ?? "\t"
45        let context = EditContext(revision: 0, text: text, tree: OrgParser.parse(text), selection: [selection], answers: ["template": key, "type": type])
46        guard case .commit(let result) = InsertStructureTemplate().run(in: context) else { return (text, 0) }
47        var new = text
48        for edit in result.edits.sorted(by: { $0.range.lowerBound > $1.range.lowerBound }) { new = edit.apply(to: new) }
49        return (new, result.selection?.first?.lowerBound ?? 0)
50    }
51
52    static let blocks = """
53        * Code
54        #+begin_src python :results output
55        def f():
56            return 1
57        ,* not a heading
58        ,#+not a keyword
59        #+end_src
60        #+begin_example
61          indented
62        #+end_example
63        #+begin_src sh
64        #+end_src
65        """
66
67    @Test(.enabled(if: oracle))
68    func editsBlocksLikeOrgEditSpecial() throws {
69        let ns = Self.blocks as NSString
70        let points = [ns.range(of: "def f").location, ns.range(of: "#+begin_example").location, ns.range(of: "#+begin_src sh").location + 3]
71        let added = "* new star\n#+new keyword\nplain"
72        let forms = points.map { point in
73            """
74            (progn (erase-buffer) (insert \(lispString(Self.blocks))) (goto-char \(EmacsOracle.point(point, in: Self.blocks)))
75              (org-edit-special)
76              (let ((content (buffer-string)))
77                (goto-char (point-max)) (insert \(lispString(added)))
78                (org-edit-src-exit)
79                (format "%s|%s" content (buffer-string))))
80            """
81        }
82        let emacs = try EmacsOracle.evaluate("", "(list \(forms.joined(separator: " ")))", prelude: "(setq org-src-preserve-indentation t org-src-window-setup 'current-window)")
83        let tree = OrgParser.parse(Self.blocks)
84        for (point, expected) in zip(points, emacs) {
85            guard let edit = BlockEdit.at(point, text: Self.blocks, tree: tree) else {
86                Issue.record("no block at \(point)")
87                continue
88            }
89            let context = EditContext(revision: 0, text: Self.blocks, tree: tree, selection: [point..<point])
90            var after = Self.blocks
91            if case .commit(let result) = ReplaceBlockLines(edit: edit, content: edit.content + added).run(in: context) {
92                for e in result.edits { after = e.apply(to: after) }
93            }
94            #expect("\(edit.content)|\(after)" == expected, "at \(point)")
95        }
96    }
97}