summaryrefslogblamecommitdiff
path: root/init.org
blob: 6b9cc3a97448a94c7b28f967f48d44d48a0fea07 (plain) (tree)
1
2
3
4
5
6
7
8
9
10

                               
                                                                       






                                                                                    



                                                                                                                                                                 


                       
                                                                   


















                                                                                                       

























































































































































































































































                                                                                                                                                                                                                                                          
                                       

















                                               
                                                                                                                   






                                           





                                             
                                   
                                            


         
                                                                  








                                      















                                                                               

















                                                                                         





                                                                         






















                                                                           






                                          

                                   


                                  


                                                                        


                                                             
              



                                                                        

                                    
 
         



                                                                          
             







                                          
           

                      
             
           
                   
         
             
                      

                      
          
           
                                       































































                                                                                   
           






                                   
           


                                   













































































































































                                                                                               
                                                    






                                                           





                                                           
                                

                                                   

                                    

                                                   

                                       

                                                   


















































































































































                                                                                                                                                                     




                                                                                                                                                                               
                  
           
                  

                                              






                                                               
         





































































                                                              

                    
          





                                                                                         





































                                                                                            
                                      









































                                                                          




































                                                           
           















                                                                     











                                                                                                                                                                                                               



                      
            





















































































































                                                                               
         


                                 








                                      
                                    














                                            





                                                                                                        

















                                                      




                      
              















                                                                      
      



























                                                                         

































                                                                           




















                                                                                     





































































































































































































































                                                                                                                                                                                                        
#+TITLE: my Emacs configuration
#+AUTHOR: Franck Cuny
#+PROPERTY: header-args :results silent :tangle ~/.config/emacs/init.el
#+STARTUP: overview indent
#+AUTO_TANGLE: t

To use this file, run =org-tangle= (or =C-c C-v C-t=).

To exclude specific source blocks from being tangled add =:tangle no= to the header.

* TODO set the mode when tangling the files
When version 9.7 of org-mode is released, I need to add the following property to ~header-args~:
- ~tangle-mode o444~ (see [[https://github.com/emacs-straight/org-mode/blob/f03b839530d86269bd23baef2965a316a4172895/etc/ORG-NEWS#L1307][this]] for more details)

* Startup
** early initialization
:PROPERTIES:
:header-args: :results silent :tangle ~/.config/emacs/early-init.el
:END:

Using an =early-init.el= file can speedup loading emacs. This is only available after emacs version 27.

#+begin_src emacs-lisp :lexical t
;;; early-init.el --- Early initialization -*- lexical-binding: t -*-

;;; Commentary:

;;; Code:

;; disable GUI elements
(scroll-bar-mode -1)      ; hide the scroll bar
(tool-bar-mode -1)        ; hide the tool bar
(menu-bar-mode -1)        ; hide the menu
(blink-cursor-mode -1)    ; don't blink the cursor

(setq make-pointer-invisible t)  ;; hide cursor while typing
(setq use-dialog-box nil)        ;; do not show GUI dialogs
(setq inhibit-startup-screen t)  ;; hide the startup screen

;; don't report warnings and errors related to native compilation
(setq native-comp-async-report-warnings-errors nil)

;; increase font size
(set-face-attribute 'default nil :height 130)

;;; early-init.el ends here
#+end_src

** set headers

#+begin_src emacs-lisp :epilogue (format-time-string ";; Last generated on %c")
;;; init.el --- This is where all emacs start. -*- lexical-binding: t -*-

;;; Commentary:

;;; Code:
#+end_src

** garbage collection
Set the garbage collection threshold.

#+begin_src emacs-lisp
(setq gc-cons-percentage 0.5
      gc-cons-threshold (* 128 1024 1024))
#+end_src

** report time

While I don't restart emacs that often, it's still useful to know how much time was spend loading the modules.

#+begin_src emacs-lisp
(defconst emacs-start-time (current-time))

(defun report-time-since-load (&optional suffix)
  (message "Loading init...done (%.3fs)%s"
	   (float-time (time-subtract (current-time) emacs-start-time))
	   suffix))

(add-hook 'after-init-hook
	  #'(lambda () (report-time-since-load " [after-init]"))
	  t)
#+end_src

** generic configuration

#+begin_src emacs-lisp
(fset 'yes-or-no-p 'y-or-n-p)          ; replace yes/no prompts with y/n

;; set utf-8 as the default encoding
(prefer-coding-system 'utf-8-unix)
(setq locale-coding-system 'utf-8)
(set-language-environment 'utf-8)
(set-terminal-coding-system 'utf-8)
(set-keyboard-coding-system 'utf-8)
#+end_src

** load =use-package=

Add MELPA and NonGNU ELPA repositories.

#+begin_src emacs-lisp
(setq load-prefer-newer t)
(setq init-file-debug t)

(package-initialize)

(setq package-archives (append
                        package-archives
                        '(("melpa" . "https://melpa.org/packages/")
                          ("elpa" . "https://elpa.nongnu.org/nongnu/"))))
#+end_src

Then we load =use-package=.

#+begin_src emacs-lisp
(eval-and-compile
  (defsubst emacs-path (path)
    (expand-file-name path user-emacs-directory))

  (setq package-enable-at-startup nil
	load-path
	(append (list (emacs-path "use-package"))
		(delete-dups load-path)
		(list (emacs-path "lisp")))))

(require 'use-package)

(setq use-package-verbose init-file-debug
      use-package-expand-minimally (not init-file-debug)
      use-package-compute-statistics nil
      debug-on-error init-file-debug)
#+end_src

*** diminish

Since =use-package= no longer requires =diminish= as a dependency (see [[https://github.com/jwiegley/use-package/blob/a6e856418d2ebd053b34e0ab2fda328abeba731c/NEWS.md?plain=1#LL103C3-L103C62][changelog]]), we need to require it before other packages.

#+begin_src emacs-lisp
(use-package diminish :ensure t)
#+end_src

** define data environment

#+begin_src emacs-lisp
(defconst user-data-directory
  (emacs-path "data"))

(defun user-data (dir)
  (expand-file-name dir user-data-directory))

(setq custom-file (user-data "customizations.el"))
(load custom-file 'noerror)
#+end_src

* Helper functions

** Push and pop window configurations

#+begin_src emacs-lisp
(defvar saved-window-configuration nil)

(defun push-window-configuration ()
  (interactive)
  (push (current-window-configuration) saved-window-configuration))

(defun pop-window-configuration ()
  (interactive)
  (let ((config (pop saved-window-configuration)))
    (if config
        (set-window-configuration config)
      (if (> (length (window-list)) 1)
          (delete-window)
        (bury-buffer)))))
#+end_src

** Rename the current buffer

#+begin_src emacs-lisp
(defun my/rename-this-buffer-and-file ()
  "Renames current buffer and file it is visiting."
  (interactive)
  (let ((name (buffer-name))
        (filename (buffer-file-name))
        (read-file-name-function 'read-file-name-default))
    (if (not (and filename (file-exists-p filename)))
        (error "Buffer '%s' is not visiting a file!" name)
      (let ((new-name (read-file-name "New name: " filename)))
        (cond ((get-buffer new-name)
               (error "A buffer named '%s' already exists!" new-name))
              (t
               (rename-file filename new-name 1)
               (rename-buffer new-name)
               (set-visited-file-name new-name)
               (set-buffer-modified-p nil)
               (message "File '%s' successfully renamed to '%s'" name (file-name-nondirectory new-name))))))))
#+end_src

** Is this my work issued machine

#+begin_src emacs-lisp
(defun my/check-work-machine-p ()
  "Return t if this is a work machine."
  (string-match "HQ\\.*" (system-name)))
#+end_src

** GitHub's code search

#+begin_src emacs-lisp
(defun my/github-code-search ()
  "Search code on github for a given language."
  (interactive)
  (let ((language (completing-read
                   "Language: "
                   '("Emacs Lisp" "Python"  "Go" "Nix")))
        (code (read-string "Code: ")))
    (browse-url
     (concat "https://github.com/search?lang=" language
             "&type=code&q=" code))))
#+end_src

** Search work's repositories

#+begin_src emacs-lisp
(defun my/work-code-search ()
  "Search code on sourcegraph for a given language."
  (interactive)
  (let ((language (completing-read
                   "Language: "
                   '("Ruby" "Python"  "Go")))
        (code (read-string "Code: ")))
    (browse-url
     (concat "https://sourcegraph.rbx.com/search?q=context:global+lang:" language
             "+" code))))
#+end_src

* Packages
** abbrev

If you want 'btw' to expand to 'by the way', type 'btw' followed by =C-x a i g=. The =g= is flow global, and you can define abbreviations per mode.

| keys      | action                                           |
|-----------+--------------------------------------------------|
| =C-x a i g= | add a new abbreviation for all modes             |
| =C-x a i l= | add a new abbreviation local to the current mode |

#+begin_src emacs-lisp
(use-package abbrev
  :diminish
  :hook
  ((text-mode prog-mode) . abbrev-mode)
  :custom
  (abbrev-file-name (emacs-path "abbrevs.el"))
  ;; save abbrevs when files are saved
  (save-abbrevs 'silently)
  :config
  (if (file-exists-p abbrev-file-name)
      (quietly-read-abbrev-file)))
#+end_src

** autorevert

Automatically revert buffers if the file has changed on disk.

#+begin_src emacs-lisp
(use-package autorevert
  :custom
  (auto-revert-use-notify nil)
  :config
  (global-auto-revert-mode t))
#+end_src

** bookmark

| keys    | action                             |
|---------+------------------------------------|
| =C-x r m= | add a new bookmark                 |
| =C-x r l= | list all the bookmarks in a buffer |
| =C-x r b= | list the bookmarks with consul     |

#+begin_src emacs-lisp
(use-package bookmark
  :defer t
  :custom
  (bookmark-default-file (user-data "bookmarks")))
#+end_src

** compile
Start a compilation with =M-x compile=.

#+begin_src emacs-lisp
(use-package compile
  :bind (:map compilation-mode-map
              ("z" . delete-window))
  :custom
  (compilation-always-kill t)
  ;; Don't freeze when process reads from stdin
  (compilation-disable-input t)
  (compilation-ask-about-save nil)
  (compilation-context-lines 10)
  (compilation-scroll-output 'first-error)
  (compilation-skip-threshold 2)
  (compilation-window-height 100))
#+end_src

** completions
*** consult
The =consult= package provides search and navigation commands based on Emacs completion function =completing-read=.

#+begin_src emacs-lisp
(use-package consult
  :ensure t
  :bind (("C-c m"   . consult-mode-command)
	 ("M-g o"   . consult-org-heading)
	 ("C-x b"   . consult-buffer)
	 ("C-x r b" . consult-bookmark)
	 ("C-x p b" . consult-project-buffer)
	 ("C-c i"   . consult-imenu)
	 ("M-g e"   . consult-compile-error)
	 ("M-g g"   . consult-goto-line)
	 ("M-g M-g" . consult-goto-line)
	 ("M-g m"   . consult-mark)
	 ("M-g k"   . consult-global-mark)))
#+end_src

*** corfu
Corfu enhances in-buffer completion with a small completion popup.

#+begin_src emacs-lisp
(use-package corfu
  :ensure t
  :demand t
  :bind (("M-/" . completion-at-point)
	 :map corfu-map
	 ("C-n"      . corfu-next)
	 ("C-p"      . corfu-previous)
	 ("<return>" . corfu-insert)
	 ("M-d"      . corfu-info-documentation)
	 ("M-l"      . corfu-info-location)
	 ("M-."      . corfu-move-to-minibuffer))
  :custom
  ;; Works with `indent-for-tab-command'. Make sure tab doesn't indent when you
  ;; want to perform completion
  (tab-always-indent 'complete)
  (completion-cycle-threshold t)      ; Always show candidates in menu

  (corfu-auto t)
  (corfu-auto-prefix 2)
  (corfu-auto-delay 0.25)

  (corfu-min-width 80)
  (corfu-max-width corfu-min-width)     ; Always have the same width
  (corfu-cycle t)

  ;; `nil' means to ignore `corfu-separator' behavior, that is, use the older
  ;; `corfu-quit-at-boundary' = nil behavior. Set this to separator if using
  ;; `corfu-auto' = `t' workflow (in that case, make sure you also set up
  ;; `corfu-separator' and a keybind for `corfu-insert-separator', which my
  ;; configuration already has pre-prepared). Necessary for manual corfu usage with
  ;; orderless, otherwise first component is ignored, unless `corfu-separator'
  ;; is inserted.
  (corfu-quit-at-boundary nil)
  (corfu-separator ?\s)            ; Use space
  (corfu-quit-no-match 'separator) ; Don't quit if there is `corfu-separator' inserted
  (corfu-preview-current 'insert)  ; Preview first candidate. Insert on input if only one
  (corfu-preselect-first t)        ; Preselect first candidate?

  ;; Other
  (corfu-echo-documentation nil)        ; Already use corfu-popupinfo

  :init
  ;; see https://github.com/minad/corfu#completing-in-the-eshell-or-shell
  (add-hook 'eshell-mode-hook
            (lambda ()
              (setq-local corfu-auto nil)
              (corfu-mode)))
  :config
  (global-corfu-mode))
#+end_src

**** corfu-popupinfo

#+begin_src emacs-lisp
(use-package corfu-popupinfo
  :after corfu
  :hook (corfu-mode . corfu-popupinfo-mode)
  :bind (:map corfu-map
              ("M-n" . corfu-popupinfo-scroll-up)
              ("M-p" . corfu-popupinfo-scroll-down)
              ([remap corfu-show-documentation] . corfu-popupinfo-toggle))
  :custom
  (corfu-popupinfo-delay 0.5)
  (corfu-popupinfo-max-width 70)
  (corfu-popupinfo-max-height 20)
  ;; Also here to be extra-safe that this is set when `corfu-popupinfo' is
  ;; loaded. I do not want documentation shown in both the echo area and in
  ;; the `corfu-popupinfo' popup.
  (corfu-echo-documentation nil))
#+end_src
*** cape

#+begin_src emacs-lisp
(use-package cape
  :demand t
  :ensure t
  :bind (("C-c . p" . completion-at-point)
	 ("C-c . h" . cape-history)
	 ("C-c . f" . cape-file)
	 ("C-c . a" . cape-abbrev)
	 ("C-c . l" . cape-line)
	 ("C-c . w" . cape-dict)
	 ("C-c . r" . cape-rfc1345))
  :init
  ;; Add `completion-at-point-functions', used by `completion-at-point'.
  (add-to-list 'completion-at-point-functions #'cape-file)
  (add-to-list 'completion-at-point-functions #'cape-abbrev))
#+end_src
*** marginalia
#+begin_src emacs-lisp
  (use-package marginalia
    :ensure t
    ;; Either bind `marginalia-cycle' globally or only in the minibuffer
    :bind (:map minibuffer-local-map
         ("M-A" . marginalia-cycle))

    :init
    ;; Must be in the :init section of use-package such that the mode gets
    ;; enabled right away. Note that this forces loading the package.
    (marginalia-mode))
#+end_src
*** orderless
#+begin_src emacs-lisp
  (use-package orderless
    :demand t
    :ensure t
    :custom
    (completion-styles '(orderless basic))
    (completion-category-defaults nil))
#+end_src
*** vertico
#+begin_src emacs-lisp
  (use-package vertico
    :ensure t
    :config
    (vertico-mode))
#+end_src
*** yasnippet
#+begin_src emacs-lisp
(use-package yasnippet
  :ensure t
  :defer t
  :diminish
  :hook ((prog-mode . yas-minor-mode)))
#+end_src

** dired

#+begin_src emacs-lisp
(use-package dired
  :diminish
  :bind (:map dired-mode-map
              ("j"     . dired)
              ("z"     . pop-window-configuration)
              ("^"     . dired-up-directory)
              ("q"     . pop-window-configuration)
              ("M-!"   . shell-command)
              ("<tab>" . dired-next-window))
  :custom
  (dired-clean-up-buffers-too nil)
  (dired-dwim-target t)
  (dired-hide-details-hide-information-lines nil)
  (dired-hide-details-hide-symlink-targets nil)
  (dired-listing-switches "-lah")
  (dired-no-confirm
   '(byte-compile chgrp chmod chown copy hardlink symlink touch))
  (dired-omit-mode nil t)
  (dired-omit-size-limit 60000)
  (dired-recursive-copies 'always)
  (dired-recursive-deletes 'always)
  :functions (dired-dwim-target-directory))
#+end_src

** direnv
Invokes [[https://direnv.net/][direnv]] to obtain environment for the current file.

#+begin_src emacs-lisp
  (use-package direnv
    :ensure t
    :custom
    (direnv-always-show-summary nil)
    :config
    (direnv-mode))
#+end_src

** docker

#+begin_src emacs-lisp
(use-package docker
  :bind ("C-c d" . docker)
  :diminish
  :init
  (use-package docker-image   :commands docker-images)
  (use-package docker-volume  :commands docker-volumes)
  (use-package docker-network :commands docker-containers)
  (use-package docker-compose :commands docker-compose)

  (use-package docker-container
    :commands docker-containers
    :custom
    (docker-containers-shell-file-name "/bin/bash")
    (docker-containers-show-all nil)))
#+end_src

*** docker-compose-mode

#+begin_src emacs-lisp
(use-package docker-compose-mode
  :ensure t
  :mode "docker-compose.*\.yml\\'")
#+end_src

*** dockerfile-mode

#+begin_src emacs-lisp
(use-package dockerfile-mode
  :ensure t
  :mode "Dockerfile[a-zA-Z.-]*\\'")
#+end_src

** emacs

#+begin_src emacs-lisp
  (use-package emacs
    :bind* ("M-j" . join-line)

    :custom-face
    (cursor ((t (:background "hotpink"))))
    ;; (highlight ((t (:background "blue4"))))
    ;; (minibuffer-prompt ((t (:foreground "grey80"))))
    ;; (mode-line-inactive ((t (:background "grey50"))))
    (nobreak-space ((t nil)))

    :custom
    (auto-save-default nil)                     ;; don't auto save files
    (auto-save-list-file-prefix nil)            ;; no backups
    (create-lockfiles nil)                      ;; don't use a lock file
    (confirm-kill-emacs #'yes-or-no-p)          ;; ask before killing emacs
    (make-backup-files nil)                     ;; really no backups
    (minibuffer-message-timeout 0.5)            ;; How long to display an echo-area message
    (next-screen-context-lines 5)               ;; scroll 5 lines at a time
    (require-final-newline t)                   ;; ensure newline exists at the end of the file
    (ring-bell-function 'ignore)                ;; really no bell
    (tab-always-indent 'complete)               ;; when using TAB, always indent
    (visible-bell nil)                          ;; no bell
    (column-number-mode t)                      ;; show column number in the mode line
    (electric-pair-mode 1)                      ;; matches parens and brackets
    (indent-tabs-mode nil)                      ;; turn off tab indentation
    (cursor-type 'box)                          ;; cursor is a horizontal bar
    (delete-by-moving-to-trash t)               ;; delete files by moving them to the trash
    (initial-scratch-message "")                ;; empty scratch buffer
    (garbage-collection-messages t)             ;; log when the gc kicks in

    ;; bytecomp.el
    (byte-compile-verbose nil)

    ;; prog-mode.el
    (prettify-symbols-unprettify-at-point 'right-edge)

    ;; startup.el
    (auto-save-list-file-prefix (user-data "auto-save-list/.saves-"))
    (initial-buffer-choice t)
    (initial-major-mode 'fundamental-mode)
    (initial-scratch-message "")

    (user-full-name "Franck Cuny")
    (user-mail-address "franck@fcuny.net")
    (add-log-mailing-address "franck@fcuny.net")

    (history-length 200)
    (history-delete-duplicates t)
    (completion-ignored-extensions
     '(".class"
       ".cp"
       ".elc"
       ".fmt"
       ".git/"
       ".pyc"
       ".so"
       "~"))

    ;; paragraphs.el
    (sentence-end-double-space nil)

    ;; switch to view-mode whenever you are in a read-only buffer (e.g.
    ;; switched to it using C-x C-q).
    (view-read-only t)

    ;; window.el
    (switch-to-buffer-preserve-window-point t)

    ;; warnings.el
    (warning-minimum-log-level :error)

    ;; frame.el
    (window-divider-default-bottom-width 1)
    (window-divider-default-places 'bottom-only)

    ;; paren.el
    (show-paren-delay 0)
    (show-paren-highlight-openparen t)
    (show-paren-when-point-inside-paren t)
    (show-paren-when-point-in-periphery t)

    :config
    (add-hook 'after-save-hook
              #'executable-make-buffer-file-executable-if-script-p))
#+end_src

** eshell
#+begin_src emacs-lisp
(use-package eshell
  :commands (eshell eshell-command)
  :custom
  (eshell-directory-name (emacs-path "eshell"))
  (eshell-hist-ignoredups t)
  (eshell-history-size 50000)
  (eshell-ls-dired-initial-args '("-h"))
  (eshell-ls-exclude-regexp "~\\'")
  (eshell-ls-initial-args "-h")
  (eshell-modules-list
   '(eshell-alias
     eshell-basic
     eshell-cmpl
     eshell-dirs
     eshell-glob
     eshell-hist
     eshell-ls
     eshell-pred
     eshell-prompt
     eshell-rebind
     eshell-script
     eshell-term
     eshell-unix
     eshell-xtra))
  (eshell-prompt-function
   (lambda nil
     (concat (abbreviate-file-name (eshell/pwd))
             (if (= (user-uid) 0)
                 " # " " $ "))))
  (eshell-save-history-on-exit t)
  (eshell-stringify-t nil)
  (eshell-term-name "ansi")

  :preface
  (defun eshell-initialize ()
    (add-hook 'eshell-expand-input-functions #'eshell-spawn-external-command)

  :init
  (add-hook 'eshell-first-time-mode-hook #'eshell-initialize)))
#+end_src

** flymake

#+begin_src emacs-lisp
(use-package flymake
  :bind (("C-c ! n" . flymake-goto-next-error)
         ("C-c ! p" . flymake-goto-next-error))
  :custom
  (flymake-start-on-save-buffer t)
  (flymake-fringe-indicator-position 'left-fringe)
  (flymake-suppress-zero-counters t)
  (flymake-proc-compilation-prevents-syntax-check t)
  (elisp-flymake-byte-compile-load-path load-path))
#+end_src

** font
Somehow I need to set the font to a higher height on MacOS.

#+BEGIN_SRC emacs-lisp
(defun my/default-font-size()
  "Set the size of the font based on the operating system."
  (if (memq window-system '(mac ns))
      150
    130))

(set-face-attribute 'default nil
                    :family "Source Code Pro"
                    :height (my/default-font-size))

(set-face-attribute 'fixed-pitch nil
                    :family "Source Code Pro"
                    :height (my/default-font-size))

(set-face-attribute 'variable-pitch nil
                    :family "DejaVu Sans"
                    :height (my/default-font-size))
#+END_SRC

** fringe

#+begin_src emacs-lisp
(use-package fringe
  :config
  ;;; no fringe on the right side
  (set-fringe-mode '(8 . 0)))
#+end_src

** git-link

A convenient package to create link to a code location in GitHub, or other forges. This is very useful to get a link and share with others when looking at some code.

#+begin_src emacs-lisp
  (use-package git-link
    :defines git-link-remote-alist
    :ensure t
    :bind ("C-c Y" . git-link)
    :commands (git-link git-link-commit git-link-homepage)

    :custom
    (git-link-open-in-browser t)

    :preface
    (defun git-link-fcuny-net (hostname dirname filename branch commit start end)
      (format "http://git.fcuny.net/%s/tree/%s?id=%s#n%s"
              (replace-regexp-in-string "^r/\\(.*\\)" "\\1.git" dirname)
              filename
              commit
              start))

    (defun git-link-commit-fcuny-net (hostname dirname commit)
      (format "http://git.fcuny.net/%s/commit/?id=%s"
              (replace-regexp-in-string "^r/\\(.*\\)" "\\1.git" dirname)
              commit))

    :config
    (add-to-list 'git-link-remote-alist '("git\\.fcuny\\.net" git-link-fcuny-net))
    (add-to-list 'git-link-commit-remote-alist '("git\\.fcuny\\.net" git-link-commit-fcuny-net))

    ;; sets up roblox git enterprise as a git-link handler
    (add-to-list 'git-link-remote-alist '("github\\.rblx\\.com" git-link-github))
    (add-to-list 'git-link-commit-remote-alist '("github\\.rblx\\.com" git-link-commit-github)))
#+end_src

** ibuffer

#+begin_src emacs-lisp
(use-package ibuffer
  :bind ("C-x C-b" . ibuffer)
  :custom
  (ibuffer-default-display-maybe-show-predicates t)
  (ibuffer-expert t)
  (ibuffer-formats
   '((mark modified read-only " "
           (name 16 -1)
           " "
           (size 6 -1 :right)
           " "
           (mode 16 16)
           " " filename)
     (mark " "
           (name 16 -1)
           " " filename)))
  (ibuffer-maybe-show-regexps nil)
  (ibuffer-saved-filter-groups
   '(("default"
      ("Magit"
       (or
        (mode . magit-status-mode)
        (mode . magit-log-mode)
        (name . "\\*magit")
        (name . "magit-")
        (name . "git-monitor")))
      ("Commands"
       (or
        (mode . shell-mode)
        (mode . eshell-mode)
        (mode . term-mode)
        (mode . compilation-mode)))
      ("Rust"
       (or
        (mode . rust-mode)
        (mode . cargo-mode)
        (name . "\\*Cargo")
        (name . "^\\*rls\\(::stderr\\)?\\*")
        (name . "eglot")))
      ("Nix"
       (mode . nix-mode))
      ("Lisp"
       (mode . emacs-lisp-mode))
      ("Dired"
       (mode . dired-mode))
      ("Org"
       (or
        (name . "^\\*Calendar\\*$")
        (name . "^\\*Org Agenda")
        (name . "^ \\*Agenda")
        (name . "^diary$")
        (mode . org-mode)))
      ("Emacs"
       (or
        (name . "^\\*scratch\\*$")
        (name . "^\\*Messages\\*$")
        (name . "^\\*\\(Customize\\|Help\\)")
        (name . "\\*\\(Echo\\|Minibuf\\)"))))))
  (ibuffer-show-empty-filter-groups nil)
  (ibuffer-shrink-to-minimum-size t t)
  (ibuffer-use-other-window t)
  :init
  (add-hook 'ibuffer-mode-hook
            #'(lambda ()
                (ibuffer-switch-to-saved-filter-groups "default"))))
#+end_src

** indent

#+begin_src emacs-lisp
(use-package indent
  :commands indent-according-to-mode
  :custom
  (tab-always-indent 'complete))
#+end_src

** ispell

#+begin_src emacs-lisp
  (use-package ispell
    :custom
    (ispell-program-name (executable-find "aspell"))
    (ispell-dictionary "en_US")
    (ispell-extra-args '("--camel-case")))
#+end_src

** jq-mode

#+begin_src emacs-lisp
  (use-package jq-mode
    :ensure t
    :mode "\\.jq\\'")
#+end_src

** js2-mode
** languages
*** eglot
After experimenting with [[https://emacs-lsp.github.io/lsp-mode/][lsp-mode]] and [[https://github.com/joaotavora/eglot][eglot]] I decided to go with eglot for LSP integration.

For languages where I want to use LSP, I need to add ~:hook (nix-mode . englot-ensure)~ in the ~use-package~ definition for the language.

#+begin_src emacs-lisp
(use-package eglot
  :ensure t
  :after yasnippet
  :bind (:map eglot-mode-map
              ("C-c l a" . eglot-code-actions)
              ("C-c l r" . eglot-rename))
  :config
  (setq-default eglot-workspace-configuration
				'((gopls
				   (usePlaceholders . t)
				   (staticcheck . t)
				   (completeUnimported . t)))))
#+end_src
*** tree-sitter

#+begin_src emacs-lisp
(use-package tree-sitter
  :ensure t
  :config
  (global-tree-sitter-mode))
#+end_src

**** tree-sitter-langs

#+begin_src emacs-lisp
(use-package tree-sitter-langs
  :after tree-sitter
  :ensure t)
#+end_src

*** lisp-mode

**** elisp-mode

#+begin_src emacs-lisp
(use-package emacs-lisp-mode
  :bind (:map emacs-lisp-mode-map
              ("C-c C-r" . eval-region)
              ("C-c C-d" . eval-defun)
              ("C-c C-b" . eval-buffer))
  :hook ((emacs-lisp-mode . flymake-mode)))
#+end_src

**** eldoc

#+begin_src emacs-lisp
(use-package eldoc
  :diminish
  :hook ((c-mode-common emacs-lisp-mode) . eldoc-mode)
  :custom
  (eldoc-idle-delay 1)
  (eldoc-documentation-strategy #'eldoc-documentation-default)
  (eldoc-echo-area-use-multiline-p 3)
  (eldoc-echo-area-prefer-doc-buffer 'maybe)
  (eldoc-echo-area-display-truncation-message nil))
#+end_src

*** hcl-mode

#+begin_src emacs-lisp
  (use-package hcl-mode
    :ensure t
    :mode "\.nomad\\'")
#+end_src

*** json-mode

#+begin_src emacs-lisp
(use-package json-mode
  :mode "\\.json\\'")
#+end_src

**** json-reformat

#+begin_src emacs-lisp
(use-package json-reformat
  :ensure t
  :after json-mode)
#+end_src

*** go-mode

#+begin_src emacs-lisp
(use-package go-mode
  :ensure t
  :defer t
  :hook ((go-mode . tree-sitter-hl-mode)
         (go-mode . eglot-ensure)
         (go-mode . (lambda () (setq tab-width 4)))
         (go-mode . (lambda () (add-hook 'before-save-hook 'eglot-format-buffer nil t))))
  :bind (:map go-mode-map
              ("C-c C-c" . compile)))
#+end_src

**** gotest

#+begin_src emacs-lisp
(use-package gotest
  :ensure t
  :after go-mode
  :custom
  (go-test-verbose t))
#+end_src

*** nix-mode

#+begin_src emacs-lisp
  (use-package nix-mode
    :ensure t
    :hook ((nix-mode . eglot-ensure)
           (nix-mode . (lambda () (add-hook 'before-save-hook 'eglot-format-buffer nil t))))
    :custom
    (nix-indent-function 'nix-indent-line))
#+end_src

*** protobuf-mode

#+begin_src emacs-lisp
  (use-package protobuf-mode
    :ensure t
    :mode "\\.proto\\'")
#+end_src

*** python-mode

Enable eglot and tree-sitter when working with python.

#+begin_src emacs-lisp
(use-package python-mode
  :hook ((python-mode . tree-sitter-hl-mode)
         (python-mode . eglot-ensure))
  :interpreter "python"
  :bind (:map python-mode-map
              ("C-c c")
              ("C-c C-z" . python-shell)))
#+end_src

**** blacken

[[https://pypi.org/project/black/][black]] is a code formatter for python.

#+begin_src emacs-lisp
(use-package blacken
  :ensure t
  :hook (python-mode . blacken-mode))
#+end_src

**** isort

[[https://pypi.org/project/isort/][Sort imports]].

#+begin_src emacs-lisp
  (use-package py-isort
    :ensure t
    :commands (py-isort-buffer py-isort-region))
#+end_src

*** ruby-mode

#+begin_src emacs-lisp
  (use-package ruby-mode
    :mode "\\.rb\\'"
    :interpreter "ruby"
    :bind (:map ruby-mode-map
                ("<return>" . my-ruby-smart-return))
    :preface
    (defun my-ruby-smart-return ()
      (interactive)
      (when (memq (char-after) '(?\| ?\" ?\'))
        (forward-char))
      (call-interactively 'newline-and-indent)))
#+end_src

*** sh-script

#+begin_src emacs-lisp
(use-package sh-script
  :defer t
  :preface
  (defvar sh-script-initialized nil)

  (defun initialize-sh-script ()
    (unless sh-script-initialized
      (setq sh-script-initialized t)
      (info-lookup-add-help :mode 'shell-script-mode
                            :regexp ".*"
                            :doc-spec '(("(bash)Index")))))
  :init
  (add-hook 'shell-mode-hook #'initialize-sh-script))
#+end_src

*** terraform-mode

#+begin_src emacs-lisp
(use-package terraform-mode
  :ensure t
  :mode "\.tf\\'")
#+end_src

*** toml-mode

#+begin_src emacs-lisp
(use-package toml-mode
  :ensure t)
#+end_src

*** yaml-mode

#+begin_src emacs-lisp
(use-package yaml-mode
  :ensure t
  :mode "\\.ya?ml\\'")
#+end_src

** MacOS specific

#+begin_src emacs-lisp
(when (memq window-system '(mac ns))
  (add-to-list 'default-frame-alist '(fullscreen . maximized))
  (add-to-list 'default-frame-alist '(ns-appearance . nil))
  (add-to-list 'default-frame-alist '(ns-transparent-titlebar . nil))
  (when (boundp 'ns-use-native-fullscreen)
    (setq ns-use-native-fullscreen nil))
  (when (boundp 'mac-allow-anti-aliasing)
    (setq mac-allow-anti-aliasing t)))
#+end_src

** path setup
This needs to be done before we configure =magit=. If done later, on MacOS, and I'm running =nix=, for some reasons emacs try to pick =git= from =/usr/bin/git= instead of =/Users/fcuny/.nix-profile/bin/git=.

#+begin_src emacs-lisp
(use-package exec-path-from-shell
  :ensure t
  :demand t
  :if (memq window-system '(mac ns))
  :config
  (exec-path-from-shell-initialize))
#+end_src

** magit

#+begin_src emacs-lisp
  (use-package magit
    :defer t
    :ensure t
    :bind (("C-x g" . magit-status)
           ("C-x G" . magit-status-with-prefix))
    :custom
    (magit-diff-options nil)
    (magit-diff-refine-hunk t)
    (magit-fetch-arguments nil)
    (magit-log-section-commit-count 10)
    (magit-pre-refresh-hook nil)
    (magit-process-popup-time 15)
    (magit-clone-default-directory "~/workspace/")
    (magit-section-initial-visibility-alist '((untracked . hide)))
    :hook (magit-mode . hl-line-mode)
    :config
    (use-package magit-commit
      :defer t
      :config
      (use-package git-commit
        :custom
        (git-commit-major-mode 'markdown-mode)
        (git-commit-setup-hook
         '(git-commit-save-message
           git-commit-turn-on-auto-fill
           git-commit-turn-on-flyspell
           bug-reference-mode))))

    (use-package magit-status
      :defer t
      :config
      (dolist (func '(magit-insert-unpushed-to-upstream-or-recent
                      magit-insert-unpulled-from-pushremote
                      magit-insert-unpulled-from-upstream
                      ))
        (remove-hook 'magit-status-sections-hook func))

      (dolist (func '(magit-insert-diff-filter-header
                      magit-insert-tags-header))
        (remove-hook 'magit-status-headers-hook func))))
#+end_src

** markdown-mode

#+begin_src emacs-lisp
(use-package markdown-mode
  :mode (("\\`README\\.md\\'" . gfm-mode)
         ("\\.md\\'"          . markdown-mode)
         ("\\.markdown\\'"    . markdown-mode))
  :custom
  (markdown-command "pandoc -f markdown_github+smart")
  (markdown-command-needs-filename t)
  (markdown-enable-math t)
  (markdown-open-command "marked")
  :init
  (setq markdown-command "multimarkdown"))
#+end_src

*** markdown-preview-mode

#+begin_src emacs-lisp
  (use-package markdown-preview-mode
    :ensure t
    :after markdown-mode
    :config
    (setq markdown-preview-stylesheets
	  (list (concat "https://github.com/dmarcotte/github-markdown-preview/"
			"blob/master/data/css/github.css"))))
#+end_src

** midnight

#+begin_src emacs-lisp
(use-package midnight
  :demand t
  :bind ("C-c z" . clean-buffer-list)
  :custom
  (midnight-delay 18000)
  (clean-buffer-list-kill-never-buffer-names
   '("*scratch*"
     "*Messages*"
     "*server*"
     "*Group*"
     "*Org Agenda*"
     "todo.org"))
  (clean-buffer-list-kill-never-regexps
   '("^ \\*Minibuf-.*\\*$"
     "^\\*Summary"
     "^\\*Article" "^#"))
  (clean-buffer-list-kill-regexps '(".*"))
  :config
  (midnight-mode t))
#+end_src

** my-cheeseboard

get the pizzas for this week at cheeseboard.

#+begin_src emacs-lisp
(use-package my-cheeseboard)
#+end_src

** my-uptime

an uptime / SLO calculator.

#+begin_src emacs-lisp
(use-package my-uptime
  :init
  (add-to-list 'display-buffer-alist '("\\*slo-calculator\\*"
                                       (display-buffer-in-side-window)
                                       (side . left)
                                       (slot . 0)
                                       (window-width . 0.35))))
#+end_src

** org-mode

#+begin_src emacs-lisp
  (use-package org
    :hook
    (org-mode . turn-on-flyspell)
    (org-mode . visual-line-mode)
    (org-mode . org-indent-mode)
    :custom
    ;;; general settings
    (org-startup-folded t)
    (org-startup-indented t)
    (org-hide-emphasis-markers t)
    (org-hide-leading-stars t)
    (org-pretty-entities t)
    (org-return-follows-link t)
    (org-startup-with-inline-images t)
    (org-export-backends '(html md))
    (org-imenu-depth 4)
    (org-insert-heading-respect-content t)
    (org-outline-path-complete-in-steps nil)
    (org-src-fontify-natively t)
    (org-src-preserve-indentation t)
    (org-src-tab-acts-natively t)
    (org-src-window-setup 'current-window)
    (org-yank-adjusted-subtrees t)
    (org-structure-template-alist
        '(("s" . "src")
          ("E" . "src emacs-lisp")
          ("p" . "src python")
          ("e" . "example")
          ("q" . "quote")
          ("V" . "verbatim")))
    :config
    (font-lock-add-keywords 'org-mode
                            '(("^ *\\(-\\) "
                               (0 (ignore (compose-region (match-beginning 1) (match-end 1) "•")))))))
#+end_src

*** org-bullet

This is to make =org-mode= document looks a bit nicer.

#+begin_src emacs-lisp
(use-package org-bullets
  :ensure t
  :hook (org-mode . org-bullets-mode))
#+end_src

*** org-auto-tangle

#+begin_src emacs-lisp
(use-package org-auto-tangle
  :ensure t
  :hook (org-mode . org-auto-tangle-mode))
#+end_src

*** org-babel

#+begin_src emacs-lisp
(use-package org-babel
  :no-require t
  :after (org)
  :config
  (org-babel-do-load-languages
   'org-babel-load-languages
   '((python     . t)
     (emacs-lisp . t)
     (calc       . t)
     (shell      . t)
     (sql        . t)
     (dot        . t)))

  (remove-hook 'kill-emacs-hook 'org-babel-remove-temporary-directory)

  (advice-add 'org-babel-edit-prep:emacs-lisp :after
              #'(lambda (_info) (run-hooks 'emacs-lisp-mode-hook))))
#+end_src

*** ox
**** ox-gfm

#+begin_src emacs-lisp
(use-package ox-gfm
  :ensure t
  :after org)
#+end_src

**** ox-md

#+begin_src emacs-lisp
(use-package ox-md
  :after org)
#+end_src

**** ox-pandoc

#+begin_src emacs-lisp
(use-package ox-pandoc
  :ensure t
  :after org
  :preface
  (defun markdown-to-org-region (start end)
    "Convert region from markdown to org, replacing selection"
    (interactive "r")
    (shell-command-on-region start end "pandoc -f markdown -t org" t t)))
#+end_src

** project
#+begin_src emacs-lisp
  (use-package project
  :ensure nil
  :custom
  (project-switch-commands
   '((project-dired "Root" ?d)
     (project-find-file "File" ?f)
     (project-switch-to-buffer "Buffer" ?b)
     (project-eshell "Shell" ?e)
     (magit-project-status "Git" ?g)))
  :init
  (setq-default project-list-file (user-data "projects.eld")))
#+end_src

** recentf

#+begin_src emacs-lisp
(use-package recentf
  :demand t
  :commands (recentf-mode
             recentf-add-file
             recentf-apply-filename-handlers)
  :custom
  (recentf-auto-cleanup 60)
  (recentf-exclude
   '("~\\'" "\\`out\\'" "\\.log\\'" "^/[^/]*:" "\\.el\\.gz\\'" "\\.gz\\'"))
  (recentf-max-saved-items 2000)
  (recentf-save-file (user-data "recentf"))
  :preface
  :config
  (recentf-mode 1))
#+end_src

** restclient

Open a file with the extension =.rest=, and start writing queries in it. For example:

#+begin_example
GET https://api.github.com
User-Agent: Emacs Restclient
#+end_example

Then one of the following binding:
| binding | action                                             |
|---------+----------------------------------------------------|
| =C-c C-c= | run the query                                      |
| =C-c C-v= | same as above but switch focus to the other buffer |

#+begin_src emacs-lisp
(use-package restclient
  :ensure t
  :mode ("\\.rest\\'" . restclient-mode))
#+end_src

** rg

#+begin_src emacs-lisp
  (use-package rg
    :ensure t
    :custom ((rg-group-result t)
             (rg-show-columns t)
             (rg-align-line-number-field-length 3)
             (rg-align-column-number-field-length 3)
             (rg-align-line-column-separator "#")
             (rg-align-position-content-separator "|")
             (rg-hide-command nil)
             (rg-align-position-numbers t)
             (rg-command-line-flags '("--follow"))))
#+end_src

** savehist

#+begin_src emacs-lisp
(use-package savehist
  :unless noninteractive
  :custom
  (savehist-additional-variables
   '(file-name-history
     kmacro-ring
     compile-history
     compile-command))
  (savehist-autosave-interval 60)
  (savehist-file (user-data "history"))
  (savehist-ignored-variables
   '(load-history
     flyspell-auto-correct-ring
     org-roam-node-history
     magit-revision-history
     org-read-date-history
     query-replace-history
     yes-or-no-p-history
     kill-ring))
  (savehist-mode t)
  :config
  (savehist-mode 1))
#+end_src

** saveplace

#+begin_src emacs-lisp
(use-package saveplace
  :unless noninteractive
  :custom
  (save-place-file (user-data "places"))
  :config
  (save-place-mode 1))
#+end_src

** theme

*** modus-themes

This theme is clean and readable. The [[https://protesilaos.com/emacs/modus-themes][online documentation]] is pretty detailed too.

#+begin_src emacs-lisp
(use-package emacs
  :custom
  ;; Syntax Highlighting
  ;; Increase the number of bolded syntax elements
  (modus-themes-bold-constructs t)
  ;; Increase the number of italicized syntax elements
  (modus-themes-italic-constructs t)

  ;; Org Mode
  ;; Make headings in org files more distinct
  (modus-themes-headings '((t . (background bold rainbow 1))))
  ;; Tint the background of code blocks in org files
  (modus-themes-org-blocks 'tinted-background)
  ;; Make tags less colorful and tables look the same as
  ;; the default foreground.
  (prose-done cyan-cooler)
  (prose-tag fg-dim)
  (prose-table fg-main)

  ;; Other
  ;; Make the fringe more intense
  (modus-themes-common-palette-overrides '((fringe bg-active)))
  ;; Enhance minibuffer completions
  (modus-themes-completions 'minimal)
  ;; Make the current line of `hl-line-mode' a fine shade of gray
  (bg-hl-line bg-dim)
  ;; Make matching parentheses a shade of magenta
  (bg-paren-match bg-magenta-intense)
  (bg-mode-line-inactive bg-dim)

  :config
  (load-theme 'modus-operandi t))
#+end_src

*** COMMENT standard-themes

I like the default theme, but there's not enough contrast. The package =standard-themes= fix this problem. The full documentation can be [[https://protesilaos.com/emacs/standard-themes][read online]].

#+begin_src emacs-lisp
(use-package standard-themes
  :ensure t
  :init
  (setq standard-themes-bold-constructs t
	standard-themes-italic-constructs t
	standard-themes-mixed-fonts t
	standard-themes-variable-pitch-ui nil
	standard-themes-mode-line-accented t
	;; Accepts a symbol value:
	standard-themes-fringes 'intense
	;; The following accept lists of properties
	standard-themes-links '(italic neutral-underline)
	standard-themes-region '(no-extend neutral intense)
	standard-themes-prompts '(bold italic)
        standard-themes-headings (quote ((1 . (bold 1.2))
                                         (2 . (regular 1.1))
                                         (agenda-date (1.1))
                                         (agenda-structure (regular 1.1)))))
  (load-theme 'standard-light :no-confirm))
#+end_src

** time

#+begin_src emacs-lisp
(use-package time
  :custom
  (display-time-interval 60)
  (display-time-mode t)
  (display-time-24hr-format t)
  (display-time-day-and-date t)
  (display-time-default-load-average nil)
  (world-clock-list t)
  (world-clock-timer-enable t)
  (world-clock-timer-second 60)
  ;; UTC      => 02:42 +0000  Wednesday 20 April
  ;; Berkeley => 19:42 -0700  Tuesday 19 April
  (world-clock-time-format "%R %z  %A %d %B")
  (zoneinfo-style-world-list '(("UTC" "UTC")
                               ("America/Los_Angeles" "Berkeley")
                               ("America/Denver" "Mountain Time")
                               ("America/Chicago" "Central Time")
                               ("America/New_York" "New York")
                               ("Europe/London" "London")
                               ("Europe/Paris" "Paris")))
  :init
  (add-to-list 'display-buffer-alist '("\\*wclock\\*"
                                       (display-buffer-in-side-window)
                                       (side . left)
                                       (slot . 0)
                                       (window-width . 0.35))))
#+end_src

** tramp

#+begin_src emacs-lisp
  (use-package tramp
    :defer t
    :custom
    (tramp-default-method "ssh")
    (tramp-auto-save-directory "~/.cache/emacs/backups")
    (tramp-ssh-controlmaster-options "-o ControlMaster=auto -o ControlPath='tramp.%%C'")
    :config
    ;; Setting this with `:custom' does not take effect.
    (setq tramp-persistency-file-name (user-data "tramp")))
#+end_src

** transient

#+begin_src emacs-lisp
(use-package transient
  :defer t
  :custom
  (transient-history-file (user-data "transient/history.el"))
  (transient-values-file (user-data "transient/values.el")))
#+end_src

** vc

#+begin_src emacs-lisp
(use-package vc
  :defer t
  :custom
  (vc-command-messages t)
  (vc-follow-symlinks t))
#+end_src

** which-key

#+begin_src emacs-lisp
(use-package which-key
  :demand t
  :diminish
  :ensure t
  :config
  (which-key-mode))
#+end_src

** whitespace

#+begin_src emacs-lisp
(use-package whitespace
  :diminish (global-whitespace-mode
             whitespace-mode
             whitespace-newline-mode)
  :commands (whitespace-buffer
             whitespace-cleanup
             whitespace-mode
             whitespace-turn-off)
  :init
  (dolist (hook '(prog-mode-hook text-mode-hook))
    (add-hook hook #'whitespace-mode))
  :custom
  (whitespace-auto-cleanup t t)
  (whitespace-rescan-timer-time nil t)
  (whitespace-silent t t)
  (whitespace-style '(face trailing space-before-tab))
  :defines
  (whitespace-auto-cleanup
   whitespace-rescan-timer-time
   whitespace-silent))
#+end_src

* Finalization
#+begin_src emacs-lisp
(report-time-since-load)

;; Local Variables:
;; byte-compile-warnings: (not docstrings lexical noruntime)
;; End:
#+end_src