1
1
mirror of https://github.com/qvacua/vimr.git synced 2024-12-26 23:36:08 +03:00
This commit is contained in:
Tae Won Ha 2020-12-10 21:54:59 +01:00
parent b31a0e40e1
commit cd798dd2fb
No known key found for this signature in database
GPG Key ID: E40743465B5B8B44

View File

@ -4,24 +4,22 @@
*/ */
import Cocoa import Cocoa
import RxSwift
import MessagePack import MessagePack
import RxSwift
extension NvimView { public extension NvimView {
override func keyDown(with event: NSEvent) {
override public func keyDown(with event: NSEvent) {
self.keyDownDone = false self.keyDownDone = false
NSCursor.setHiddenUntilMouseMoves(true) NSCursor.setHiddenUntilMouseMoves(true)
let modifierFlags = event.modifierFlags let modifierFlags = event.modifierFlags
let isMeta = (self.isLeftOptionMeta && modifierFlags.contains(.leftOption)) let isMeta = (self.isLeftOptionMeta && modifierFlags.contains(.leftOption))
|| (self.isRightOptionMeta && modifierFlags.contains(.rightOption)) || (self.isRightOptionMeta && modifierFlags.contains(.rightOption))
if !isMeta { if !isMeta {
let cocoaHandledEvent let cocoaHandledEvent = NSTextInputContext.current?.handleEvent(event) ?? false
= NSTextInputContext.current?.handleEvent(event) ?? false if self.keyDownDone, cocoaHandledEvent { return }
if self.keyDownDone && cocoaHandledEvent { return }
} }
let capslock = modifierFlags.contains(.capsLock) let capslock = modifierFlags.contains(.capsLock)
@ -46,41 +44,27 @@ extension NvimView {
self.keyDownDone = true self.keyDownDone = true
} }
public func insertText(_ object: Any, replacementRange: NSRange) { func insertText(_ object: Any, replacementRange: NSRange) {
self.log.debug("\(object) with \(replacementRange)") self.log.debug("\(object) with \(replacementRange)")
let text: String let text: String
switch object { switch object {
case let string as String: text = string
case let string as String: case let attributedString as NSAttributedString: text = attributedString.string
text = string default: return
case let attributedString as NSAttributedString:
text = attributedString.string
default:
return
} }
let length = self.markedText?.count ?? 0 let length = self.markedText?.count ?? 0
try? self.bridge try? self.bridge
.deleteCharacters( .deleteCharacters(length, andInputEscapedString: self.vimPlainString(text))
length,
andInputEscapedString: self.vimPlainString(text)
)
.wait() .wait()
if length > 0 { if length > 0 {
self.ugrid.unmarkCell(at: self.markedPosition) self.ugrid.unmarkCell(at: self.markedPosition)
self.markForRender(position: self.markedPosition) self.markForRender(position: self.markedPosition)
if self.ugrid.isNextCellEmpty(self.markedPosition) { if self.ugrid.isNextCellEmpty(self.markedPosition) {
self.ugrid.unmarkCell( self.ugrid.unmarkCell(at: self.markedPosition.advancing(row: 0, column: 1))
at: self.markedPosition.advancing(row: 0, column: 1) self.markForRender(position: self.markedPosition.advancing(row: 0, column: 1))
)
self.markForRender(
position: self.markedPosition.advancing(row: 0, column: 1)
)
} }
} }
@ -90,7 +74,7 @@ extension NvimView {
self.keyDownDone = true self.keyDownDone = true
} }
public override func doCommand(by aSelector: Selector) { override func doCommand(by aSelector: Selector) {
if self.responds(to: aSelector) { if self.responds(to: aSelector) {
self.log.debug("calling \(aSelector)") self.log.debug("calling \(aSelector)")
self.perform(aSelector, with: self) self.perform(aSelector, with: self)
@ -99,40 +83,35 @@ extension NvimView {
return return
} }
self.log.debug("\(aSelector) not implemented, " + self.log.debug("\(aSelector) not implemented, forwarding input to neovim")
"forwarding input to neovim")
self.keyDownDone = false self.keyDownDone = false
} }
override public func performKeyEquivalent(with event: NSEvent) -> Bool { override func performKeyEquivalent(with event: NSEvent) -> Bool {
if .keyDown != event.type { return false } if event.type != .keyDown { return false }
let flags = event.modifierFlags.intersection(.deviceIndependentFlagsMask) let flags = event.modifierFlags.intersection(.deviceIndependentFlagsMask)
// <C-Tab> & <C-S-Tab> do not trigger keyDown events. // <C-Tab> & <C-S-Tab> do not trigger keyDown events.
// Catch the key event here and pass it to keyDown. // Catch the key event here and pass it to keyDown.
// By rogual in NeoVim dot app: // By rogual in NeoVim dot app:
// https://github.com/rogual/neovim-dot-app/pull/248/files // https://github.com/rogual/neovim-dot-app/pull/248/files
if flags.contains(.control) && 48 == event.keyCode { if flags.contains(.control), event.keyCode == 48 {
self.keyDown(with: event) self.keyDown(with: event)
return true return true
} }
// Emoji menu: Cmd-Ctrl-Space // Emoji menu: Cmd-Ctrl-Space
if flags.contains([.command, .control]) && 49 == event.keyCode { if flags.contains([.command, .control]), event.keyCode == 49 { return false }
return false
}
// Space key (especially in combination with modifiers) can result in // Space key (especially in combination with modifiers) can result in
// unexpected chars (e.g. ctrl-space = \0), so catch the event early and // unexpected chars (e.g. ctrl-space = \0), so catch the event early and
// pass it to keyDown. // pass it to keyDown.
if 49 == event.keyCode { if event.keyCode == 49 {
self.keyDown(with: event) self.keyDown(with: event)
return true return true
} }
guard let chars = event.characters else { guard let chars = event.characters else { return false }
return false;
}
// Control code \0 causes rpc parsing problems. // Control code \0 causes rpc parsing problems.
// So we escape as early as possible // So we escape as early as possible
@ -149,7 +128,7 @@ extension NvimView {
// For the following two conditions: // For the following two conditions:
// See special cases in vim/os_win32.c from vim sources // See special cases in vim/os_win32.c from vim sources
// Also mentioned in MacVim's KeyBindings.plist // Also mentioned in MacVim's KeyBindings.plist
if .control == flags && chars == "6" { if flags == .control, chars == "6" {
self.api self.api
.input(keys: "\u{1e}", errWhenBlocked: false) // AKA ^^ .input(keys: "\u{1e}", errWhenBlocked: false) // AKA ^^
.subscribe(onError: { [weak self] error in .subscribe(onError: { [weak self] error in
@ -159,7 +138,7 @@ extension NvimView {
return true return true
} }
if .control == flags && chars == "2" { if flags == .control, chars == "2" {
// <C-2> should generate \0, escaping as above // <C-2> should generate \0, escaping as above
self.api self.api
.input(keys: self.wrapNamedKeys("Nul"), errWhenBlocked: false) .input(keys: self.wrapNamedKeys("Nul"), errWhenBlocked: false)
@ -175,49 +154,37 @@ extension NvimView {
return false return false
} }
public func setMarkedText( func setMarkedText(_ object: Any, selectedRange: NSRange, replacementRange: NSRange) {
_ object: Any, self.log.debug(
selectedRange: NSRange, "object: \(object), selectedRange: \(selectedRange), replacementRange: \(replacementRange)"
replacementRange: NSRange )
) {
self.log.debug("object: \(object), selectedRange: \(selectedRange), " +
"replacementRange: \(replacementRange)")
defer { self.keyDownDone = true } defer { self.keyDownDone = true }
if self.markedText == nil { if self.markedText == nil { self.markedPosition = self.ugrid.cursorPosition }
self.markedPosition = self.ugrid.cursorPosition
}
let oldMarkedTextLength = self.markedText?.count ?? 0 let oldMarkedTextLength = self.markedText?.count ?? 0
switch object { switch object {
case let string as String: case let string as String: self.markedText = string
self.markedText = string case let attributedString as NSAttributedString: self.markedText = attributedString.string
case let attributedString as NSAttributedString: default: self.markedText = String(describing: object) // should not occur
self.markedText = attributedString.string
default:
self.markedText = String(describing: object) // should not occur
} }
if replacementRange != .notFound { if replacementRange != .notFound {
guard let newMarkedPosition = self.ugrid.firstPosition( guard let newMarkedPosition = self.ugrid.firstPosition(
fromFlatCharIndex: replacementRange.location fromFlatCharIndex: replacementRange.location
) else { ) else { return }
return
}
self.markedPosition = newMarkedPosition self.markedPosition = newMarkedPosition
self.log.debug("Deleting \(replacementRange.length) " + self.log.debug("Deleting \(replacementRange.length) and inputting \(self.markedText!)")
"and inputting \(self.markedText!)")
try? self.bridge.deleteCharacters( try? self.bridge.deleteCharacters(
replacementRange.length, replacementRange.length,
andInputEscapedString: self.vimPlainString(self.markedText!) andInputEscapedString: self.vimPlainString(self.markedText!)
).wait() ).wait()
} else { } else {
self.log.debug("Deleting \(oldMarkedTextLength) " + self.log.debug("Deleting \(oldMarkedTextLength) and inputting \(self.markedText!)")
"and inputting \(self.markedText!)")
try? self.bridge.deleteCharacters( try? self.bridge.deleteCharacters(
oldMarkedTextLength, oldMarkedTextLength,
andInputEscapedString: self.vimPlainString(self.markedText!) andInputEscapedString: self.vimPlainString(self.markedText!)
@ -227,7 +194,7 @@ extension NvimView {
self.keyDownDone = true self.keyDownDone = true
} }
public func unmarkText() { func unmarkText() {
let position = self.markedPosition let position = self.markedPosition
self.ugrid.unmarkCell(at: position) self.ugrid.unmarkCell(at: position)
self.markForRender(position: position) self.markForRender(position: position)
@ -247,7 +214,7 @@ extension NvimView {
Emoji-popup at the rect by firstRectForCharacterRange(actualRange:) where Emoji-popup at the rect by firstRectForCharacterRange(actualRange:) where
the first range is the result of this method. the first range is the result of this method.
*/ */
public func selectedRange() -> NSRange { func selectedRange() -> NSRange {
// When the app starts and the Hangul input method is selected, // When the app starts and the Hangul input method is selected,
// this method gets called very early... // this method gets called very early...
guard self.ugrid.hasData else { guard self.ugrid.hasData else {
@ -257,9 +224,7 @@ extension NvimView {
let result: NSRange let result: NSRange
result = NSRange( result = NSRange(
location: self.ugrid.flatCharIndex( location: self.ugrid.flatCharIndex(forPosition: self.ugrid.cursorPosition),
forPosition: self.ugrid.cursorPosition
),
length: 0 length: 0
) )
@ -267,7 +232,7 @@ extension NvimView {
return result return result
} }
public func markedRange() -> NSRange { func markedRange() -> NSRange {
guard let marked = self.markedText else { guard let marked = self.markedText else {
self.log.debug("No marked text, returning not found") self.log.debug("No marked text, returning not found")
return .notFound return .notFound
@ -282,76 +247,56 @@ extension NvimView {
return result return result
} }
public func hasMarkedText() -> Bool { func hasMarkedText() -> Bool { self.markedText != nil }
return self.markedText != nil
}
public func attributedSubstring( func attributedSubstring(
forProposedRange aRange: NSRange, forProposedRange aRange: NSRange,
actualRange: NSRangePointer? actualRange _: NSRangePointer?
) -> NSAttributedString? { ) -> NSAttributedString? {
self.log.debug("\(aRange)") self.log.debug("\(aRange)")
if aRange.location == NSNotFound { if aRange.location == NSNotFound { return nil }
return nil
}
guard guard
let position = self.ugrid.firstPosition( let position = self.ugrid.firstPosition(fromFlatCharIndex: aRange.location),
fromFlatCharIndex: aRange.location
),
let inclusiveEndPosition = self.ugrid.lastPosition( let inclusiveEndPosition = self.ugrid.lastPosition(
fromFlatCharIndex: aRange.location + aRange.length - 1 fromFlatCharIndex: aRange.location + aRange.length - 1
) )
else { else { return nil }
return nil
}
self.log.debug("\(position) ... \(inclusiveEndPosition)") self.log.debug("\(position) ... \(inclusiveEndPosition)")
let string = self.ugrid.cells[position.row...inclusiveEndPosition.row] let string = self.ugrid.cells[position.row...inclusiveEndPosition.row]
.map { row in .map { row in
row.filter { cell in row.filter { cell in
return aRange.location <= cell.flatCharIndex aRange.location <= cell.flatCharIndex && cell.flatCharIndex <= aRange.inclusiveEndIndex
&& cell.flatCharIndex <= aRange.inclusiveEndIndex
} }
} }
.flatMap { $0 } .flatMap { $0 }
.map { $0.string } .map(\.string)
.joined() .joined()
let delta = aRange.length - string.utf16.count let delta = aRange.length - string.utf16.count
if delta != 0 { if delta != 0 { self.log.debug("delta = \(delta)!") }
self.log.debug("delta = \(delta)!")
}
self.log.debug("returning '\(string)'") self.log.debug("returning '\(string)'")
return NSAttributedString(string: string) return NSAttributedString(string: string)
} }
public func validAttributesForMarkedText() -> [NSAttributedString.Key] { func validAttributesForMarkedText() -> [NSAttributedString.Key] { [] }
return []
}
public func firstRect( func firstRect(forCharacterRange aRange: NSRange, actualRange _: NSRangePointer?) -> NSRect {
forCharacterRange aRange: NSRange, actualRange: NSRangePointer? guard let position = self.ugrid.firstPosition(fromFlatCharIndex: aRange.location) else {
) -> NSRect {
guard let position = self.ugrid.firstPosition(
fromFlatCharIndex: aRange.location
) else {
return CGRect.zero return CGRect.zero
} }
self.log.debug("\(aRange)-> \(position.row):\(position.column)") self.log.debug("\(aRange)-> \(position.row):\(position.column)")
let resultInSelf = self.rect(forRow: position.row, column: position.column) let resultInSelf = self.rect(forRow: position.row, column: position.column)
let result = self.window?.convertToScreen( let result = self.window?.convertToScreen(self.convert(resultInSelf, to: nil))
self.convert(resultInSelf, to: nil)
)
return result! return result!
} }
public func characterIndex(for aPoint: NSPoint) -> Int { func characterIndex(for aPoint: NSPoint) -> Int {
let position = self.position(at: aPoint) let position = self.position(at: aPoint)
let result = self.ugrid.flatCharIndex(forPosition: position) let result = self.ugrid.flatCharIndex(forPosition: position)
@ -360,7 +305,7 @@ extension NvimView {
return result return result
} }
func vimModifierFlags(_ modifierFlags: NSEvent.ModifierFlags) -> String? { internal func vimModifierFlags(_ modifierFlags: NSEvent.ModifierFlags) -> String? {
var result = "" var result = ""
let control = modifierFlags.contains(.control) let control = modifierFlags.contains(.control)
@ -368,34 +313,19 @@ extension NvimView {
let command = modifierFlags.contains(.command) let command = modifierFlags.contains(.command)
let shift = modifierFlags.contains(.shift) let shift = modifierFlags.contains(.shift)
if control { if control { result += "C-" }
result += "C-" if option { result += "M-" }
} if command { result += "D-" }
if shift { result += "S-" }
if option { if result.count > 0 { return result }
result += "M-"
}
if command {
result += "D-"
}
if shift {
result += "S-"
}
if result.count > 0 {
return result
}
return nil return nil
} }
func wrapNamedKeys(_ string: String) -> String { internal func wrapNamedKeys(_ string: String) -> String { "<\(string)>" }
return "<\(string)>"
}
func vimPlainString(_ string: String) -> String { internal func vimPlainString(_ string: String) -> String {
return string.replacingOccurrences(of: "<", with: self.wrapNamedKeys("lt")) string.replacingOccurrences(of: "<", with: self.wrapNamedKeys("lt"))
} }
} }