Last active
April 26, 2023 08:48
-
-
Save trungnt13/997f6d1fe2023d4841b6f6e3d421f04d to your computer and use it in GitHub Desktop.
[Code] Convert vscode MacOS keybindings to Linux/Windows keybindings
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
19/04/2023: MacOS | |
- cmd+shift+y: select all selected occurence | |
- cmd+ctrl+o: outline | |
20/04/2023: Search Editor (MacOS) | |
- ctrl+f: open | |
- ctrl+0 or 9: focus Next or prev | |
- cmd+[ or ]: next or prev input box | |
- better split vertical and horizontal (> 2 editor tabs) | |
24/04/2023: diff editor (MacOS) | |
- ctrl+0 or 9: next or prev diff | |
26/04/2023: sync settings (MacOS) | |
""" | |
from collections import defaultdict | |
import os | |
import re | |
import shutil | |
import sys | |
import warnings | |
try: | |
import jstyleson | |
except ImportError: | |
raise RuntimeError("jstyleson is not installed, it is needed for JSON with comments.") | |
################################################################### | |
# sync all keybindings | |
################################################################### | |
enable_sync_profiles = True | |
if sys.platform == "darwin": | |
profile_dir = os.path.expanduser("~/Library/Application Support/Code/User/profiles") | |
snippets_dir = os.path.expanduser("~/Library/Application Support/Code/User/snippets") | |
default_keys = os.path.expanduser("~/Library/Application Support/Code/User/keybindings.json") | |
default_settings = os.path.expanduser("~/Library/Application Support/Code/User/settings.json") | |
elif sys.platform == "win32": | |
warnings.warn("Windows is not supported yet") | |
elif sys.platform == "linux": | |
profile_dir = os.path.expanduser("~/.config/Code/User/profiles") | |
snippets_dir = os.path.expanduser("~/.config/Code/User/snippets") | |
default_keys = os.path.expanduser("~/.config/Code/User/keybindings.json") | |
default_settings = os.path.expanduser("~/.config/Code/User/settings.json") | |
else: | |
enable_sync_profiles = False | |
print(f"Unknown platform: {sys.platform}") | |
# ======== For now only sync the keybindings | |
if os.path.exists(profile_dir): | |
assert os.path.isfile(default_keys), f"Keybindings file not found {default_keys}" | |
assert os.path.isfile(default_settings), f"Settings file not found {default_settings}" | |
assert os.path.isdir(snippets_dir), f"Snippets dir not found {snippets_dir}" | |
files = defaultdict(keys=[default_keys], settings=[default_settings]) | |
snippets = [snippets_dir] | |
for p in os.listdir(profile_dir): | |
p = os.path.join(profile_dir, p) | |
if not os.path.isdir(p): | |
continue | |
p_keys = os.path.join(p, "keybindings.json") | |
assert os.path.isfile(p_keys), f"Keybindings file not found in {p_keys}" | |
files["keys"].append(p_keys) | |
p_settings = os.path.join(p, "settings.json") | |
assert os.path.isfile(p_settings), f"Settings file not found in {p}" | |
files["settings"].append(p_settings) | |
p_snippets = os.path.join(p, "snippets") | |
assert os.path.isdir(p_snippets), f"Snippets dir not found in {p}" | |
snippets.append(p_snippets) | |
# sort the files by newest modification | |
for key, values in files.items(): | |
values.sort(key=os.path.getmtime, reverse=True) | |
print("*" * 8, key, "*" * 8) | |
print(values[0], "will be synced to all other profiles:") | |
text_newest = open(values[0], mode="r", encoding="utf-8").read() | |
for i in values[1:]: | |
text_old = open(i, mode="r", encoding="utf-8").read() | |
if text_newest != text_old: | |
# copy or overwrite | |
shutil.copy(values[0], i) | |
print(" - ", i) | |
# sync snippets | |
snippets.sort(key=os.path.getmtime, reverse=True) | |
print("Syncing snippets from", snippets[0], "to") | |
for p_old in snippets[1:]: | |
print("***", p_old, "***") | |
for f_new in os.listdir(snippets[0]): | |
f_new = os.path.join(snippets[0], f_new) | |
f_old = os.path.join(p_old, os.path.basename(f_new)) | |
text_new = open(f_new, mode="r", encoding="utf-8").read() | |
text_old = "" if not os.path.exists(f_old) else open(f_old, mode="r", encoding="utf-8").read() | |
if text_old != text_new: | |
print(" - ", f_old) | |
shutil.copy(f_new, f_old) | |
# path = os.path.expanduser("~/Library/Application Support/Code/User/profiles/-7d9d45a1/settings.json") | |
################################################################### | |
# Now only support convert MacOS keybindings to Windows or Linux | |
################################################################### | |
# if not macOS then exit | |
if sys.platform != "darwin": | |
raise RuntimeError("Only support MacOS") | |
################################################################### | |
# Load keybindings from known path | |
################################################################### | |
user_keys = os.path.expanduser("~/Library/Application Support/Code/User/keybindings.json") | |
if os.path.exists(user_keys) and os.path.isfile(user_keys): | |
print(f"Found {user_keys}, copying to /tmp/keys.json") | |
if os.path.exists("/tmp/keys.json"): | |
os.remove("/tmp/keys.json") | |
shutil.copyfile(user_keys, "/tmp/keys.json") | |
else: | |
raise RuntimeError(f"{user_keys} not found") | |
################################################################### | |
# Input and output path | |
################################################################### | |
input_file = "/tmp/keys.json" | |
# check if the json is valid | |
try: | |
jstyleson.loads(open(input_file, mode="r", encoding="utf-8").read()) | |
except jstyleson.decoder.JSONDecodeError as e: | |
raise RuntimeError(f"Invalid json file: {input_file}") from e | |
output_file = os.path.join(os.path.dirname(input_file), "keys_new.json") | |
################################################################### | |
# Processing | |
################################################################### | |
keybindings = open(input_file, mode="r", encoding="utf-8").read() | |
keybindings_new = str(keybindings) | |
alt = re.compile(r'"key": ".*(alt).*"') | |
cmd = re.compile(r'"key": ".*(cmd).*"') | |
ctrl = re.compile(r'"key": ".*(ctrl).*"') | |
# NOTE: every modification change the position of the string | |
changes = defaultdict(list) | |
# do nothing for alt | |
for match in alt.finditer(keybindings_new): | |
match: re.Match | |
s = match.start(1) | |
e = match.end(1) | |
# Do this first, convert all ctrl to alt | |
match: re.Match = ctrl.search(keybindings_new) | |
while match: | |
s = match.start(1) | |
e = match.end(1) | |
keybindings_new = keybindings_new[:s] + "alt" + keybindings_new[e:] | |
changes["ctrl->alt"].append(match.group()) | |
match = ctrl.search(keybindings_new) | |
# otherwise, this will create "ctrl+ctrl" cases, convert all cmd to ctrl | |
match: re.Match = cmd.search(keybindings_new) | |
while match: | |
s = match.start(1) | |
e = match.end(1) | |
keybindings_new = keybindings_new[:s] + "ctrl" + keybindings_new[e:] | |
changes["cmd->ctrl"].append(match.group()) | |
match = cmd.search(keybindings_new) | |
# change all alt+ctrl to ctrl+alt | |
alt_ctrl = re.compile(r'"key": ".*(alt\+ctrl).*"') | |
match: re.Match = alt_ctrl.search(keybindings_new) | |
while match: | |
s = match.start(1) | |
e = match.end(1) | |
keybindings_new = keybindings_new[:s] + "ctrl+alt" + keybindings_new[e:] | |
changes["alt+ctrl->ctrl+alt"].append(match.group()) | |
match = alt_ctrl.search(keybindings_new) | |
# change all alt+alt (ctrl+alt) to empty | |
alt_alt = re.compile(r'"key": ".*(alt).*(alt).*"') | |
match: re.Match = alt_alt.search(keybindings_new) | |
while match: | |
s, e = match.span() | |
keybindings_new = keybindings_new[:s] + '"key": ""' + keybindings_new[e:] | |
changes["alt+alt->empty"].append(match.group()) | |
match = alt_alt.search(keybindings_new) | |
################################################################### | |
# Show the changes | |
################################################################### | |
print("Changes:") | |
for k, v in changes.items(): | |
print(f" - {k:20s}: {len(v)}") | |
for i in v: | |
print(f" - {i}") | |
################################################################### | |
# Save the new keybindings | |
################################################################### | |
# save the new keybindings | |
with open(output_file, mode="w", encoding="utf-8") as f: | |
f.write(keybindings_new) | |
try: | |
jstyleson.load(open(output_file, mode="r", encoding="utf-8")) | |
except jstyleson.decoder.JSONDecodeError as e: | |
raise RuntimeError(f"Invalid json file: {output_file}") from e | |
else: | |
print("Success saving new keybindings to", output_file) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment