From c8ece4b7fd5c0053a723f05c1febcf86dcc7a7c9 Mon Sep 17 00:00:00 2001 From: Khan Winter <35942988+thecoolwinter@users.noreply.github.com> Date: Mon, 16 Dec 2024 13:09:42 -0600 Subject: [PATCH 1/2] Fix CaptureModifierSet.insert, Add Tests --- .../Controller/TextViewController.swift | 2 +- .../Enums/CaptureModifier.swift | 41 -------------- .../Enums/CaptureModifierSet.swift | 51 ++++++++++++++++++ .../CaptureModifierSetTests.swift | 53 +++++++++++++++++++ 4 files changed, 105 insertions(+), 42 deletions(-) create mode 100644 Sources/CodeEditSourceEditor/Enums/CaptureModifierSet.swift create mode 100644 Tests/CodeEditSourceEditorTests/CaptureModifierSetTests.swift diff --git a/Sources/CodeEditSourceEditor/Controller/TextViewController.swift b/Sources/CodeEditSourceEditor/Controller/TextViewController.swift index cafbbc4b6..057d532b9 100644 --- a/Sources/CodeEditSourceEditor/Controller/TextViewController.swift +++ b/Sources/CodeEditSourceEditor/Controller/TextViewController.swift @@ -329,6 +329,6 @@ public class TextViewController: NSViewController { extension TextViewController: GutterViewDelegate { public func gutterViewWidthDidUpdate(newWidth: CGFloat) { gutterView?.frame.size.width = newWidth - textView?.edgeInsets = HorizontalEdgeInsets(left: newWidth, right: textViewTrailingInset) + textView?.edgeInsets = HorizontalEdgeInsets(left: newWidth, right: 0.0) } } diff --git a/Sources/CodeEditSourceEditor/Enums/CaptureModifier.swift b/Sources/CodeEditSourceEditor/Enums/CaptureModifier.swift index 34bf8653d..895f04b2e 100644 --- a/Sources/CodeEditSourceEditor/Enums/CaptureModifier.swift +++ b/Sources/CodeEditSourceEditor/Enums/CaptureModifier.swift @@ -90,44 +90,3 @@ public enum CaptureModifier: Int8, CaseIterable, Sendable { extension CaptureModifier: CustomDebugStringConvertible { public var debugDescription: String { stringValue } } - -/// A set of capture modifiers, efficiently represented by a single integer. -public struct CaptureModifierSet: OptionSet, Equatable, Hashable, Sendable { - public var rawValue: UInt - - public init(rawValue: UInt) { - self.rawValue = rawValue - } - - public static let declaration = CaptureModifierSet(rawValue: 1 << CaptureModifier.declaration.rawValue) - public static let definition = CaptureModifierSet(rawValue: 1 << CaptureModifier.definition.rawValue) - public static let readonly = CaptureModifierSet(rawValue: 1 << CaptureModifier.readonly.rawValue) - public static let `static` = CaptureModifierSet(rawValue: 1 << CaptureModifier.static.rawValue) - public static let deprecated = CaptureModifierSet(rawValue: 1 << CaptureModifier.deprecated.rawValue) - public static let abstract = CaptureModifierSet(rawValue: 1 << CaptureModifier.abstract.rawValue) - public static let async = CaptureModifierSet(rawValue: 1 << CaptureModifier.async.rawValue) - public static let modification = CaptureModifierSet(rawValue: 1 << CaptureModifier.modification.rawValue) - public static let documentation = CaptureModifierSet(rawValue: 1 << CaptureModifier.documentation.rawValue) - public static let defaultLibrary = CaptureModifierSet(rawValue: 1 << CaptureModifier.defaultLibrary.rawValue) - - /// All values in the set. - public var values: [CaptureModifier] { - var rawValue = self.rawValue - - // This set is represented by an integer, where each `1` in the binary number represents a value. - // We can interpret the index of the `1` as the raw value of a ``CaptureModifier`` (the index in 0b0100 would - // be 2). This loops through each `1` in the `rawValue`, finds the represented modifier, and 0's out the `1` so - // we can get the next one using the binary & operator (0b0110 -> 0b0100 -> 0b0000 -> finish). - var values: [Int8] = [] - while rawValue > 0 { - values.append(Int8(rawValue.trailingZeroBitCount)) - // Clears the bit at the desired index (eg: 0b110 if clearing index 0) - rawValue &= ~UInt(1 << rawValue.trailingZeroBitCount) - } - return values.compactMap({ CaptureModifier(rawValue: $0) }) - } - - public mutating func insert(_ value: CaptureModifier) { - rawValue &= 1 << value.rawValue - } -} diff --git a/Sources/CodeEditSourceEditor/Enums/CaptureModifierSet.swift b/Sources/CodeEditSourceEditor/Enums/CaptureModifierSet.swift new file mode 100644 index 000000000..5fb1dbab7 --- /dev/null +++ b/Sources/CodeEditSourceEditor/Enums/CaptureModifierSet.swift @@ -0,0 +1,51 @@ +// +// CaptureModifierSet.swift +// CodeEditSourceEditor +// +// Created by Khan Winter on 12/16/24. +// + +/// A set of capture modifiers, efficiently represented by a single integer. +public struct CaptureModifierSet: OptionSet, Equatable, Hashable, Sendable { + public var rawValue: UInt + + public init(rawValue: UInt) { + self.rawValue = rawValue + } + + public static let declaration = CaptureModifierSet(rawValue: 1 << CaptureModifier.declaration.rawValue) + public static let definition = CaptureModifierSet(rawValue: 1 << CaptureModifier.definition.rawValue) + public static let readonly = CaptureModifierSet(rawValue: 1 << CaptureModifier.readonly.rawValue) + public static let `static` = CaptureModifierSet(rawValue: 1 << CaptureModifier.static.rawValue) + public static let deprecated = CaptureModifierSet(rawValue: 1 << CaptureModifier.deprecated.rawValue) + public static let abstract = CaptureModifierSet(rawValue: 1 << CaptureModifier.abstract.rawValue) + public static let async = CaptureModifierSet(rawValue: 1 << CaptureModifier.async.rawValue) + public static let modification = CaptureModifierSet(rawValue: 1 << CaptureModifier.modification.rawValue) + public static let documentation = CaptureModifierSet(rawValue: 1 << CaptureModifier.documentation.rawValue) + public static let defaultLibrary = CaptureModifierSet(rawValue: 1 << CaptureModifier.defaultLibrary.rawValue) + + /// All values in the set. + /// + /// Results will be returned in order of ``CaptureModifier``'s raw value. + /// This variable ignores garbage values in the ``rawValue`` property. + public var values: [CaptureModifier] { + var rawValue = self.rawValue + + // This set is represented by an integer, where each `1` in the binary number represents a value. + // We can interpret the index of the `1` as the raw value of a ``CaptureModifier`` (the index in 0b0100 would + // be 2). This loops through each `1` in the `rawValue`, finds the represented modifier, and 0's out the `1` so + // we can get the next one using the binary & operator (0b0110 -> 0b0100 -> 0b0000 -> finish). + var values: [Int8] = [] + while rawValue > 0 { + values.append(Int8(rawValue.trailingZeroBitCount)) + // Clears the bit at the desired index (eg: 0b110 if clearing index 0) + rawValue &= ~UInt(1 << rawValue.trailingZeroBitCount) + } + return values.compactMap({ CaptureModifier(rawValue: $0) }) + } + + /// Inserts the modifier into the set. + public mutating func insert(_ value: CaptureModifier) { + rawValue |= 1 << value.rawValue + } +} diff --git a/Tests/CodeEditSourceEditorTests/CaptureModifierSetTests.swift b/Tests/CodeEditSourceEditorTests/CaptureModifierSetTests.swift new file mode 100644 index 000000000..57b18352d --- /dev/null +++ b/Tests/CodeEditSourceEditorTests/CaptureModifierSetTests.swift @@ -0,0 +1,53 @@ +import XCTest +@testable import CodeEditSourceEditor + +final class CaptureModifierSetTests: XCTestCase { + func test_init() { + // Init empty + let set1 = CaptureModifierSet(rawValue: 0) + XCTAssertEqual(set1, []) + XCTAssertEqual(set1.values, []) + + // Init with multiple values + let set2 = CaptureModifierSet(rawValue: 0b1101) + XCTAssertEqual(set2, [.declaration, .readonly, .static]) + XCTAssertEqual(set2.values, [.declaration, .readonly, .static]) + } + + func test_insert() { + var set = CaptureModifierSet(rawValue: 0) + XCTAssertEqual(set, []) + + // Insert one item + set.insert(.declaration) + XCTAssertEqual(set, [.declaration]) + + // Inserting again does nothing + set.insert(.declaration) + XCTAssertEqual(set, [.declaration]) + + // Insert more items + set.insert(.declaration) + set.insert(.async) + set.insert(.documentation) + XCTAssertEqual(set, [.declaration, .async, .documentation]) + + // Order doesn't matter + XCTAssertEqual(set, [.async, .declaration, .documentation]) + } + + func test_values() { + // Invalid rawValue returns non-garbage results + var set = CaptureModifierSet([.declaration, .readonly, .static]) + set.rawValue |= 1 << 48 // No real modifier with raw value 48, but we still have all the other values + + XCTAssertEqual(set.values, [.declaration, .readonly, .static]) + + set = CaptureModifierSet() + set.insert(.declaration) + set.insert(.async) + set.insert(.documentation) + XCTAssertEqual(set.values, [.declaration, .async, .documentation]) + XCTAssertNotEqual(set.values, [.declaration, .documentation, .async]) // Order matters + } +} From 13670ddd1c95b3a3846e7e02bf763bc123efae89 Mon Sep 17 00:00:00 2001 From: Khan Winter <35942988+thecoolwinter@users.noreply.github.com> Date: Mon, 16 Dec 2024 13:23:53 -0600 Subject: [PATCH 2/2] Revert Accidental Change --- .../CodeEditSourceEditor/Controller/TextViewController.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/CodeEditSourceEditor/Controller/TextViewController.swift b/Sources/CodeEditSourceEditor/Controller/TextViewController.swift index 057d532b9..cafbbc4b6 100644 --- a/Sources/CodeEditSourceEditor/Controller/TextViewController.swift +++ b/Sources/CodeEditSourceEditor/Controller/TextViewController.swift @@ -329,6 +329,6 @@ public class TextViewController: NSViewController { extension TextViewController: GutterViewDelegate { public func gutterViewWidthDidUpdate(newWidth: CGFloat) { gutterView?.frame.size.width = newWidth - textView?.edgeInsets = HorizontalEdgeInsets(left: newWidth, right: 0.0) + textView?.edgeInsets = HorizontalEdgeInsets(left: newWidth, right: textViewTrailingInset) } }