Skip to content

Instantly share code, notes, and snippets.

@3v1n0
Last active December 9, 2020 09:22
Show Gist options
  • Save 3v1n0/f5f05005811669d81155abe0826477c8 to your computer and use it in GitHub Desktop.
Save 3v1n0/f5f05005811669d81155abe0826477c8 to your computer and use it in GitHub Desktop.
Make VSCode default key-bindings more HJLK friendly using Alt to alternate to them
#/usr/bin/python3
import json
import sys
import os
import re
import copy
input = sys.argv[1]
if not input or not os.path.exists(input):
print('No such file', input)
sys.exit(1)
ALLOWED_MODIFIERS = ['shift', 'meta']
ALLOWED_MODIFIERS_PER_KEY = {
'left': ['ctrl+shift'],
'right': ['ctrl+shift'],
'up': ['ctrl+shift'],
'down': ['ctrl+shift'],
'home': ['ctrl', 'ctrl+shift'],
'end': ['ctrl', 'ctrl+shift'],
'f3': ['ctrl', 'ctrl+shift'],
'z': ['ctrl', 'ctrl+shift'],
'x': ['ctrl'],
'c': ['ctrl'],
'v': ['ctrl'],
'f': ['ctrl'],
}
MODIFIER_TRANSLATIONS = {
'left': { 'ctrl+shift': 'shift' },
'right': { 'ctrl+shift': 'shift' },
'pagedown': { None: 'ctrl' },
'pageup': { None: 'ctrl' },
'z': { 'ctrl': None, 'ctrl+shift': None },
'x': { 'ctrl': None },
'c': { 'ctrl': None },
'v': { 'ctrl': None },
'f': { 'ctrl': None },
}
MODIFIER_KEY_TRANSLATIONS = {
'up': { None: 'k', 'ctrl': None },
'down': { None: 'j', 'ctrl': None },
'left': { None: 'h', 'ctrl': 'b', 'ctrl+shift': 'b' },
'right': { None: 'l', 'ctrl': 'e', 'ctrl+shift': 'e' },
'pageup': { None: 'b', 'shift': 'u', 'ctrl': None, 'ctrl+shift': None },
'pagedown': { None: 'f', 'ctrl': None, 'ctrl+shift': None },
'home': { None: '1' },
'end': { None: ['0', '4'] }, # 4 matches '$' in most layouts
'f3': { None: 'n' },
'z': { None: 'u', 'ctrl+shift': ['ctrl+r', 'shift+r'] },
'x': { 'ctrl': 'd' },
'c': { 'ctrl': 'y' },
'v': { 'ctrl': 'p' },
'f': { 'ctrl': '[Slash]' },
}
SCAN_CODE_TRANSLATIONS = {
'[ArrowLeft]': 'left',
'[ArrowUp]': 'arrow',
'[ArrowRight]': 'right',
'[ArrowDown]': 'down',
'[PageUp]': 'pageup',
'[PageDown]': 'pagedown',
'[End]': 'end',
'[Home]': 'home',
}
HADCODED_EXTRAS = [
# Add support for Alt+Shift+{w,e,b} to move by white-spaces
{ 'key': 'alt+j', 'command': 'workbench.action.quickOpenSelectNext', 'when': 'inQuickOpen' },
{ 'key': 'alt+k', 'command': 'workbench.action.quickOpenSelectPrevious', 'when': 'inQuickOpen' },
{ 'key': 'alt+w', 'command': 'cursorWordStartRight', 'when': 'textInputFocus' },
{ 'key': 'alt+shift+w', 'command': 'cursorWordStartRightSelect', 'when': 'textInputFocus' },
# FIXME: Use 'by': 'halfPage' once we have it
{ 'key': 'alt+u', 'variants': ['ctrl'], 'command': 'cursorMove', 'args': {'to': 'up', 'by': 'line', 'value': 20 }, 'when': 'textInputFocus' },
{ 'key': 'alt+u', 'variants': ['shift'], 'command': 'cursorMove', 'args': {'to': 'up', 'by': 'line', 'value': 20, 'select': True }, 'when': 'textInputFocus' },
{ 'key': 'alt+d', 'variants': ['ctrl'], 'command': 'cursorMove', 'args': {'to': 'down', 'by': 'line', 'value': 20 }, 'when': 'textInputFocus' },
{ 'key': 'alt+d', 'variants': ['shift'], 'command': 'cursorMove', 'args': {'to': 'down', 'by': 'line', 'value': 20, 'select': True }, 'when': 'textInputFocus' },
{ 'key': 'alt+y', 'variants': ['ctrl', 'shift'], 'command': 'scrollLineUp', 'when': 'textInputFocus' },
{ 'key': 'alt+e', 'variants': ['ctrl'], 'command': 'scrollLineDown', 'when': 'textInputFocus' },
{ 'key': 'alt+i', 'variants': ['ctrl', 'shift'], 'command': 'workbench.action.navigateToLastEditLocation', 'when': 'textInputFocus' },
{ 'key': 'alt+o', 'variants': ['ctrl', 'shift'], 'command': 'workbench.action.navigateForward', 'when': 'textInputFocus' },
{ 'key': 'alt+g alt+g', 'command': 'cursorTop', 'when': 'textInputFocus' },
{ 'key': 'alt+g alt+shift+g', 'command': 'cursorTopSelect', 'when': 'textInputFocus' },
{ 'key': 'alt+shift+g', 'command': 'cursorBottom', 'when': 'textInputFocus' },
{ 'key': 'alt+ctrl+shift+g', 'command': 'cursorBottomSelect', 'when': 'textInputFocus' },
# FIXME: use [Percent] once defined
{ 'key': 'alt+shift+5', 'command': 'editor.action.jumpToBracket', 'when': 'textInputFocus' },
]
def arrow_to_hjkl(arrow, modifier=None):
arrow = SCAN_CODE_TRANSLATIONS.get(arrow, arrow)
if arrow in MODIFIER_KEY_TRANSLATIONS.keys():
key_translations = MODIFIER_KEY_TRANSLATIONS.get(arrow, {})
return key_translations.get(modifier, key_translations.get(None))
print('// These keybindings are auto-generated with',sys.argv[0])
with open(input) as f:
input_str = re.sub(r'\\\n', '', f.read())
input_str = re.sub(r'//.*\n', '\n', input_str)
data = json.loads(input_str)
for binding in data:
translations = '|'.join(MODIFIER_KEY_TRANSLATIONS.keys())
match = re.match('^((shift|meta|ctrl|ctrl\+shift)\+)?({})'.format(translations),
binding['key'])
if not match:
continue
# print (binding)
modifier = match.group(2)
key = match.group(3)
new_keys = arrow_to_hjkl(key, modifier)
if not new_keys:
continue
if not isinstance(new_keys, list):
new_keys = [new_keys]
for new_key in new_keys:
new_binding = copy.copy(binding)
new_binding['key'] = 'alt+'
if not modifier or modifier in ALLOWED_MODIFIERS or modifier in ALLOWED_MODIFIERS_PER_KEY.get(key, []):
if key in MODIFIER_TRANSLATIONS.keys():
modifier = MODIFIER_TRANSLATIONS.get(key, {}).get(modifier, modifier)
if modifier:
new_binding['key'] += '{}+'.format(modifier)
new_binding['key'] += '{}'.format(new_key)
if new_binding['key'] != binding['key']:
print('{},'.format(json.dumps(new_binding)))
# else:
# print('NOTCHANGED: {},'.format(json.dumps(new_binding)))
print('// Hardcoded keybindings')
for binding in HADCODED_EXTRAS:
variants = binding.get('variants', None)
if variants:
binding = copy.copy(binding)
del binding['variants']
for variant in variants:
variant_binding = copy.copy(binding)
variant_binding['key'] = '{}+{}'.format(variant, binding['key'])
print('{},'.format(json.dumps(variant_binding)))
else:
print('{},'.format(json.dumps(binding)))
print('// End of generated keybindings')
@vchirikov
Copy link

Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment