1
1
mirror of https://github.com/qvacua/vimr.git synced 2024-12-01 18:39:52 +03:00
vimr/SwiftNeoVim/NeoVimView+Key.swift
Tae Won Ha 5d3aca696d
Refactor cursor drawing
- draw only when flushed and use the normal position from goto
2017-06-05 14:22:12 +02:00

233 lines
7.1 KiB
Swift

/**
* Tae Won Ha - http://taewon.de - @hataewon
* See LICENSE
*/
import Cocoa
extension NeoVimView {
override public func keyDown(with event: NSEvent) {
self.keyDownDone = false
let context = NSTextInputContext.current()!
let cocoaHandledEvent = context.handleEvent(event)
if self.keyDownDone && cocoaHandledEvent {
return
}
// self.logger.debug("\(#function): \(event)")
let modifierFlags = event.modifierFlags
let capslock = modifierFlags.contains(.capsLock)
let shift = modifierFlags.contains(.shift)
let chars = event.characters!
let charsIgnoringModifiers = shift || capslock
? event.charactersIgnoringModifiers!.lowercased()
: event.charactersIgnoringModifiers!
if KeyUtils.isSpecial(key: charsIgnoringModifiers) {
if let vimModifiers = self.vimModifierFlags(modifierFlags) {
self.agent.vimInput(
self.wrapNamedKeys(vimModifiers + KeyUtils.namedKeyFrom(key: charsIgnoringModifiers))
)
} else {
self.agent.vimInput(self.wrapNamedKeys(KeyUtils.namedKeyFrom(key: charsIgnoringModifiers)))
}
} else {
if let vimModifiers = self.vimModifierFlags(modifierFlags) {
self.agent.vimInput(self.wrapNamedKeys(vimModifiers + charsIgnoringModifiers))
} else {
self.agent.vimInput(self.vimPlainString(chars))
}
}
self.keyDownDone = true
}
public func insertText(_ aString: Any, replacementRange: NSRange) {
// self.logger.debug("\(#function): \(replacementRange): '\(aString)'")
switch aString {
case let string as String:
self.agent.vimInput(self.vimPlainString(string))
case let attributedString as NSAttributedString:
self.agent.vimInput(self.vimPlainString(attributedString.string))
default:
break;
}
// unmarkText()
self.lastMarkedText = self.markedText
self.markedText = nil
self.markedPosition = Position.null
self.keyDownDone = true
}
public override func doCommand(by aSelector: Selector) {
// self.logger.debug("\(#function): \(aSelector)");
// FIXME: handle when -> delete
if self.responds(to: aSelector) {
Swift.print("\(#function): calling \(aSelector)")
self.perform(aSelector, with: self)
self.keyDownDone = true
return
}
// self.logger.debug("\(#function): "\(aSelector) not implemented, forwarding input to vim")
self.keyDownDone = false
}
public func setMarkedText(_ aString: Any, selectedRange: NSRange, replacementRange: NSRange) {
if self.markedText == nil {
self.markedPosition = self.grid.position
}
// eg -> hanja popup, cf comment for self.lastMarkedText
if replacementRange.length > 0 {
self.agent.deleteCharacters(replacementRange.length)
}
switch aString {
case let string as String:
self.markedText = string
case let attributedString as NSAttributedString:
self.markedText = attributedString.string
default:
self.markedText = String(describing: aString) // should not occur
}
// self.logger.debug("\(#function): \(self.markedText), \(selectedRange), \(replacementRange)")
self.agent.vimInputMarkedText(self.markedText!)
self.keyDownDone = true
}
public func unmarkText() {
// self.logger.debug("\(#function): ")
self.markedText = nil
self.markedPosition = Position.null
self.keyDownDone = true
// TODO: necessary?
self.markForRender(row: self.grid.position.row, column: self.grid.position.column)
}
/// Return the current selection (or the position of the cursor with empty-length range).
/// For example when you enter "Cmd-Ctrl-Return" you'll get the Emoji-popup at the rect
/// by firstRectForCharacterRange(actualRange:) where the first range is the result of this method
public func selectedRange() -> NSRange {
// When the app starts and the Hangul input method is selected,
// this method gets called very early...
guard self.grid.hasData else {
// self.logger.debug("\(#function): not found")
return NSRange(location: NSNotFound, length: 0)
}
let result = NSRange(location: self.grid.singleIndexFrom(self.grid.position), length: 0)
// self.logger.debug("\(#function): \(result)")
return result
}
public func markedRange() -> NSRange {
// FIXME: do we have to handle positions at the column borders?
if let markedText = self.markedText {
let result = NSRange(location: self.grid.singleIndexFrom(self.markedPosition),
length: markedText.characters.count)
// self.logger.debug("\(#function): \(result)")
return result
}
self.logger.debug("\(#function): returning empty range")
return NSRange(location: NSNotFound, length: 0)
}
public func hasMarkedText() -> Bool {
// self.logger.debug("\(#function)")
return self.markedText != nil
}
// FIXME: take into account the "return nil"-case
// FIXME: just fix me, PLEASE...
public func attributedSubstring(forProposedRange aRange: NSRange,
actualRange: NSRangePointer?) -> NSAttributedString? {
// self.logger.debug("\(#function): \(aRange), \(actualRange[0])")
if aRange.location == NSNotFound {
// self.logger.debug("\(#function): range not found: returning nil")
return nil
}
guard let lastMarkedText = self.lastMarkedText else {
// self.logger.debug("\(#function): no last marked text: returning nil")
return nil
}
// we only support last marked text, thus fill dummy characters when Cocoa asks for more
// characters than marked...
let fillCount = aRange.length - lastMarkedText.characters.count
guard fillCount >= 0 else {
return nil
}
let fillChars = Array(0..<fillCount).reduce("") { (result, _) in return result + " " }
// self.logger.debug("\(#function): \(aRange), \(actualRange[0]): \(fillChars + lastMarkedText)")
return NSAttributedString(string: fillChars + lastMarkedText)
}
public func validAttributesForMarkedText() -> [String] {
return []
}
public func firstRect(forCharacterRange aRange: NSRange, actualRange: NSRangePointer?) -> NSRect {
let position = self.grid.positionFromSingleIndex(aRange.location)
// self.logger.debug("\(#function): \(aRange),\(actualRange[0]) -> " +
// "\(position.row):\(position.column)")
let resultInSelf = self.rect(forRow: position.row, column: position.column)
let result = self.window?.convertToScreen(self.convert(resultInSelf, to: nil))
return result!
}
public func characterIndex(for aPoint: NSPoint) -> Int {
// self.logger.debug("\(#function): \(aPoint)")
return 1
}
func vimModifierFlags(_ modifierFlags: NSEventModifierFlags) -> String? {
var result = ""
let control = modifierFlags.contains(.control)
let option = modifierFlags.contains(.option)
let command = modifierFlags.contains(.command)
let shift = modifierFlags.contains(.shift)
if control {
result += "C-"
}
if option {
result += "M-"
}
if command {
result += "D-"
}
if shift {
result += "S-"
}
if result.characters.count > 0 {
return result
}
return nil
}
}