Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: allow using ⇧symbols instead of R #216

Open
wants to merge 30 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
7138400
add socket repl profile
eugenesvk Mar 21, 2023
9f9a480
Add symbol data tables
eugenesvk Mar 23, 2023
f3598b3
Add symbol tables helper functions
eugenesvk Mar 21, 2023
4babb13
fix: key symbols order issue
eugenesvk Mar 21, 2023
958a7c6
Add no-break space to symbol data tables for config alignment
eugenesvk Mar 22, 2023
eeec8ff
fix syntax errors not being parsed
eugenesvk Mar 23, 2023
b3892d5
Add symbol helper fn to group modifiers with must/opt prefixes
eugenesvk Mar 24, 2023
207a9f4
fix syntax check hanging forever with many warnings
eugenesvk Mar 23, 2023
a4b2151
Add symbol helper fn to replace symbol keys with regular ones
eugenesvk Mar 24, 2023
f7bc2da
feat: allow using ⇧symbols instead of R
eugenesvk Mar 24, 2023
d8c90c1
Add keys-symbols test
eugenesvk Mar 24, 2023
89a7971
disable debug print
eugenesvk Mar 24, 2023
5013489
Fix symbol key replacement helpers not accepting maps
eugenesvk Mar 24, 2023
d136cbb
Add additional symbols
eugenesvk Mar 24, 2023
4a37728
ignore built html doc files
eugenesvk Mar 25, 2023
86e0082
doc: add KeySymbols detailed tables
eugenesvk Mar 25, 2023
5207f11
doc: add a reference to key symbols
eugenesvk Mar 25, 2023
dc45560
add ❖ as command's symbol
eugenesvk Mar 25, 2023
e5fe2bb
doc: add ❖ as command's symbol
eugenesvk Mar 25, 2023
5e28edd
add modifiers as (last) keys symbols map
eugenesvk Mar 25, 2023
a51d111
treat modifier symbols at the end of a key as a literal keys, not mod…
eugenesvk Mar 27, 2023
fc2485e
doc: treat modifier symbols at the end of a key as a literal keys, no…
eugenesvk Mar 25, 2023
106cf7f
test: treat modifier symbols at the end of a key as a literal keys, n…
eugenesvk Mar 27, 2023
296b53f
Add additional symbols
eugenesvk Mar 29, 2023
febc0f5
allow strings as keys
eugenesvk Mar 28, 2023
aa5e430
fix symbol keys in vectors like :sim not being converted
eugenesvk Mar 30, 2023
72a26b9
fix error trying to convert variable values
eugenesvk Apr 5, 2023
6e40e5b
fix user functions with symbols being converted
eugenesvk Apr 5, 2023
52df06f
__temp: disable exit on syntax warnings
eugenesvk Mar 23, 2023
339b3f3
add insert symbol
eugenesvk Jul 22, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,5 @@ pom.xml.asc
/.clj-kondo/.cache/
/.cpcache/
/karabiner-configurator-0.1.0-standalone.build_artifacts.txt
ReadMe.html
KeySymbols.html
91 changes: 91 additions & 0 deletions KeySymbols.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
## New modifiers symbols vs Current
|↓Label type | Key→ |Shift |Ctrl |Ctrl |Alt |Alt |Cmd |Cmd |Cmd |
|:-------------- | :- | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: |
|Any (mandatory) | | ⇧ | ⎈ | ⌃ | ⎇ | ⌥ | ⌘ | ◆ | ❖ |
|+ Left |‹ `'` | ‹⇧ | | | | | | | |
|+ Right |› `'` | ⇧› | | | | | | | |
|+ Optional |﹖ ? | ⇧﹖ | | | | | | | |
|Current Left | | S | T | T | O | O | C | C | C |
|...Right | | R | W | W | E | E | Q | Q | Q |
|...Any | | SS | TT | TT | OO | OO | CC | CC | CC |
|+ Mandatory |! | !S | | | | | | | |
|+ Optional |# | #S | | | | | | | |

Examples

|New label | Side | Must/opt | Key |Old |
|:-------------- | :- | :- | :- | :- |
|‹⇧ | left | mandatory | shift | !S |
|⎈› | right | mandatory | control | !W |
|⎇›﹖ | right | optional | alt | #E |
|⎇›⇧ | right | optional | alt | !Eshift |

You can also input keys as strings to include whitespace which is helpful to achieve vertical alignment like so:
```edn
{:des "Vertically aligned modifiers in the FROM keys, note the mandatory ‘ at the beginning" :rules [
["‘‹⎈ k" :k]
["‘ ‹⇧ k" :k]
["‘ ⇧› k" :k]
["‘ ⇧ ⎇k" :k]
["‘ ‹⌘ k" :k]
["‘ ⎇k" :k]
]}
```

__NB!__

- Left/Right side indicators __must__ be at their respective sides (‹Left, Right›)
- Optional﹖ indicator __must__ bet at the Right side and __after__ the side indicator (✓`⎈›﹖` ✗`⎈﹖›`)
- Modifiers at the end of a key definition are treated as literal keys
- Keys-as-strings __must__ have a `‘` in the beginning to differentiate them from, e.g., strings that contain script commands
- User functions need to escape special symbols listed here with `_` (`[:e [:echo␠ "e"]]`→`[:e [:echo_␠ "e"]]`) to avoid conflict

### Other key symbols

|Symbol(s)[^1] |Key `name` |
|--------- |-------- |
|🌐 ƒ ⓕ Ⓕ 🄵 🅕 🅵 |`!F` |
|⇪ |`P` capslock |
|∀ |`!A` any modifier regardless of side |
|✱ |`!!` hyper |
|∀﹖ ∀? ﹖﹖ ?? |`##` optional any |
|⎋ |`escape` |
|⭾ ↹ |`tab` |
|␠ ␣ |`spacebar` |
|␈ ⌫ |`delete_or_backspace` |
|␡ ⌦ |`delete_forward` |
|⏎ ↩ ⌤ ␤ |`return_or_enter` |
|︔ ⸴ .⁄ |`semicolon` / `comma` / `period` / `slash` |
|“ ” " « » |`quote` |
|⧵ \ |`backslash` |
|﹨ |`non_us_backslash` |
|【 「 〔 ⎡ |`open_bracket` |
|】 」 〕 ⎣ |`close_bracket` |
|£ |`non_us_pound` |
|ˋ ˜ |`grave_accent_and_tilde` |
|‐ ₌ |`hyphen` `equal_sign` |
|▲ ▼ ◀ ▶ |`up`/`down`/`left`/`right` `_arrow`
|⇞ ⇟ |`page_` `up`/`down` |
|⎀ |`insert` |
|⇤ ⤒ ↖ |`home` |
|⇥ ⤓ ↘ |`end` |
| ` ` |no-break space removed |
|🔢₁ 🔢₂ 🔢₃ 🔢₄ 🔢₅ |`keypad_` `1`–`5` |
|🔢₆ 🔢₇ 🔢₈ 🔢₉ 🔢₀ |`keypad_` `6`–`0` |
|🔢₋ 🔢₌ 🔢₊ |`keypad_` `hyphen`/`equal_sign`/`plus` |
|🔢⁄ 🔢.🔢∗ |`keypad_` `slash`/`period`/`asterisk` |
|◀◀ ▶⏸ ▶▶ |`vk_consumer_` `previous`/`play`/`next` |
|🔊 🔈+ or ➕₊⊕ |`volume_up` |
|🔉 🔈− or ➖₋⊖ |`volume_down` |
|🔇 🔈⓪ ⓿ ₀ |`mute` |
|🔆 🔅 |`vk_consumer_brightness_` `up`/`down` |
|⌨💡+ or ➕₊⊕ |`vk_consumer_illumination_up` |
|⌨💡− or ➖₋⊖ |`vk_consumer_illumination_down` |
|▦ |`vk_launchpad` |
|🎛 |`vk_dashboard` |
|▭▯ |`vk_mission_control` |
|▤ ☰ 𝌆 |`application` |
|🖰1 🖰2 ... 🖰32 |`button` `1`–`32` |

[^1]: space-separated list of keys; `or` means only last symbol in a pair changes

1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ questions there.

## Note

- From/to key definitions can contain key symbols like ‹⇧ instead of !S for a left shift, more details at [List of supported key symbols](./KeySymbols.md)
- ~~Using `#_` to comment out rules [like
this](https://github.com/yqrashawn/yqdotfiles/blob/2699f833f9431ca197d50f6905c825712f7aee8d/.config/karabiner.edn#L41)
will leave a null rule (see below) in karabiner.json, it won't cause any
Expand Down
4 changes: 3 additions & 1 deletion project.clj
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@
:dependencies [[org.clojure/clojure "1.11.1"]
[org.clojure/tools.cli "1.0.206"]
[me.raynes/fs "1.4.6"]
[org.clojure/core.match "1.0.1"]
[babashka/process "0.1.7"]
[cheshire "5.10.2"]
[com.github.clj-easy/graal-build-time "0.1.4"]]
:plugins [[lein-cloverage "1.2.3"]])
:plugins [[lein-cloverage "1.2.3"]]
:profiles {:socket {:jvm-opts ["-Dclojure.server.repl={:port 5555 :accept clojure.core.server/repl :server-daemon false}"]}})


8 changes: 5 additions & 3 deletions src/karabiner_configurator/core.clj
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
(defn check-edn-syntax
"Call joker to check syntax of karabiner.edn"
[path]
(-> @(p/process [(System/getenv "SHELL") "-i" "-c" (format "joker --lint %s" path)])
(-> (p/process {:out :string :err :string}[(System/getenv "SHELL") "-i" "-c" (format "joker --lint %s" path)])
:err))

(defn exit
Expand Down Expand Up @@ -130,11 +130,13 @@
(defn parse
"Root function to parse karabiner.edn and update karabiner.json."
[path & [dry-run dry-run-all]]
(let [edn-syntax-err (:err (check-edn-syntax path))]
(let [edn-syntax-err-stream (check-edn-syntax path)]
(def edn-syntax-err (slurp edn-syntax-err-stream))
(when (> (count edn-syntax-err) 0)
(println "Syntax error in config:")
(println edn-syntax-err)
(exit 1)))
; (exit 1)
))
(update-to-karabiner-json (parse-edn (load-edn path)) dry-run dry-run-all))

(defn open-log-file []
Expand Down
252 changes: 252 additions & 0 deletions src/karabiner_configurator/keys_symbols.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,252 @@
; Add various key symbols to be used instead of capitalized letters
(ns karabiner-configurator.keys-symbols
(:require
[clojure.string :as string]
[clojure.core.match :refer [match]]))
(import [java.util.regex Pattern])

; S T O C → left_shift left_control left_option left_command
; R W E Q → right_shift right_control right_option right_command
; SS → shift ...
; labels must = same position
(def modi-sym ["⇧" "⎈" "⌃" "⎇" "⌥" "⌘" "◆" "❖" ])
(def modi-‹l ["S" "T" "T" "O" "O" "C" "C" "C" ])
(def modi-l› ["R" "W" "W" "E" "E" "Q" "Q" "Q" ])
(def modi-l∀ ["SS" "TT" "TT" "OO" "OO" "CC" "CC" "CC" ])
(def modi-l∀-as-key ["shift" "control" "control" "option" "option" "command" "command" "command" ])
(def ‹key ["‹" "'"])
(def key› ["›" "'"])
(def key﹖ ["﹖" "?"])
(def modi-‹l-as-key (mapv #(str "left_" %) modi-l∀-as-key))
(def modi-l›-as-key (mapv #(str "right_" %) modi-l∀-as-key))

(def keys-symbols-other {
"🌐" "!F","ƒ""!F","ⓕ""!F","Ⓕ""!F","🄵""!F","🅕""!F","🅵""!F"
"⇪" "P" ; capslock
"∀" "!A" ; any regardless of side
"✱" "!!" ; hyper
"∀﹖" "##","∀?""##","﹖﹖""##","??""##"; optional any
"︔" "semicolon"
"“" "quote","”""quote",""""quote","«""quote","»""quote"
"⧵" "backslash","\""backslash"
"﹨" "non_us_backslash"
"【" "open_bracket" ,"「""open_bracket" ,"〔""open_bracket" ,"⎡""open_bracket"
"】" "close_bracket","」""close_bracket","〕""close_bracket","⎣""close_bracket"
"⸴" "comma"
"." "period"
"⁄" "slash"
"⎋" "escape"
"⭾" "tab","↹""tab"
"‐" "hyphen"
"₌" "equal_sign"
"£" "non_us_pound"
"⇞" "page_up","⇟""page_down"
"⇤" "home","⇥""end","⤒""home","⤓""end","↖""home","↘""end",
"⎀" "insert",
"ˋ" "grave_accent_and_tilde","˜""grave_accent_and_tilde"
"␠" "spacebar","␣""spacebar"
"␈" "delete_or_backspace","⌫""delete_or_backspace"
"␡" "delete_forward","⌦""delete_forward"
"⏎" "return_or_enter","↩""return_or_enter","⌤""return_or_enter","␤""return_or_enter",
"▲" "up_arrow","▼""down_arrow","◀""left_arrow","▶""right_arrow"
" " "" ; no-break space removed, used only for rudimentary alignment
"🔢₁""keypad_1","🔢₂""keypad_2","🔢₃""keypad_3","🔢₄""keypad_4","🔢₅""keypad_5"
"🔢₆""keypad_6","🔢₇""keypad_7","🔢₈""keypad_8","🔢₉""keypad_9","🔢₀""keypad_0"
"🔢₌""keypad_equal_sign","🔢₋""keypad_hyphen","🔢₊""keypad_plus"
"🔢⁄""keypad_slash","🔢.""keypad_period","🔢∗""keypad_asterisk","🔢⏎""keypad_enter"
"🔊""volume_up" ,"🔈+""volume_up" ,"🔈➕""volume_up" ,"🔈₊""volume_up" ,"🔈⊕""volume_up"
"🔉""volume_down","🔈−""volume_down","🔈➖""volume_down","🔈₋""volume_down","🔈⊖""volume_down"
"🔇""mute","🔈⓪""mute","🔈⓿""mute","🔈₀""mute"
"🔆""vk_consumer_brightness_up"
"🔅""vk_consumer_brightness_down"
"⌨💡+""vk_consumer_illumination_up" ,"⌨💡➕""vk_consumer_illumination_up" ,"⌨💡₊""vk_consumer_illumination_up" ,"⌨💡⊕""vk_consumer_illumination_up"
"⌨💡−""vk_consumer_illumination_down","⌨💡➖""vk_consumer_illumination_down","⌨💡₋""vk_consumer_illumination_down","⌨💡⊖""vk_consumer_illumination_down"
"▦""vk_launchpad"
"🎛""vk_dashboard"
"▭▯""vk_mission_control"
"◀◀""vk_consumer_previous"
"▶⏸""vk_consumer_play"
"▶▶""vk_consumer_next"
"▤""application","☰""application","𝌆""application"
"🖰1" "button1" ,"🖰2" "button2" ,"🖰3" "button3" ,"🖰4" "button4" ,"🖰5" "button5"
"🖰6" "button6" ,"🖰7" "button7" ,"🖰8" "button8" ,"🖰9" "button9" ,"🖰10""button10"
"🖰11""button11","🖰12""button12","🖰13""button13","🖰14""button14","🖰15""button15"
"🖰16""button16","🖰17""button17","🖰18""button18","🖰19""button19"
"🖰21""button21","🖰22""button22","🖰23""button23","🖰24""button24","🖰25""button25"
"🖰26""button26","🖰27""button27","🖰28""button28","🖰29""button29","🖰30""button30"
"🖰31""button31","🖰32""button32"
})
(def keys-symbols-generated (into {} (
mapcat (fn [mod]
(mapcat (fn [‹]
(mapcat (fn [›]
(map (fn [﹖]
(def mI (.indexOf modi-sym mod)) ; modifier index to pick labels (which have the same position)
(def ‹l (nth modi-‹l mI))
(def l› (nth modi-l› mI))
(def l∀ (nth modi-l∀ mI))
(match [ ; !mandatory ; #optional
(some? ‹) (some? ›) (some? ﹖)]
[true false false] {(str ‹ mod ) (str "!" ‹l)}
[false true false] {(str mod › ) (str "!" l›)}
[false false false] {(str mod ) (str "!" l∀)}
[true false true] {(str ‹ mod ﹖ ) (str "#" ‹l)}
[false true true] {(str mod › ﹖ ) (str "#" l›)}
[false false true] {(str mod ﹖ ) (str "#" l∀)}
:else nil
)) (concat key﹖ '(nil))
)) (concat key› '(nil))
)) (concat ‹key '(nil))
)) modi-sym))
)
(def keys-symbols-generated-modi-as-key (into {} (
mapcat (fn [mod]
(mapcat (fn [‹]
(map (fn [›]
(def mI (.indexOf modi-sym mod)) ; modifier index to pick labels (which have the same position)
(def ‹l (nth modi-‹l-as-key mI))
(def l› (nth modi-l›-as-key mI))
(def l∀ (nth modi-l∀-as-key mI))
(match [ ; !mandatory ; #optional
(some? ‹) (some? ›)]
[true false ] {(str ‹ mod ) ‹l}
[false true ] {(str mod › ) l›}
[false false ] {(str mod ) l∀}
:else nil
)) (concat key› '(nil))
)) (concat ‹key '(nil))
)) modi-sym))
)
(def keys-symbols-other-modi-as-key {
"⇪" "caps_lock"
})

(def keys-symbols-unordered (merge keys-symbols-generated keys-symbols-other ))
(def keys-symbols-unordered-modi-as-key (merge keys-symbols-generated-modi-as-key keys-symbols-other-modi-as-key))
; Sort by key length (BB > A) to match ⇧› before ⇧
(defn sort-map-key-len
([m ] (sort-map-key-len m "asc"))
([m ord] (into
(sorted-map-by (fn [key1 key2] (
compare
(if (or (= ord "asc") (= ord "↑")) [(count (str key1)) key1] [(count (str key2)) key2])
(if (or (= ord "asc") (= ord "↑")) [(count (str key2)) key2] [(count (str key1)) key1])
))) m)))
(def keys-symbols (sort-map-key-len keys-symbols-unordered "↓"))
(def keys-symbols-modi-as-key (sort-map-key-len keys-symbols-unordered-modi-as-key "↓"))

(defn replace-map-h "input string + hash-map ⇒ string with all map-keys → map-values in input"
[s_in m_in & {:keys [modi-as-key] :or {modi-as-key nil}}]
(def keys_in (keys m_in)) ;{"⎇""A","⎈""C"} → "⎇""⎈"
(if modi-as-key ;
(def keys_in_q (map #(str #"(?<!_)" (Pattern/quote %) "$") keys_in)) ;"\\Q⎇\\E" "\\Q⎈\\E" + "$" if passed
(def keys_in_q (map #(str #"(?<!_)" (Pattern/quote %) ) keys_in)) )
(def keys_in_q_or (interpose "|" keys_in_q)) ;"\\Q⎇\\E""|""\\Q⎈\\E"
(def keys_in_q_s (apply str keys_in_q_or)) ;"\\Q⎇\\E|\\Q⎈\\E"
(def keys_in_re (re-pattern keys_in_q_s)) ;#"\Q⎇\E|\Q⎈\E"
(string/replace s_in keys_in_re m_in)
)

(defn contains-in?
[m ks]
(not= ::absent (get-in m ks ::absent)))
(defn update-in-if-has
[m_ ks f & args]
(def m m_)
(mapv #(if (contains-in? m [%])
(def m (apply (partial update-in m [%] f) args))
)
ks)
m)

(defn despace "replace all whitespace" [str_in]
(string/replace str_in #"[\s]" "")
)
(defn key-name-sub-or-self [k]
(if (keyword? k)
(do
(def k1 (replace-map-h k keys-symbols-modi-as-key :modi-as-key true)) ; first replace modi-as-keys
(def k2 (replace-map-h k1 keys-symbols)) ; then replace other key symbols
(keyword (string/replace k2 #"^:" ""))
)
(if (map? k)
(update-in-if-has k [:key :sim] key-name-sub-or-self)
(if (vector? k)
(mapv #(key-name-sub-or-self %) k)
(if (and (string? k) (string/starts-with? k "‘"))
(key-name-sub-or-self (keyword (string/replace (despace k) #"^‘" "")))
k
)
)
)
)
)

(def modi-re #"[ACSTOQWERFP]+")
(defn find-modi
[s prefix matches-found]
(def modi-prefix-re (re-pattern (str prefix modi-re)))
(def modi-match (re-find modi-prefix-re s))
(if (nil? modi-match)
[s matches-found]
(do
(if (vector? modi-match) ; [full-match G1...]
(def modi-match-str (first modi-match))
(def modi-match-str modi-match )
)
(def matches-found-cc (conj matches-found modi-match-str))
(recur (string/replace-first s modi-match-str "") prefix matches-found-cc)
))
)
(defn move-modi-front
"Replace individual modifiers `!CC!TT` (prefix=!) into a single group with one `!CCTT`"
[k prefix]
(def s (if (keyword? k)
(name k)
(if (string? k)
k
nil
)))
(if (string? s) ; skip non-string keys like ["var" 1]
(do
(def modi-must [])
(let [[s-remain modi-must] (find-modi s prefix modi-must)]
(def modi-must-str
(if (empty? modi-must)
""
(str prefix (string/replace (string/join "" modi-must) prefix "")) ; :!CC!AA → :!CCAA
))
(keyword (str modi-must-str s-remain))
))
k
))
(defn move-modi-mandatory-front
[k]
(def prefix "!")
(move-modi-front k prefix)
)
(defn move-modi-optional-front
[k]
(def prefix "#")
(move-modi-front k prefix)
)
(defn move-modi-prefix-front
[k]
(if (vector? k)
(mapv #(move-modi-mandatory-front %) (mapv #(move-modi-optional-front %) k))
( move-modi-mandatory-front (move-modi-optional-front k))
))
(defn key-sym-to-key
"Takes key with symbols as input and returns keys without; optional :dbg debug print value"
[k & {:keys [dbg] :or {dbg nil}}]
(def sub1 (key-name-sub-or-self k))
(if (not= k sub1)
(if (map? sub1)
(def sub (update-in-if-has sub1 [:key :sim] move-modi-prefix-front))
(def sub (move-modi-prefix-front sub1))
)
(def sub sub1 )
)
; (if (and (some? dbg) (not= k sub)) (println (str " " dbg "¦" k " ⟶⟶⟶ " sub)))
sub
)
Loading