import type { Nvim } from '@vvim/nvim' ; import { Screen } from 'src/screen' ; const specialKey = ( { key , code } ) : string => ( ( { Insert : 'Insert' , Numpad0 : 'k0' , Numpad1 : 'k1' , Numpad2 : 'k2' , Numpad3 : 'k3' , Numpad4 : 'k4' , Numpad5 : 'k5' , Numpad6 : 'k6' , Numpad7 : 'k7' , Numpad8 : 'k8' , Numpad9 : 'k9' , NumpadAdd : 'kPlus' , NumpadSubtract : 'kMinus' , NumpadMultiply : 'kMultiply' , NumpadDivide : 'kDivide' , NumpadEnter : 'kEnter' , NumpadDecimal : 'kPoint' , Escape : 'Esc' , Backspace : 'BS' , Delete : 'Del' , Enter : 'CR' , Tab : 'Tab' , ArrowUp : 'Up' , ArrowDown : 'Down' , ArrowLeft : 'Left' , ArrowRight : 'Right' , PageUp : 'PageUp' , PageDown : 'PageDown' , Home : 'Home' , End : 'End' , F1 : 'F1' , F2 : 'F2' , F3 : 'F3' , F4 : 'F4' , F5 : 'F5' , F6 : 'F6' , F7 : 'F7' , F8 : 'F8' , F9 : 'F9' , F10 : 'F10' , F11 : 'F11' , F12 : 'F12' , } as Record < string , string > ) [ code ] || ( { '<' : 'lt' , '\\' : 'Bslash' , '|' : 'Bar' , } as Record < string , string > ) [ key ] ) ; const skip = ( key ) => ( ( { Shift : true , Control : true , Alt : true , Meta : true , CapsLock : true , } as Record < string , boolean > ) [ key ] ) ; export const modifierPrefix = ( { metaKey , altKey , ctrlKey } , insertMode ? ) : string => { if ( insertMode && altKey && ! ctrlKey && ! metaKey ) { return '' ; } return `${ metaKey ? 'D-' : '' }${ altKey ? 'A-' : '' }${ ctrlKey ? 'C-' : '' }` ; } ; export const shiftPrefix = ( { shiftKey , key } ) : string => shiftKey && key !== '<' ? 'S-' : '' ; /**
 * Filter hotkeys from menu.
 * TODO: Make it customizable and make it work differently in browser and electron app.
 */ const filterResult = ( result ) => ! ( { '<D-c>' : true , '<D-v>' : true , '<D-a>' : true , '<D-=>' : true , '<D-->' : true , '<D-0>' : true , '<D-C-f>' : true , '<D-m>' : true , '<D-h>' : true , '<D-q>' : true , '<D-o>' : true , '<D-n>' : true , '<D-w>' : true , } as Record < string , boolean > ) [ result ] && result ; const replaceResult = ( result ) => ( ( { '<C-6>' : '<C-^>' , '<C-->' : '<C-_>' , '<C-2>' : '<C-@>' , } as Record < string , string > ) [ result ] || result ) ; const eventKeyCode = ( event , insertMode ? ) : string | null => { const { key } = event ; if ( skip ( key ) ) return null ; let deadKey ; if ( key === 'Dead' ) { if ( ! insertMode && event . altKey && event . code . match ( /^Key[A-Z]$/ ) ) { deadKey = event . code [ 3 ] . toLowerCase ( ) ; } else { return null ; } } const modifier = modifierPrefix ( event , insertMode ) ; const shift = shiftPrefix ( event ) ; const special = specialKey ( event ) ; const keyCode = deadKey || special || key ; const result = modifier || special ? `<${ modifier }${ shift }${ keyCode }>` : keyCode ; const filteredResult = filterResult ( result ) ; if ( ! filteredResult ) { return null ; } return replaceResult ( filteredResult ) ; } ; const initKeyboard = ( { nvim , screen } ) : void => { const { getCursorElement } = screen ; let disableNextInput = false ; let inputKey = null ; let isComposing = false ; let compositionValue = null ; let insertMode = false ; const input = document . createElement ( 'input' ) ; input . style . position = 'absolute' ; input . style . opacity = '0' ; input . style . left = '0' ; input . style . top = '0' ; input . style . width = '0' ; input . style . height = '0' ; ( getCursorElement ( ) || document . getElementsByTagName ( 'body' ) [ 0 ] ) . appendChild ( input ) ; const handleKeydown = async ( event ) => { disableNextInput = true ; if ( ! isComposing ) { inputKey = eventKeyCode ( event , insertMode ) ; if ( inputKey ) { nvim . input ( inputKey ) ; } } } ; const handleInput = ( event ) => { if ( disableNextInput || isComposing ) { disableNextInput = false ; return ; } if ( event . data ) { nvim . input ( event . data ) ; } } ; const handleCompositionStart = ( ) => { isComposing = true ; compositionValue = inputKey || '' ; } ; const handleCompositionEnd = ( ) => { isComposing = false ; } ; const handleCompositionUpdate = ( event ) => { nvim . input ( `${ '<BS>' . repeat ( compositionValue . length ) }${ event . data }` ) ; compositionValue = event . data ; } ; document . addEventListener ( 'keydown' , handleKeydown ) ; input . addEventListener ( 'input' , handleInput ) ; input . addEventListener ( 'compositionstart' , handleCompositionStart ) ; input . addEventListener ( 'compositionupdate' , handleCompositionUpdate ) ; input . addEventListener ( 'compositionend' , handleCompositionEnd ) ; nvim . on ( 'redraw' , ( args ) => { args . forEach ( ( arg ) => { if ( arg [ 0 ] === 'mode_change' ) { const [ mode ] = arg [ 1 ] ; if ( [ 'insert' , 'cmdline_normal' ] . includes ( mode ) ) { insertMode = true ; input . focus ( ) ; } else { insertMode = false ; input . blur ( ) ; } } } ) ; } ) ; } ; export default initKeyboard ;