Skip to content

GeorgesAlkhouri/.emacs.d

Repository files navigation

My Emacs Configuration

An Explanation

This is a literate configuration for Emacs. Tangling this file creates two Elisp files, init.el and early-init.el.

Start with make tangle to create the init.el and early-init.el from this org file.

Requirements

Dependencies you need to fully run all configured features:

early-init.el and init.el

early-init

Idea and basics for tuning startup time from https://github.com/progfolio/.emacs.d

Emacs 27.0 introduced an early-init file. It allows customization before package and UI initialization.

;;; early-init.el --- Emacs pre package.el & GUI configuration -*- lexical-binding: t; no-byte-compile: t -*-
;;; Code:

(when (version< emacs-version "27.2")
  (error  "Emacs version is to low (minimum 27.2)."))

Remove bundled Org version

We want the latest version of Org mode.

Removing the Emacs bundled version from the load-path should prevent loading mixed Org versions. e.g. After updating Org mode.

(when-let (orglib (locate-library "org" nil load-path))
  (setq-default load-path (delete (substring (file-name-directory orglib) 0 -1)
                                  load-path)))

Save some time at startup.

(setq package-enable-at-startup nil)

Debugging

Running this form will launch the debugger after loading a package. This is useful for finding out when a dependency is requiring a package (perhaps earlier than you want). Use by tangling this block and launching Emacs with emacs --debug-init.

(unless (string-empty-p file)
  (eval-after-load file
    '(debug)))

Similarly, this variable will hit the debugger when a message matches its regexp.

(setq debug-on-message "")

Adding a variable watcher can be a useful way to track down initialization and mutation of a variable.

(add-variable-watcher 'org-capture-after-finalize-hook
                      (lambda (symbol newval operation where)
                        (debug)
                        (message "%s set to %s" symbol newval)))

initial-major-mode

Prevent the scratch buffer from loading a mode.

(setq initial-major-mode 'fundamental-mode)

file-name-handler-alist

Skipping a bunch of regular expression searching in the file-name-handler-alist should improve start time.

(defvar default-file-name-handler-alist file-name-handler-alist)
(when  (< emacs-major-version 29)
  (setq file-name-handler-alist nil))

gc-cons threshold

Emacs collects garbage every 800KB. This is overly aggressive on a modern machine during our init. Temporarily turning it off should decrease startup times. Resetting it afterward will ensure that normal operations don’t suffer from a large GC period.

I’m still not sure on the optimal gc-cons-threshold value. The following is a table of values from popular Emacs configurations.

Distributiongc-cons-threshold
Default800000
Doom16777216
Spacemacs100000000
(setq gc-cons-threshold most-positive-fixnum)

(defun +gc-after-focus-change ()
  "Run GC when frame loses focus."
  (run-with-idle-timer
   5 nil
   (lambda () (unless (frame-focus-state) (garbage-collect)))))
(defun +reset-init-values ()
  (run-with-idle-timer
   5 nil
   (lambda ()
     (setq file-name-handler-alist default-file-name-handler-alist
           gc-cons-threshold 100000000)
     (message "gc-cons-threshold & file-name-handler-alist restored")
     (when (boundp 'after-focus-change-function)
       (add-function :after after-focus-change-function #'+gc-after-focus-change)))))

(add-hook 'emacs-startup-hook '+reset-init-values)

read process output max value

Enlargen the maximum number of bytes to read from subprocess in a single chunk. Good for speeding up language servers.

(setq read-process-output-max (* 1024 1024)) ;; 1mb

UI

Turning off these visual elements before UI initialization should speed up init.

(push '(menu-bar-lines . 0) default-frame-alist)
(push '(tool-bar-lines . 0) default-frame-alist)
(push '(vertical-scroll-bars) default-frame-alist)

Implicitly resizing the Emacs frame adds to init time. Fonts larger than the system default can cause frame resizing, which adds to startup time.

(setq frame-inhibit-implied-resize t)

Ignore X resources.

(advice-add #'x-apply-session-resources :override #'ignore)

Taken from:

https://github.com/vsemyonoff/emacsrc/blob/14649a5bafea99cc7e13e7d048e9d15aed7926ce/early-init.el

This helps with a bug I was hitting when using desktop-save-mode’s desktop-read.

(setq desktop-restore-forces-onscreen nil)

provide early-init

(provide 'early-init)
;;; early-init.el ends here

init.el

The following line turns on lexical binding for performance reasons.

;;; init.el --- Personal configuration file -*- lexical-binding: t; no-byte-compile: t; -*-

Package Manager

straight.el: next-generation, purely functional package manager for the Emacs hacker.

https://github.com/raxod502/straight.el

Straight installs packages directly from there git repository.

Bootstrap straight Troubleshoot:

Sometimes, in a corporate environment, url-retrieve-synchronously may not work and straight.el will be unable to download the installation script mentioned in the bootstrap snippet. In this case, you may simply clone this repository into ~/.emacs.d/straight/repos/straight.el and check out your desired revision/branch.

 (setq straight-repository-branch "master")
 (setq straight-check-for-modifications '(check-on-save))
 (setq straight-use-package-by-default t)
 (setq straight-vc-git-default-protocol 'https)
 (setq straight-vc-git-force-protocol nil)
 
 (defvar bootstrap-version)
 (let ((bootstrap-file
	 (expand-file-name "straight/repos/straight.el/bootstrap.el" user-emacs-directory))
	(bootstrap-version 5))
   (unless (file-exists-p bootstrap-file)
     (with-current-buffer
	  (url-retrieve-synchronously
	   "https://raw.githubusercontent.com/raxod502/straight.el/develop/install.el"
	   'silent 'inhibit-cookies)
	(goto-char (point-max))
	(eval-print-last-sexp)))
   (load bootstrap-file nil 'nomessage))

profiling

This function displays how long Emacs took to start. It’s a rough way of knowing when/if I need to optimize my init file.

(add-hook 'emacs-startup-hook
          (lambda ()
            (message "Emacs loaded in %s with %d garbage collecitons."
                     (format "%.2f seconds"
                             (float-time
                              (time-subtract after-init-time before-init-time)))
                     gcs-done)))

packaging

use-package

  (defmacro use-feature (name &rest args)
  "Like `use-package' but with `straight-use-package-by-default' disabled.
NAME and ARGS are in `use-package'."
  (declare (indent defun))
  `(use-package ,name
     :straight nil
     :ensure nil
     ,@args))
(straight-use-package 'use-package)
(eval-when-compile
  (require 'use-package))
(setq init-file-debug nil)
(if init-file-debug
    (setq use-package-verbose t
          use-package-expand-minimally nil
          use-package-compute-statistics t
          debug-on-error t)
  (setq use-package-verbose nil
        use-package-expand-minimally t))

define constants

 (defconst *sys/win32*
   (eq system-type 'windows-nt)
   "Are we running on a Win system?")

 (defconst *sys/linux*
   (eq system-type 'gnu/linux)
   "Are we running on a GNU/Linux system?")

 (defconst *sys/mac*
   (eq system-type 'darwin)
   "Are we running on a Mac system?")

 (defconst *sys/project-home*
   "~/Devel")

 (defconst sys/leader-key "SPC"
   "The default leader key.")

 (defconst sys/leader-secondary-key "C-SPC"
   "The secondary leader key.")

 (defconst sys/major-leader-key "SPC m"
   "The default major mode leader key.")

 (defconst sys/major-leader-secondary-key "C-SPC m"
   "The secondary major mode leader key.")

 (defconst *sys/shell-history-file* "~/.bash_history")
 (defconst *sys/shell-config-file* "~/.bashrc")

 (cond (*sys/mac*
	 (defconst *sys/font-default-height* 160)
	 (defconst *sys/font-variable-height* 160)
	 (defconst *sys/shell-executable* "/usr/local/bin/bash")
	 (defconst *sys/omnisharp-server-path* (expand-file-name "util/omnisharp-run.sh" user-emacs-directory))
	 (setenv "LSP_MONO_BASE_DIR" "/usr/local/cellar/mono/6.12.0.122")
	 (setenv "LSP_OMNISHARP_EXE" "/usr/local/share/omnisharp-osx/omnisharp/omnisharp.exe")
	 )
	(*sys/linux*
	 (defconst *sys/font-default-height* 110)
	 (defconst *sys/font-variable-height* 130)
	 ;; get bash path without new line
	 ;; TODO assert that paths exists
	 ;; From POSIX specs (https://pubs.opengroup.org/onlinepubs/009695399/utilities/sh.html)
	 ;; Applications should note that the standard PATH to the shell
	 ;; cannot be assumed to be either /bin/sh or /usr/bin/sh
	 (defconst *sys/shell-executable* (shell-command-to-string "printf %s $SHELL"))
	 ))

Packages

evil

Evil is an extensible vi layer for Emacs. It emulates the main features of Vim, and provides facilities for writing custom extensions. […] evil-collection assumes evil-want-keybinding is set to nil and evil-want-integration is set to t before loading evil and evil-collection.

https://github.com/emacs-evil/evil

(use-package evil
  :demand t
  :init
  (setq evil-want-integration t)
  (setq evil-undo-system 'undo-redo)
  (setq evil-want-keybinding nil)
  :hook (after-init . evil-mode))

evil-collection

This is a collection of Evil bindings for the parts of Emacs that Evil does not cover properly by default.

https://github.com/emacs-evil/evil-collection

(use-package evil-collection
  :after evil
  :init
  (progn
    ;;Whether to setup Evil bindings in the minibuffer.
    (setq evil-collection-setup-minibuffer t))
  :config
  (progn
    (evil-collection-init)
    ))

general (key-bindings)

general.el provides a more convenient method for binding keys in emacs (for both evil and non-evil users).

https://github.com/noctuid/general.el#about

Load general before the remaining packages so they can make use of the :general keyword in their declarations.

 (use-package general
   :demand t
   :init
   (progn
     (setq general-override-states '(insert emacs hybrid normal visual motion operator replace)))
   :config
   (progn
 
     (defun sys/major-mode-name (arg)
	"Return major mode name"
	(cons
	 (cadr (split-string (car arg) " "))
	 (replace-regexp-in-string
	  "-mode$"
	  ""
	  (symbol-name major-mode))))
 
     (general-evil-setup)
 
     (general-create-definer
	global-leader
	:keymaps 'override
	:states '(normal insert emacs motion visual)
	:prefix sys/leader-key
	:non-normal-prefix sys/leader-secondary-key)
 
     (general-create-definer
	global-major-leader
	:states '(normal insert emacs motion viusal)
	:prefix sys/major-leader-key
	:non-normal-prefix sys/major-leader-secondary-key
	"" '(:ignore t :which-key sys/major-mode-name))
 
     (general-nmap "," (general-simulate-key "SPC m"))
 
     (global-leader
	"a" '(:ignore t :wk "applications")
 
	"b" '(:ignore t :wk "buffers")
	"bx"  'kill-current-buffer
	"bd"  'dired
	"bD" 'dired-jump
	"bm" '((lambda () (interactive) (switch-to-buffer "*Messages*"))
	       :which-key "messages-buffer")
	"bs" '((lambda () (interactive) (switch-to-buffer "*scratch*"))
	       :which-key "scratch-buffer")
 
	"f" '(:ignore t :wk "files")
	"fe" '(:ignore t :which-key "env")
	"fed" '((lambda () (interactive) (find-file (expand-file-name "init.org" user-emacs-directory))) :which-key "init.org")
	"feb" '((lambda () (interactive) (find-file *sys/shell-config-file*)) :which-key ".bashrc")
	"fey" '((lambda () (interactive) (dired (expand-file-name "snippets" user-emacs-directory))) :which-key "yasnippet folder")
	"fep" '(straight-freeze-versions :which-key "freeze packages")
 
	"g" '(:ignore t :wk "git")
	"j" '(:ignore t :wk "jump")
	"p" '(:ignore t :wk "projects")
	"s" '(:ignore t :wk "search")
	"S" '(:ignore t :wk "spelling")
	"t" '(:ignore t :wk "themes")
 
	"T" '(:ignore t :wk "toggle")
 
	"w" '(:ignore t :wk "windows")
	"w?" 'split-window-vertically
	"w=" 'balance-windows-area
	"w/" 'split-window-horizontally
	"wH" 'evil-window-move-far-left
	"wJ" 'evil-window-move-very-bottom
	"wK" 'evil-window-move-very-top
	"wL" 'evil-window-move-far-right
	"wd" 'delete-window
	"wh" 'windmove-left
	"wj" 'windmove-down
	"wk" 'windmove-up
	"wl" 'windmove-right
	"wo" 'other-window
	"wO" 'delete-other-windows
	"wx" 'kill-buffer-and-window
	"wX" '((lambda () (interactive) (call-interactively #'other-window) (kill-buffer-and-window))
	       :which-key "kill-other-buffer-and-window")
 
	"q" '(:ignore t :wk "quit")
 
	"!" 'shell-command
	":" 'eval-expression
	"TAB" '((lambda () (interactive) (switch-to-buffer nil))
		:which-key "other-buffer")
 
 
 
	)))

which-key

which-key is a minor mode for Emacs that displays the key bindings following your currently entered incomplete command (a prefix) in a popup.

https://github.com/justbur/emacs-which-key

(use-package which-key
  :demand t
  :config
  (progn
    (setq which-key-side-window-location 'bottom)
    (setq which-key-popup-type 'side-window)
    (setq which-key-sort-order 'which-key-key-order-alpha
	    which-key-side-window-max-width 0.33
	    which-key-idle-delay 0.75)
    (which-key-mode)
    )
  :diminish )

magit

Magit is an interface to the version control system Git, implemented as an Emacs package.

https://magit.vc/

(use-package magit
  :defer t
  :after (general)
  :general
  (global-leader
    "gb"  'magit-blame
    "gi"  'magit-init
    "gs"  'magit-status
    )
  :config
  (transient-bind-q-to-quit))

magit-gitflow

Plugin in for git-flow in magit.

https://github.com/jtatarik/magit-gitflow

(use-package magit-gitflow
  :defer t
  :init (setq magit-gitflow-popup-key "%")
  ;; TODO add % key to magit-dispatch-popup
  ;; https://magit.vc/manual/magit-popup.html#Customizing-Existing-Popups
  ;; :config
  ;; (progn
  ;;   (magit-define-popup-action 'magit-dispatch-popup
  ;;    "%" "Git Flow" 'magit-gitflow-popup t))
  :hook (magit-mode . magit-gitflow-mode)
  :general
  (general-def magit-mode-map
    "%" 'magit-gitflow-popup)
  )

company

Company is a text completion framework for Emacs. The name stands for “complete anything”. It uses pluggable back-ends and front-ends to retrieve and display completion candidates.

http://company-mode.github.io/

 (use-package company
   :hook ((prog-mode yaml-mode) . company-mode)
   :diminish
   :general
   (general-def company-active-map
     "C-k" 'company-select-previous
     "C-j" 'company-select-next
     "<tab>" 'company-complete-common-or-cycle
     "S-<tab>" 'company-select-previous
     ;;for x11 https://emacs.stackexchange.com/a/53469
     "S-<iso-lefttab>" 'company-select-previous)
   :config
   (progn

     (defun add-yasnippet-backend (backend)
	"Add company-yasnippet backend to given company backend"
	(if (and (listp backend) (member 'company-yasnippet backend))
	    backend
	  (append (if (consp backend) backend (list backend))
		  '(:with company-yasnippet))))

     ;; add yasnippet-backend to all company backends
     (setq company-backends (mapcar #'add-yasnippet-backend company-backends))

     (setq company-tooltip-align-annotations t
	    company-idle-delay 0.1
	    company-show-numbers t
	    company-dabbrev-ignore-case nil
	    company-dabbrev-downcase nil
	    company-minimum-prefix-length 2
	    company-require-match nil)
     )
   )


 (use-package company-tabnine
   :defer t
   :commands company-tabnine-install-binary
   :after company
   :config
   (progn
     (setq company-tabnine-max-num-results 9)
     (company-tabnine-toggle t)
     )
   :init
   (progn
     ;; tabnine integration from https://github.com/MatthewZMD/.emacs.d/blob/master/elisp/init-company.el
     (defun company//sort-by-tabnine (candidates)
	"Integrate company-tabnine with lsp-mode"
	(if (or (functionp company-backend)
		(not (and (listp company-backend) (memq 'company-tabnine company-backends))))
	    candidates
	  (let ((candidates-table (make-hash-table :test #'equal))
		candidates-lsp
		candidates-tabnine)
	    (dolist (candidate candidates)
	      (if (eq (get-text-property 0 'company-backend candidate)
		      'company-tabnine)
		  (unless (gethash candidate candidates-table)
		    (push candidate candidates-tabnine))
		(push candidate candidates-lsp)
		(puthash candidate t candidates-table)))
	    (setq candidates-lsp (nreverse candidates-lsp))
	    (setq candidates-tabnine (nreverse candidates-tabnine))
	    (nconc (seq-take candidates-tabnine 3)
		   (seq-take candidates-lsp 6)))))

     (defun lsp-after-open-tabnine ()
	"Hook to attach to `lsp-after-open'."
	(setq-local company-tabnine-max-num-results 3)
	(add-to-list 'company-transformers 'company//sort-by-tabnine t)
	(add-to-list 'company-backends '(company-capf :with company-tabnine :separate)))

     (defun company-tabnine-toggle (&optional enable)
	"Enable/Disable TabNine. If ENABLE is non-nil, definitely enable it."
	(interactive)
	(if (or enable (not (memq 'company-tabnine company-backends)))
	    (progn
	      (add-hook 'lsp-after-open-hook #'lsp-after-open-tabnine)
	      (add-to-list 'company-backends #'company-tabnine)
	      (when (bound-and-true-p lsp-mode) (lsp-after-open-tabnine))
	      (message "TabNine enabled."))
	  (setq company-backends (delete 'company-tabnine company-backends))
	  (setq company-backends (delete '(company-capf :with company-tabnine :separate) company-backends))
	  (remove-hook 'lsp-after-open-hook #'lsp-after-open-tabnine)
	  (company-tabnine-kill-process)
	  (message "TabNine disabled.")))
     )
   :general
   (global-major-leader :keymaps 'prog-mode-map
     "c" '(:ignore t :wk "company")
     "ct" '(company-tabnine-toggle :wk "toggle tabnine"))
   )

company-statistics

(use-package company-statistics
  :defer t
  :config
  (progn
    (setq company-statistics-size 10000))
  :init
  (progn
    (add-hook 'company-mode-hook 'company-statistics-mode)))

company-box

A company front-end with icons.

https://github.com/sebastiencs/company-box

(use-package company-box
  :if (display-graphic-p)
  :after company
  :hook (company-mode . company-box-mode))

consult

Consult provides practical commands based on the Emacs completion function completing-read. Completion allows you to quickly select an item from a list of candidates.

https://github.com/minad/consult

 (use-package consult
   :hook (completion-list-mode . consult-preview-at-point-mode)
   :init
   (progn

     ;; Optionally configure the register formatting. This improves the register
     ;; preview for `consult-register', `consult-register-load',
     ;; `consult-register-store' and the Emacs built-ins.
     (setq register-preview-delay 0.5
	    register-preview-function #'consult-register-format)

     ;; Optionally tweak the register preview window.
     ;; This adds thin lines, sorting and hides the mode line of the window.
     (advice-add #'register-preview :override #'consult-register-window)

     ;; Use Consult to select xref locations with preview
     (setq xref-show-xrefs-function #'consult-xref
	    xref-show-definitions-function #'consult-xref)
     )
   :config
   (progn

     (defun consult--preview-p ()
	"Are we in a consult preview buffer?"
	(when-let (win (active-minibuffer-window))
	  (not (eq nil (buffer-local-value
			'consult--preview-function
			(window-buffer win))))))

     ;; Optionally configure a function which returns the project root directory.
     (setq consult-project-root-function #'projectile-project-root)
     )

   :general
   (general-def :states '(normal)
     "P" #'consult-yank-from-kill-ring
     )
   (global-leader
     "SPC" '(execute-extended-command :which-key "M-x")
     "/" '(consult-ripgrep :wk "ripgrep")
     "ss" '(consult-line :wk "search")
     "bb" '(consult-buffer :which-key "buffer list")
     "ff" '(find-file :wk "find files")
     "fr" '(consult-recent-file :wk "recent files")
     "ji" '(consult-imenu :wk "imenu")
     )
   :defer 1
   )

projectile

Projectile is a project interaction library for Emacs. Its goal is to provide a nice set of features operating on a project level without introducing external dependencies (when feasible).

https://github.com/bbatsov/projectile

 (use-package projectile
   :after (general)
   :commands (projectile-project-root)
   :general
   (global-leader
     "p!" 'projectile-run-shell-command-in-root
     "pp" 'projectile-switch-project
     "pf" 'projectile-find-file
     "pD" 'projectile-dired
     "pe" 'projectile-edit-dir-locals
     "pR" 'projectile-replace
     "pS" 'projectile-discover-projects-in-search-path
     "pt" 'projectile-run-vterm)
   :config
   (progn
     (defun sys/switch-project-action ()
	"Switch to a workspace with the project name."
	(persp-switch (projectile-project-name))
	(projectile-find-file))
     (setq projectile-project-search-path (list *sys/project-home*))
     (setq projectile-switch-project-action #'sys/switch-project-action)
     (add-to-list 'projectile-globally-ignored-directories "site-packages")
     (projectile-mode t))
   )

vterm

Emacs-libvterm (vterm) is fully-fledged terminal emulator inside GNU Emacs based on libvterm, a C library.

https://github.com/akermu/emacs-libvterm

(use-package vterm
  :commands (vterm vterm-other-window)
  :general
  (global-leader "at" '(:ignore t :which-key "terminal")
    "att" 'vterm-other-window
    "at." 'vterm
    )
  ;; (general-def vterm-mode-map "C-r" 'helm-vterm-search-history :states '(normal emacs))
  (general-def vterm-mode-map "C-l" 'vterm-clear :states '(normal emacs))
  ;; copied from spacemacs
  :config
  (setq vterm-shell *sys/shell-executable*)
  ;; (defun vterm-make-history-candidates ()
  ;;   (with-temp-buffer
  ;;     (insert-file-contents *sys/shell-history-file*)
  ;;     (reverse
  ;;      (delete-dups
  ;; 	(split-string (buffer-string) "\n")))))

  ;; (defun helm-vterm-search-history ()
  ;;   "Narrow down bash history with helm."
  ;;   (interactive)
  ;;   (cl-assert (string-equal mode-name "VTerm") nil "Not in VTerm mode")
  ;;   (helm :sources (helm-build-sync-source "Bash history"
  ;; 		     :candidates (vterm-make-history-candidates)
  ;; 		     :action #'vterm-send-string)
  ;; 	  :buffer "*helm-bash-history*"
  ;; 	  :candidate-number-limit 10000))

  (evil-set-initial-state 'vterm-mode 'emacs)
  (add-hook 'vterm-mode-hook #'(lambda ()
				   (setq-local global-hl-line-mode nil)
				   (setq buffer-face-mode-face '(:family "MesloLGS NF"))
				   (buffer-face-mode)
				   ))
  )

diminish

This package implements hiding or abbreviation of the mode line displays (lighters) of minor-modes.

https://github.com/emacsmirror/diminish

(use-package diminish
  :defer 3)

expand-region

Expand region increases the selected region by semantic units. Just keep pressing the key until it selects what you want.

https://github.com/magnars/expand-region.el

See also https://github.com/hlissner/doom-emacs/blob/develop/docs/faq.org#why-do-non-evil-users-get-expand-region-but-not-evil-users to learn about the VIM way.

(use-package expand-region
  :commands er/expand-region
  :config
  (setq expand-region-contract-fast-key "V"
	  expand-region-reset-fast-key "r")
  :general
  (global-leader
    "v"   'er/expand-region)
  )

Development

Following packages are used majorly for programming

(use-package highlight-indent-guides
  :defer t
  :hook (prog-mode . highlight-indent-guides-mode)
  :if (display-graphic-p)
  :diminish
  :config
  (setq highlight-indent-guides-method 'character)
  (setq highlight-indent-guides-responsive 'top)
  (setq highlight-indent-guides-delay 0)
  (setq highlight-indent-guides-auto-character-face-perc 7)
  )

rainbow-delimiters

rainbow-delimiters is a “rainbow parentheses”-like mode which highlights delimiters such as parentheses, brackets or braces according to their depth.

https://github.com/Fanael/rainbow-delimiters

(use-package rainbow-delimiters
  :defer t
  :hook (prog-mode . rainbow-delimiters-mode))

evil-nerd-commenter

A Nerd Commenter emulation, help you comment code efficiently.

https://github.com/redguardtoo/evil-nerd-commenter

(use-package evil-nerd-commenter
  :commands evilnc-comment-or-uncomment-lines
  :general
  (global-leader
    ";" '(evilnc-comment-or-uncomment-lines :which-key "evil-comment"))
  )

lsp-mode

Client for Language Server Protocol. lsp-mode aims to provide IDE-like experience by providing optional integration with the most popular Emacs packages like company, flycheck and projectile.

(use-package lsp-mode
  :defer t
  :hook
  ((lsp-mode . lsp-enable-which-key-integration))
  :commands (lsp lsp-deferred)
  :config
  (progn
    ;; disable flycheck override with lsp checker in python-mode
    (setq lsp-diagnostics-disabled-modes '(python-mode)
	    lsp-keep-workspace-alive nil
	    lsp-auto-guess-root t
	    lsp-ui-doc-enable nil
	    lsp-ui-doc-position 'at-point
	    lsp-signature-function 'lsp-signature-posframe
	    ;; disable lsp company completion provider
	    lsp-completion-provider :none
	    )
    )
  :general
  (global-major-leader :keymaps '(python-mode-map csharp-mode-map)
    "l" '(:keymap lsp-command-map :wk "lsp"))
  )

(use-package lsp-ui
  :after lsp-mode
  :commands lsp-ui-mode
  :config
  (setq lsp-ui-sideline-ignore-duplicate t)
  )

flycheck

Flycheck is a modern on-the-fly syntax checking extension for GNU Emacs, intended as replacement for the older Flymake extension which is part of GNU Emacs.

https://www.flycheck.org/en/latest/

(use-package flycheck
  :defer t
  :init
  (add-hook 'emacs-lisp-mode-hook #'flycheck-mode)
  (add-hook 'sh-mode-hook #'flycheck-mode)
  (add-hook 'yaml-mode-hook #'flycheck-mode)
  (add-hook 'python-mode-hook #'(lambda ()
				    (flycheck-mode)
				    ;; checker setup locally for python-mode
				    ;; explicitly set flake8 checker
				    ;; implicitly set mypy and pylint in checker chain
				    (setq-local flycheck-checker 'python-flake8)
				    ;; safe time and just determine the line of error
				    (setq-local flycheck-highlighting-mode 'lines)
				    ;; only apply syntax check on save and mode-enabled
				    (setq-local flycheck-check-syntax-automatically '(save mode-enabled)
						flycheck-relevant-error-other-file-show nil)
				    ;; disable highlight for flycheck infos
				    (face-remap-add-relative 'flycheck-info :underline nil)
				    (face-remap-add-relative 'flycheck-warning :underline nil)
				    ))
  :custom (flycheck-emacs-lisp-load-path 'inherit "necessary with straight.el")
  :general
  (global-major-leader :keymaps '(prog-mode-map yaml-mode-map)
    "f" '(:ignore t :wk "flycheck")
    "fe" '(flycheck-list-errors :wk "list errors"))
  )

format-all

Lets you auto-format source code in many languages using the same command for all languages, instead of learning a different Emacs package and formatting command for each language.

https://github.com/lassik/emacs-format-all-the-code

(use-package format-all
  :defer t
  :init
  (add-hook 'prog-mode-hook 'format-all-mode)
  :hook (prog-mode . format-all-ensure-formatter)
  :general
  (global-major-leader
    :keymaps
    'emacs-lisp-mode-map
    "b"
    '(:ignore t :which-key "buffers")
    "bf"
    'format-all-buffer)
  ;; :hook ((python-mode) . format-all-mode)
  )

Python

 (use-feature python
   :defer t
   :config
   (progn
     (setq python-prettify-symbols-alist '(("in" . ?∈) ("lambda" . ) ("not in" . ?∉))))
   :hook ((python-mode . semantic-mode)
	   (python-mode . prettify-symbols-mode)
	   (python-mode . (lambda ()
			    ;; disable project errors on modeline
			    (setq-local lsp-modeline-diagnostics-enable nil
					lsp-headerline-breadcrumb-enable nil))))
   :init
   (progn
     (setq semantic-default-submodes nil)

     (defun doq-region ()
	"Doc the Python function content using `doq'"
	(interactive)
	(shell-command-on-region
	 ;; beginning and end of buffer
	 (region-beginning)
	 (region-end)
	 ;; command and parameters
	 "doq --omit self"
	 ;; output buffer
	 (current-buffer)
	 ;; replace and put no mark
	 'no-mark
	 ;; name of the error buffer
	 "*Doq Error Buffer*"
	 ;; show error buffer?
	 t))
     )
   :general
   (global-major-leader :keymaps 'python-mode-map
     "D" '(doq-region :wk "Add docstring to region"))
   )

importmagic.el

Emacs package which tries to suggest imports for unresolved symbols.

https://github.com/anachronic/importmagic.el

(use-package importmagic
  :defer t
  :init
  (add-hook 'venv-postactivate-hook  #'importmagic-mode)
  :general
  (global-major-leader :keymaps 'python-mode-map
    "i" '(:ignore t :wk "importmagic")
    "ii" '(importmagic-fix-symbol-at-point :wk "fix import at point")
    "ia" '(importmagic-fix-imports :wk "fix all imports")))

lsp-pyright

 (use-package lsp-pyright
   :defer t
   :init
   (progn
     (defun sys/lsp-start-pyright ()
	;;Do not start lsp-mode when in consult preview
	(unless (consult--preview-p)
	  (require 'lsp-pyright)
	  (lsp-deferred)))
     )
   :hook (python-mode . sys/lsp-start-pyright)
   )

blacken

(use-package blacken :defer t :commands blacken-buffer
  ;; only format buffer when in python-mode
  :init (add-hook 'before-save-hook #'(lambda () (when (derived-mode-p 'python-mode)
     (blacken-buffer)
     )))
)

pytest-el

https://github.com/ionrock/pytest-el

  • FIX Package cl is deprecated
(use-package pytest :defer t
  :commands (pytest-one ptytest-module pytest-all)
  :config (add-to-list 'pytest-project-root-files "setup.cfg")
  :general
  (global-major-leader :keymaps 'python-mode-map
    "t" '(:ignore t :which-key "testing")
    "tt" 'pytest-one
    "ta" 'pytest-all
    "tb" 'pytest-module
    )
  )

py-isort

(use-package py-isort
  :commands py-isort-before-save
  :init
;;isort checks if in python-mode
  (add-hook 'before-save-hook 'py-isort-before-save))

CSharp

Use lsp-mode and OmniSharp-Roslyn as a language server for C#. Download OmniSharp-Roslyn from https://github.com/OmniSharp/omnisharp-roslyn/releases. Because OmniSharp comes with its own embedded Mono with no references to other assemblies, we also need Mono (https://www.mono-project.com) installed. Then tell the run script (*sys/omnisharp-server-path*) where to find the OmniSharp executable and the path to Mono by setting the env variable LSP_MONO_BASE_DIR and LSP_OMNISHARP_EXE respectively. Also, tell lsp-mode where to find the OmniSharp executable by setting lsp-csharp-server-path.

(Could be necessary to do chmod +x run.)

(use-package csharp-mode
  :if (bound-and-true-p *sys/omnisharp-server-path*)
  :init
  (progn
    (setq  lsp-csharp-server-path *sys/omnisharp-server-path*))
  :defer t
  :hook (csharp-mode . lsp-deferred)
  :config
  (progn
    ;; todo ignore unity folder then remove line
    (setq lsp-enable-file-watchers nil)
    ;; (make-variable-buffer-local 'lsp-file-watch-ignored-directories)
    ;; (add-to-list 'lsp-file-watch-ignored-directories "[/\\\\]\\Library\\'")
    (setq-local lsp-auto-guess-root t)
    )
  )

unity.el

This package provides some Emacs integration with the Unity game engine. Most notably, it provides the ability to open source files from Unity in Emacs or Emacsclient while still generating the solution and project files for use with lsp-mode.

https://github.com/elizagamedev/unity.el

Generate a code binary ([emacs-user-directory]/var/unity/) with (unity-build-code-shim) and select it in Unity’s preferences External Script Editor. To open C# files with Emacs also add emacs +$(Line):$(Column) $(File) to External Script Editor Args.

(use-package unity
  :defer t
  :init
  (progn
    (add-hook 'csharp-mode-hook #'unity-setup))
  :straight
  (unity
   :type git
   :host github
   :repo "elizagamedev/unity.el"
   :files ("*.el" "*.c")))

Themes

https://github.com/hlissner/emacs-doom-themes

(use-package doom-themes
  :config
  ;; Global settings (defaults)
  (setq doom-themes-enable-bold t ; if nil, bold is universally disabled
	  doom-themes-enable-italic t ; if nil, italics is universally disabled
	  doom-themes-treemacs-theme "doom-atom")
  ;; Enable flashing mode-line on errors
  (doom-themes-visual-bell-config)
  ;; Corrects (and improves) org-mode's native fontification.
  (doom-themes-org-config)
  (doom-themes-treemacs-config)
  :general
  (global-leader "tt" '(:ignore t :which-key "choose themes")
    "tt1" '((lambda () (interactive)
		(load-theme 'doom-one t))
	      :which-key "doom-one")
    "tt2" '((lambda () (interactive)
		(load-theme 'doom-one-light t))
	      :which-key "doom-one-light")
    "tt3" '((lambda () (interactive)
		(load-theme 'doom-dracula t))
	      :which-key "doom-dracula")
    )
  )

doom-modeline

A fancy and fast mode-line inspired by minimalism design.

https://github.com/seagle0128/doom-modeline

Troubleshoot It could happen that when behind a proxy you have to manually download the fonts for the all-the-icons.el package included in doom-modeline.

(use-package doom-modeline
  :defer t
  :config
  (progn
    (setq doom-modeline-icon (display-graphic-p)
	    doom-modeline-buffer-file-name-style 'truncate-all
	    doom-modeline-buffer-encoding nil)
    (set-face-attribute 'mode-line nil :family "JetBrains Mono" :height 100)
    (set-face-attribute 'mode-line-inactive nil :family "JetBrains Mono" :height 100)
    )

  :hook
  (after-init . doom-modeline-mode))

solaire

solaire-mode is an aesthetic plugin designed to visually distinguish “real” buffers (i.e. file-visiting code buffers where you do most of your work) from “unreal” buffers (like popups, sidebars, log buffers, terminals, etc) by giving the latter a slightly different – often darker – background

https://github.com/hlissner/emacs-solaire-mode

(use-package solaire-mode
  :defer t)

Hyda

Hydra helps to design transient key bindings.

https://github.com/abo-abo/hydra

(use-package hydra
  :defer t
  :config
  (defhydra hydra-text-scale (:timeout 4)
    "scale text"
    ("j" text-scale-increase "in")
    ("k" text-scale-decrease "out")
    ("q" nil "finished" :exit t))
  :general
  (global-leader
    "ts" '(hydra-text-scale/body :which-key "scale text"))
  )

ispell

(use-package ispell
  :init
  (progn
    ;; env variable is important for hunspell to find
    ;; the dictionary
    (setenv "DICTIONARY" "en_US")
    (add-to-list 'ispell-hunspell-dictionary-alist '("deutsch-hunspell"
						       "[[:alpha:]]"
						       "[^[:alpha:]]"
						       "[']"
						       t
						       ("-d" "de_DE"); Dictionary file name
						       nil
						       iso-8859-1))

    (add-to-list 'ispell-hunspell-dictionary-alist '("english-hunspell"
						       "[[:alpha:]]"
						       "[^[:alpha:]]"
						       "[']"
						       t
						       ("-d" "en_US")
						       nil
						       iso-8859-1))
    (setq ispell-program-name (executable-find "hunspell")
	    )))

flyspell

Flyspell enables on-the-fly spell checking in Emacs by the means of a minor mode.

http://www-sop.inria.fr/members/Manuel.Serrano/flyspell/flyspell.html

  • FIX ispell starts also in init major mode which is fundamental mode
  • TODO add German dict to ispell
  • TODO add cycling hydra menu for spell checking
(use-feature flyspell
  :after ispell
  :hook ((prog-mode . flyspell-prog-mode)
	   (text-mode . flyspell-mode))
  :config
  (progn
    ;; better performance, see https://www.emacswiki.org/emacs/FlySpell#h5o-3
    (setq flyspell-issue-message-flag nil)))

flyspell-correct

Correcting misspelled words with flyspell using favourite interface. Helm in this case.

https://github.com/d12frosted/flyspell-correct

(use-package flyspell-correct
  :defer t
  :after flyspell
  :general
  (global-leader
    "Sc" '(flyspell-correct-wrapper :which-key "check"))
  )

winner

Winner Mode is a global minor mode that allows you to “undo” and “redo” changes in WindowConfiguration (Changes in window state).

(use-package winner
  :defer 3
  :general
  (global-leader
    "wu" 'winner-undo
    "wr" 'winner-redo)
  :config
  (add-to-list 'winner-boring-buffers "*Help*")
  (winner-mode t))

winum

Window numbers for Emacs: Navigate your windows and frames using numbers !

https://github.com/deb0ch/emacs-winum

 (use-package winum
   :defer 1
   :config
 (setq winum-auto-assign-0-to-minibuffer nil
           winum-auto-setup-mode-line nil
           winum-ignored-buffers '(" *LV*" " *which-key*"))
(global-leader "0" 'winum-select-window-0
  "1" 'winum-select-window-1
      "2" 'winum-select-window-2
      "3" 'winum-select-window-3
      "4" 'winum-select-window-4
      "5" 'winum-select-window-5
      "6" 'winum-select-window-6
      "7" 'winum-select-window-7
      "8" 'winum-select-window-8
      "9" 'winum-select-window-9)
;; Rename the entry winum 0-9 at SPC root, to 0..9
(push '(("\\(.*\\) 0" . "winum-select-window-0") . ("\\1 0..9" . "window 0..9"))
    which-key-replacement-alist)
(push '((nil . "winum-select-window-[1-9]") . t) which-key-replacement-alist)
  (winum-mode))

shackle

Enforce rules for popup windows

https://depp.brause.cc/shackle/

(use-package shackle :defer t
  :commands (shackle-mode)
  :custom (shackle-rules '(("*Flycheck errors*"  :align below :size 0.15)
			     ("*vterm*" :align below :size 0.3)
			     (magit-status-mode :select t)
			     ))
  :hook ((flycheck-mode global-flycheck-mode magit-mode vterm-mode) . shackle-mode))

smartparens

Smartparens is a minor mode for dealing with pairs in Emacs.

https://github.com/Fuco1/smartparens

(use-package smartparens
:defer t
:hook ((prog-mode org-mode) . smartparens-mode))

paran

Show matching delimiters highlighted.

(use-feature paren
  :defer 1
  :config (show-paren-mode t))

compile

  (use-feature compile
  :config
  (setq compilation-scroll-output 'first-error)
  (defun +compilation-colorize ()
    "Colorize from `compilation-filter-start' to `point'."
    (require 'ansi-color)
    (let ((inhibit-read-only t))
      (ansi-color-apply-on-region (point-min) (point-max))))
(add-hook 'compilation-filter-hook #'+compilation-colorize))

yaml-mode

(use-package yaml-mode
  :defer t
  :mode ("\\.\\(yml\\|yaml\\)\\'" . yaml-mode)
  :init
  (add-hook 'yaml-mode-hook #'lsp)
  )

perspective

The Perspective package provides multiple named workspaces (or “perspectives”) in Emacs, similar to multiple desktops in window managers like Awesome and XMonad, and Spaces on the Mac.

https://github.com/nex3/perspective-el

Perspective package is essentiell for buffer organisation. Therefore we load it early and before other packages like centaur-tab.

 (use-package perspective
   :hook (after-init . persp-mode)
   :general
   (global-leader
     "pP" 'persp-switch)
   :config
   (progn
     (setq persp-state-default-file (expand-file-name "persp-save-file.el" user-emacs-directory)
	    persp-modestring-short t
	    persp-mode-prefix-key "")

     (add-hook 'kill-emacs-hook #'persp-state-save)

     (unless (equal persp-mode t)
	(persp-mode)))
   )

dashboard

 (use-package dashboard
   :demand t
   :after all-the-icons
   ;; :if (< emacs-major-version 29)
   :init
   (progn
     (add-hook 'dashboard-mode-hook #'(lambda () (setq-local global-hl-line-mode nil))))
   :config
   (progn

     (defun dashboard-insert-hackernews (list-size)
	"Request LIST-SIZE number of top-stories from hackernews."
	;; TODO implement time interval check (e.g. update every 15 min)

	;; only load one time
	(when (equal dashboard-hackernews-init-state dashboard-hackernews-state)
	  (hackernews-get-topstories
	   list-size
	   (lambda (stories)
	     (when stories
	       (let* ((formatted-stories (dashboard-hackernews-add-formatted-stories-alist stories)))
		 (setq dashboard-hackernews-state formatted-stories)
		 ;; update dashboard
		 (let ((dashboard-force-refresh t)) (dashboard-insert-startupify-lists))
		 )))))
	;; TODO add short-cut
	(dashboard-insert-section
	 "Hackernews:"
	 dashboard-hackernews-state
	 list-size
	 nil
	 (lambda (&rest ignore)
	   (let ((url (cdr (assoc 'url el))))
	     (browse-url url)
	     (kill-new url)
	     (message "[dashboard] copied '%s' to clipboard." url)))
	 (format "%s" (cdr (assoc 'formatted-string el))))
	)

     ;; (add-to-list 'dashboard-item-generators '(hackernews . dashboard-insert-hackernews))
     ;; (add-to-list 'dashboard-items '(hackernews) t)

     (setq dashboard-startup-banner
	    (expand-file-name "emacs.svg" (expand-file-name "media" user-emacs-directory)))
     (setq dashboard-items '((recents  . 5)
			      (projects . 5)
			      ;; (hackernews . 30)
			      )
	    dashboard-set-heading-icons t
	    dashboard-set-file-icons t
	    dashboard-center-content t)
     (dashboard-setup-startup-hook)
     (dashboard-refresh-buffer)))

all-the-icons

(use-package all-the-icons
  :if (display-graphic-p)
  :defer t)

treemacs

Treemacs is a file and project explorer similar to NeoTree or vim’s NerdTree, but largely inspired by the Project Explorer in Eclipse.

https://github.com/Alexander-Miller/treemacs

Currently treemacs is only supported in projects by toggling the treemacs window. Further todos would be to make it possible to switch projects correctly (e.g. activating projectile-after-switch-project-hook) with treemacs.

 
 (use-package treemacs :defer t
   :commands (treemacs-select-window
	       treemacs-current-visibility)
   :init
   (progn
     ;; copied from spacemacs
     (defun sys/treemacs-project-toggle ()
	"Toggle and add the current project to treemacs if not already added."
	(interactive)
	(if (eq (treemacs-current-visibility) 'visible)
	    (delete-window (treemacs-get-local-window))
	  (let ((path (projectile-ensure-project (projectile-project-root)))
		(name (projectile-project-name)))
	    (unless (treemacs-current-workspace)
	      (treemacs--find-workspace))
	    (treemacs-do-add-project-to-workspace path name)
	    (treemacs-select-window)))))
   :config
   (progn
     (when (display-graphic-p)
	(require 'all-the-icons)
	(require 'treemacs-all-the-icons)
	(treemacs-load-theme 'all-the-icons)))
   :general
   (global-leader
     "pT" 'sys/treemacs-project-toggle))
 
 (use-package treemacs-all-the-icons
   :if (display-graphic-p)
   :defer t)

avy

avy is a GNU Emacs package for jumping to visible text using a char-based decision tree.

https://github.com/abo-abo/avy

(use-package avy
  :defer t
  :general
  (global-leader
    "jj" '(evil-avy-goto-char-timer :wk "jump to char")
    "jl" '(evil-avy-goto-line :wk "jump to line")
    "jo" 'avy-pop-mark)
  )

restart-emacs

(use-package restart-emacs
  :defer t
  :general
  (global-leader
    "qq" '(save-buffers-kill-terminal :wk "quit Emacs")
    "qR" '(restart-emacs :wk "restart Emacs"))
    ;; "qr" '((restart-emacs (list "--resume-layouts")) :wk "restart Emacs (resume layouts)"))
  )

emacs

 (use-feature emacs
   :init
   (progn

     (defun sys/after-startup ()

	(set-face-font 'default "JetBrains Mono")
	(set-face-font 'fixed-pitch "JetBrains Mono")
	(set-face-font 'variable-pitch "Gentium Book Basic")

	(set-face-attribute 'default nil :font "JetBrains Mono" :height *sys/font-default-height*)
	(set-face-attribute 'fixed-pitch nil :font "JetBrains Mono")
	(set-face-attribute 'variable-pitch nil :font "Gentium Book Basic" :height *sys/font-variable-height*)

	;; When buffer is closed, saves the cursor location
	(save-place-mode t)
	(toggle-frame-maximized)
	(global-hl-line-mode t)
	(solaire-global-mode t)
	(global-auto-revert-mode t)
	(load-theme 'doom-one-light t)
	)

     ;; always allow 'y' instead of 'yes'.
     (defalias 'yes-or-no-p 'y-or-n-p)
     ;; write over selected text on input... like all modern editors do
     (delete-selection-mode t)
     ;; Don't persist a custom file, this bites me more than it helps
     ;; (setq custom-file (make-temp-file "")) ; use a temp file as a placeholder
     (setq custom-safe-themes t)            ; mark all themes as safe, since we can't persist now
     (setq enable-local-variables :all)     ; fix =defvar= warnings
     ;; stop emacs from littering the file system with backup files
     (setq make-backup-files nil
	    ;; auto-save-default nil
	    create-lockfiles nil)
     ;; follow symlinks
     (setq vc-follow-symlinks t)
      ;; Silence native compilation compiler warnings for as they can be pretty disruptive.
     (setq native-comp-async-report-warnings-errors nil)

     ;; Tramp config (own use-feature tramp package was to slow)
     (setq tramp-default-method "ssh")
     ;; Disable version control on tramp buffers to avoid freezes.
     (setq vc-ignore-dir-regexp
	    (format "\\(%s\\)\\|\\(%s\\)"
		    vc-ignore-dir-regexp
		    tramp-file-name-regexp))

     ;; enable commands in minibuffer
     (setq enable-recursive-minibuffers t)

     ;; Emacs 28: Hide commands in M-x which do not work in the current mode.
     ;; Vertico commands are hidden in normal buffers.
     (setq read-extended-command-predicate
	    #'command-completion-default-include-p)

     (add-hook 'after-init-hook #'sys/after-startup)
     (add-hook 'text-mode-hook #'(lambda ()
				    (setq-local line-spacing 0.1)))
     (add-hook 'prog-mode-hook #'(lambda ()
				    (display-line-numbers-mode)
				    (setq-local line-spacing 0.1
						display-line-numbers-width 2
						)))
     )
   )

dockerfile-mode

(use-package dockerfile-mode
  :defer t
  :mode "Dockerfile\\'")

unicode

Packages for better unicode support.

This package maps ordinary graphemes (characters) to fancy ligatures, if both your version of Emacs and the font supports it.

https://github.com/mickeynp/ligature.el

(use-package ligature
  :defer t
  :hook (prog-mode . ligature-mode)
  :config
  (progn
    (ligature-set-ligatures '(prog-mode) '("|||>" "<|||" "<==>" "<!--" "####" "~~>" "***" "||=" "||>"
					     ":::" "::=" "=:=" "===" "==>" "=!=" "=>>" "=<<" "=/=" "!=="
					     "!!." ">=>" ">>=" ">>>" ">>-" ">->" "->>" "-->" "---" "-<<"
					     "<~~" "<~>" "<*>" "<||" "<|>" "<$>" "<==" "<=>" "<=<" "<->"
					     "<--" "<-<" "<<=" "<<-" "<<<" "<+>" "</>" "###" "#_(" "..<"
					     "..." "+++" "/==" "///" "_|_" "www" "&&" "^=" "~~" "~@" "~="
					     "~>" "~-" "**" "*>" "*/" "||" "|}" "|]" "|=" "|>" "|-" "{|"
					     "[|" "]#" "::" ":=" ":>" ":<" "$>" "==" "=>" "!=" "!!" ">:"
					     ">=" ">>" ">-" "-~" "-|" "->" "--" "-<" "<~" "<*" "<|" "<:"
					     "<$" "<=" "<>" "<-" "<<" "<+" "</" "#{" "#[" "#:" "#=" "#!"
					     "##" "#(" "#?" "#_" "%%" ".=" ".-" ".." ".?" "+>" "++" "?:"
					     "?=" "?." "??" ";;" "/*" "/=" "/>" "//" "__" "~~" "(*" "*)"
					     "\\\\" "://")))
  :straight
  (ligature
   :type git
   :host github
   :repo "mickeynp/ligature.el"
   :files (:defaults))
  )

yasnippet

YASnippet is a template system for Emacs. It allows you to type an abbreviation and automatically expand it into function templates.

https://github.com/joaotavora/yasnippet

(use-package yasnippet
  :defer t
  :hook ((prog-mode org-mode) . yas-minor-mode)
  :config
  (progn
    (setq yas-snippet-dirs (list (expand-file-name "snippets" user-emacs-directory)))
    (yas-reload-all))
  )

Org

(use-package org
  :defer t
  :init
  (progn
    (add-hook 'org-mode-hook 'variable-pitch-mode))
  :config
  (progn

    (let* ((variable-tuple '(:font "Gentium Book Basic"))
	       (headline           `(:inherit default :weight bold)))

	  (custom-theme-set-faces
	   'user
	   `(org-level-8 ((t (,@headline ,@variable-tuple))))
	   `(org-level-7 ((t (,@headline ,@variable-tuple))))
	   `(org-level-6 ((t (,@headline ,@variable-tuple))))
	   `(org-level-5 ((t (,@headline ,@variable-tuple))))
	   `(org-level-4 ((t (,@headline ,@variable-tuple :height 1.1))))
	   `(org-level-3 ((t (,@headline ,@variable-tuple :height 1.25))))
	   `(org-level-2 ((t (,@headline ,@variable-tuple :height 1.5))))
	   `(org-level-1 ((t (,@headline ,@variable-tuple :height 1.75))))
	   `(org-document-title ((t (,@headline ,@variable-tuple :height 2.0 :underline nil))))))

    (custom-theme-set-faces
     'user
     '(org-block ((t (:inherit fixed-pitch))))
     '(org-code ((t (:inherit fixed-pitch))))
     '(org-document-info-keyword ((t (:inherit (shadow fixed-pitch)))))
     '(org-indent ((t (:inherit (org-hide fixed-pitch)))))
     '(org-meta-line ((t (:inherit (font-lock-comment-face fixed-pitch)))))
     '(org-property-value ((t (:inherit fixed-pitch))) t)
     '(org-table ((t (:inherit fixed-pitch))))
     '(org-special-keyword ((t (:inherit (font-lock-comment-face fixed-pitch)))))
     '(org-tag ((t (:inherit (shadow fixed-pitch) :weight bold :height 0.8))))
     '(org-verbatim ((t (:inherit (shadow fixed-pitch))))))
    )
  )

;; https://emacs.stackexchange.com/questions/28940/how-to-overwrite-properly-a-face-for-a-particular-theme
(use-package org-bullets
  :defer t
  :init
  (add-hook 'org-mode-hook #'(lambda () (org-bullets-mode t))))

request

Request.el – Easy HTTP request for Emacs Lisp

https://github.com/tkf/emacs-request

(use-package request
  :commands request
  :defer t)

dash

A modern list library for Emacs

https://github.com/magnars/dash.el

(use-package dash
  :defer t)

centaur-tab

This projects aims to become an aesthetic, functional and efficient tabs plugin for Emacs with a lot of customization options.

https://github.com/ema2159/centaur-tabs

 (use-package centaur-tabs
   :after perspective
   :hook
   (dashboard-mode . centaur-tabs-local-mode)
   (vterm-mode . centaur-tabs-local-mode)
   (helpful-mode . centaur-tabs-local-mode)
   (projectile-mode . centaur-tabs-local-mode)
   :config
   (progn
     ;; TODO group by perspective and perspective-key
     (defun centaur-tabs-buffer-groups ()
	"`centaur-tabs-buffer-groups' control buffers' group rules.

 Group centaur-tabs with mode if buffer is derived from `eshell-mode'
 `emacs-lisp-mode' `dired-mode' `org-mode' `magit-mode'.
 All buffer name start with * will group to \"Emacs\".
 Other buffer group by `centaur-tabs-get-group-name' with project name."
	(list
	 (cond
	  ((memq major-mode '(magit-process-mode
			      magit-status-mode
			      magit-diff-mode
			      magit-log-mode
			      magit-file-mode
			      magit-blob-mode
			      magit-blame-mode
			      ))
	   "Magit")
	  ((and (string-equal "*" (substring (buffer-name) 0 1))
		(not (string-match-p "*scratch*" (buffer-name))))
	   "Emacs")
	  ((derived-mode-p 'eshell-mode)
	   "EShell")
	  ((derived-mode-p 'emacs-lisp-mode)
	   "Elisp")
	  ((derived-mode-p 'dired-mode)
	   "Dired")
	  ((memq major-mode '(org-mode org-agenda-mode diary-mode))
	   "OrgMode")
	  (t
	   (centaur-tabs-get-group-name (current-buffer))))))

     (setq centaur-tabs-style "bar"
	    centaur-tabs-set-icons t
	    ;; centaur-tabs-height 15
	    centaur-tabs-gray-out-icons 'buffer
	    centaur-tabs-set-bar 'left
	    centaur-tabs-set-modified-marker t
	    centaur-tabs-close-button ""
	    centaur-tabs-modified-marker ""
	    centaur-tabs-cycle-scope 'tabs)
     ;; (centaur-tabs-headline-match)
     ;; (centaur-tabs-group-by-projectile-project)
     ;; (centaur-tabs-buffer-groups)
     (centaur-tabs-mode t))
   :general
   (general-def
     "C-<tab>" 'centaur-tabs-forward
     "C-S-<tab>" 'centaur-tabs-backward
     "C-<iso-lefttab>" 'centaur-tabs-backward
     "C-w" 'kill-current-buffer
     :states '(normal)))

completion

(use-package vertico
  :hook (after-init . vertico-mode)
  :config
  (progn
    ;; Grow and shrink the Vertico minibuffer
    (setq vertico-resize t)
    )
  :general
  (general-def vertico-map :keymaps 'override
    "C-j" #'vertico-next
    "C-k" #'vertico-previous
    )
  )

(use-package mini-frame
  ;; workaround for strange behavior with wayland
  ;; https://github.com/muffinmad/emacs-mini-frame/issues/60
  :if (not (string= (getenv "XDG_SESSION_TYPE") "wayland"))
  :after evil
  :hook (after-init . mini-frame-mode)
  :init
  (progn
    ;; (setq mini-frame-advice-functions '(read-from-minibuffer
    ;; 					read-string
    ;; 					yes-or-no-p
    ;; 					read-shell-command
    ;; 					)
    ;; 	  )
    )
  :config
  (progn
    (setq mini-frame-show-parameters '((top . 0.2)
					 (width . 0.8)
					 (left . 0.5))
	    )
    (add-to-list 'mini-frame-ignore-commands 'shell-command)
    (add-to-list 'mini-frame-ignore-commands 'evil-ex)
    ))

(use-package marginalia
  :hook (after-init . marginalia-mode))

(use-package orderless
  :init
  ;; Configure a custom style dispatcher (see the Consult wiki)
  ;; (setq orderless-style-dispatchers '(+orderless-dispatch)
  ;;       orderless-component-separator #'orderless-escapable-split-on-space)
  (setq completion-styles '(orderless)
	  completion-category-overrides '((file (styles partial-completion)))))

;; Persist history over Emacs restarts. Vertico sorts by history position.
(use-package savehist
  :hook (after-init . savehist-mode))

olivetti

A simple Emacs minor mode for a nice writing environment.

https://github.com/rnkn/olivetti

(use-package  olivetti
  :defer t
  :init
  (progn
    (setq olivetti-body-width 0.618))
  :general
  (global-leader "To" '(olivetti-mode :wk "olivetti mode"))
  )

nix

Nix-mode uses nixfmt for formatting and rnix-lsp as language server.

(use-package nix-mode
  :defer t
  :mode "\\.nix\\'"
  :init
  (add-hook 'nix-mode-hook #'(lambda ()
				 (setq-local format-all-formatters '(("Nix" alejandra)))
				 (lsp-deferred)))
  :general
  (global-major-leader :keymaps 'nix-mode-map
    "b"
    '(:ignore t :which-key "buffers")
    "bf"
    'format-all-buffer
    )
  :config
  (electric-indent-mode -1)
  )

direnv

This package provides direnv integration for emacs.

https://github.com/wbolster/emacs-direnv

(use-package direnv
  :hook (after-init . direnv-mode)
  :config
  (setq direnv-use-faces-in-summary nil)
  )

Utility

Hackernews Request

Asynchronously download top stories from Hacker News.

 
 
 (defconst hackernews-api-base "https://hacker-news.firebaseio.com")
 (defconst hackernews-api-version "v0")
 
 (defun hackernews-get-topstory-ids (list-size callback)
   "Asynchronously request hackernews topstories and clip them to LIST-SIZE if necessary and call CALLBACK when request is done."
   (let ((result (request (format "%s/%s/topstories.json" hackernews-api-base hackernews-api-version)
		    :type "GET"
		    :parser 'json-read
		    :error
		    (cl-function (lambda (&rest args &key error-thrown &allow-other-keys)
				   (message "hackernews-get-topstory-ids error: %S" error-thrown)))
		    :success
		    (cl-function
		     (lambda (&key data &allow-other-keys)
		       ;; append converts vector into list
		       (funcall callback (-slice (append data nil) 0 list-size))
		       )))))))
 
 (defun hackernews-get-items (list-of-ids callback)
   "Asynchronously request LIST-OF-IDS from hackernews.  After requests are completed call CALLBACK."
   (setq hackernews-get-items--completed-items '())
   (defun hackernews-get-items--callback (count-of-requests idx data callback)
     (add-to-list 'hackernews-get-items--completed-items (list idx data) t)
     (when (equal count-of-requests (length hackernews-get-items--completed-items))
	(let* ((sorted-items (-sort (-on #'< #'-first-item) hackernews-get-items--completed-items))
	       (mapped-items (-map #'-last-item sorted-items))
	       (filtered-items (-non-nil mapped-items)))
	  (funcall callback filtered-items)
	  (setq hackernews-get-items--completed-items '()))
	))
 
   (dotimes (idx (length list-of-ids))
     (let* ((count-of-requests (length list-of-ids))
	     (request-string (format "%s/%s/item/%s.json" hackernews-api-base hackernews-api-version (elt list-of-ids idx)))
	     (result (request request-string
		       :type "GET"
		       :parser 'json-read
		       :error
		       (cl-function (lambda (&rest args &key error-thrown &allow-other-keys)
				      (message "hackernews-get-items error: %S" error-thrown)
				      (hackernews-get-items--callback count-of-requests idx nil callback)
				      ))
		       :success  (cl-function
				  (lambda (&key data &allow-other-keys)
				    (hackernews-get-items--callback count-of-requests idx (append data nil) callback)))))))))
 
 (defun hackernews-get-topstories (list-size callback)
   "Asynchronously request LIST-SIZE topstories from Hackernews and call CALLBACK with stories when finished."
   (hackernews-get-topstory-ids list-size
				 (lambda (list-of-ids)
				   (hackernews-get-items list-of-ids
							 (lambda (items)
							   (funcall callback items))))))

Hackernews Dashboard

Utility and formatting functions to display downloaded stories from Hacker News.

(defconst dashboard-hackernews-init-state (list '((formatted-string . "Loading...")))
  "Initial state of dashboard hackernews items.")

(defvar dashboard-hackernews-state dashboard-hackernews-init-state
  "State of dashboard hackernews items.")

(defun dashboard-hackernews-add-formatted-story (max-digit-length item)
  "Format hackernews story ITEM to the string '[score] title' by also respecting MAX-DIGIT-LENGTH to align all score strings."
  ;; format-string: [%MAX_DIGIT-LENGTHd] %s
  (let* ((format-string (format "[%%%dd] %%s" max-digit-length))
	   (formatted-string (format format-string (cdr (assoc 'score item)) (decode-coding-string (cdr (assoc 'title item)) 'utf-8))))
    ;; Backquoting https://www.gnu.org/software/emacs/manual/html_node/elisp/Backquote.html
    (push `(formatted-string . ,formatted-string) item)))

(defun dashboard-hackernews-add-formatted-stories-alist (stories)
  "Add formatted-string key and value from story title to all STORIES."
  (let* ((max-digit-length (dashboard-hackernews-max-digit-length stories))
	   (formatted-stories (mapcar (-partial 'dashboard-hackernews-add-formatted-story max-digit-length) stories)))
    formatted-stories))

(defun dashboard-hackernews-max-digit-length (stories)
  "Return the max digit length of all scores in STORIES."
  (-max (-flatten (mapcar (lambda (story) (length (number-to-string (cdr (assoc 'score story))))) stories))))

Build Emacs

This Dockerfile builds and sets up Emacs and various dependencies on a Debian system during make tangle.

Work Build

 FROM debian:bullseye AS builder
 ARG DEBIAN_FRONTEND=noninteractive
 ARG EMACS_COMMIT=a45aed9
 # for --shallow-since to speed up cloning
 # example: --shallow-since "2 months" or "yyyy-MM-ddTHH:mm:ss"
 ARG DATE=2021-06-01
 # no --depth=1 because we want specific EMACS_COMMIT, could take longer
 
 RUN apt-get update
 RUN apt-get install -y git autoconf texinfo binutils flex bison \
	libmpc-dev libmpfr-dev libgmp-dev coreutils make \
	libtinfo5 texinfo libjpeg-dev libtiff-dev libgif-dev libxpm-dev \
	libgtk-3-dev libgnutls28-dev libncurses5-dev libxml2-dev libxt-dev \
	libjansson4 gcc-multilib g++-8 libcanberra-gtk3-module libjansson-dev \
	    #not build gcc 
	    librsvg2-dev libpng-dev gcc-10 libgccjit0 libgccjit-10-dev
 
 WORKDIR / 
 
 RUN git clone https://git.savannah.gnu.org/git/emacs.git \
	-b master emacs-native --shallow-since "$DATE"
 
 WORKDIR /emacs-native/	  
 
 RUN git checkout "$EMACS_COMMIT"
 RUN ./autogen.sh
 RUN ./configure --with-native-compilation --with-mailutils --with-gnutls --with-cairo --prefix=/install_dir
 RUN make NATIVE_FULL_AOT=1 -j"$(nproc)"
 RUN make install-strip
 
 # 2. Stage
 
 FROM debian:bullseye
 
 ARG DEBIAN_FRONTEND=noninteractive
 # should be changed
 ARG SSH_PASS=test1611312
 
 RUN apt-get update && \
     apt-get install -y libmpc3 libmpfr6 libgmp10 coreutils libjpeg62-turbo \
     libtiff5 libgif7 libxpm4 libgtk-3-0 libgnutlsxx28 libncurses5 libxml2 \
     libxt6 libjansson4 libcanberra-gtk3-module libx11-xcb1 binutils libc6-dev \	
     librsvg2-2 libpng-dev install-info texinfo gcc-10 libgccjit0 openssh-server xorg \
	  git vim curl unzip make cmake libtool-bin 	libvterm-dev \
	  # tackle bug when emacs freezes because of xserver and clipboard handling
	  xsel \
	  silversearcher-ag \
	  git-flow \
	  # build python3.7
	  make build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm libncursesw5-dev xz-utils tk-dev libxml2-dev libxmlsec1-dev libffi-dev liblzma-dev \
	  && apt-get clean && rm -rf /var/lib/apt/lists/*
 
 
 RUN apt install -y npm ispell
 RUN npm install -g pyright conventional-changelog-cli
 
 COPY --from=builder /install_dir /install_dir
 
 RUN curl -O https://www.python.org/ftp/python/3.7.3/Python-3.7.3.tar.xz
 RUN tar -xf Python-3.7.3.tar.xz
 RUN rm Python-3.7.3.tar.xz
 WORKDIR /Python-3.7.3
 # --enable-optimizations executes test for profiling to create a faster executable
 # takes longer
 RUN ./configure --enable-optimizations
 RUN make -j "$(nproc)"
 RUN make install
 
 WORKDIR /
 
 RUN rm -rf/Python-3.7.3
 
 SHELL ["/bin/bash", "-o", "pipefail", "-c"]
 RUN echo "root:$SSH_PASS" | chpasswd
 RUN echo "PermitRootLogin yes" >> /etc/ssh/sshd_config
 
 WORKDIR /root/
 
 RUN pip3.7 install -U pip virtualenv virtualenvwrapper
 #virtualenvwrapper is installed to /usr/local/bin/virtualenvwrapper.sh
 
 # bashrc
 
 RUN touch .bashrc
 RUN echo "export PATH=/install_dir/bin/:${PATH}" >> .bashrc
 RUN echo "export LD_LIBRARY_PATH=/install_dir/lib" >> .bashrc
 RUN echo "export LIBRARY_PATH=/install_dir/lib" >> .bashrc
 RUN echo "export VIRTUALENVWRAPPER_PYTHON=/usr/local/bin/python3.7" >> .bashrc
 RUN echo "export WORKON_HOME=$HOME/.virtualenvs" >> .bashrc
 RUN echo "export PROJECT_HOME=$HOME/Devel" >> .bashrc
 RUN echo "source /usr/local/bin/virtualenvwrapper_lazy.sh" >> .bashrc
 
 
 # Setting this is very important to allow x11 forwarding
 RUN echo "X11UseLocalhost No">> /etc/ssh/sshd_config
 RUN echo "X11Forwarding yes" >> /etc/ssh/sshd_config
 RUN echo "AllowTcpForwarding yes" >> /etc/ssh/sshd_config
 
 #TODO add proxy config
 
 #Install font
 RUN curl -o fonts.zip  https://fonts.google.com/download?family=Source%20Code%20Pro
 RUN unzip fonts.zip -d /usr/local/share/fonts
 RUN fc-cache -f -v
 RUN rm fonts.zip
 
 EXPOSE 22
 
 RUN service ssh start
 
 #TODO logs nothing
 CMD ["journalctl", "-t", "ssh", "-f"]