Skip to content

Commit 25ba06f

Browse files
committed
Small improvements to the new payload changes to steer users in the right direction
1 parent dcd9a32 commit 25ba06f

4 files changed

Lines changed: 52 additions & 5 deletions

File tree

Sources/SwiftTerm/CharData.swift

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,10 @@ public struct TinyAtom {
200200
self.code = code
201201
}
202202

203-
/// Returns the TinyAtom associated with the specified url, or nil if we ran out of space
203+
/// Creates a caller-owned TinyAtom for the specified value, or returns nil if no codes remain.
204+
///
205+
/// The caller must call ``release()`` when the atom is no longer in use. Use
206+
/// ``Terminal/makePayload(value:)`` for an atom whose lifetime is managed by a terminal.
204207
public static func lookup (value: Any) -> TinyAtom? {
205208
lock.lock()
206209
defer { lock.unlock() }
@@ -219,6 +222,13 @@ public struct TinyAtom {
219222
release(codes: [code])
220223
}
221224

225+
/// Releases a caller-owned atom.
226+
///
227+
/// After this call, ``target`` returns nil for this atom and for all copies of it.
228+
public func release() {
229+
TinyAtom.release(code: code)
230+
}
231+
222232
static func release<S: Sequence>(codes: S) where S.Element == UInt16 {
223233
lock.lock()
224234
defer { lock.unlock() }

Sources/SwiftTerm/Documentation.docc/Extensions/Terminal.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ queue and the terminal will synchronize internally.
127127
- ``makeCharData(attribute:scalar:size:)``
128128
- ``updateCharData(_:char:size:)``
129129
- ``updateCharData(_:code:size:)``
130+
- ``makePayload(value:)``
130131

131132
### Housekeeping
132133

Sources/SwiftTerm/Terminal.swift

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1800,15 +1800,27 @@ open class Terminal {
18001800
var hyperLinkTracking: (start: Position, payload: String)? = nil
18011801
private var payloadCodes = Set<UInt16>()
18021802

1803+
/// Creates a payload atom whose lifetime is managed by this terminal.
1804+
///
1805+
/// ``garbageCollectPayload()`` releases the atom after it is no longer present in
1806+
/// either terminal buffer. The terminal also releases its remaining atoms when it
1807+
/// is deinitialized.
1808+
public func makePayload(value: Any) -> TinyAtom? {
1809+
guard let atom = TinyAtom.lookup(value: value) else {
1810+
return nil
1811+
}
1812+
payloadCodes.insert(atom.code)
1813+
return atom
1814+
}
1815+
18031816
func oscHyperlink (_ data: ArraySlice<UInt8>)
18041817
{
18051818
let buffer = self.buffer
18061819
if data.count == 1 && data [data.startIndex] == UInt8 (ascii: ";") {
18071820
// We only had the terminator, so we can close ";"
18081821
if let hlt = hyperLinkTracking {
18091822
let str = hlt.payload
1810-
if let urlToken = TinyAtom.lookup (value: str) {
1811-
payloadCodes.insert(urlToken.code)
1823+
if let urlToken = makePayload(value: str) {
18121824
//print ("Setting the text from \(hlt.start) to \(buffer.x) on line \(buffer.y+buffer.yBase) to \(str)")
18131825

18141826
// Between the time the flag was set, and now `y` might have changed negatively,

Tests/SwiftTermTests/LinkLookupTests.swift

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ final class LinkLookupTests: TerminalDelegate {
3232
terminal.feed(text: "abc")
3333

3434
let payload = "id;https://example.com"
35-
let atom = TinyAtom.lookup(value: payload)!
35+
let atom = terminal.makePayload(value: payload)!
3636
let line = terminal.displayBuffer.lines[0]
3737
var cd = line[1]
3838
cd.setPayload(atom: atom)
@@ -69,7 +69,7 @@ final class LinkLookupTests: TerminalDelegate {
6969
return false
7070
}
7171
let matched = atom.target as? Int == value
72-
TinyAtom.release(code: atom.code)
72+
atom.release()
7373
return matched
7474
}
7575
}
@@ -84,6 +84,30 @@ final class LinkLookupTests: TerminalDelegate {
8484
#expect(allValuesMatched)
8585
}
8686

87+
@Test func testTerminalOwnedPayloadIsGarbageCollected() throws {
88+
let terminal = Terminal(delegate: self, options: TerminalOptions(cols: 10, rows: 1))
89+
terminal.feed(text: "abc")
90+
91+
let atom = try #require(terminal.makePayload(value: "https://example.com"))
92+
let line = try #require(terminal.getLine(row: 0))
93+
var cell = line[0]
94+
cell.setPayload(atom: atom)
95+
line[0] = cell
96+
97+
terminal.feed(text: "\u{1b}[2J")
98+
terminal.garbageCollectPayload()
99+
100+
#expect(atom.target == nil)
101+
}
102+
103+
@Test func testCallerOwnedPayloadCanBeReleased() throws {
104+
let atom = try #require(TinyAtom.lookup(value: "https://example.com"))
105+
106+
atom.release()
107+
108+
#expect(atom.target == nil)
109+
}
110+
87111
@Test func testImplicitUrlLookup() {
88112
let terminal = Terminal(delegate: self, options: TerminalOptions(cols: 40, rows: 1))
89113
terminal.feed(text: "https://example.com tail")

0 commit comments

Comments
 (0)