emacs: some updates

This commit is contained in:
Pavel Korytov 2025-11-23 21:52:49 +03:00
parent 6c489b4a32
commit 2e64d77ba8
5 changed files with 153 additions and 38 deletions

View file

@ -381,14 +381,14 @@ DIR is either 'left or 'right."
_t_: Terminal (Alacritty)
_b_: Browser (Firefox)
_s_: Rocket.Chat
_e_: Element
_d_: Discord
_d_: DBeaver
_c_: Chromium
"
("t" (lambda () (interactive) (my/run-in-background "alacritty")))
("b" (lambda () (interactive) (my/run-in-background "firefox")))
("s" (lambda () (interactive) (my/run-in-background "flatpak run chat.rocket.RocketChat")))
("e" (lambda () (interactive) (my/run-in-background "flatpak run im.riot.Riot")))
("d" (lambda () (interactive) (my/run-in-background "flatpak run com.discordapp.Discord"))))
("s" (lambda () (interactive) (my/run-in-background "rocketchat-desktop")))
("d" (lambda () (interactive) (my/run-in-background "dbeaver")))
("c" (lambda () (interactive) (my/run-in-background "chromium"))))
(defun my/exwm-lock ()
(interactive)
@ -594,10 +594,10 @@ _d_: Discord
(my/exwm-set-wallpaper)
(my/exwm-run-shepherd)
(my/exwm-run-systemd)
(my/run-in-background "gpgconf --reload gpg-agent")
(my/exwm-run-polybar)
(setenv "DBUS_SESSION_BUS_ADDRESS" "unix:path=/run/user/1000/bus")
(my/exwm-run-systemd)
(when (my/is-arch)
(my/run-in-background "set_layout")))

View file

@ -82,6 +82,8 @@
;; I have everything I need in polybar
(emms-mode-line-mode -1)
(emms-playing-time-display-mode -1)
(delq 'emms-mark-mode evil-emacs-state-modes)
(delq 'emms-browser-mode evil-emacs-state-modes)
(defun emms-info-mpd-process (track info)
(dolist (data info)
(let ((name (car data))

View file

@ -375,8 +375,11 @@ REMOTE = '<rclone-remote>'
FOLDERS = json.loads('<rclone-folders-json>')
OPTIONS = json.loads('<rclone-options>')
for folder, i in zip(FOLDERS, range(len(FOLDERS))):
folder['id'] = i
def rclone_make_command(local_path, remote_path, remote):
def rclone_make_command(local_path, remote_path, remote, extra_args=[]):
return [
'rclone',
'bisync',
@ -390,7 +393,8 @@ def rclone_make_command(local_path, remote_path, remote):
'NEVER',
'--use-json-log',
'--stats',
'9999m'
'9999m',
*extra_args
]
@ -418,15 +422,26 @@ def process_output(output):
except Exception:
print(line)
def rclone_run(folder):
def process_command_for_print(command):
res = []
for c in command:
if ' ' in c:
res.append(f'\\'{c}\\'')
else:
res.append(c)
return ' '.join(res)
def rclone_run(folder, extra_args=[]):
command = rclone_make_command(
folder['local-path'], folder['remote-path'], folder['remote']
folder['local-path'], folder['remote-path'], folder['remote'], extra_args
)
try:
print(str(folder['id']) + '. ' + process_command_for_print(command))
result = subprocess.run(command, check=True, capture_output=True, text=True)
except subprocess.CalledProcessError as e:
print(f'=== Error syncing {folder['local-path']} ===')
print(f'Command: {' '.join(command)}')
print(f'Command: {process_command_for_print(command)}')
print(f'--- STDOUT ---')
process_output(e.stdout)
print(f'--- STDERR ---')
@ -434,7 +449,6 @@ def rclone_run(folder):
return {'success': False, 'stats': {}}
return {'success': True, 'stats': parse_rclone_stats(result.stderr)}
def notify(summary, body, level='normal', expire_time=5000):
subprocess.run(['notify-send', '-u', level, '-t', str(expire_time), summary, body])
@ -447,17 +461,16 @@ def sizeof_fmt(num, suffix='B'):
return f'{num:.1f}Yi{suffix}'
def rclone_run_all(folders):
def rclone_run_all(folders, extra_args=[]):
error_folders = []
total_bytes = 0
total_transfers = 0
total_deleted = 0
total_renamed = 0
for folder in folders:
print(f'Running rclone for {folder}')
res = rclone_run(folder)
res = rclone_run(folder, extra_args)
if not res['success']:
error_folders.append(folder['local-path'])
error_folders.append(folder)
else:
total_bytes += res.get('stats', {}).get('bytes', 0)
total_transfers += res.get('stats', {}).get('transfers', 0)
@ -469,21 +482,44 @@ def rclone_run_all(folders):
if total_transfers > 0:
msg += f'''Transferred {total_transfers} files ({sizeof_fmt(total_bytes)})\n'''
if total_deleted > 0:
msg += f'''Deleted {total_transfers} files\n'''
msg += f'''Deleted {total_deleted} files\n'''
if total_renamed > 0:
msg += f'''Renamed {total_renamed} files\n'''
if len(error_folders) > 0:
msg += '''\nSync errors for the following folders:'''
for folder in error_folders:
msg += '''\n- ''' + folder
msg += '''\n- ''' + str(folder['id']) + '. ' + folder['local-path']
level = 'critical'
if len(msg) > 0:
notify(f'rclone sync {REMOTE}', msg, level=level)
def parse_arguments():
if len(sys.argv) < 2:
return None, []
id_arg = sys.argv[1]
folder_ids = [int(x.strip()) for x in id_arg.split(',')]
extra_args = sys.argv[2:]
return folder_ids, extra_args
if __name__ == '__main__':
rclone_run_all(FOLDERS)
folder_ids, extra_args = parse_arguments()
if folder_ids is None:
selected_folders = FOLDERS
else:
selected_folders = [f for f in FOLDERS if f['id'] in folder_ids]
found_ids = {f['id'] for f in selected_folders}
missing_ids = set(folder_ids) - found_ids
if missing_ids:
print(f'Warning: folder IDs not found: {sorted(missing_ids)}')
rclone_run_all(selected_folders, extra_args)
"))
(setq script
(thread-last script
@ -562,6 +598,23 @@ The return value is a list of commands as defined by
commands))
(nreverse commands)))
(defun my/index-rclone-reset ()
"Delete all rclone test files."
(interactive)
(let* ((tree (my/index--tree-retrive))
(folders (my/index--rclone-get-folders tree))
files-to-delete)
(dolist (folder folders)
(let ((test-file-path
(concat
(alist-get :local-path folder)
(format ".rclone-test-%s" (alist-get :remote folder)))))
(when (file-exists-p test-file-path)
(push test-file-path files-to-delete))))
(when (y-or-n-p (format "Delete %d files" (seq-length files-to-delete)))
(dolist (file files-to-delete)
(delete-file file)))))
(defun my/index--git-commands (tree)
"Get commands to clone the yet uncloned git repos in TREE.

View file

@ -965,14 +965,14 @@ A +transient+ hydra for shortcuts for the most frequent apps.
_t_: Terminal (Alacritty)
_b_: Browser (Firefox)
_s_: Rocket.Chat
_e_: Element
_d_: Discord
_d_: DBeaver
_c_: Chromium
"
("t" (lambda () (interactive) (my/run-in-background "alacritty")))
("b" (lambda () (interactive) (my/run-in-background "firefox")))
("s" (lambda () (interactive) (my/run-in-background "flatpak run chat.rocket.RocketChat")))
("e" (lambda () (interactive) (my/run-in-background "flatpak run im.riot.Riot")))
("d" (lambda () (interactive) (my/run-in-background "flatpak run com.discordapp.Discord"))))
("s" (lambda () (interactive) (my/run-in-background "rocketchat-desktop")))
("d" (lambda () (interactive) (my/run-in-background "dbeaver")))
("c" (lambda () (interactive) (my/run-in-background "chromium"))))
#+end_src
*** Locking up
Run i3lock.
@ -1219,10 +1219,10 @@ And the EXWM config itself.
(my/exwm-set-wallpaper)
(my/exwm-run-shepherd)
(my/exwm-run-systemd)
(my/run-in-background "gpgconf --reload gpg-agent")
(my/exwm-run-polybar)
(setenv "DBUS_SESSION_BUS_ADDRESS" "unix:path=/run/user/1000/bus")
(my/exwm-run-systemd)
(when (my/is-arch)
(my/run-in-background "set_layout")))
@ -4239,6 +4239,7 @@ This section generates manifests for various desktop software that I'm using.
| office | okular |
| office | obs-studio |
| office | rocketchat-desktop |
** LaTeX
| Category | Arch dependency | Disabled |
|----------+--------------------------+----------|
@ -4540,6 +4541,7 @@ Other desktop programs I use are listed below.
| desktop-misc | noto-fonts-emoji | |
| desktop-misc | remmina | |
| desktop-misc | android-file-transfer | |
| desktop-misc | veracrypt | |
#+NAME: packages
#+begin_src emacs-lisp :tangle no

View file

@ -10433,6 +10433,8 @@ References:
;; I have everything I need in polybar
(emms-mode-line-mode -1)
(emms-playing-time-display-mode -1)
(delq 'emms-mark-mode evil-emacs-state-modes)
(delq 'emms-browser-mode evil-emacs-state-modes)
<<emms-fixes>>)
#+end_src
**** MPD
@ -12870,8 +12872,11 @@ REMOTE = '<rclone-remote>'
FOLDERS = json.loads('<rclone-folders-json>')
OPTIONS = json.loads('<rclone-options>')
for folder, i in zip(FOLDERS, range(len(FOLDERS))):
folder['id'] = i
def rclone_make_command(local_path, remote_path, remote):
def rclone_make_command(local_path, remote_path, remote, extra_args=[]):
return [
'rclone',
'bisync',
@ -12885,7 +12890,8 @@ def rclone_make_command(local_path, remote_path, remote):
'NEVER',
'--use-json-log',
'--stats',
'9999m'
'9999m',
,*extra_args
]
@ -12913,15 +12919,26 @@ def process_output(output):
except Exception:
print(line)
def rclone_run(folder):
def process_command_for_print(command):
res = []
for c in command:
if ' ' in c:
res.append(f'\\'{c}\\'')
else:
res.append(c)
return ' '.join(res)
def rclone_run(folder, extra_args=[]):
command = rclone_make_command(
folder['local-path'], folder['remote-path'], folder['remote']
folder['local-path'], folder['remote-path'], folder['remote'], extra_args
)
try:
print(str(folder['id']) + '. ' + process_command_for_print(command))
result = subprocess.run(command, check=True, capture_output=True, text=True)
except subprocess.CalledProcessError as e:
print(f'=== Error syncing {folder['local-path']} ===')
print(f'Command: {' '.join(command)}')
print(f'Command: {process_command_for_print(command)}')
print(f'--- STDOUT ---')
process_output(e.stdout)
print(f'--- STDERR ---')
@ -12929,7 +12946,6 @@ def rclone_run(folder):
return {'success': False, 'stats': {}}
return {'success': True, 'stats': parse_rclone_stats(result.stderr)}
def notify(summary, body, level='normal', expire_time=5000):
subprocess.run(['notify-send', '-u', level, '-t', str(expire_time), summary, body])
@ -12942,17 +12958,16 @@ def sizeof_fmt(num, suffix='B'):
return f'{num:.1f}Yi{suffix}'
def rclone_run_all(folders):
def rclone_run_all(folders, extra_args=[]):
error_folders = []
total_bytes = 0
total_transfers = 0
total_deleted = 0
total_renamed = 0
for folder in folders:
print(f'Running rclone for {folder}')
res = rclone_run(folder)
res = rclone_run(folder, extra_args)
if not res['success']:
error_folders.append(folder['local-path'])
error_folders.append(folder)
else:
total_bytes += res.get('stats', {}).get('bytes', 0)
total_transfers += res.get('stats', {}).get('transfers', 0)
@ -12964,21 +12979,44 @@ def rclone_run_all(folders):
if total_transfers > 0:
msg += f'''Transferred {total_transfers} files ({sizeof_fmt(total_bytes)})\n'''
if total_deleted > 0:
msg += f'''Deleted {total_transfers} files\n'''
msg += f'''Deleted {total_deleted} files\n'''
if total_renamed > 0:
msg += f'''Renamed {total_renamed} files\n'''
if len(error_folders) > 0:
msg += '''\nSync errors for the following folders:'''
for folder in error_folders:
msg += '''\n- ''' + folder
msg += '''\n- ''' + str(folder['id']) + '. ' + folder['local-path']
level = 'critical'
if len(msg) > 0:
notify(f'rclone sync {REMOTE}', msg, level=level)
def parse_arguments():
if len(sys.argv) < 2:
return None, []
id_arg = sys.argv[1]
folder_ids = [int(x.strip()) for x in id_arg.split(',')]
extra_args = sys.argv[2:]
return folder_ids, extra_args
if __name__ == '__main__':
rclone_run_all(FOLDERS)
folder_ids, extra_args = parse_arguments()
if folder_ids is None:
selected_folders = FOLDERS
else:
selected_folders = [f for f in FOLDERS if f['id'] in folder_ids]
found_ids = {f['id'] for f in selected_folders}
missing_ids = set(folder_ids) - found_ids
if missing_ids:
print(f'Warning: folder IDs not found: {sorted(missing_ids)}')
rclone_run_all(selected_folders, extra_args)
#+end_src
A function that templates the script above:
@ -13073,6 +13111,26 @@ The return value is a list of commands as defined by
(nreverse commands)))
#+end_src
Also, a command to remove all =.rclone-test-*= files. This is necessary, e.g. after a change in the filter file.
#+begin_src emacs-lisp
(defun my/index-rclone-reset ()
"Delete all rclone test files."
(interactive)
(let* ((tree (my/index--tree-retrive))
(folders (my/index--rclone-get-folders tree))
files-to-delete)
(dolist (folder folders)
(let ((test-file-path
(concat
(alist-get :local-path folder)
(format ".rclone-test-%s" (alist-get :remote folder)))))
(when (file-exists-p test-file-path)
(push test-file-path files-to-delete))))
(when (y-or-n-p (format "Delete %d files" (seq-length files-to-delete)))
(dolist (file files-to-delete)
(delete-file file)))))
#+end_src
**** Git repos
To sync git, we just need to clone the required git repos. Removing the repos is handled by the folder sync commands.