Skip to content

Commit

Permalink
documentation updates and swiftformat pass (#194)
Browse files Browse the repository at this point in the history
  • Loading branch information
heckj authored Jul 13, 2024
1 parent e2bb692 commit 410b1d1
Show file tree
Hide file tree
Showing 14 changed files with 64 additions and 42 deletions.
2 changes: 2 additions & 0 deletions Sources/Automerge/Automerge.docc/Automerge.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ Read <doc:FiveMinuteQuickstart> to get a quick taste of how to use Automerge, or

- ``Automerge/AutomergeText``
- ``Automerge/Cursor``
- ``Automerge/Position``
- ``Automerge/Mark``
- ``Automerge/ExpandMark``

Expand Down Expand Up @@ -111,6 +112,7 @@ Read <doc:FiveMinuteQuickstart> to get a quick taste of how to use Automerge, or

### Type Conversion Errors

- ``Automerge/ScalarValueConversionError``
- ``Automerge/BooleanScalarConversionError``
- ``Automerge/BytesScalarConversionError``
- ``Automerge/IntScalarConversionError``
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ var myColors = ColorList(colors: ["blue", "red"])
try encoder.encode(myColors)
```

To support cross-platform usage, when provided a optional type to encode, the encoder writes a
``ScalarValue/Null`` into the Document as opposed to not creating the relevant entry in a map or list.

## Topics

### Creating an Encoder
Expand Down
1 change: 1 addition & 0 deletions Sources/Automerge/Automerge.docc/Curation/Document.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@
### Observing Documents

- ``objectWillChange``
- ``objectDidChange``

### Transfering Documents

Expand Down
2 changes: 1 addition & 1 deletion Sources/Automerge/BoundTypes/AutomergeText.swift
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ public final class AutomergeText: Codable, @unchecked Sendable {
/// document.
/// - Parameters:
/// - doc: The Automerge document associated with this reference.
/// - path: A string path that represents a `Text` container within the Automerge document.
/// - id: The Automerge object ID of the text object to bind.
public func bind(doc: Document, id: ObjId) throws {
// this assert runs afoul of the encoder, which doesn't make sense right now, but
// I don't want to second guess it at the moment.
Expand Down
9 changes: 4 additions & 5 deletions Sources/Automerge/ChangeHash.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ public struct ChangeHash: Equatable, Hashable, CustomDebugStringConvertible, Sen
}

public extension Set<ChangeHash> {

/// Transforms each `ChangeHash` in the set into its byte array (`[UInt8]`). This raw byte representation
/// captures the state of the document at a specific point in its history, allowing for efficient storage
/// and retrieval of document states.
Expand All @@ -25,16 +24,16 @@ public extension Set<ChangeHash> {
}

public extension Data {

/// Interprets the data to return the data as a set of change hashes that represent a state within an Automerge document. If the data is not a multiple of 32 bytes, returns nil.
/// Interprets the data to return the data as a set of change hashes that represent a state within an Automerge
/// document. If the data is not a multiple of 32 bytes, returns nil.
func heads() -> Set<ChangeHash>? {
let rawBytes: [UInt8] = Array(self)
guard rawBytes.count % 32 == 0 else { return nil }
let totalHashes = rawBytes.count / 32
let heads = (0..<totalHashes).map { index in
let heads = (0 ..< totalHashes).map { index in
let lowerBound = index * 32
let upperBound = (index + 1) * 32
let bytes = rawBytes[lowerBound..<upperBound]
let bytes = rawBytes[lowerBound ..< upperBound]
return ChangeHash(bytes: Array(bytes))
}
return Set(heads)
Expand Down
2 changes: 1 addition & 1 deletion Sources/Automerge/Codable/AnyCodingKey.swift
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ extension AnyCodingKey: CodingKey {
///
/// For a non-failable initializer for ``AnyCodingKey``, use ``AnyCodingKey/init(_:)-5azuh``.
///
/// - Parameter stringVal: The key for a keyed container.
/// - Parameter stringValue: The key for a keyed container.
public init?(stringValue: String) {
pathElement = Automerge.Prop.Key(stringValue)
}
Expand Down
6 changes: 3 additions & 3 deletions Sources/Automerge/Codable/Decoding/AutomergeDecoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public struct AutomergeDecoder {
}

/// Returns the type you specify, decoded from the Automerge document referenced by the decoder.
/// - Parameter _: _ The type of the value to decode from the Automerge document.
/// - Parameter : The type of the value to decode from the Automerge document.
@inlinable public func decode<T: Decodable>(_: T.Type) throws -> T {
if T.self == AutomergeText.self {
// Special case decoding AutomergeText - when it's the top level type being encoded,
Expand All @@ -37,8 +37,8 @@ public struct AutomergeDecoder {

/// Returns the type you specify, decoded from the Automerge document referenced by the decoder.
/// - Parameters:
/// - _: _ The type of the value to decode from the Automerge document.
/// - path: The path to the schema location within the Automerge document to attempt to decode into the type you
/// - : The type of the value to decode from the Automerge document.
/// - path: The path to the schema location within the Automerge document to attempt to decode into the type you
/// provide.
///
/// The `path` parameter accepts any type conforming to the `CodingKey` protocol.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,6 @@ struct AutomergeSingleValueDecodingContainer: SingleValueDecodingContainer {
debugDescription: "Expected to decode \(T.self) from \(value), but it wasn't `.text`."
))
}

default:
return try T(from: impl)
}
Expand Down
2 changes: 1 addition & 1 deletion Sources/Automerge/Codable/Encoding/AutomergeEncoder.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/// An encoder that stores types that conform to the codable protocol into an Automerge document.
/// An encoder that stores types that conform to the Codable protocol into an Automerge document.
public struct AutomergeEncoder {
/// The user info dictionary for the encoder.
public var userInfo: [CodingUserInfoKey: Any] = [:]
Expand Down
4 changes: 2 additions & 2 deletions Sources/Automerge/Cursor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ public enum Position {
extension Position {
func toFfi() -> FfiPosition {
switch self {
case .cursor(let cursor):
case let .cursor(cursor):
return .cursor(position: cursor.bytes)
case .index(let index):
case let .index(index):
return .index(position: index)
}
}
Expand Down
66 changes: 42 additions & 24 deletions Sources/Automerge/Document.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,13 @@ public final class Document: @unchecked Sendable {
try work()
}
#endif

#if canImport(Combine)
public let objectDidChange: PassthroughSubject<(), Never> = .init()
/// A publisher that sends a signal after the document is updated.
///
/// You can use the signal from this publisher to read the and record ``Document/heads()``
/// to get the state indicator of the document after the change is complete.
public let objectDidChange: PassthroughSubject<Void, Never> = .init()
#endif

var reportingLogLevel: LogVerbosity
Expand Down Expand Up @@ -129,7 +133,7 @@ public final class Document: @unchecked Sendable {
/// - ty: The type of object to add to the dictionary.
/// - Returns: The object Id that references the object added.
public func putObject(obj: ObjId, key: String, ty: ObjType) throws -> ObjId {
return try lock {
try lock {
sendObjectWillChange()
defer { sendObjectDidChange() }
return try self.doc.wrapErrors {
Expand All @@ -149,7 +153,7 @@ public final class Document: @unchecked Sendable {
/// If the index position doesn't yet exist within the array, this method will throw an error.
/// To add an object that extends the array, use the method ``insertObject(obj:index:ty:)``.
public func putObject(obj: ObjId, index: UInt64, ty: ObjType) throws -> ObjId {
return try lock {
try lock {
sendObjectWillChange()
defer { sendObjectDidChange() }
return try self.doc.wrapErrors {
Expand Down Expand Up @@ -186,7 +190,7 @@ public final class Document: @unchecked Sendable {
/// If you want to change an existing index, use the ``putObject(obj:index:ty:)`` to put in an object or
/// ``put(obj:index:value:)`` to put in a value.
public func insertObject(obj: ObjId, index: UInt64, ty: ObjType) throws -> ObjId {
return try lock {
try lock {
sendObjectWillChange()
defer { sendObjectDidChange() }
return try self.doc.wrapErrors {
Expand Down Expand Up @@ -558,7 +562,7 @@ public final class Document: @unchecked Sendable {
/// - position: The index position in the list, or index of the UTF-8 view in the string for a text object.
/// - Returns: A cursor that references the position you specified.
public func cursor(obj: ObjId, position: UInt64) throws -> Cursor {
return try lock {
try lock {
sendObjectWillChange()
defer { sendObjectDidChange() }
return try Cursor(bytes: self.doc.wrapErrors { try $0.cursor(obj: obj.bytes, position: position) })
Expand All @@ -573,7 +577,7 @@ public final class Document: @unchecked Sendable {
/// - heads: The set of ``ChangeHash`` that represents a point of time in the history the document.
/// - Returns: A cursor that references the position and point in time you specified.
public func cursorAt(obj: ObjId, position: UInt64, heads: Set<ChangeHash>) throws -> Cursor {
return try lock {
try lock {
sendObjectWillChange()
defer { sendObjectDidChange() }
return try Cursor(bytes: self.doc.wrapErrors { try $0.cursorAt(
Expand Down Expand Up @@ -641,7 +645,7 @@ public final class Document: @unchecked Sendable {
/// deleting.
/// - delete: The number of unicode scalars to delete from the `start` index.
/// If negative, the function deletes characters preceding `start` index, rather than following it.
/// - values: The characters to insert after the `start` index.
/// - value: The characters to insert after the `start` index.
///
/// With `spliceText`, the `start` and `delete` parameters represent integer distances of unicode scalars of the
/// Swift strings, not the counts of Characters (or grapheme clusters).
Expand Down Expand Up @@ -797,7 +801,8 @@ public final class Document: @unchecked Sendable {
///
/// - Parameters:
/// - obj: The identifier of the text object, represented by an ``ObjId``.
/// - position: The position within the text, represented by a ``Position`` enum which can be a ``Cursor`` or an `UInt64` as a fixed position.
/// - position: The position within the text, represented by a ``Position`` enum which can be a ``Cursor`` or an
/// `UInt64` as a fixed position.
/// - heads: A set of `ChangeHash` values that represents a point in time in the document's history.
/// - Returns: An array of `Mark` objects for the text object at the specified position.
///
Expand All @@ -811,16 +816,22 @@ public final class Document: @unchecked Sendable {
/// ```
///
/// ## Recommendation
///
/// Use this method to query the marks applied to a text object at a specific position.
/// This can be useful for retrieving ``Marks`` related to a character without traversing the full document.
/// This can be useful for retrieving the list of ``Automerge/Mark`` related to a character without
/// traversing the full document.
///
/// ## When to Use Cursor vs. Index
///
/// While you can specify the position either with a `Cursor` or an `Index`, there are important distinctions:
///
/// - **Cursor**: Use a `Cursor` when you need to track a position that might change over time due to edits in the text object. A `Cursor` provides a way to maintain a reference to a logical position within the text even if the text content changes, making it more robust in collaborative or frequently edited documents.
/// - **Cursor**: Use a `Cursor` when you need to track a position that might change over time due to edits in the
/// text object. A `Cursor` provides a way to maintain a reference to a logical position within the text even if the
/// text content changes, making it more robust in collaborative or frequently edited documents.
///
/// - **Index**: Use an `Index` when you have a fixed position and you are sure that the text content will not change, or changes are irrelevant to your current operation. An index is a straightforward approach for static text content.
/// - **Index**: Use an `Index` when you have a fixed position and you are sure that the text content will not
/// change, or changes are irrelevant to your current operation. An index is a straightforward approach for static
/// text content.
///
/// # See Also
/// ``marksAt(obj:position:)``
Expand All @@ -845,7 +856,8 @@ public final class Document: @unchecked Sendable {
///
/// - Parameters:
/// - obj: The identifier of the text object, represented by an ``ObjId``.
/// - position: The position within the text, represented by a ``Position`` enum which can be a ``Cursor`` or an `UInt64` as a fixed position.
/// - position: The position within the text, represented by a ``Position`` enum which can be a ``Cursor`` or an
/// `UInt64` as a fixed position.
/// - Returns: An array of `Mark` objects for the text object at the specified position.
/// - Note: This method retrieves marks from the latest version of the document.
/// If you need to specify a point in the document's history, refer to ``marksAt(obj:position:heads:)``.
Expand All @@ -861,15 +873,20 @@ public final class Document: @unchecked Sendable {
///
/// ## Recommendation
/// Use this method to query the marks applied to a text object at a specific position.
/// This can be useful for retrieving ``Marks`` related to a character without traversing the full document.
/// This can be useful for retrieving the list of ``Automerge/Mark`` related to a character without
/// traversing the full document.
///
/// ## When to Use Cursor vs. Index
///
/// While you can specify the position either with a `Cursor` or an `Index`, there are important distinctions:
///
/// - **Cursor**: Use a `Cursor` when you need to track a position that might change over time due to edits in the text object. A `Cursor` provides a way to maintain a reference to a logical position within the text even if the text content changes, making it more robust in collaborative or frequently edited documents.
/// - **Cursor**: Use a `Cursor` when you need to track a position that might change over time due to edits in the
/// text object. A `Cursor` provides a way to maintain a reference to a logical position within the text even if the
/// text content changes, making it more robust in collaborative or frequently edited documents.
///
/// - **Index**: Use an `Index` when you have a fixed position and you are sure that the text content will not change, or changes are irrelevant to your current operation. An index is a straightforward approach for static text content.
/// - **Index**: Use an `Index` when you have a fixed position and you are sure that the text content will not
/// change, or changes are irrelevant to your current operation. An index is a straightforward approach for static
/// text content.
///
/// # See Also
/// ``marksAt(obj:position:heads:)``
Expand Down Expand Up @@ -901,7 +918,7 @@ public final class Document: @unchecked Sendable {
/// The `save` function also compacts the memory footprint of an Automerge document and increments the result of
/// ``heads()``, which indicates a specific point in time for the history of the document.
public func save() -> Data {
return lock {
lock {
sendObjectWillChange()
defer { sendObjectDidChange() }
return self.doc.wrapErrors {
Expand Down Expand Up @@ -956,7 +973,7 @@ public final class Document: @unchecked Sendable {
/// - message: The message from the peer to update this document and sync state.
/// - Returns: An array of ``Patch`` that represent the changes applied from the peer.
public func receiveSyncMessageWithPatches(state: SyncState, message: Data) throws -> [Patch] {
return try lock {
try lock {
sendObjectWillChange()
defer { sendObjectDidChange() }
let patches = try self.doc.wrapErrors {
Expand Down Expand Up @@ -1007,7 +1024,7 @@ public final class Document: @unchecked Sendable {
/// - Parameter other: another ``Document``
/// - Returns: A list of ``Patch`` the represent the changes applied when merging the other document.
public func mergeWithPatches(other: Document) throws -> [Patch] {
return try lock {
try lock {
sendObjectWillChange()
defer { sendObjectDidChange() }
let patches = try self.doc.wrapErrorsWithOther(other: other.doc) {
Expand Down Expand Up @@ -1078,8 +1095,8 @@ public final class Document: @unchecked Sendable {
/// ```
///
/// - Parameters:
/// - from: The set of heads at beginning point in the documents history.
/// - to: The set of heads at ending point in the documents history.
/// - before: The set of heads at beginning point in the documents history.
/// - after: The set of heads at ending point in the documents history.
/// - Note: `from` and `to` do not have to be chronological. Document state can move backward.
/// - Returns: The difference needed to produce a document at `to` when it is set at `from` in history.
public func difference(from before: Set<ChangeHash>, to after: Set<ChangeHash>) -> [Patch] {
Expand All @@ -1100,7 +1117,7 @@ public final class Document: @unchecked Sendable {
/// ```
///
/// - Parameters:
/// - since: The set of heads at the point in the documents history to compare to.
/// - lhs: The set of heads at the point in the documents history to compare to.
/// - Returns: The difference needed to produce current document given an arbitrary
/// point in the history.
public func difference(since lhs: Set<ChangeHash>) -> [Patch] {
Expand All @@ -1116,7 +1133,7 @@ public final class Document: @unchecked Sendable {
/// ```
///
/// - Parameters:
/// - to: The set of heads at ending point in the documents history.
/// - rhs: The set of heads at ending point in the documents history.
/// - Returns: The difference needed to move current document to a previous point
/// in the history.
public func difference(to rhs: Set<ChangeHash>) -> [Patch] {
Expand Down Expand Up @@ -1186,7 +1203,7 @@ public final class Document: @unchecked Sendable {
/// ``encodeNewChanges()``, ``encodeChangesSince(heads:)`` or any
/// concatenation of those.
public func applyEncodedChangesWithPatches(encoded: Data) throws -> [Patch] {
return try lock {
try lock {
sendObjectWillChange()
defer { sendObjectDidChange() }
let patches = try self.doc.wrapErrors {
Expand Down Expand Up @@ -1253,6 +1270,7 @@ extension Document: ObservableObject {
// #endif
objectWillChange.send()
}

fileprivate func sendObjectDidChange() {
objectDidChange.send()
}
Expand Down
2 changes: 1 addition & 1 deletion Tests/AutomergeTests/TestChanges.swift
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ class ChangeSetTests: XCTestCase {
try doc.merge(other: doc2)
try doc.merge(other: doc3)

let rawHashes = (0..<500).map { _ in doc.heads().raw() }
let rawHashes = (0 ..< 500).map { _ in doc.heads().raw() }

XCTAssertEqual(Set(rawHashes).count, 1)
}
Expand Down
4 changes: 2 additions & 2 deletions Tests/AutomergeTests/TestMarks.swift
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ class MarksTestCase: XCTestCase {

XCTAssertEqual(marks, [
Mark(start: 2, end: 2, name: "bold", value: .Boolean(true)),
Mark(start: 2, end: 2, name: "italic", value: .Boolean(true))
Mark(start: 2, end: 2, name: "italic", value: .Boolean(true)),
])
}

Expand All @@ -93,7 +93,7 @@ class MarksTestCase: XCTestCase {

XCTAssertEqual(marks, [
Mark(start: 2, end: 2, name: "bold", value: .Boolean(true)),
Mark(start: 2, end: 2, name: "italic", value: .Boolean(true))
Mark(start: 2, end: 2, name: "italic", value: .Boolean(true)),
])
}
}
2 changes: 1 addition & 1 deletion Tests/AutomergeTests/TestObservableDocument.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class ObservableDocumentTestCase: XCTestCase {
stashedHeads = doc.heads()
}
XCTAssertNotNil(willChangeHandle)

let didChangeHandle = doc.objectDidChange.sink {
_ = doc.heads()
}
Expand Down

0 comments on commit 410b1d1

Please sign in to comment.