GokuRakuJoudo icon indicating copy to clipboard operation
GokuRakuJoudo copied to clipboard

Create Karabiner JSON -> Goku converter

Open nikitavoloboev opened this issue 5 years ago • 10 comments

Would be useful for people trying to switch over from painstakingly writing Karabiner JSON to Goku.

Not a huge issue but can be helpful and there was a person in Telegram Karabiner group asking for something like it.

nikitavoloboev avatar Jun 07 '19 10:06 nikitavoloboev

I would still be very interested in this!

classicrob avatar Feb 14 '20 19:02 classicrob

I need this feature.

llity avatar Feb 23 '20 07:02 llity

I need this feature 👍

misha-tgshv avatar Mar 20 '20 05:03 misha-tgshv

The original intention of this project is to build a DSL to ease the frustration of jumping between multiple large blocks of JSON code while tweaking my karabiner config. A JSON->goku converter will indeed ease the onboarding process users still need to learn the syntax to really get the benefits from it. And it's really hard to convert the raw JSON config to goku syntax edn code.

We need to guess the user's intention from all rules in JSON let alone there will be rules that just contain a typo or get overwritten by other rules.

If you just want to import your JSON config into the edn file. You can find some json->edn converter and put all your rules in edn file like this.

I think a gui configuration tool like the tmk keymap editor is a better onboarding solution. But I don't think I'll really use it if there's one. It's really easy to manipulate texts in an editor than open a webpage and click on it.

I don't plan to implement this feature. I'll add a help wanted label here. If there's anyone interested in contributing or just write something up like walking through of all those logics in converting JSON to goku. I'm happy to discuss it here and implement it in goku.

yqrashawn avatar Mar 29 '20 13:03 yqrashawn

The tool looks really cool, I'm interested to have a go at this, but I' m one of those people who hand-cranked a json file over many painstaking hours over the years, there's just too much in it for me to switch in one move, since i would have to set aside a considerable amount of time to write it once i have go tto grips with the syntax, and then there is the inevitable quirks of things breaking and needing to be tweaked/fixed. I have enough problems getting my work done without getting sidetracked with new ideas for hacking my mac without adding in a wholesale migration project for ... unknown benefits, but on the face of it, exactly the same as what i have now. Same goes for drawing it up in a UI ( i'll take the JSON thanks)

The problem I have with the GokuRaku tool is - the first thing it wants tto do is overwrite my karabiner.json and change my keys. That's fine if you are starting from a blank slate but i cannot be the only one who is not. Why not allow them to be generated as complex modifications so that i can import them in to my existing config, and i can switch incrementally over time

tigger04 avatar Nov 14 '20 04:11 tigger04

The problem I have with the GokuRaku tool is - the first thing it wants tto do is overwrite my karabiner.json and change my keys. That's fine if you are starting from a blank slate but i cannot be the only one who is not. Why not allow them to be generated as complex modifications so that i can import them in to my existing config, and i can switch incrementally over time

Goku only changes the specified profile. You can specify a separate profile and migrate to it gradually. Also you can use the --dry-run option to test before goku really changes your karainer.json file.

yqrashawn avatar Nov 15 '20 01:11 yqrashawn

I use 4 complex modifications in karabiner, and I am totally lost in trying to migrate them into an EDN version of this.

This is the list of the complex modifications I use:

  • Change fn-key to command+control+option+shift.
  • Post left_ctrl when return_or_enter is hod.
  • Post escape if caps is pressed alone, left_ctrl otherwise
  • Emacs key bindings [control+keys] (rev 10)

Does anyone know a project that deals with migrating the "complex modifications" to edn format?

pragmat1c1 avatar Feb 14 '21 18:02 pragmat1c1

@pragmat1c1 commented on Feb 15, 2021, 2:02 AM GMT+8:

I use 4 complex modifications in karabiner, and I am totally lost in trying to migrate them into an EDN version of this.

This is the list of the complex modifications I use:

  • Change fn-key to command+control+option+shift.
  • Post left_ctrl when return_or_enter is hod.
  • Post escape if caps is pressed alone, left_ctrl otherwise
  • Emacs key bindings [control+keys] (rev 10)

Does anyone know a project that deals with migrating the "complex modifications" to edn format?

;; 1 
;; this one may not work
;; found this at https://github.com/pqrs-org/KE-complex_modifications/issues/149#issuecomment-337031035
{:des   "fn to hyper"
 :rules [[:##fn :!CSOleft_control nil {:alone :fn}]]}
;; 2
{:des   "return_or_enter to control when pressed with other keys"
              :rules [[:##return_or_enter :left_control nil {:alone :return_or_enter}]]}
;; 3
{:des   "caps_lock"
              :rules [[:##caps_lock :left_control nil {:alone :escape}]]}

4 You can check the emacs one I use here. Don't forget to define the :emacs-mode-disable-app and Emacs applications. See here and here

yqrashawn avatar Feb 20 '21 05:02 yqrashawn

@pragmat1c1 commented on Feb 15, 2021, 2:02 AM GMT+8:

I use 4 complex modifications in karabiner, and I am totally lost in trying to migrate them into an EDN version of this. This is the list of the complex modifications I use:

  • Change fn-key to command+control+option+shift.
  • Post left_ctrl when return_or_enter is hod.
  • Post escape if caps is pressed alone, left_ctrl otherwise
  • Emacs key bindings [control+keys] (rev 10)

Does anyone know a project that deals with migrating the "complex modifications" to edn format?

;; 1 
;; this one may not work
;; found this at https://github.com/pqrs-org/KE-complex_modifications/issues/149#issuecomment-337031035
{:des   "fn to hyper"
 :rules [[:##fn :!CSOleft_control nil {:alone :fn}]]}
;; 2
{:des   "return_or_enter to control when pressed with other keys"
              :rules [[:##return_or_enter :left_control nil {:alone :return_or_enter}]]}
;; 3
{:des   "caps_lock"
              :rules [[:##caps_lock :left_control nil {:alone :escape}]]}

4 You can check the emacs one I use here. Don't forget to define the :emacs-mode-disable-app and Emacs applications. See here and here

Wow! Thank you very much! This is very helpful. Now I finally can migrate to Goku/edn files. :)

pragmat1c1 avatar Feb 20 '21 17:02 pragmat1c1

This only partially works. It doesn't handle variables or applications, for example. And you will definitely have to edit the result it produces. For example, the result is not formatted/indented in any way. But if you know how to program and have a long karabiner.json, this might be useful to produce a good starting point, since anyway you only have to migrate JSON → EDN one time.

#!/usr/bin/env python3

# Usage: json_to_edn.py
# Expects to find ~/.config/karabiner/karabiner.json
# Prints its results to STDOUT

import json
import os

# Apple seems to use ⌃⌥⇧⌘ as the order.
# https://support.apple.com/en-ca/HT201236
SORTED_MODS = [
    'control',
    'left_control',
    'right_control',
    'option',
    'left_option',
    'right_option',
    'alt',
    'left_alt',
    'right_alt',
    'shift',
    'left_shift',
    'right_shift',
    'command',
    'left_command',
    'right_command',
]

LONG_TO_SHORT_MOD = {
    'control':       'T',
    'left_control':  'T',
    'right_control': 'W',
    'option':        'O',
    'left_option':   'O',
    'right_option':  'E',
    'alt':           'O',
    'left_alt':      'O',
    'right_alt':     'E',
    'shift':         'S',
    'left_shift':    'S',
    'right_shift':   'R',
    'command':       'C',
    'left_command':  'C',
    'right_command': 'Q',
}

def short_mods(mods):
    """Return canonically-in-order mods, converted to short form.

    Args:
        mods: "left_command", etc.
    """
    if 'any' in mods:
        return '#'
    key = {mod: index for index, mod in enumerate(SORTED_MODS)}
    for mod in mods:
        if mod not in SORTED_MODS:
            raise ValueError(f'mod {mod} not found in SORTED_MODS')
    return ''.join(LONG_TO_SHORT_MOD[m] for m in sorted(mods, key=lambda m: key[m]))

def get_key_description(f):
    result = ':'
    if 'modifiers' in f and f['modifiers']:
        if 'mandatory' in f['modifiers'] and f['modifiers']['mandatory']:
            result = f'{result}!{short_mods(f["modifiers"]["mandatory"])}'
        if 'optional' in f['modifiers'] and f['modifiers']['optional']:
            result = f'{result}#{short_mods(f["modifiers"]["optional"])}'
        if type(f['modifiers']) is list:
            result = f'{result}!{short_mods(f["modifiers"])}'
    result = f'{result}{f["key_code"]}'
    return result

def get_to_description(t):
    if 'key_code' in t:
        return get_key_description(t)
    elif 'set_variable' in t:
        sv = t['set_variable']
        return f'["{sv["name"]}" {sv["value"]}]'
    else:
        raise NotImplementedError

def print_root(blob):
    print('{')
    print_applications(blob)
    print_main(blob)
    print('}')

def print_applications(blob):
    print(':applications')
    print('{')
    print('}')

def print_main(blob):
    print(':main')
    print('[')
    for d in blob['profiles'][0]['complex_modifications']['rules']:
        print_main_block(d)
    print(']')

def print_main_block(d):
    print('{')
    print(':des')
    print(f'"{d["description"]}"')
    print(':rules')
    print('[')
    for manipulator in d['manipulators']:
        print_rule(manipulator)
    print(']')
    print('}')

def print_rule(d):
    print('[')
    print(get_key_description(d['from']))
    if len(d['to']) == 1:
        print(get_to_description(d['to'][0]))
    else:
        print('[')
        for t in d['to']:
            print(get_to_description(t))
        print(']')
    print(']')

with open(str(os.getenv('HOME')) + '/.config/karabiner/karabiner.json') as f:
    print_root(json.load(f))

andmis avatar Apr 12 '22 15:04 andmis