Skip to content

Instantly share code, notes, and snippets.

@jessegrosjean
Last active December 18, 2022 16:37
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jessegrosjean/1bfc57d0251e557d3c606e43bf7fe288 to your computer and use it in GitHub Desktop.
Save jessegrosjean/1bfc57d0251e557d3c606e43bf7fe288 to your computer and use it in GitHub Desktop.
import Foundation
/// KillRing with macOS text text system semantics.
///
/// Use when implementing `yank:` and `yankAndSelect:` actions.
///
/// Learn more at http://hogbaysoftware.com/posts/mac-text-editing-mark-kill-yank/
public class KillRing {
public static let shared = KillRing()
public enum Action {
case delete
case deleteToMark
case deleteToBeginningOfLine
case deleteToEndOfLine
case deleteToBeginningOfParagraph
case deleteToEndOfParagraph
}
private var index: Int = 0
private var buffer: [String]
private var yanking: Bool = false
private var lastAction: Action?
init() {
let killRingSize = UserDefaults.standard.integer(forKey: "NSTextKillRingSize")
buffer = Array(repeating: "", count: max(killRingSize, 1))
}
/// Call when selection changes in editor
public func selectionChanged() {
lastAction = nil
}
/// Call when text changes in editor
public func textChanged() {
yanking = false
lastAction = nil
}
/// Call when text editor performs any of the defined Actions.
/// The editor should perform all selection and buffer mutations within the `killBlock`.
public func kill(action: Action, killBlock: () -> String) {
let savedLastAction = lastAction
let string = killBlock()
if action != savedLastAction {
lastAction = action
if index == buffer.count - 1 {
index = 0
} else {
index += 1
}
buffer[index] = ""
} else {
self.lastAction = savedLastAction
}
switch action {
case .delete:
buffer[index] = string
case .deleteToBeginningOfLine, .deleteToBeginningOfParagraph:
buffer[index] = string + buffer[index]
case .deleteToEndOfLine, .deleteToEndOfParagraph, .deleteToMark:
buffer[index] = buffer[index] + string
}
}
/// Call in response to `yank:` action.
/// The editor should perform all selection and buffer mutations within the `yankBlock`.
public func yank(yankBlock: (String) -> ()) {
yankBlock(buffer[index])
yanking = true
}
/// Call in response to `yankAndSelect:` action.
/// The editor should perform all selection and buffer mutations within the `yankAndSelectBlock`.
public func yankAndSelect(yankAndSelectBlock: (String) -> ()) {
if yanking {
if index == 0 {
index = buffer.count - 1
} else {
index -= 1
}
}
yankAndSelectBlock(buffer[index])
yanking = true
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment