Workflow Configuration
- This repository
- Intro
- MinEmacs config files
- General Emacs settings
- Package configuration
- Applications
- Programming
- Office
- System configuration
This repository
This repository (abougouffa/dotfiles) contains my configuration files for Zsh, MinEmacs, Vim, Alacritty and other Linux related stuff.
If you want to reuse some of these configurations, you will need to modify some directories and add some user specific information (usernames, passwords…)
Notice
This is the main configuration file, it contains the literal personal configuration for MinEmacs, and I use it to generate some other Linux configuration files (define aliases, environment variables, user tools, Git configuration…).
For my old deprecated Doom Emacs configuration, see dotfiles/dot_doom.d
.
Intro
I’ve been using Linux exclusively since 2010, GNU Emacs was always installed on my machine, but I didn’t discover the real Emacs until 2020. In the beginning, I started my Vanilla Emacs configuration from scratch, but after a while, it becomes a mess. As a new Emacs user, I didn’t understand the in the beginning how to optimize my configuration and how to do things correctly. I discovered then Spacemacs, which made things much easier, but it was a little slow, and just after, I found the awesome Doom Emacs, which helped me progress and learn more about Emacs and Lisp, but at some point, I faced multiple problems with Doom, so I started my own configuration framework, MinEmacs.
MinEmacs is intended to be a minimal configuration framework. In the beginning,
I planned to use only necessary packages. However, it is starting to grow to
respond to my daily needs.
#!/bin/env bash
# NOTE: This file is generated from "config-literate.org".
# This shell script has been generated from the litterate Org configuration.
# It helps installing required tools (for Arch/Manjaro Linux) and tweak some
# system settings
MinEmacs config files
Early configuration (early-config.el
)
;;; early-config.el -*- coding: utf-8-unix; lexical-binding: t; -*-
;; NOTE: This file is generated from "config-literate.org".
;; MinEmacs specific stuff
(unless minemacs-verbose
(setq minemacs-msg-level 2)) ; print info messages
;; Disable `dashboard'
(setq +dashboard-disable t)
;; Force loading lazy packages immediately, not in idle time
;; (setq minemacs-not-lazy t)
;; Enable full screen at startup
;; (if-let ((fullscreen (assq 'fullscreen default-frame-alist)))
;; (setcdr fullscreen 'fullboth)
;; (push '(fullscreen . fullboth) default-frame-alist))
;; Setup a `debug-on-message' to catch a wired message!
;; (setq debug-on-message "\\(?:error in process filter: \\(?:\\(?:mu4e-warn: \\)?\\[mu4e] No message at point\\)\\)")
;; (setq debug-on-message "Package cl is deprecated")
;; (setenv "MINEMACS_IGNORE_VERSION_CHECK" "1")
Modules (modules.el
)
;;; modules.el -*- coding: utf-8-unix; lexical-binding: t; -*-
;; NOTE: This file is generated from "config-literate.org".
;; This file can be used to override `minemacs-modules'
;; and `minemacs-core-modules'
(setq
;; MinEmacs core modules
minemacs-core-modules
'(me-splash ; Simple splash screen (inspired by emacs-splash)
me-keybindings ; general.el, which-key, hydra, ...
me-evil ; evil, evil-collection, evil-mc, ...
me-core-ui ; Theme and modeline
me-completion) ; vertico, marginalia, corfu, cape, consult, ...
;; MinEmacs modules
minemacs-modules
'(me-ui ; focus, writeroom-mode, emojify, ...
me-editor ; yasnippet, unicode-fonts, ligature, ...
me-extra ; better-jumper, ...
me-undo ; undo-fu-session, vundo, ...
me-multi-cursors ; iedit, evil-mc, ...
me-vc ; magit, forge, core-review, diff-hl, ...
me-project ; project, consult-project-extra, ...
me-prog ; tree-sitter, eglot, editorconfig, ...
me-checkers ; flymake, flymake-easy, gdb-mi, ...
me-lsp ; lsp-mode, dap-mode, consult-lsp, ...
me-debug ; realgud, disaster, ...
me-lisp ; parinfer-rust, macrostep, geiser, elisp, ...
me-data ; csv, yaml, toml, json, ...
me-org ; org, org-modern, ...
me-notes ; org-roam, ...
me-email ; mu4e, mu4e-alert, org-msg, ...
me-lifestyle ; awqat, ...
me-docs ; pdf-tools, nov, ...
me-calendar ; calfw, calfw-org, calfw-ical, ...
me-latex ; tex, auctex, reftex, ...
me-natural-langs ; spell-fu, eglot-ltex, ...
me-files ; dirvish, dired, vlf, ...
me-tools ; vterm, tldr, docker, systemd, ...
me-biblio ; org-cite, citar, ...
me-daemon ; Emacs daemon tweaks
me-tty ; Emacs from terminal
me-rss ; elfeed, ...
me-robot ; Robotics stuff (ros, robot-mode, ...)
me-embedded ; Embedded systems (arduino, openocd, bitbake, ...)
me-eaf ; EAF apps (browser, jupyter, file-sender, ...)
me-math ; maxima, ess, ...
me-modeling ; OpenSCAD, ...
me-workspaces ; tabspaces, tab-bar, ...
me-window ; frame & window tweaks, ...
me-media ; empv, ...
me-fun ; xkcd, speed-type, ...
me-binary) ; hexl, decompile (using objdump)...
;; MinEmacs disabled packages
minemacs-disabled-packages
(append
'(dashboard)))
User configuration (config.el
)
;;; config.el -*- coding: utf-8-unix; lexical-binding: t; -*-
;; NOTE: This file is generated from "config-literate.org".
General Emacs settings
User information
;; Personal info
(setq user-full-name "Abdelhak Bougouffa"
user-mail-address (concat "abougouffa" "@" "fedora" "project" "." "org"))
Crypto stuff
(setq-default
;; Encrypt files to my self by default
epa-file-encrypt-to '("F808A020A3E1AC37"))
Bidirectional settings
This combo should speedup opening files:
(setq-default
;; Better support for files with long lines
bidi-paragraph-direction 'left-to-right
;; Speeds redisplay, may break paranthesis rendering for bidirectional files
bidi-inhibit-bpa t)
Directories
(defvar +biblio-notes-path (expand-file-name "~/PhD/bibliography/notes/"))
(defvar +biblio-styles-path (expand-file-name "~/Zotero/styles/"))
(defvar +biblio-storage-path (expand-file-name "~/Zotero/storage/"))
(defvar +biblio-libraries-path (expand-file-name "~/Zotero/library.bib"))
(setq org-directory "~/Dropbox/Org/"
source-directory "~/Softwares/aur/emacs-git/src/emacs-git/")
Misc
(setq +binary-hexl-enable t
+binary-objdump-enable t
browse-url-chromium-program (or (executable-find "brave")
(executable-find "chromium")
(executable-find "chromium-browser"))
browse-url-chrome-program browse-url-chromium-program)
Awqat
(+lazy-when! (featurep 'me-lifestyle)
;; Calendar settings (from `solar')
(setq calendar-latitude 48.86
calendar-longitude 2.35
calendar-location-name "Paris, FR"
calendar-time-display-form '(24-hours ":" minutes))
(awqat-display-prayer-time-mode 1)
(awqat-set-preset-french-muslims))
Projects
(+lazy!
(setq +project-scan-dir-paths
'("~/PhD/papers/"
"~/PhD/workspace/"
"~/PhD/workspace-no/"
"~/PhD/workspace-no/ez-wheel/swd-starter-kit-repo/"
"~/Projects/foss/packages/"
"~/Projects/foss/repos/"))
(+shutup!
(+project-scan-for-projects)))
Package configuration
User interface
Theme & font
(setq
minemacs-theme 'doom-one-light ; 'apropospriate-light
minemacs-fonts
'(:font-family "Iosevka Fixed Curly Slab"
:font-size 13
:variable-pitch-font-family "IBM Plex Serif" ; "Lato"
:variable-pitch-font-size 13
:unicode-font-family "JuliaMono")) ; Default font for Unicode chars
Writing mode
(with-eval-after-load 'me-writing-mode
(setq +writing-mixed-pitch-enable nil
+writing-text-scale 2.0))
Completion & IDE
LSP
Register LSP over Tramp for Python using pyls
. The pyls
LSP server should be
installed on the distant machine for this to work.
(with-eval-after-load 'lsp-mode
(lsp-register-client
(make-lsp-client
:new-connection (lsp-tramp-connection "pyls")
:major-modes '(python-mode python-ts-mode)
:remote? t
:server-id 'pyls-remote)))
Natural languages
Offline dictionaries
Needs sdcv
to be installed, needs also StarDict dictionaries, you can download
some from here and here for french.
Spell-fu
(with-eval-after-load 'spell-fu
(+spell-fu-register-dictionaries! "en" "fr"))
Applications
News feed (elfeed
)
Set RSS news feeds
(with-eval-after-load 'elfeed
(setq elfeed-feeds
'(("https://arxiv.org/rss/cs.RO" robotics academic)
("https://interstices.info/feed" science academic)
("https://spectrum.ieee.org/rss/robotics/fulltext" robotics academic)
("https://spectrum.ieee.org/rss/aerospace/fulltext" academic aerospace)
("https://spectrum.ieee.org/rss/computing/fulltext" academic computing)
("https://spectrum.ieee.org/rss/blog/automaton/fulltext" academic automation robotics)
("https://www.technologyreview.com/feed" tech science)
("https://itsfoss.com/feed" linux foss)
("https://lwn.net/headlines/rss" linux foss)
("https://linuxhandbook.com/feed" linux foss)
("https://www.omgubuntu.co.uk/feed" linux foss)
("https://this-week-in-rust.org/rss.xml" rust prog)
("https://planet.emacslife.com/atom.xml" emacs prog foss)
("https://developers.redhat.com/blog/feed" linux foss))))
Email (mu4e
)
Configuring mu4e
as email client needs three parts:
- Incoming mail configuration IMAP (using
mbsync
) - Outgoing mail configuration SMTP (using
smtpmail
ormsmtp
) - Email indexer and viewer (via
mu
andmu4e
)
IMAP (mbsync
)
You will need to:
- Install
mu
andisync
(sudo pacman -S mu isync
) - Set up a proper configuration file for your accounts at
~/.mbsyncrc
- Run
mu init --maildir=~/Maildir --my-address=user@host1 --my-address=user@host2
- Run
mbsync -c ~/.mbsyncrc -a
- For sending mails from
mu4e
, add a~/.authinfo
file, file contains a line in this formatmachine MAIL.DOMAIN.TLD login USER port 587 password PASSWD
- Encrypt the
~/.authinfo
file using GPGgpg -c ~/.authinfo
and delete the original unencrypted file.
I use a mbsyncrc
file for multi-accounts, with some hacks for Gmail accounts (to
rename the [Gmail]/...
folders). Here is an explained configuration example.
In the configuration file, there is a parameter named Pass
which should be set
to the password in plain text. Most of the examples you can find online uses
this parameter, but in real life, nobody uses it, it is extremely unsafe to put
the password in plain text configuration file. Instead, mbsync
configuration
file provides the alternative PassCmd
parameter, which can be set to an
arbitrary shell command which gets the password for you. You can set it for
example to call the pass
password manager to output the account password, or to
bw
command (for Bitwarden users). For me, I’m using it with Emacs'
~/.authinfo.gpg
, the PassCmd
in my configuration uses GPG and awk
to decrypt and
filter the file content to find the required account’s password. I set PassCmd
to something like this:
gpg -q --for-your-eyes-only --no-tty --logger-file /dev/null --batch -d ~/.authinfo.gpg | awk '/machine smtp\.googlemail\.com login username@gmail\.com/ {print $NF}'
Remember the line format in the ~/.authinfo.gpg
file:
machine smtp.googlemail.com login username@gmail.com port 587 password PASSWD
This PassCmd
command above, decrypts the ~/.authinfo.gpg
, passes it to awk
to
search the line containing "machine smtp.googlemail.com login username@gmail.com"
and prints the last field (the last field $NF
in the awk
command corresponds to the password, as you can see in the line format).
The whole ~/.mbsync
file should look like this:
# mbsync config file
# GLOBAL OPTIONS
BufferLimit 50mb # Global option: Default buffer size is 10M, too small for modern machines.
Sync All # Channels global: Sync everything "Pull Push New ReNew Delete Flags" (default option)
Create Both # Channels global: Automatically create missing mailboxes on both sides
Expunge Both # Channels global: Delete messages marked for deletion on both sides
CopyArrivalDate yes # Channels global: Propagate arrival time with the messages
# SECTION (IMAP4 Accounts)
IMAPAccount work # IMAP Account name
Host mail.host.ccc # The host to connect to
User user@host.ccc # Login user name
SSLVersions TLSv1.2 TLSv1.1 # Supported SSL versions
# Extract password from encrypted ~/.authinfo.gpg
# File format: "machine <SERVER> login <LOGIN> port <PORT> password <PASSWORD>"
# This uses sed to extract <PASSWORD> from line matching the account's <SERVER>
PassCmd "gpg2 -q --for-your-eyes-only --no-tty --logger-file /dev/null --batch -d ~/.authinfo.gpg | awk '/machine smtp.domain.tld/ {print $NF}'"
AuthMechs * # Authentication mechanisms
SSLType IMAPS # Protocol (STARTTLS/IMAPS)
CertificateFile /etc/ssl/certs/ca-certificates.crt
# END OF SECTION
# IMPORTANT NOTE: you need to keep the blank line after each section
# SECTION (IMAP Stores)
IMAPStore work-remote # Remote storage name
Account work # Associated account
# END OF SECTION
# SECTION (Maildir Stores)
MaildirStore work-local # Local storage (create directories with mkdir -p ~/Maildir/<ACCOUNT-NAME>)
Path ~/Maildir/work/ # The local store path
Inbox ~/Maildir/work/Inbox # Location of the INBOX
SubFolders Verbatim # Download all sub-folders
# END OF SECTION
# Connections specify links between remote and local folders
# they are specified using patterns, which match remote mail
# folders. Some commonly used patters include:
#
# - "*" to match everything
# - "!DIR" to exclude "DIR"
# - "DIR" to match DIR
#
# SECTION (Channels)
Channel work # Channel name
Far :work-remote: # Connect remote store
Near :work-local: # to the local one
Patterns "INBOX" "Drafts" "Sent" "Archives/*" "Spam" "Trash"
SyncState * # Save state in near side mailbox file ".mbsyncstate"
# END OF SECTION
# =================================================================================
IMAPAccount gmail
Host imap.gmail.com
User user@gmail.com
PassCmd "gpg2 -q --for-your-eyes-only --no-tty --logger-file /dev/null --batch -d ~/.authinfo.gpg | awk '/machine smtp.domain.tld/ {print $NF}'"
AuthMechs LOGIN
SSLType IMAPS
CertificateFile /etc/ssl/certs/ca-certificates.crt
IMAPStore gmail-remote
Account gmail
MaildirStore gmail-local
Path ~/Maildir/gmail/
Inbox ~/Maildir/gmail/Inbox
# For Gmail, I like to make multiple channels, one for each remote directory
# this is a trick to rename remote "[Gmail]/mailbox" to "mailbox"
Channel gmail-inbox
Far :gmail-remote:
Near :gmail-local:
Patterns "INBOX"
SyncState *
Channel gmail-trash
Far :gmail-remote:"[Gmail]/Trash"
Near :gmail-local:"Trash"
SyncState *
Channel gmail-drafts
Far :gmail-remote:"[Gmail]/Drafts"
Near :gmail-local:"Drafts"
SyncState *
Channel gmail-sent
Far :gmail-remote:"[Gmail]/Sent Mail"
Near :gmail-local:"Sent Mail"
SyncState *
Channel gmail-all
Far :gmail-remote:"[Gmail]/All Mail"
Near :gmail-local:"All Mail"
SyncState *
Channel gmail-starred
Far :gmail-remote:"[Gmail]/Starred"
Near :gmail-local:"Starred"
SyncState *
Channel gmail-spam
Far :gmail-remote:"[Gmail]/Spam"
Near :gmail-local:"Spam"
SyncState *
# GROUPS PUT TOGETHER CHANNELS, SO THAT WE CAN INVOKE
# MBSYNC ON A GROUP TO SYNC ALL CHANNELS
#
# FOR INSTANCE: "mbsync gmail" GETS MAIL FROM
# "gmail-inbox", "gmail-sent", and "gmail-trash"
#
# SECTION (Groups)
Group gmail
Channel gmail-inbox
Channel gmail-sent
Channel gmail-trash
Channel gmail-drafts
Channel gmail-all
Channel gmail-starred
Channel gmail-spam
# END OF SECTION
SMTP (msmtp
)
I was using the standard smtpmail
to send mails; but recently, I’m getting
problems when sending mails. I passed a whole day trying to fix mail sending for
one of my accounts, at the end of the day, I got a working setup; BUT, sending
the first mail always ask me about password! I need to enter the password to be
able to send the mail, Emacs asks me then if I want to save it to
~/.authifo.gpg
, when I confirm saving it, it got duplicated in the .authinfo.gpg
file.
This seems to be a bug; I also found somewhere that smtpmail
is buggy, and that
msmtp
seems to be a good alternative, so now I’m using a msmtp
-based setup, and
it works like a charm!
For this, we will need an additional configuration file, ~/.msmtprc
, I configure
it the same way as mbsync
, specifying this time SMTP servers instead of IMAP
ones. I extract the passwords from ~/.authinfo.gpg
using GPG and awk
, the same
way we did in mbsync
’s configuration.
The following is a sample file ~/.msmtprc
.
# Set default values for all following accounts.
defaults
auth on
tls on
tls_starttls on
tls_trust_file /etc/ssl/certs/ca-certificates.crt
logfile ~/.msmtp.log
# Gmail
account gmail
auth plain
host smtp.googlemail.com
port 587
from username@gmail.com
user username
passwordeval "gpg -q --for-your-eyes-only --no-tty --logger-file /dev/null --batch -d ~/.authinfo.gpg | awk '/machine smtp.googlemail.com login .*@gmail.com/ {print $NF}'"
add_missing_date_header on
## Gmail - aliases
account alias-account : gmail
from alias@mail.com
account other-alias : gmail
from other.alias@address.org
# Work
account work
auth on
host smtp.domaine.tld
port 587
from username@domaine.tld
user username
passwordeval "gpg -q --for-your-eyes-only --no-tty --logger-file /dev/null --batch -d ~/.authinfo.gpg | awk '/machine smtp.domaine.tld/ {print $NF}'"
tls_nocertcheck # ignore TLS certificate errors
Mail client and indexer (mu
and mu4e
)
I configure my email accounts in a private file in private/mu4e-accounts.el
,
which will be loaded after this common part:
(with-eval-after-load 'mu4e
;; Custom files
(setq mail-personal-alias-file (concat minemacs-config-dir "private/mail-aliases.mailrc")
mu4e-icalendar-diary-file (concat org-directory "icalendar-diary.org"))
;; Add a unified inbox shortcut
(add-to-list
'mu4e-bookmarks
'(:name "Unified inbox" :query "maildir:/.*inbox/" :key ?i) t)
;; Add shortcut to view spam messages
(add-to-list
'mu4e-bookmarks
'(:name "Spams" :query "maildir:/.*\\(spam\\|junk\\).*/" :key ?s) t)
;; The `+mu4e-extras-ignore-spams-query' function is defined in
;; `me-mu4e-extras'.
(with-eval-after-load 'me-mu4e-extras
;; Add shortcut to view yesterday's messages
(add-to-list
'mu4e-bookmarks
`(:name "Yesterday's messages" :query ,(+mu4e-extras-ignore-spams-query "date:1d..today") :key ?y) t))
;; Load my accounts
(+load minemacs-config-dir "private/mu4e-accounts.el")
(+load minemacs-config-dir "private/mu4e-extra-commands.el"))
Calendar
(with-eval-after-load 'calfw-ical
(+load minemacs-config-dir "private/calfw-sources.el"))
I like to use an MPD powered EMMS, so when I restart Emacs I do not lose my music.
EMPV
(with-eval-after-load 'empv
(setq
;; Set the radio channels, you can get streams from https://www.radio-browser.info
empv-radio-channels
'(("El-Bahdja FM" . "http://webradio.tda.dz:8001/ElBahdja_64K.mp3")
("El-Chaabia" . "https://radio-dzair.net/proxy/chaabia?mp=/stream")
("Quran Radio" . "http://stream.radiojar.com/0tpy1h0kxtzuv")
("Algeria International" . "https://webradio.tda.dz/Internationale_64K.mp3")
("JOW Radio" . "https://str0.creacast.com/jowradio")
("Europe1" . "http://ais-live.cloud-services.paris:8000/europe1.mp3")
("France Iter" . "http://direct.franceinter.fr/live/franceinter-hifi.aac")
("France Info" . "http://direct.franceinfo.fr/live/franceinfo-hifi.aac")
("France Culture" . "http://icecast.radiofrance.fr/franceculture-hifi.aac")
("France Musique" . "http://icecast.radiofrance.fr/francemusique-hifi.aac")
("FIP" . "http://icecast.radiofrance.fr/fip-hifi.aac")
("Beur FM" . "http://broadcast.infomaniak.ch/beurfm-high.aac")
("Skyrock" . "http://icecast.skyrock.net/s/natio_mp3_128k"))
;; See https://docs.invidious.io/instances/
empv-invidious-instance "https://invidious.projectsegfau.lt/api/v1"))
Programming
Tramp
(with-eval-after-load 'tramp
(setq
;; Do not use a separate history file for tramp sessions (buggy!)
tramp-histfile-override nil
;; Use Bash as a default remote shell
tramp-default-remote-shell "/bin/bash"
;; Use bash for encoding and decoding commands on the local host
tramp-encoding-shell "/bin/bash"))
Robot Operating System (ROS)
(with-eval-after-load 'ros
(setq ros-workspaces
(list
(ros-dump-workspace
:tramp-prefix "/docker:ros@ros-machine:"
:workspace "~/ros_ws"
:extends '("/opt/ros/noetic/"))
(ros-dump-workspace
:tramp-prefix "/docker:ros@ros-machine:"
:workspace "~/ros2_ws"
:extends '("/opt/ros/foxy/"))
(ros-dump-workspace
:tramp-prefix "/ssh:swd_sk@172.16.96.42:"
:workspace "~/ros_ws"
:extends '("/opt/ros/noetic/"))
(ros-dump-workspace
:tramp-prefix "/ssh:swd_sk@172.16.96.42:"
:workspace "~/ros2_ws"
:extends '("/opt/ros/foxy/")))))
Office
Org mode
Org mode tweaks
(with-eval-after-load 'org
(setq
;; Let's put our Org files here
org-directory "~/Dropbox/Org/"
;; Do not ask before tangling
org-confirm-babel-evaluate nil
;; The last level which is still exported as a headline
org-export-headline-levels 5
;; Default file for notes (for org-capture)
org-default-notes-file (concat org-directory "inbox.org")
+org-inbox-file (concat org-directory "inbox.org")
+org-projects-file (concat org-directory "projects.org")
;; Custom todo keyword faces
org-todo-keyword-faces
'(("IDEA" . (:foreground "goldenrod" :weight bold))
("NEXT" . (:foreground "IndianRed1" :weight bold))
("STRT" . (:foreground "OrangeRed" :weight bold))
("WAIT" . (:foreground "coral" :weight bold))
("KILL" . (:foreground "DarkGreen" :weight bold))
("PROJ" . (:foreground "LimeGreen" :weight bold))
("HOLD" . (:foreground "orange" :weight bold)))
;; Custom org-capture templates, see: https://orgmode.org/manual/Capture.html
org-capture-templates
`(("t" "Todo" entry (file+headline ,+org-inbox-file "Inbox")
"* TODO %?\n%i\n%a")
("i" "Idea" entry (file+headline ,+org-inbox-file "Ideas")
"* IDEA %?\n%T\n%i\n%a")
("p" "Project note" entry (file ,+org-projects-file)
"* %?\n%T\n%i\n%a")
("n" "Note" entry (file+headline ,+org-inbox-file "Notes")
"* NOTE %?\n%T\n%i\n%a")))
(setq org-tag-persistent-alist
'((:startgroup . nil)
("home" . ?h)
("research" . ?r)
("work" . ?w)
(:endgroup . nil)
(:startgroup . nil)
("tool" . ?o)
("dev" . ?d)
("report" . ?p)
(:endgroup . nil)
(:startgroup . nil)
("easy" . ?e)
("medium" . ?m)
("hard" . ?a)
(:endgroup . nil)
("urgent" . ?u)
("key" . ?k)
("bonus" . ?b)
("ignore" . ?i)
("noexport" . ?x)))
(setq org-tag-faces
'(("home" . (:foreground "goldenrod" :weight bold))
("research" . (:foreground "goldenrod" :weight bold))
("work" . (:foreground "goldenrod" :weight bold))
("tool" . (:foreground "IndianRed1" :weight bold))
("dev" . (:foreground "IndianRed1" :weight bold))
("report" . (:foreground "IndianRed1" :weight bold))
("urgent" . (:foreground "red" :weight bold))
("key" . (:foreground "red" :weight bold))
("easy" . (:foreground "green4" :weight bold))
("medium" . (:foreground "orange" :weight bold))
("hard" . (:foreground "red" :weight bold))
("bonus" . (:foreground "goldenrod" :weight bold))
("ignore" . (:foreground "Gray" :weight bold))
("noexport" . (:foreground "LimeGreen" :weight bold))))
;; stolen from https://github.com/yohan-pereira/.emacs#babel-config
;; (defun +org-confirm-babel-evaluate (lang body)
;; (not (string= lang "scheme"))) ;; Don't ask for scheme
;; (setq org-confirm-babel-evaluate #'+org-confirm-babel-evaluate)
(setq org-agenda-files (list +org-inbox-file
+org-projects-file
(concat org-directory "gcal-agenda.org"))
org-agenda-deadline-faces
'((1.001 . error)
(1.000 . org-warning)
(0.500 . org-upcoming-deadline)
(0.000 . org-upcoming-distant-deadline))
org-list-demote-modify-bullet
'(("+" . "-")
("-" . "+")
("*" . "+")
("1." . "a.")))
;; Needs to make a src_latex{\textsc{text}}?, with this hack you can write [[latex:textsc][Some text]].
(+shutup!
(org-add-link-type
"latex" nil
(lambda (path desc format)
(cond
((eq format 'html)
(format "<span class=\"%s\">%s</span>" path desc))
((eq format 'latex)
(format "{\\%s{%s}}" path desc))))))
(add-hook
'org-mode-hook
(defun +org--time-stamp-setup-h ()
(setq-local
time-stamp-active t
time-stamp-format "%04Y-%02m-%02d"
time-stamp-start "#\\+lastmod:[ \t]*"
time-stamp-end "$")))
(add-hook 'before-save-hook #'time-stamp))
Org + Hugo
(with-eval-after-load 'ox-hugo
(setq org-hugo-auto-set-lastmod t))
Org + LaTeX
(with-eval-after-load 'ox-latex
(add-to-list 'org-latex-packages-alist '("svgnames" "xcolor")))
Org-roam
(setq org-roam-directory "~/Dropbox/Org/slip-box/"
org-roam-db-location (concat org-roam-directory "org-roam.db"))
(with-eval-after-load 'org-roam
(add-to-list 'recentf-exclude org-roam-directory)
(advice-add
#'doom-modeline-buffer-file-name
:around
(defun +doom-modeline--org-roam-buffer-file-name-a (orig-fun)
(if (string-search (expand-file-name org-roam-directory) (or buffer-file-name ""))
(replace-regexp-in-string
"\\(?:^\\|.*/\\)\\([0-9]\\{4\\}\\)\\([0-9]\\{2\\}\\)\\([0-9]\\{2\\}\\)[0-9]*-"
(concat (nerd-icons-codicon "nf-cod-note") " (\\1-\\2-\\3) ")
(subst-char-in-string ?_ ? buffer-file-name))
(funcall orig-fun)))))
Bibliography
Org-cite
(with-eval-after-load 'oc
(setq org-cite-csl-styles-dir +biblio-styles-path
org-cite-global-bibliography (ensure-list +biblio-libraries-path)))
Citar
(with-eval-after-load 'citar
(setq citar-library-paths (ensure-list +biblio-storage-path)
citar-notes-paths (ensure-list +biblio-notes-path)
citar-bibliography (ensure-list +biblio-libraries-path)))
Text editing
Helper commands
(defun +helper--in-buffer-replace (old new)
"Replace OLD with NEW in the current buffer."
(save-excursion
(goto-char (point-min))
(let ((case-fold-search nil)
(matches 0))
(while (re-search-forward old nil t)
(replace-match new)
(cl-incf matches))
matches)))
(defun +helper-clear-frenchy-ponctuations ()
"Replace french ponctuations (like unsectable space) by regular ones."
(interactive)
(let ((chars
'(("[\u00a0\u200b]" . "") ;; Non-breaking and zero-width spaces
;; Special spaces and quads
("[\u2000-\u200A\u202F\u205F\u3000]" . " ")
("[‘’‚’]" . "'")
("[“”„”«»]" . "\"")))
(matches 0))
(dolist (pair chars)
(cl-incf matches (+helper--in-buffer-replace (car pair) (cdr pair))))
(message "Replaced %d match%s." matches (if (> matches 1) "es" ""))))
(defun +yank-region-as-paragraph ()
"Yank region as one paragraph. This removes new line characters between lines."
(interactive)
(when (use-region-p)
(let ((text (buffer-substring-no-properties (region-beginning) (region-end))))
(with-temp-buffer
(insert text)
(goto-char (point-min))
(let ((case-fold-search nil))
(while (re-search-forward "\n[^\n]" nil t)
(replace-region-contents
(- (point) 2) (- (point) 1)
(lambda (&optional a b) " ")))
(kill-new (buffer-string)))))))
System configuration
Mime types
Org mode files
Org mode isn’t recognized as its own mime type by default, but that can easily
be changed with the following file. For system-wide changes try
/usr/share/mime/packages/org.xml
.
<mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'>
<mime-type type="text/org">
<comment>Emacs Org-mode File</comment>
<glob pattern="*.org"/>
<alias type="text/org"/>
</mime-type>
</mime-info>
What’s nice is that Papirus now has an icon for text/org
. One simply needs to
refresh their mime database:
update-mime-database ~/.local/share/mime
Then set Emacs as the default editor:
xdg-mime default emacs-client.desktop text/org
Registering org-protocol://
The recommended method of registering a protocol is by registering a desktop application, which seems reasonable.
[Desktop Entry]
Name=Emacs Org-Protocol
Exec=emacsclient %u
Icon=/home/hacko/.doom.d/assets/org-mode.svg
Type=Application
Terminal=false
MimeType=x-scheme-handler/org-protocol
To associate org-protocol://
links with the desktop file:
xdg-mime default org-protocol.desktop x-scheme-handler/org-protocol
Configuring Chrome/Brave
As specified in the official documentation, we would like to invoke the
org-protocol://
without confirmation. To do this, we need to add this
system-wide configuration.
read -p "Do you want to set Chrome/Brave to show the 'Always open ...' checkbox, to be used with the 'org-protocol://' registration? [Y | N]: " INSTALL_CONFIRM
if [[ "$INSTALL_CONFIRM" == "Y" ]]
then
sudo mkdir -p /etc/opt/chrome/policies/managed/
sudo tee /etc/opt/chrome/policies/managed/external_protocol_dialog.json > /dev/null <<'EOF'
{
"ExternalProtocolDialogShowAlwaysOpenCheckbox": true
}
EOF
sudo chmod 644 /etc/opt/chrome/policies/managed/external_protocol_dialog.json
fi
Then add a bookmarklet in your browser with this code:
javascript:location.href =
'org-protocol://roam-ref?template=r&ref='
+ encodeURIComponent(location.href)
+ '&title='
+ encodeURIComponent(document.title)
+ '&body='
+ encodeURIComponent(window.getSelection())
Git
Git diffs
Based on this gist and this article.
*.tex diff=tex
*.bib diff=bibtex
*.{c,h,c++,h++,cc,hh,cpp,hpp} diff=cpp
*.m diff=matlab
*.py diff=python
*.rb diff=ruby
*.php diff=php
*.pl diff=perl
*.{html,xhtml} diff=html
*.f diff=fortran
*.{el,lisp,scm} diff=lisp
*.r diff=rstats
*.texi* diff=texinfo
*.org diff=org
*.rs diff=rust
*.odt diff=odt
*.odp diff=libreoffice
*.ods diff=libreoffice
*.doc diff=doc
*.xls diff=xls
*.ppt diff=ppt
*.docx diff=docx
*.xlsx diff=xlsx
*.pptx diff=pptx
*.rtf diff=rtf
*.{png,jpg,jpeg,gif} diff=exif
*.pdf diff=pdf
*.djvu diff=djvu
*.epub diff=pandoc
*.chm diff=tika
*.mhtml? diff=tika
*.{class,jar} diff=tika
*.{rar,7z,zip,apk} diff=tika
Then adding some regular expressions for it to ~/.config/git/config
, with some
tools to view diffs on binary files.
# ====== TEXT FORMATS ======
[diff "org"]
xfuncname = "^(\\*+ +.*)$"
[diff "lisp"]
xfuncname = "^(\\(.*)$"
[diff "rstats"]
xfuncname = "^([a-zA-z.]+ <- function.*)$"
[diff "texinfo"]
# from http://git.savannah.gnu.org/gitweb/?p=coreutils.git;a=blob;f=.gitattributes;h=c3b2926c78c939d94358cc63d051a70d38cfea5d;hb=HEAD
xfuncname = "^@node[ \t][ \t]*\\([^,][^,]*\\)"
[diff "rust"]
xfuncname = "^[ \t]*(pub|)[ \t]*((fn|struct|enum|impl|trait|mod)[^;]*)$"
# ====== BINARY FORMATS ======
[diff "pdf"]
binary = true
# textconv = pdfinfo
# textconv = sh -c 'pdftotext "$@" -' # sudo apt install pdftotext
textconv = sh -c 'pdftotext -layout "$0" -enc UTF-8 -nopgbrk -q -'
cachetextconv = true
[diff "djvu"]
binary = true
# textconv = pdfinfo
textconv = djvutxt # yay -S djvulibre
cachetextconv = true
[diff "odt"]
textconv = odt2txt
# textconv = pandoc --standalone --from=odt --to=plain
binary = true
cachetextconv = true
[diff "doc"]
# textconv = wvText
textconv = catdoc # yay -S catdoc
binary = true
cachetextconv = true
[diff "xls"]
# textconv = in2csv
# textconv = xlscat -a UTF-8
# textconv = soffice --headless --convert-to csv
textconv = xls2csv # yay -S catdoc
binary = true
cachetextconv = true
[diff "ppt"]
textconv = catppt # yay -S catdoc
binary = true
cachetextconv = true
[diff "docx"]
textconv = pandoc --standalone --from=docx --to=plain
# textconv = sh -c 'docx2txt.pl "$0" -'
binary = true
cachetextconv = true
[diff "xlsx"]
textconv = xlsx2csv # pip install xlsx2csv
# textconv = in2csv
# textconv = soffice --headless --convert-to csv
binary = true
cachetextconv = true
[diff "pptx"]
# pip install --user pptx2md (currently not wotking with Python 3.10)
# textconv = sh -c 'pptx2md --disable_image --disable_wmf -i "$0" -o ~/.cache/git/presentation.md >/dev/null && cat ~/.cache/git/presentation.md'
# Alternative hack, convert PPTX to PPT, then use the catppt tool
textconv = sh -c 'soffice --headless --convert-to ppt --outdir /tmp "$0" && TMP_FILENAME=$(basename -- "$0") && catppt "/tmp/${TMP_FILENAME%.*}.ppt"'
binary = true
cachetextconv = true
[diff "rtf"]
textconv = unrtf --text # yay -S unrtf
binary = true
cachetextconv = true
[diff "epub"]
textconv = pandoc --standalone --from=epub --to=plain
binary = true
cachetextconv = true
[diff "tika"]
textconv = tika --config=~/.local/share/tika/tika-conf.xml --text
binary = true
cachetextconv = true
[diff "libreoffice"]
textconv = soffice --cat
binary = true
cachetextconv = true
[diff "exif"]
binary = true
textconv = exiftool # sudo apt install perl-image-exiftool
Extensions
This tool is taken from https://gist.github.com/nottrobin/5758221. It is
attributed to David Underhill. It is a script to permanently delete
files/folders from a Git repository. To use it, cd to your repository’s root,
then run the script with a list of paths you want to delete, e.g.,
git-prune-files path1 path2
#!/usr/bin/env bash
set -o errexit
if [ $# -eq 0 ]; then
echo "Usage: git prune-files <path1> [<path2> ...]" 1>&2
exit 0
fi
# make sure we're at the root of git repo
if [ ! -d .git ]; then
echo "Error: must run this script from the root of a git repository" 1>&2
exit 1
fi
# remove all paths passed as arguments from the history of the repo
files=$@
git filter-branch --index-filter "git rm -rf --cached --ignore-unmatch $files" HEAD
# remove the temporary history git-filter-branch otherwise leaves behind for a long time
rm -rf .git/refs/original/ && git reflog expire --all && git gc --aggressive --prune
Apache Tika App wrapper
Apache Tika is a content detection and analysis framework. It detects and extracts metadata and text from over a thousand different file types. We will be using the Tika App in command-line mode to show some meaningful diff information for some binary files.
First, let’s add a custom script to run tika-app
:
#!/bin/sh
APACHE_TIKA_JAR="$HOME/.local/share/tika/tika-app.jar"
if [ -f "${APACHE_TIKA_JAR}" ]
then
exec java -Dfile.encoding=UTF-8 -jar "${APACHE_TIKA_JAR}" "$@" 2>/dev/null
else
echo "JAR file not found at ${APACHE_TIKA_JAR}"
fi
Add tika
’s installation instructions to the setup.sh
file.
update_apache_tika () {
TIKA_JAR_PATH="$HOME/.local/share/tika"
if [ ! -d "${TIKA_JAR_PATH}" ]
then
mkdir -p "${TIKA_JAR_PATH}"
fi
TIKA_BASE_URL=https://archive.apache.org/dist/tika/
TIKA_JAR_LINK="${TIKA_JAR_PATH}/tika-app.jar"
echo -n "Checking for new Apache Tika App version... "
# Get the lastest version
TIKA_VERSION=$(
curl -s "${TIKA_BASE_URL}" | # Get the page
pandoc -f html -t plain | # Convert HTML page to plain text.
awk '/([0-9]+\.)+[0-1]\// {print substr($1, 0, length($1)-1)}' | # Get the versions directories (pattern: X.X.X/)
sort -rV | # Sort versions, the newest first
head -n 1 # Get the first (newest) version
)
if [ -z "${TIKA_VERSION}" ]
then
echo "Failed, check your internet connection."
exit 1
fi
echo "Lastest version is ${TIKA_VERSION}"
TIKA_JAR="${TIKA_JAR_PATH}/tika-app-${TIKA_VERSION}.jar"
TIKA_JAR_URL="${TIKA_BASE_URL}${TIKA_VERSION}/tika-app-${TIKA_VERSION}.jar"
if [ ! -f "${TIKA_JAR}" ]
then
echo "New version available!"
read -p "Do you want to download Apache Tika App v${TIKA_VERSION}? [Y | N]: " INSTALL_CONFIRM
if [[ "$INSTALL_CONFIRM" == "Y" ]]
then
curl -o "${TIKA_JAR}" "${TIKA_JAR_URL}" && echo "Apache Tika App v${TIKA_VERSION} downloaded successfully"
fi
else
echo "Apache Tika App is up-to-date, version ${TIKA_VERSION} already downloaded to '${TIKA_JAR}'"
fi
# Check the existance of the symbolic link
if [ -L "${TIKA_JAR_LINK}" ]
then
unlink "${TIKA_JAR_LINK}"
fi
# Create a symbolic link to the installed version
ln -s "${TIKA_JAR}" "${TIKA_JAR_LINK}"
}
update_apache_tika;
When it detects that Tesseract is installed, Tika App will try to extract text
from some file types. For some reason, it tries to use Tesseract with some
compressed files like *.bz2
, *.apk
… etc. I would like to disable this feature
by exporting an XML config file which will be used when launching the Tika App
(using --config=<tika-config.xml>
).
<?xml version="1.0" encoding="UTF-8"?>
<properties>
<parsers>
<parser class="org.apache.tika.parser.DefaultParser">
<parser-exclude class="org.apache.tika.parser.ocr.TesseractOCRParser"/>
</parser>
</parsers>
</properties>
Command-line wrapper
A wrapper around emacsclient
:
- Accepting
stdin
by putting it in a temporary file and immediately opening it. - Guessing that the
tty
is a good idea when$DISPLAY
is unset (relevant with SSH sessions, among other things). - With a whiff of 24-bit color support, sets
TERM
variable to aterminfo
that (probably) announces 24-bit color support. - Changes GUI
emacsclient
instances to be non-blocking by default (--no-wait
), and instead take a flag to suppress this behavior (-w
).
I would use sh
, but using arrays for argument manipulation is just too
convenient, so I’ll raise the requirement to bash
. Since arrays are the only
’extra’ compared to sh
, other shells like ksh
etc. should work too.
#!/usr/bin/env bash
force_tty=false
force_wait=false
stdin_mode=""
args=()
usage () {
echo -e "Usage: e [-t] [-m MODE] [OPTIONS] FILE [-]
Emacs client convenience wrapper.
Options:
-h, --help Show this message
-t, -nw, --tty Force terminal mode
-w, --wait Don't supply --no-wait to graphical emacsclient
- Take stdin (when last argument)
-m MODE, --mode MODE Mode to open stdin with
-mm, --maximized Start Emacs client in maximized window
Run emacsclient --help to see help for the emacsclient."
}
while :
do
case "$1" in
-t | -nw | --tty)
force_tty=true
shift ;;
-w | --wait)
force_wait=true
shift ;;
-m | --mode)
stdin_mode=" ($2-mode)"
shift 2 ;;
-mm | --maximized)
args+=("--frame-parameters='(fullscreen . maximized)")
shift ;;
-h | --help)
usage
exit 0 ;;
--*=*)
set -- "$@" "${1%%=*}" "${1#*=}"
shift ;;
*)
[ "$#" = 0 ] && break
args+=("$1")
shift ;;
esac
done
if [ ! "${#args[*]}" = 0 ] && [ "${args[-1]}" = "-" ]
then
unset 'args[-1]'
TMP="$(mktemp /tmp/emacsstdin-XXX)"
cat > "$TMP"
args+=(--eval "(let ((b (generate-new-buffer \"*stdin*\"))) (switch-to-buffer b) (insert-file-contents \"$TMP\") (delete-file \"$TMP\")${stdin_mode})")
fi
if [ -z "$DISPLAY" ] || $force_tty
then
# detect terminals with sneaky 24-bit support
if { [ "$COLORTERM" = truecolor ] || [ "$COLORTERM" = 24bit ]; } \
&& [ "$(tput colors 2>/dev/null)" -lt 257 ]
then
if echo "$TERM" | grep -q "^\w\+-[0-9]"
then
termstub="${TERM%%-*}"
else
termstub="${TERM#*-}"
fi
if infocmp "$termstub-direct" >/dev/null 2>&1
then
TERM="$termstub-direct"
else
TERM="xterm-direct"
fi # should be fairly safe
fi
emacsclient --tty -create-frame --alternate-editor="/usr/bin/emacs" "${args[@]}"
else
if ! $force_wait
then
args+=(--no-wait)
fi
emacsclient -create-frame --alternate-editor="/usr/bin/emacs" "${args[@]}"
fi
Useful aliases
Now, to set an alias to use e
with magit
, and then for maximum laziness we can
set aliases for the terminal-forced variants.
# -*- mode: sh; -*-
# Aliases to run emacs+magit
alias magit='e --eval "(progn (magit-status) (delete-other-windows))"'
alias magitt='e -t --eval "(progn (magit-status) (delete-other-windows))"'
# Aliases to run emacs+mu4e
alias emu='e --eval "(progn (=mu4e) (delete-other-windows))"'
alias emut='e -t --eval "(progn (=mu4e) (delete-other-windows))"'
And this to launch Emacs in terminal mode et
, I use this as a default $EDITOR
#!/usr/bin/env bash
e -t "$@"
And ev
for use with $VISUAL
:
#!/usr/bin/env bash
e -w "$@"
export EDITOR="$HOME/.local/bin/et"
export VISUAL="$HOME/.local/bin/ev"
AppImage
Install/update the appimageupdatetool.AppImage
tool:
update_appimageupdatetool () {
TOOL_NAME=appimageupdatetool
MACHINE_ARCH=$(uname -m)
APPIMAGE_UPDATE_TOOL_PATH="$HOME/.local/bin/${TOOL_NAME}"
APPIMAGE_UPDATE_TOOL_URL="https://github.com/AppImage/AppImageUpdate/releases/download/continuous/${TOOL_NAME}-${MACHINE_ARCH}.AppImage"
if [ -f "${APPIMAGE_UPDATE_TOOL_PATH}" ] && "$APPIMAGE_UPDATE_TOOL_PATH" -j "${APPIMAGE_UPDATE_TOOL_PATH}" 2&>/dev/null
then
echo "${TOOL_NAME} already up to date"
else
if [ -f "${APPIMAGE_UPDATE_TOOL_PATH}" ]
then
echo "Update available, downloading latest ${MACHINE_ARCH} version to ${APPIMAGE_UPDATE_TOOL_PATH}"
mv "${APPIMAGE_UPDATE_TOOL_PATH}" "${APPIMAGE_UPDATE_TOOL_PATH}.backup"
else
echo "${TOOL_NAME} not found, downloading latest ${MACHINE_ARCH} version to ${APPIMAGE_UPDATE_TOOL_PATH}"
fi
wget -O "${APPIMAGE_UPDATE_TOOL_PATH}" "${APPIMAGE_UPDATE_TOOL_URL}" && # 2&>/dev/null
echo "Downloaded ${TOOL_NAME}-${MACHINE_ARCH}.AppImage" &&
[ -f "${APPIMAGE_UPDATE_TOOL_PATH}.backup" ] &&
rm "${APPIMAGE_UPDATE_TOOL_PATH}.backup"
chmod a+x "${APPIMAGE_UPDATE_TOOL_PATH}"
fi
}
update_appimageupdatetool;
Oh-my-Zsh
Path
Path to your oh-my-zsh installation.
export ZSH="$HOME/.oh-my-zsh"
Themes and customization
Set name of the theme to load, if set to "random"
, it will load a random theme
each time oh-my-zsh is loaded, in which case, to know which specific one was
loaded, run: echo $RANDOM_THEME
See github.com/ohmyzsh/ohmyzsh/wiki/Themes.
# Typewritten customizations
TYPEWRITTEN_RELATIVE_PATH="adaptive"
TYPEWRITTEN_CURSOR="underscore"
ZSH_THEME="typewritten/typewritten"
# Set list of themes to pick from when loading at random
# Setting this variable when ZSH_THEME=random will cause zsh to load
# a theme from this variable instead of looking in $ZSH/themes/
# If set to an empty array, this variable will have no effect.
# ZSH_THEME_RANDOM_CANDIDATES=( "robbyrussell" "agnoster" )
Behavior
# Uncomment the following line to use case-sensitive completion.
# CASE_SENSITIVE="true"
# Uncomment the following line to use hyphen-insensitive completion.
# Case-sensitive completion must be off. _ and - will be interchangeable.
# HYPHEN_INSENSITIVE="true"
# Uncomment the following line to disable bi-weekly auto-update checks.
# DISABLE_AUTO_UPDATE="true"
# Uncomment the following line to automatically update without prompting.
DISABLE_UPDATE_PROMPT="true"
# Uncomment the following line to change how often to auto-update (in days).
export UPDATE_ZSH_DAYS=3
# Uncomment the following line if pasting URLs and other text is messed up.
# DISABLE_MAGIC_FUNCTIONS="true"
# Uncomment the following line to disable colors in ls.
# DISABLE_LS_COLORS="true"
# Uncomment the following line to disable auto-setting terminal title.
# DISABLE_AUTO_TITLE="true"
# Uncomment the following line to enable command auto-correction.
# ENABLE_CORRECTION="true"
# Uncomment the following line to display red dots whilst waiting for completion.
# COMPLETION_WAITING_DOTS="true"
# Uncomment the following line if you want to disable marking untracked files
# under VCS as dirty. This makes repository status check for large repositories
# much, much faster.
# DISABLE_UNTRACKED_FILES_DIRTY="true"
# Uncomment the following line if you want to change the command execution time
# stamp shown in the history command output.
# You can set one of the optional three formats:
# "mm/dd/yyyy"|"dd.mm.yyyy"|"yyyy-mm-dd"
# or set a custom format using the strftime function format specifications,
# see 'man strftime' for details.
# HIST_STAMPS="mm/dd/yyyy"
Plugins
# Would you like to use another custom folder than $ZSH/custom?
ZSH_CUSTOM=$HOME/.config/my_ohmyzsh_customizations
# Which plugins would you like to load?
# Standard plugins can be found in $ZSH/plugins/
# Custom plugins may be added to $ZSH_CUSTOM/plugins/
# Example format: plugins=(rails git textmate ruby lighthouse)
# Add wisely, as too many plugins slow down shell startup.
plugins=(
zsh-autosuggestions
zsh-navigation-tools
zsh-interactive-cd
archlinux
ssh-agent
sudo
docker
systemd
tmux
python
pip
rust
repo
cp
rsync
ripgrep
fzf
fd
z
)
Bootstrap Oh-my-Zsh
source $ZSH/oh-my-zsh.sh
Aliases
# Aliases
alias zshconfig="vim ~/.zshrc"
alias ohmyzsh="ranger $ZSH"
Zsh user configuration
pbcopy
and pbpaste
I like to define MacOS-like commands (pbcopy
and pbpaste
) to copy and paste in
terminal (from stdin
, to stdout
). The pbcopy
and pbpaste
are defined using
either xclip
or xsel
, you would need to install these tools, otherwise we wouldn’t
define the aliases.
# Define aliases to 'pbcopy' and 'pbpaste'
if command -v xclip &> /dev/null
then
# Define aliases using xclip
alias pbcopy='xclip -selection clipboard'
alias pbpaste='xclip -selection clipboard -o'
elif command -v xsel &> /dev/null
then
# Define aliases using xsel
alias pbcopy='xsel --clipboard --input'
alias pbpaste='xsel --clipboard --output'
fi
netpaste
Define a netpaste
command to paste to a Pastebin server.
alias netpaste='curl -F file=@- 0x0.st' # OR 'curl -F f:1=<- ix.io '
Sudo GUI!
And then define gsuon
and gsuoff
aliases to run graphical apps from terminal
with root permissions, this requires xhost
.
# To run GUI apps from terminal with root permissions
if command -v xhost &> /dev/null
then
alias gsuon='xhost si:localuser:root'
alias gsuoff='xhost -si:localuser:root'
fi
Neovim
Use Neovim instead of VIM to provide vi
and vim
commands.
# NeoVim
if command -v nvim &> /dev/null
then
alias vim="nvim"
alias vi="nvim"
fi
ESP-IDF
Add some aliases to work with the ESP-IDF framework.
if [ -d "$HOME/Softwares/src/esp-idf/" ]
then
alias esp-prepare-env='source $HOME/Softwares/src/esp-idf/export.sh'
alias esp-update='echo "Updating ESP-IDF framework..." && cd $HOME/src/esp-idf && git pull --all && echo "Updated successfully"'
else
alias esp-prepare-env='echo "esp-idf repo not found. You can clone the esp-idf repo using git clone https://github.com/espressif/esp-idf.git"'
alias esp-update=esp-prepare-env
fi
CLI wttrin client
Define an alias to get weather information for my city:
alias wttrin='curl wttr.in/$WTTRIN_CITY'
alias wttrin2='curl v2.wttr.in/$WTTRIN_CITY'
Minicom
Enable Meta key and colors in minicom
:
export MINICOM='-m -c on'
Rust
Define Rust sources path, and add packages installed from cargo
to the PATH
.
export RUST_SRC_PATH=$HOME/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/
export PATH=$PATH:$HOME/.cargo/bin
I’m using the AUR package clang-format-static-bin
, which provide multiple
versions of Clang-format, I use it with some work projects requiring a specific
version of Clang-format.
Clang-format
[ -d /opt/clang-format-static ] && export PATH=$PATH:/opt/clang-format-static
CMake
Add my manually built libraries to CMake and PATH
.
export CMAKE_PREFIX_PATH=$HOME/Softwares/src/install
export PATH=$PATH:$HOME/Softwares/src/install/bin
Node
Set NPM installation path to local:
NPM_PACKAGES="${HOME}/.npm-packages"
# Export NPM bin path
export PATH="$PATH:$NPM_PACKAGES/bin"
# Preserve MANPATH if you already defined it somewhere in your config.
# Otherwise, fall back to `manpath` so we can inherit from `/etc/manpath`.
export MANPATH="${MANPATH-$(manpath)}:$NPM_PACKAGES/share/man"
# Tell Node about these packages
export NODE_PATH="$NPM_PACKAGES/lib/node_modules:$NODE_PATH"
Tell NPM to use this directory for its global package installs by adding this
in ~/.npmrc
:
prefix = ~/.npm-packages
Some useful stuff (fzf
, opam
, Doom Emacs…)
tmux
I like to use tmux
by default, even on my local sessions, I like to start a tmux
in a default
session on the first time I launch a terminal, and then, attach any
other terminal to this default session:
# If not running inside Emacs (via vterm/eshell...)
if [ -z $INSIDE_EMACS ]
then
if command -v tmux &> /dev/null && [ -z "$TMUX" ]
then
tmux attach -t default || tmux new -s default
fi
fi
Other stuff
# You may need to manually set your language environment
# export LANG=en_US.UTF-8
# Preferred editor for local and remote sessions
# if [[ -n $SSH_CONNECTION ]]; then
# export EDITOR='vim'
# else
# export EDITOR='mvim'
# fi
# Compilation flags
# export ARCHFLAGS="-arch x86_64"
# FZF
[ -f ~/.fzf.zsh ] && source ~/.fzf.zsh
# OPAM configuration
[[ ! -r $HOME/.opam/opam-init/init.zsh ]] || source $HOME/.opam/opam-init/init.zsh > /dev/null 2> /dev/null
# Add ~/.config/emacs/bin to path (for DOOM Emacs stuff)
export PATH=$PATH:$HOME/.config/emacs/bin
Define some environment variables.
source ~/.zshrc_private
Load my bitwarden-cli
session, exported to BW_SESSION
.
[ -f ~/.bitwarden-session ] && source ~/.bitwarden-session
Rust format
For Rust code base, the file $HOME/.rustfmt.toml
contains the global format
settings, I like to set it to:
# Rust edition 2018
edition = "2018"
# Use Unix style newlines, with 2 spaces tabulation.
newline_style = "Unix"
tab_spaces = 2
hard_tabs = false
# Make one line functions in a single line
fn_single_line = true
# Format strings
format_strings = true
# Increase the max line width
max_width = 120
# Merge nested imports
merge_imports = true
# Enum and Struct alignement
enum_discrim_align_threshold = 20
struct_field_align_threshold = 20
# Reorder impl items: type > const > macros > methods.
reorder_impl_items = true
# Comments and documentation formating
wrap_comments = true
normalize_comments = true
normalize_doc_attributes = true
format_code_in_doc_comments = true
report_fixme = "Always"
todo = "Always"
eCryptfs
Unlock and mount script
#!/bin/sh -e
# This script mounts a user's confidential private folder
#
# Original by Michael Halcrow, IBM
# Extracted to a stand-alone script by Dustin Kirkland <kirkland@ubuntu.com>
# Modified by: Abdelhak Bougouffa <abougouffa@fedoraproject.org>
#
# This script:
# * interactively prompts for a user's wrapping passphrase (defaults to their
# login passphrase)
# * checks it for validity
# * unwraps a users mount passphrase with their supplied wrapping passphrase
# * inserts the mount passphrase into the keyring
# * and mounts a user's encrypted private folder
PRIVATE_DIR="Private"
PW_ATTEMPTS=3
MESSAGE=`gettext "Enter your login passphrase:"`
if [ -f $HOME/.ecryptfs/wrapping-independent ]
then
# use a wrapping passphrase different from the login passphrase
MESSAGE=`gettext "Enter your wrapping passphrase:"`
fi
WRAPPED_PASSPHRASE_FILE="$HOME/.ecryptfs/wrapped-passphrase"
MOUNT_PASSPHRASE_SIG_FILE="$HOME/.ecryptfs/$PRIVATE_DIR.sig"
# First, silently try to perform the mount, which would succeed if the appropriate
# key is available in the keyring
if /sbin/mount.ecryptfs_private >/dev/null 2>&1
then
exit 0
fi
# Otherwise, interactively prompt for the user's password
if [ -f "$WRAPPED_PASSPHRASE_FILE" -a -f "$MOUNT_PASSPHRASE_SIG_FILE" ]
then
tries=0
while [ $tries -lt $PW_ATTEMPTS ]
do
LOGINPASS=`zenity --password --title "eCryptFS: $MESSAGE"`
if [ $(wc -l < "$MOUNT_PASSPHRASE_SIG_FILE") = "1" ]
then
# No filename encryption; only insert fek
if printf "%s\0" "$LOGINPASS" | ecryptfs-unwrap-passphrase "$WRAPPED_PASSPHRASE_FILE" - | ecryptfs-add-passphrase -
then
break
else
zenity --error --title "eCryptfs" --text "Error: Your passphrase is incorrect"
tries=$(($tries + 1))
continue
fi
else
if printf "%s\0" "$LOGINPASS" | ecryptfs-insert-wrapped-passphrase-into-keyring "$WRAPPED_PASSPHRASE_FILE" -
then
break
else
zenity --error --title "eCryptfs" --text "Error: Your passphrase is incorrect"
tries=$(($tries + 1))
continue
fi
fi
done
if [ $tries -ge $PW_ATTEMPTS ]
then
zenity --error --title "eCryptfs" --text "Too many incorrect password attempts, exiting"
exit 1
fi
/sbin/mount.ecryptfs_private
else
zenity --error --title "eCryptfs" --text "Encrypted private directory is not setup properly"
exit 1
fi
if grep -qs "$HOME/.Private $PWD ecryptfs " /proc/mounts 2>/dev/null; then
zenity --info --title "eCryptfs" --text "Your private directory has been mounted."
fi
dolphin "$HOME/Private"
exit 0
Desktop integration
[Desktop Entry]
Type=Application
Version=1.0
Name=eCryptfs Unlock Private Directory
Icon=unlock
Exec=/home/hacko/.ecryptfs/ecryptfs-mount-private-gui
Terminal=False
GDB
Early init
I like to disable the initial message (containing copyright info and other
stuff), the right way to do this is either by starting gdb
with -q
option, or
(since GDB v11 I think), by setting in
~/.gdbearlyinit
.
# GDB early init file
# Abdelhak Bougouffa (c) 2022
# Disable showing the initial message
set startup-quietly
Init
GDB loads $HOME/.gdbinit
at startup, I like to define some default options in
this file, this is a WIP, but it won’t evolve too much, as it is recommended to
keep the .gdbinit
clean and simple. For the moment, it does just enable pretty
printing, and defines the c
and n
commands to wrap continue
and next
with a post
refresh
, which is helpful with the annoying TUI when the program outputs to the
stdout.
# GDB init file
# Abdelhak Bougouffa (c) 2022
# Save history
set history save on
set history filename ~/.gdb_history
set history remove-duplicates 2048
# When debugging my apps, debug information of system libraries
# aren't that important
set debuginfod enabled off
# Set pretty print
set print pretty on
# I hate stepping into system libraries when I'm debugging my
# crappy stuff, so lets add system headers to skipped files
skip pending on
python
import os
# Add paths here, they will be explored recursivly
LIB_PATHS = ["/usr/include" "/usr/local/include"]
for lib_path in LIB_PATHS:
for root, dirs, files in os.walk(lib_path):
for file in files:
cmd = f"skip file {os.path.join(root, file)}"
gdb.execute(cmd, True, to_string=True)
end
skip enable
skip pending on
guile
<<gdb-init-guile>>
end
skip enable
# This fixes the annoying ncurses TUI gliches and saves typing C-l each time to refresh the screen
define cc
continue
refresh
end
define nn
next
refresh
end
GnuPG
I add this to my ~/.gnupg/gpg-agent.conf
, to set the time-to-live to one day.
# Do not ask me about entered passwords for 24h (during the same session)
default-cache-ttl 86400
max-cache-ttl 86400
# As I'm using KDE, use Qt based pinentry tool instead of default GTK+
pinentry-program /usr/bin/pinentry-qt
# Allow pinentry in Emacs minibuffer (combined with epg-pinentry-mode)
allow-loopback-pinentry
allow-emacs-pinentry
OCR This
This creates a script named ocrthis
that can be bound to OS shortcut. When
triggered:
- it shows a selection tool to take a partial screenshot,
- it runs
tesseract
to extract the text, - and then, it copies it to clipboard.
#!/bin/bash
IMG=$(mktemp -u --suffix=".png")
scrot -s "$IMG" -q 100
mogrify -modulate 100,0 -resize 400% "$IMG"
tesseract "$IMG" - -l eng 2> /dev/null | xsel -ib
Slack
This script is called at system startup.
#!/bin/bash
WEEK_DAY=$(date +%u)
HOUR=$(date +%H)
SLACK=$(which slack)
if [ ! "$WEEK_DAY" == "6" ] && [ ! "$WEEK_DAY" == "7" ] && [ "$HOUR" -gt 7 ] && [ "$HOUR" -lt 18 ] ; then
$SLACK -u %U
else
echo "It is not work time!"
fi
Arch Linux packages
Here, we install Arch packages
check_and_install_pkg() {
PKG_NAME="$1"
if ! pacman -Qiq "${PKG_NAME}" &>/dev/null; then
echo "Package ${PKG_NAME} is missing, installing it using yay"
yay -S "${PKG_NAME}"
fi
}
PKGS_LIST=(
git ripgrep fd gnupg fzf the_silver_searcher
ttf-ibm-plex ttf-fira-code ttf-roboto-mono ttf-overpass ttf-lato ttf-input
ttf-cascadia-code ttf-jetbrains-mono ttf-fantasque-sans-mono
ttc-iosevka ttf-iosevka-nerd ttc-iosevka-slab ttc-iosevka-curly
ttc-iosevka-curly-slab ttc-iosevka-etoile ttc-iosevka-ss09
ccls cppcheck clang gcc gdb lldb valgrind rr openocd
sbcl cmucl clisp chez-scheme mit-scheme chibi-scheme chicken
vls vlang rustup semgrep-bin
mu isync msmtp xsel xorg-xhost
mpc mpv mpd vlc yt-dlp
maxima fricas octave scilab-bin graphviz jupyterlab jupyter-notebook r
djvulibre catdoc unrtf perl-image-exiftool wkhtmltopdf
chezmoi neovim repo ecryptfs-utils
pandoc hugo inkscape imagemagick
aspell aspell-en aspell-fr aspell-ar grammalecte language-tool ltex-ls-bin
libvterm brave zotero bitwarden-cli binutils
poppler ffmpegthumbnailer mediainfo imagemagick tar unzip
)
for PKG in "${PKGS_LIST[@]}"; do
check_and_install_pkg "$PKG"
done
KDE Plasma
On KDE, there is a good support for HiDPI displays, however, I faced annoying
small icons in some contexts (for example, a right click on desktop). This can
be fixed by setting PLASMA_USE_QT_SCALING=1
before starting KDE Plasma. KDE
sources the files with .sh
extension found on ~/.config/plasma-workspace/env
, so
let’s create ours.
export PLASMA_USE_QT_SCALING=1