diff --git a/Sources/CodeEditTextView/TextView/TextView+Delete.swift b/Sources/CodeEditTextView/TextView/TextView+Delete.swift index e8d6ecce..2691c37f 100644 --- a/Sources/CodeEditTextView/TextView/TextView+Delete.swift +++ b/Sources/CodeEditTextView/TextView/TextView+Delete.swift @@ -60,6 +60,9 @@ extension TextView { guard extendedRange.location >= 0 else { continue } textSelection.range.formUnion(extendedRange) } + KillRing.shared.kill( + strings: selectionManager.textSelections.map(\.range).compactMap({ textStorage.substring(from: $0) }) + ) replaceCharacters(in: selectionManager.textSelections.map(\.range), with: "") unmarkTextIfNeeded() } diff --git a/Sources/CodeEditTextView/TextView/TextView+Insert.swift b/Sources/CodeEditTextView/TextView/TextView+Insert.swift index f51e891d..8c4fc408 100644 --- a/Sources/CodeEditTextView/TextView/TextView+Insert.swift +++ b/Sources/CodeEditTextView/TextView/TextView+Insert.swift @@ -15,4 +15,35 @@ extension TextView { override public func insertTab(_ sender: Any?) { insertText("\t") } + + override public func yank(_ sender: Any?) { + let strings = KillRing.shared.yank() + insertMultipleString(strings) + } + + /// Not documented or in any headers, but required if kill ring size > 1. + /// From Cocoa docs: "note that yankAndSelect: is not listed in any headers" + @objc func yankAndSelect(_ sender: Any?) { + let strings = KillRing.shared.yankAndSelect() + insertMultipleString(strings) + } + + private func insertMultipleString(_ strings: [String]) { + let selectedRanges = selectionManager.textSelections.map(\.range) + + guard selectedRanges.count > 0 else { return } + + for idx in (0.. [String] { + return buffer[index] + } + + /// Yanks an item from the ring, and selects the next one in the ring. + func yankAndSelect() -> [String] { + let retVal = buffer[index] + incrementIndex() + return retVal + } + + private func incrementIndex() { + index = (index + 1) % buffer.count + } +} diff --git a/Tests/CodeEditTextViewTests/KillRingTests.swift b/Tests/CodeEditTextViewTests/KillRingTests.swift new file mode 100644 index 00000000..ad092eb4 --- /dev/null +++ b/Tests/CodeEditTextViewTests/KillRingTests.swift @@ -0,0 +1,73 @@ +import XCTest +@testable import CodeEditTextView + +class KillRingTests: XCTestCase { + func test_killRingYank() { + var ring = KillRing.shared + ring.kill(strings: ["hello"]) + for _ in 0..<100 { + XCTAssertEqual(ring.yank(), ["hello"]) + } + + ring.kill(strings: ["hello", "multiple", "strings"]) + // should never change on yank + for _ in 0..<100 { + XCTAssertEqual(ring.yank(), ["hello", "multiple", "strings"]) + } + + ring = KillRing(2) + ring.kill(strings: ["hello"]) + for _ in 0..<100 { + XCTAssertEqual(ring.yank(), ["hello"]) + } + + ring.kill(strings: ["hello", "multiple", "strings"]) + // should never change on yank + for _ in 0..<100 { + XCTAssertEqual(ring.yank(), ["hello", "multiple", "strings"]) + } + } + + func test_killRingYankAndSelect() { + let ring = KillRing(5) + ring.kill(strings: ["1"]) + ring.kill(strings: ["2"]) + ring.kill(strings: ["3", "3", "3"]) + ring.kill(strings: ["4", "4"]) + ring.kill(strings: ["5"]) + // should loop + for _ in 0..<5 { + XCTAssertEqual(ring.yankAndSelect(), ["5"]) + XCTAssertEqual(ring.yankAndSelect(), ["1"]) + XCTAssertEqual(ring.yankAndSelect(), ["2"]) + XCTAssertEqual(ring.yankAndSelect(), ["3", "3", "3"]) + XCTAssertEqual(ring.yankAndSelect(), ["4", "4"]) + } + } + + func test_textViewYank() { + let view = TextView(string: "Hello World") + view.selectionManager.setSelectedRange(NSRange(location: 0, length: 1)) + view.delete(self) + XCTAssertEqual(view.string, "ello World") + + view.yank(self) + XCTAssertEqual(view.string, "Hello World") + view.selectionManager.setSelectedRange(NSRange(location: 0, length: 0)) + view.yank(self) + XCTAssertEqual(view.string, "HHello World") + } + + func test_textViewYankMultipleCursors() { + let view = TextView(string: "Hello World") + view.selectionManager.setSelectedRanges([NSRange(location: 1, length: 0), NSRange(location: 4, length: 0)]) + view.delete(self) + XCTAssertEqual(view.string, "elo World") + + view.yank(self) + XCTAssertEqual(view.string, "Hello World") + view.selectionManager.setSelectedRanges([NSRange(location: 0, length: 0)]) + view.yank(self) + XCTAssertEqual(view.string, "H\nlHello World") + } +}