Skip to content

Latest commit

 

History

History
2143 lines (1853 loc) · 64.8 KB

README.org

File metadata and controls

2143 lines (1853 loc) · 64.8 KB

My literate Emacs config

Preliminaries

;;; init.el -*- lexical-binding: t; -*-

;; DO NOT EDIT THIS FILE DIRECTLY
;; This is a file generated from a literate programing source file located at
;; https://github.com/christophe-gouel/dotemacs/blob/master/README.org
;; You should make any changes there and regenerate it from Emacs org-mode
;; using org-babel-tangle (C-c C-v t)

Package management

(use-package package
  :ensure nil
  :config
  (add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/"))
  (package-initialize))

(use-package use-package
  :ensure nil
  :custom
  ;; Always download packages if not available
  (use-package-always-ensure t))

To use the keyword :ensure-system-package that install system command automatically:

(use-package use-package-ensure-system-package)

To keep GPG keys up to date (let’s try to remove it temporarily to check if we really need it)

(use-package gnu-elpa-keyring-update)

Add environment variables (macOS only)

(when (equal window-system 'ns)
  (use-package exec-path-from-shell
    :config
    (dolist (var '("DROPBOX" "BIBINPUTS" "BSTINPUTS" "GH_TOKEN"))
      (add-to-list 'exec-path-from-shell-variables var))
    (exec-path-from-shell-initialize)))

Emacs startup

(use-package dashboard
  :custom
  (dashboard-projects-switch-function 'project-switch-project)
  (dashboard-set-navigator t) ; raccourcis de rubrique
  (dashboard-center-content t)
  (dashboard-items '((recents   . 6)
                     (projects  . 6)
                     (bookmarks . 6)))
  (dashboard-set-heading-icons t)
  (dashboard-set-file-icons t)
  :config
  (dashboard-setup-startup-hook))

Appearance

Standard Emacs options

Remove menu, scrool-bar, tool-bar, and tooltip

(unless (eq window-system 'ns)
  (menu-bar-mode 0))
(scroll-bar-mode 0)
(tool-bar-mode 0)
(tooltip-mode 0)
(setq blink-cursor-blinks 0 ; curseur clignote indéfiniment
      custom-safe-themes t ; consider all themes as safe
      display-time-24hr-format t ; Affichage de l'heure format 24h
      column-number-mode t ; affichage du numéro de la colonne
      prettify-symbols-unprettify-at-point t
      show-trailing-whitespace t
      pixel-scroll-precision-mode t)
(global-hl-line-mode +1) ; Highlight the current line
(add-hook 'text-mode-hook 'prettify-symbols-mode)
(add-hook 'prog-mode-hook (lambda ()
			    (display-fill-column-indicator-mode)))
(when (display-graphic-p)
  ;; Cursor (in terminal mode, to be set in terminal options)
  (setq-default cursor-type 'bar) ; curseur étroit
  (set-face-background 'cursor "#CC0000") ; curseur rouge foncé
  ;; Fonts and unicode characters
  ;;   Main font
  (set-face-attribute 'default nil :family "JetBrainsMono NF" :height 120)
  (set-face-attribute 'variable-pitch nil :family "Noto Serif" :height 1.2)
  ;;   Additional font for some unicode characters missing in prettify symbols
  (set-fontset-font t 'unicode (font-spec :name "XITS Math") nil 'prepend))

(defun my-screen-27 ()
  "Adjust font for 27 inch screen."
  (interactive)
  (set-face-attribute 'default nil :family "JetBrainsMono NF" :height 140)
  (setq org-format-latex-options
          (plist-put org-format-latex-options :scale 1.8)
        preview-scale-function 2))
(defun my-screen-default ()
  "Adjust font for default screen."
  (interactive)
  (set-face-attribute 'default nil :family "JetBrainsMono NF" :height 120)
  (setq org-format-latex-options
          (plist-put org-format-latex-options :scale 1.7)
        preview-scale-function 1.5))

To list all available fonts, use

(dolist (font (x-list-fonts "*"))
  (insert (format "%s\n" font)))

Use mixed-pitch to have a proportional font for text and a monospace font for code:

(use-package mixed-pitch
  :hook
  (text-mode . mixed-pitch-mode))

Hex color codes displayed in color

(use-package rainbow-mode
  :hook (prog-mode . rainbow-mode))

Icons

(use-package nerd-icons
  :custom
  (nerd-icons-font-family "Symbols Nerd Font Mono")) ; JetBrains font did not work well
(use-package nerd-icons-dired
  :hook
  (dired-mode . nerd-icons-dired-mode))
(use-package nerd-icons-ibuffer
  :hook (ibuffer-mode . nerd-icons-ibuffer-mode))
(use-package nerd-icons-completion
  :after marginalia
  :config
  (nerd-icons-completion-mode)
  :hook
  (marginalia-mode . nerd-icons-completion-marginalia-setup))

Ligatures

(use-package ligature
  :config
  ;; Enable all JetBrains Mono ligatures in programming modes
  (defconst jb-ligatures
    '("-|" "-~" "---" "-<<" "-<" "--" "->" "->>" "-->" "///" "/=" "/==" "/>"
      "//" "/*" "*>" "***" ",*/" "<-" "<<-" "<=>" "<=" "<|" "<||" "<|||" "<|>"
      "<:" "<>" "<-<" "<<<" "<==" "<<=" "<=<" "<==>" "<-|" "<<" "<~>" "<=|"
      "<~~" "<~" "<$>" "<$" "<+>" "<+" "</>" "</" "<*" "<*>" "<->" "<!--" ":>"
      ":<" ":::" "::" ":?" ":?>" ":=" "::=" "=>>" "==>" "=/=" "=!=" "=>" "==="
      "=:=" "==" "!==" "!!" "!=" ">]" ">:" ">>-" ">>=" ">=>" ">>>" ">-" ">="
      "&&&" "&&" "|||>" "||>" "|>" "|]" "|}" "|=>" "|->" "|=" "||-" "|-" "||="
      "||" ".." ".?" ".=" ".-" "..<" "..." "+++" "+>" "++" "[||]" "[<" "[|" "{|"
      "??" "?." "?=" "?:" "##" "###" "####" "#[" "#{" "#=" "#!" "#:" "#_(" "#_"
      "#?" "#(" ";;" "_|_" "__" "~~" "~~>" "~>" "~-" "~@" "$>" "^=" "]#"))
  (ligature-set-ligatures 'prog-mode jb-ligatures)
  (ligature-set-ligatures 'text-mode jb-ligatures)
  (ligature-set-ligatures 'comint-mode jb-ligatures)
  (ligature-set-ligatures 'special-mode jb-ligatures)
  ;; Enables ligature checks globally in all buffers. You can also do it
  ;; per mode with `ligature-mode'.
  (global-ligature-mode t))

Modeline

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

Parentheses

(use-package rainbow-delimiters
  :hook ((prog-mode yaml-mode) . rainbow-delimiters-mode)
  :custom-face
  (rainbow-delimiters-depth-1-face ((t (:foreground "red"))))
  (rainbow-delimiters-depth-2-face ((t (:foreground "orange"))))
  (rainbow-delimiters-depth-3-face ((t (:foreground "cyan"))))
  (rainbow-delimiters-depth-4-face ((t (:foreground "green"))))
  (rainbow-delimiters-depth-5-face ((t (:foreground "blue"))))
  (rainbow-delimiters-depth-6-face ((t (:foreground "violet"))))
  (rainbow-delimiters-depth-7-face ((t (:foreground "purple"))))
  (rainbow-delimiters-depth-8-face ((t (:foreground "black"))))
  (rainbow-delimiters-unmatched-face ((t (:background "yellow")))))

Theme

(use-package modus-themes
  :ensure t
  :config
  (setq modus-themes-italic-constructs t)
  (setq modus-themes-bold-constructs t)
  (setq modus-themes-to-toggle '(modus-operandi-deuteranopia modus-vivendi-deuteranopia))
  ;; Remove the mode-line border
  (setq modus-themes-common-palette-overrides
   '((border-mode-line-active unspecified)
     (border-mode-line-inactive unspecified)))
  (load-theme 'modus-vivendi-deuteranopia)
  (define-key global-map (kbd "S-<f5>") #'modus-themes-toggle))

Other Emacs settings and tools

Encoding

Set up encoding to Unicode

(set-language-environment "UTF-8")
(prefer-coding-system       'utf-8)
(set-selection-coding-system 'utf-8)
(set-default-coding-systems 'utf-8)
(set-terminal-coding-system 'utf-8)
(set-keyboard-coding-system 'utf-8)
(setq default-buffer-file-coding-system 'utf-8-unix
      x-select-request-type '(UTF8_STRING COMPOUND_TEXT TEXT STRING))
(if (equal system-type 'windows-nt)    ;; MS Windows clipboard is UTF-16LE
    (set-clipboard-coding-system 'utf-16le-dos))

Personal information

(setq user-full-name "Christophe Gouel"
      user-mail-address "[email protected]")

Scratch buffer

(setq initial-scratch-message nil)

Other Emacs settings

(setq show-paren-mode t ; coupler les parenthèses
      auth-sources '("~/.authinfo") ; Define file that stores secrets
      backup-directory-alist '(("." . "~/.emacs.d/backup"))
      default-major-mode 'text-mode ; mode par défaut
      delete-by-moving-to-trash t ; Sent deleted files to trash
      comment-column 0 ; Prevent indentation of lines starting with one comment
      next-line-add-newlines t
      jit-lock-chunk-size 50000
      ;; set large file threshold at 100 megabytes
      large-file-warning-threshold 100000000
      ring-bell-function 'ignore ; disable the bell (useful for macOS)
      ;; Options to make lsp usable in emacs (from
      ;; https://emacs-lsp.github.io/lsp-mode/page/performance/)
      ;; gc-cons-threshold (* 10 800000)
      ;; read-process-output-max (* 1024 1024)
      )
(setq-default mouse-yank-at-point t     ; coller avec la souris
              case-fold-search t)       ; recherche sans égard à la casse
(delete-selection-mode t)               ; entrée efface texte sélectionné
(fset 'yes-or-no-p 'y-or-n-p)           ; Replace yes or no with y or n
(auto-compression-mode t)
(when (equal system-type 'windows-nt)
    (setq tramp-default-method "plink"))

Server

(use-package server
  :ensure nil
  :defer 1
  :config
  (when (and (display-graphic-p) (not (server-running-p)))
    (server-start)))

Dictionary

(use-package dictionary
  :ensure nil
  :defer t
  :custom
  (dictionary-server "dict.org"))

Auto-revert

(use-package autorevert
  :ensure nil
  :custom
  (auto-revert-verbose nil)) ; Prevent autorevert from generating messages

Dired

(use-package dired
  :ensure nil
  :commands (dired dired-jump)
  :config ; macOS ls is not the standard ls so we substitute it by GNU ls
  (when (and (eq system-type 'darwin) (executable-find "gls"))
    (setq insert-directory-program "gls"))
  :custom
  (dired-listing-switches "-agho --group-directories-first")
  :hook
  (dired-mode . (lambda ()
                  (dired-hide-details-mode)))
  (dired-mode . auto-revert-mode))

diredfl is for more font-locking in dired (e.g., file extensions):

(use-package diredfl
  :hook
  (dired-mode . diredfl-mode))

Compilation

(use-package compile
  :ensure nil
  :bind (:map compilation-mode-map ("r" . recompile))
  :hook
  ;; Get proper coloring of compile buffers (does not seem to work under Windows, probably because cmd does not support ANSI colors)
  (compilation-filter . ansi-color-compilation-filter)
  :custom
  ;; compilation buffer automatically scrolls and stops at first error
  (compilation-scroll-output 'first-error))

Expand region

(use-package expand-region
  :bind ("C-!" . er/expand-region))

ibuffer

Gather buffers per project in ibuffer using ibuffer-project.

(use-package ibuffer-project
  :hook
  (ibuffer .
	   (lambda ()
	     (setq ibuffer-filter-groups (ibuffer-project-generate-filter-groups))
	     (unless (eq ibuffer-sorting-mode 'project-file-relative)
	       (ibuffer-do-sort-by-project-file-relative)))))

imenu

(use-package imenu
  :ensure nil
  :defer t
  :custom
  (imenu-auto-rescan t))

Show imenu in a separate buffer with imenu-list:

(use-package imenu-list
  :bind
  (("C-c =" . imenu-list-smart-toggle)
   :map imenu-list-major-mode-map
   ("M-<return>" . my-imenu-list-goto-entry))
  :custom
  (imenu-list-focus-after-activation t)
  (imenu-list-position 'right)
  :config
  (defun my-imenu-list-goto-entry ()
    "Goto entry and exit imenu"
    (interactive)
    (imenu-list-goto-entry)
    (imenu-list-smart-toggle)))

Flatten imenu so that we can jump to any subheading from the main menu. No longer needed with consult-imenu

(use-package flimenu
  :after imenu
  :config
  (flimenu-global-mode))

Calc

(use-package casual-calc
  :ensure casual
  :after calc
  :bind (:map
         calc-mode-map
         ("C-o" . casual-calc-tmenu)
         :map
         calc-alg-map
         ("C-o" . casual-calc-tmenu)))

PDF viewers

(use-package doc-view
  :ensure nil
  :if (display-graphic-p)
  :defer t
  :custom
  (doc-view-ghostscript-program (executable-find "rungs")))
(use-package pdf-tools
  :if (display-graphic-p)
  :mode  ("\\.pdf\\'" . pdf-view-mode)
  :bind (:map pdf-view-mode-map
	      ("C-s" . isearch-forward))
  :custom
  (pdf-view-display-size 'fit-page)
  (pdf-view-selection-style 'glyph)
  :config
  (pdf-tools-install))

Proced

(use-package proced
  :ensure nil
  :defer t
  :custom
  (proced-enable-color-flag t))

Recent files

(use-package recentf
  :custom
  (recentf-max-saved-items 50))

Grep and friends

The find program included with Windows is not POSIX-compatible, so we need to use a different find. Since we cannot always change the PATH on all Windows computers, it is better to use the find provided by Git for Windows, which is always needed anyway.

(use-package grep
  :ensure nil
  :defer t
  :config
  (if (equal system-type 'windows-nt)
      (setq find-program "\"C:\\Program Files\\Git\\usr\\bin\\find.exe\"")))

ripgrep package needed to have a proper interface for ripgrep.

It should also be possible to directly substitute grep by ripgrep as explained in https://stegosaurusdormant.com/emacs-ripgrep/.

(use-package ripgrep
  :bind
  ("C-c f" . my-ripgrep-in-same-extension)
  :config
  (defun my-ripgrep-in-same-extension (expression)
    "Search for EXPRESSION in files with the same extension as the
current buffer within the project or the current directory if not in a project."
    (interactive
     (list
      (read-from-minibuffer "Ripgrep search for: " (thing-at-point 'symbol))))
    (let* ((extension (file-name-extension (buffer-file-name)))
           (glob (if extension (concat "*." extension) "*"))
           ;; Check if we are inside a project. If not, use `nil`.
           (project (if (ignore-errors (project-current)) (project-current) nil))
           ;; Use project root if in a project, otherwise use `default-directory`.
           (root (if project (project-root project) default-directory)))
      (ripgrep-regexp expression
                    root
                    (list (format "-g %s" glob)))))
    :ensure-system-package rg)

Outline (minor) mode

(use-package outline
  :ensure nil
  :hook ((prog-mode text-mode) . outline-minor-mode)
  :custom
  (outline-minor-mode-use-buttons 'in-margins) ; add in-margin buttons to fold/unfold
  :config
  (unbind-key "RET" outline-overlay-button-map))

Use bicycle to easily cycle visibility in outline minor mode (à la orgmode).

(use-package bicycle
  :after outline
  :bind (:map outline-minor-mode-map
	      ([C-tab] . bicycle-cycle)
	      ([S-tab] . my-bibycle-cycle-global)
	      ([backtab] . my-bibycle-cycle-global))
  :config
  ;; bicycle-cycle-global should not be used in org-mode, hence this function
  (defun my-bibycle-cycle-global ()
    (interactive)
    (if (derived-mode-p 'org-mode)
        (org-cycle-global)
      (bicycle-cycle-global))))

Use outline-minor-faces to use a special face for outline sections.

(use-package outline-minor-faces
  :after outline
  :hook
  (outline-minor-mode . outline-minor-faces-mode))

Windows management

(use-package windmove
  :ensure nil
  :config
  (windmove-default-keybindings))

Keys

Custom keybindings

;; Remove a bug appearing on Linux GTK and preventing the use of S-space (https://lists.gnu.org/archive/html/bug-gnu-emacs/2021-07/msg00071.html)
(when (equal window-system 'pgtk)
  (setq pgtk-use-im-context-on-new-connection nil))
(keymap-global-set "C-x C-b" 'ibuffer)
(keymap-global-set "C-<apps>" 'menu-bar-mode) ; for Windows
(keymap-global-set "C-<menu>" 'menu-bar-mode) ; For Linux
(keymap-global-set "<f5>" 'revert-buffer)
;; Replace upcase-word, downcase-word, and capitalize-word by DWIM versions
(keymap-global-set "M-u" 'upcase-dwim)
(keymap-global-set "M-l" 'downcase-dwim)
(keymap-global-set "M-c" 'capitalize-dwim)

MacOS specific keybindings

(when (equal system-type 'darwin)
  (setq
   mac-command-modifier 'meta
   mac-function-modifier 'control
   mac-option-modifier 'meta)
  (keymap-global-set "<home>" 'move-beginning-of-line)
  (keymap-global-set "<end>" 'move-end-of-line)
  (keymap-global-set "§" (lambda () (interactive) (insert "-")))
  (keymap-global-set "M-é" (lambda () (interactive) (insert "~")))
  (keymap-global-set "M-\"" (lambda () (interactive) (insert "#")))
  (keymap-global-set "M-'" (lambda () (interactive) (insert "{")))
  (keymap-global-set "M-(" (lambda () (interactive) (insert "[")))
  (keymap-global-set "M-§" (lambda () (interactive) (insert "|")))
  (keymap-global-set "M-è" (lambda () (interactive) (insert "`")))
  (keymap-global-set "M-!" (lambda () (interactive) (insert "\\")))
  (keymap-global-set "M-à" (lambda () (interactive) (insert "@")))
  (keymap-global-set "M-)" (lambda () (interactive) (insert "]")))
  (keymap-global-set "M--" (lambda () (interactive) (insert "}")))
  (keymap-global-set "M-e" (lambda () (interactive) (insert ""))))

Keycast

keycast displays the Emacs command name corresponding to keybindings.

(use-package keycast
  :defer t)

Insert Greek letters in Unicode

(unless (equal system-type 'darwin)
  (use-package greek-unicode-insert
    :vc (:url "https://github.com/Malabarba/greek-unicode-insert")
    :bind ("²" . greek-unicode-insert-map)))
; :init (setq greek-unicode-insert-key "`"))

Parentheses

(use-package elec-pair
  :ensure nil
  :config
  (electric-pair-mode))
(use-package smartparens
  :ensure smartparens  ;; install the package
  :hook (prog-mode markdown-mode yaml-mode)
  :config
  ;; load default config
  (require 'smartparens-config))

Which-keys

(use-package which-key
  :diminish which-key-mode
  :init
  (setq which-key-sort-uppercase-first nil
        max-mini-window-height 15)
  ;; On va utiliser une fenêtre dédiée plutôt que le minibuffer
  (which-key-setup-side-window-bottom)
  ;; On l'active partout, tout le temps
  (which-key-mode t))

Auto-completion

Prescient

(use-package prescient
  :config
  (prescient-persist-mode))

Company

(use-package company
  :hook (after-init . global-company-mode)
  :custom
  (company-show-numbers t)
  (company-idle-delay 0)
  ;; company configuration from
  ;; <https://github.com/radian-software/radian/blob/develop/emacs/radian.el>
  :bind (;; Replace `completion-at-point' and `complete-symbol' with
         ;; `company-manual-begin'. You might think this could be put
         ;; in the `:bind*' declaration below, but it seems that
         ;; `bind-key*' does not work with remappings.
         ([remap completion-at-point] . company-manual-begin)
         ([remap complete-symbol] . company-manual-begin)

	     ("C-c y" . company-yasnippet)

         ;; The following are keybindings that take effect whenever
         ;; the completions menu is visible, even if the user has not
         ;; explicitly interacted with Company.

         :map company-active-map

         ;; Make TAB always complete the current selection. Note that
         ;; <tab> is for windowed Emacs and TAB is for terminal Emacs.
         ("<tab>" . company-complete-selection)
         ("TAB" . company-complete-selection)

         ;; Prevent SPC from ever triggering a completion.
         ("SPC" . nil)

         ;; The following are keybindings that only take effect if the
         ;; user has explicitly interacted with Company.

         :map company-active-map
         :filter (company-explicit-action-p)

         ;; Make RET trigger a completion if and only if the user has
         ;; explicitly interacted with Company. Note that <return> is
         ;; for windowed Emacs and RET is for terminal Emacs.
         ("<return>" . company-complete-selection)
         ("RET" . company-complete-selection))

  ;; :bind* (;; The default keybinding for `completion-at-point' and
  ;;         ;; `complete-symbol' is M-TAB or equivalently C-M-i. Here we
  ;;         ;; make sure that no minor modes override this keybinding.
  ;;         ("M-TAB" . company-manual-begin))
  )

(use-package company-math
  :custom
  (company-math-allow-latex-symbols-in-faces t)) ; use LaTeX symbols everywhere (avoid unicode symbols to dominate outside LaTeX mode)

(use-package company-reftex)

(use-package company-jedi)

(if (not (equal system-type 'windows-nt))
    (setq company-backends
	  (append
	   '((:separate
	      company-reftex-labels
	      company-reftex-citations
	      company-math-symbols-latex
	      company-math-symbols-unicode
	      company-latex-commands
	      company-yasnippet))
	   company-backends))
  ;; deactivate company-reftex-labels on Windows because it is too slow
  (setq company-backends
	(append
	 '((:separate
	    company-reftex-citations
	    company-math-symbols-latex
	    company-math-symbols-unicode
	    company-latex-commands
	    company-yasnippet))
	 company-backends)))

Use company-box for a better position of the autocompletion when using copilot.

(use-package company-box
  :hook (company-mode . company-box-mode)
  :custom
  (company-box-doc-enable nil))
(use-package company-prescient
  :config
  (company-prescient-mode))

Vertico and friends (orderless, marginalia, consult)

(use-package vertico
  :init
  (vertico-mode)
  :bind
  (:map vertico-map
	("<next>"  . vertico-scroll-up)
	("<prior>" . vertico-scroll-down)))

Use vertico-directory to press DEL to jump back one directory instead of one character

(use-package vertico-directory
  :after vertico
  :ensure nil
  :bind
  (:map vertico-map	("DEL" . vertico-directory-delete-char)))

Orderless for more flexible completion style

(use-package orderless
  :custom
  (completion-styles '(orderless basic)))
(use-package marginalia
  :init
  (marginalia-mode))
(use-package consult
  :bind
  (("C-x b" . consult-buffer)
   ("M-y"   . consult-yank-pop)
   ;; M-s bindings in `search-map'
   ("M-s g" . consult-grep)
   ("M-s G" . consult-git-grep)
   ("M-s r" . consult-ripgrep)
   ("M-s l" . consult-line)
   ("M-s L" . consult-line-multi)
   ;; Other custom bindings
   ("M-g i" . consult-imenu)
   ("M-g I" . consult-imenu-multi)
   ("M-g o" . consult-outline)
   :map org-mode-map
   ("M-g o" . consult-org-heading)))
(use-package vertico-prescient
  :after vertico
  :init
  (vertico-prescient-mode))

Git

(use-package magit
  :init
  ;; this binds `magit-project-status' to `project-prefix-map' when project.el is loaded.
  (require 'magit-extras)
  :bind ("C-x g" . magit-status)
  :custom
  (magit-diff-refine-hunk (quote all))
  :config
  ; Do not diff when committing
  (remove-hook 'server-switch-hook 'magit-commit-diff)
  (remove-hook 'with-editor-filter-visit-hook 'magit-commit-diff))

magit-delta allows to have syntax highlighting in magit diffs.

(use-package magit-delta
  :hook (magit-mode . magit-delta-mode)
  :ensure-system-package (delta . git-delta))

diff-hl displays indications about git status in the gutters.

(use-package diff-hl
  :defer t
  :after magit
  :hook
  (prog-mode . diff-hl-mode)
  (latex-mode . diff-hl-mode)
  (dired-mode . diff-hl-dired-mode)
  (magit-post-refresh . diff-hl-magit-post-refresh))

Support for syntax highlighting of Git configuration files

(use-package git-modes
  :mode ("/.dockerignore\\'" . gitignore-mode)) ; works also with other ignore files

Shells

ChatGPT

(use-package chatgpt-shell
  :commands chatgpt-shell-prompt-compose
  :config
  (defun my-chatgpt-save-block ()
    (interactive)
    (chatgpt-shell-mark-block)
    (kill-ring-save (region-beginning) (region-end)))
  :bind (
	 :map chatgpt-shell-mode-map ("C-c C-b" . my-chatgpt-save-block)
	 :map chatgpt-shell-prompt-compose-view-mode-map ("C-c C-b" . my-chatgpt-save-block))
  :custom
  (chatgpt-shell-openai-key
      (auth-source-pick-first-password :host "api.openai.com")))

(use-package gptel
  :defer t
  :custom
  (gptel-use-curl nil)
  :config
  (add-to-list 'gptel-directives '(academic . "You are an editor specialized in academic paper in economics. You are here to help me generate the best text for my academic articles. I will provide you texts and I would like you to review them for any spelling, grammar, or punctuation errors. Do not stop at simple proofreading, if it is useful, propose to refine the content's structure, style, and clarity. Once you have finished editing the text, provide me with any necessary corrections or suggestions for improving the text.")))

eshell

(use-package eshell-git-prompt
  :defer 2
  :config
  (eshell-git-prompt-use-theme 'powerline))

Other shells

(use-package comint
  :ensure nil
  :defer t
  :custom
  (comint-scroll-to-bottom-on-input 'this)
  (comint-scroll-to-bottom-on-output t)
  (comint-move-point-for-output t))
(use-package shell
  :ensure nil
  :defer t
  :hook
  (shell-mode . (lambda ()
		  (face-remap-set-base 'comint-highlight-prompt :inherit nil))))

Text

BibTeX

(use-package citar
  :after (org nerd-icons)
  :hook
  (org-mode . citar-capf-setup)
  :bind
  (:map org-mode-map :package org ("C-c b" . #'org-cite-insert))
  :config
  ;; Configuration to use nerd-icons in citar
  (defvar citar-indicator-files-icons
    (citar-indicator-create
     :symbol (nerd-icons-faicon
              "nf-fa-file_o"
              :face 'nerd-icons-green
              :v-adjust -0.1)
     :function #'citar-has-files
     :padding "  " ; need this because the default padding is too low for these icons
     :tag "has:files"))
  (defvar citar-indicator-links-icons
    (citar-indicator-create
     :symbol (nerd-icons-faicon
              "nf-fa-link"
              :face 'nerd-icons-orange
              :v-adjust 0.01)
     :function #'citar-has-links
     :padding "  "
     :tag "has:links"))
  (defvar citar-indicator-notes-icons
    (citar-indicator-create
     :symbol (nerd-icons-codicon
              "nf-cod-note"
              :face 'nerd-icons-blue
              :v-adjust -0.3)
     :function #'citar-has-notes
     :padding "    "
     :tag "has:notes"))
  (defvar citar-indicator-cited-icons
    (citar-indicator-create
     :symbol (nerd-icons-faicon
              "nf-fa-circle_o"
              :face 'nerd-icon-green)
     :function #'citar-is-cited
     :padding "  "
     :tag "is:cited"))
  (setq citar-indicators
	(list citar-indicator-files-icons
          citar-indicator-links-icons
          citar-indicator-notes-icons
          citar-indicator-cited-icons))
  :custom
  (org-cite-insert-processor 'citar)
  (org-cite-follow-processor 'citar)
  (org-cite-activate-processor 'citar)
  (citar-bibliography org-cite-global-bibliography)
  (citar-library-paths
   (list (substitute-in-file-name "${DROPBOX}/Bibliography/Papers")))
  (citar-notes-paths
   (list (substitute-in-file-name "${DROPBOX}/Bibliography/notes")))
  (citar-templates
   '((main . "${author editor:30%sn}     ${date year issued:4}     ${title:48}")
     (suffix . "          ${=key= id:7}    ${=type=:12}    ${journal journaltitle}")
     (preview . "${author editor:%etal} (${year issued date}) ${title}, ${journal journaltitle publisher container-title collection-title}.\n")
        (note . "Notes on ${author editor:%etal}, ${title}"))))

Screenshots

Take a screenshot and copy it to a file

(defun my-screenshot-to-file (arg)
  "Take a screenshot or copy from the clipboard (depending on OS),
  save it to a file in the 'images' folder, and copy the relative file path to the kill ring.
  If called with a universal argument (C-u), prompt for the file name (including the folder)."
  (interactive "P")
  (let* ((default-dir (concat (file-name-directory (buffer-file-name)) "images/"))
         ;; Prompt for filename if universal argument is used
         (filename (if arg
                       (expand-file-name (read-file-name "Save screenshot as: " default-dir))
                     (expand-file-name (concat default-dir (format-time-string "%Y-%m-%d_%H%M%S") ".png"))))
         (dir (file-name-directory filename))  ;; Extract directory from provided or default filename
         (relative-filename (file-relative-name filename)))
    ;; Ensure the directory exists
    (unless (file-exists-p dir)
      (make-directory dir t))
    
    ;; macOS screenshot
    (cond
     ((eq system-type 'darwin)
      (call-process "screencapture" nil nil nil "-i" filename))

     ;; Linux screenshot
     ((eq system-type 'gnu/linux)
      (call-process "import" nil nil nil filename))

     ;; Windows clipboard
     ((eq system-type 'windows-nt)
      (let ((powershell-command
             (concat "powershell -command \"Add-Type -AssemblyName System.Windows.Forms;"
                     "if ($([System.Windows.Forms.Clipboard]::ContainsImage())) {"
                     "$image = [System.Windows.Forms.Clipboard]::GetImage();"
                     "[System.Drawing.Bitmap]$image.Save('" (shell-quote-argument filename) "',"
                     "[System.Drawing.Imaging.ImageFormat]::Png);"
                     "Write-Output 'clipboard content saved as file'} else {"
                     "Write-Output 'clipboard does not contain image data'}\"")))
        (shell-command powershell-command))))
    
    ;; Handle file existence and copy relative path to kill ring
    (if (file-exists-p filename)
        (progn
          (kill-new relative-filename)
          (message "Screenshot saved to %s and relative path copied to kill ring" relative-filename))
      (message "Screenshot failed."))))

csv files

(use-package csv-mode
  :hook
  (csv-mode . csv-guess-set-separator))

LaTeX

   (use-package tex
     :defer t
     :ensure auctex
     :hook
     (TeX-mode . latex-math-mode)
     (TeX-mode . TeX-fold-buffer)
     (TeX-mode . flymake-mode)
     (TeX-mode . my-center-text)
     :hook
     (TeX-mode . TeX-fold-mode)
     :custom
     (TeX-auto-save t)
     (TeX-save-query nil) ; don't ask to save the file before compiling
     (TeX-parse-self t)
     (LaTeX-item-indent 0)
     (LaTeX-default-options "12pt")
     (TeX-PDF-mode t)
     (TeX-electric-sub-and-superscript 1)
     (LaTeX-math-list
      '(
        (?\) "right)")
        (?\( "left(")
        (?/ "frac{}{}")
        ))
     (LaTeX-flymake-chktex-options
      '("-n3")) ; You should enclose the previous parenthesis with ‘{}’.

     ;; View PDF
     (TeX-view-program-selection '((output-pdf "PDF Tools")))
     (TeX-view-program-list '(("PDF Tools" TeX-pdf-tools-sync-view)))
     (TeX-source-correlate-mode t)
     (TeX-source-correlate-start-server t)
     ;; (TeX-source-correlate-method (quote synctex))

     ;; Fold-mode
     (TeX-fold-auto-reveal t)
     ;; Personalize the list of commands to be folded
     (TeX-fold-macro-spec-list
      '(("[f]"
         ("footnote" "marginpar"))
        ("[c]"
         ("citeyear" "citeauthor" "citep" "citet" "cite"))
        ("[l]"
         ("label"))
        ("[r]"
         ("ref" "pageref" "eqref" "footref" "fref" "Fref"))
        ("[i]"
         ("index" "glossary"))
        ("[1]:||*"
         ("item"))
        ("..."
         ("dots"))
        ("(C)"
         ("copyright"))
        ("(R)"
         ("textregistered"))
        ("TM"
         ("texttrademark"))
        (1
         ("part" "chapter" "section" "subsection" "subsubsection" "paragraph" "subparagraph"
          "part*" "chapter*" "section*" "subsection*" "subsubsection*" "paragraph*"
          "subparagraph*" "emph" "textit" "textsl" "textmd" "textrm" "textsf" "texttt" "textbf"
          "textsc" "textup" "caption" "frametitle" "framesubtitle"))))
     ;; Prevent folding of math to let prettify-symbols do the job
     (TeX-fold-math-spec-list-internal nil)
     (TeX-fold-math-spec-list nil)
     (LaTeX-fold-math-spec-list nil)
     :config
     (setq-default TeX-auto-parse-length 200
                   TeX-master nil)
     (add-hook 'TeX-after-compilation-finished-functions
   	    #'TeX-revert-document-buffer)

     ;; To prevent TeX-view from jumping to the _region_.pdf file created by the
     ;; preview from
     ;; https://tex.stackexchange.com/questions/89399/auctex-how-to-jump-to-pdf-with-synctex-without-recompile-when-inline-preview
     (defun my-TeX-view-advice (orig-fun &rest args)
	"Advice to ensure TeX-view always views the master file."
	(let ((TeX-current-process-region-p nil))
	  (apply orig-fun args)))
     (advice-add 'TeX-view :around #'my-TeX-view-advice)

     (defun my-tex-compile ()
       "Save and compile TeX document"
       (interactive)
       (save-buffer)
       (TeX-command-menu "latex"))

     ;; Beamer
     (defun my-tex-frame ()
       "Run pdflatex on current frame.  Frame must be declared as an environment."
       (interactive)
       (let (beg)
         (save-excursion
   	(search-backward "\\begin{frame}")
   	(setq beg (point))
   	(forward-char 1)
   	(LaTeX-find-matching-end)
   	(TeX-pin-region beg (point))
   	(cl-letf (( (symbol-function 'TeX-command-query) (lambda (x) "LaTeX")))
   	  (TeX-command-region)))))
     :bind
     (:map TeX-mode-map
   	("C-c e" . TeX-next-error)
   	("M-RET" . latex-insert-item)
   	("S-<return>" . my-tex-frame)
   	("<f9>" . my-tex-compile)))
(use-package reftex
  :hook
  (TeX-mode . turn-on-reftex)
  :bind (:map reftex-mode-map
	      ("C-c f" . reftex-fancyref-fref)
	      ("C-c F" . reftex-fancyref-Fref)
	      ("C-c -" . reftex-toc))
  :custom
  (reftex-bibpath-environment-variables (quote ("BIBINPUTS")))
  (reftex-default-bibliography '("References.bib"))
  (reftex-cite-format (quote natbib))
  (reftex-sort-bibtex-matches (quote author))
  (reftex-plug-into-AUCTeX t)
  (reftex-label-alist '(AMSTeX)) ; Use \eqref by default instead of \ref
  ;; Increase reftex speed (especially on Windows)
  (reftex-enable-partial-scans t)
  (reftex-save-parse-info t)
  (reftex-use-multiple-selection-buffers t))

Use svg for previews. Much slower than png, but it is not blurry on MacOS.

(use-package preview-dvisvgm
  :after latex
  :custom
  (preview-image-type 'dvisvgm))
(use-package preview
  :ensure nil
  :after latex
  :custom
  (preview-auto-cache-preamble t)
  (preview-auto-reveal t)
  (preview-default-option-list '("displaymath" "textmath"))
  (preview-scale-function 1.5))

CDLatex for super fast input of TeX mathematical expressions.

(use-package cdlatex
  :hook
  (LaTeX-mode . turn-on-cdlatex)
  (LaTeX-mode . my-slow-company)
  (org-mode . my-slow-company)
  (cdlatex-tab . my-cdlatex-indent-maybe)
  :config
  ;; Prevent cdlatex from defining LaTeX math subscript everywhere
  (define-key cdlatex-mode-map "_" nil)
  ;; Allow tab to be used to indent when the cursor is at the beginning of the line
  (defun my-cdlatex-indent-maybe ()
    "Indent in TeX when CDLaTeX is active"
    (when (or (bolp) (looking-back "^[ \t]+"))
      (LaTeX-indent-line)))
  (defun my-slow-company ()
    "Slow down company for a better use of CDLaTeX"
    (make-local-variable 'company-idle-delay)
		  (setq company-idle-delay 0.3))
  (unless (equal system-type 'darwin)
    (setq cdlatex-math-symbol-prefix ?\262)) ; correspond to key "²"
  :custom
  (cdlatex-command-alist
   '(("equ*" "Insert equation* env"   "" cdlatex-environment ("equation*") t nil)
     ("fra" "Insert frame env"   "" cdlatex-environment ("frame") t nil)
     ("frd" "Insert \\frac{\\partial }{\\partial }" "\\frac{\\partial ?}{\\partial }" cdlatex-position-cursor nil nil t)
     ("frat" "Insert \\frametitle{}" "\\frametitle{?}" cdlatex-position-cursor nil t nil)
     ("frast" "Insert \\framesubtitle{}" "\\framesubtitle{?}" cdlatex-position-cursor nil t nil)
     ("su" "Insert \\sum" "\\sum?" cdlatex-position-cursor nil nil t))))

Markdown

(use-package markdown-mode
  :mode ("README\\.md\\'" . gfm-mode)
  :custom
  (markdown-command
   (concat "pandoc"
	   " --from=markdown --to=html"
	   " --standalone --mathjax"
	   ;; " --citeproc --bibliography="
	   ;; (shell-quote-argument (substitute-in-file-name "${BIBINPUTS}\\References.bib"))
	   ))
  (markdown-enable-math t)
  (markdown-enable-prefix-prompts nil)
  (markdown-header-scaling nil)
  (markdown-hide-markup nil)
  (markdown-hide-urls t)
  (markdown-fontify-code-blocks-natively t)
  (markdown-enable-highlighting-syntax t)
  :config
  ;; Code to import screenshots in markdown files
  ;; from <https://www.nistara.net/post/2022-11-14-emacs-markdown-screenshots> and
  ;; <https://stackoverflow.com/questions/17435995/paste-an-image-on-clipboard-to-emacs-org-mode-file-without-saving-it/31868530#31868530>
  (defun my-markdown-screenshot ()
    "Copy a screenshot into a time stamped unique-named file in the
same directory as the working and insert a link to this file."
    (interactive)
    (setq filename
          (concat
           (make-temp-name
            (concat (file-name-nondirectory (buffer-file-name))
                    "_screenshots/"
                    (format-time-string "%Y-%m-%d_%a_%kh%Mm_")) ) ".png"))
    (unless (file-exists-p (file-name-directory filename))
      (make-directory (file-name-directory filename)))
    ;; copy the screenshot to file
    (shell-command
     (concat "powershell -command \"Add-Type -AssemblyName System.Windows.Forms;if ($([System.Windows.Forms.Clipboard]::ContainsImage())) {$image = [System.Windows.Forms.Clipboard]::GetImage();[System.Drawing.Bitmap]$image.Save('" filename "',[System.Drawing.Imaging.ImageFormat]::Png); Write-Output 'clipboard content saved as file'} else {Write-Output 'clipboard does not contain image data'}\""))
    ;; insert into file if correctly taken
    (if (file-exists-p filename)
	(insert (concat "![](" filename ")")))
    (markdown-display-inline-images)
    (newline))
  ;; Code to use RefTeX to input references in markdown
  ;; from https://gist.github.com/kleinschmidt/5ab0d3c423a7ee013a2c01b3919b009a
  (defvar markdown-cite-format
    '(
      (?\C-m . "@%l")
      (?p . "[@%l]")
      (?t . "@%l")
      (?y . "[-@%l]"))
    "Markdown citation formats")
  (defun my-markdown-reftex-citation ()
    "Wrap reftex-citation with local variables for markdown format"
    (interactive)
    (let ((reftex-cite-format markdown-cite-format)
          (reftex-cite-key-separator "; @"))
      (reftex-citation)))
  :bind (:map markdown-mode-map
	      ("C-c [" . my-markdown-reftex-citation)))

(use-package pandoc-mode
  :hook
  (markdown-mode . pandoc-mode)
  (pandoc-mode . pandoc-load-default-settings))

Org

(use-package org
  :ensure nil
  :mode ("\\.org\\'" . org-mode)
  :hook
  (org-mode . turn-on-org-cdlatex)
  :custom
  (org-edit-src-content-indentation 0)
  (org-todo-keywords '((type "TODO(t)" "STARTED(s)" "WAITING(w)" "|" "DONE(d)")))
  (org-tag-alist '(("OFFICE" . ?o) ("COMPUTER" . ?c) ("HOME" . ?h) ("PROJECT" . ?p) ("CALL" . ?a) ("ERRANDS" . ?e) ("TASK" . ?t)))
  (org-confirm-babel-evaluate nil)
  (org-babel-python-command "python3")
  (org-refile-targets '((nil :maxlevel . 3)))
  ;; Appareance
  (org-pretty-entities 1) ; equivalent of prettify symbols for org
  (org-cycle-hide-drawer-startup t)	; fold drawers at startup
  ; remove some prettification for sub- and superscripts because it makes editing difficult
  (org-pretty-entities-include-sub-superscripts nil)
  (org-hide-emphasis-markers t) ; remove markup markers
  (org-ellipsis " [+]")
  (org-highlight-latex-and-related '(native))
  (org-startup-indented t) ; Indent text relative to section
  (org-startup-with-inline-images t)
  (org-startup-with-latex-preview t)
  (org-cycle-inline-images-display t)
  (org-imenu-depth 4)
  (org-blank-before-new-entry '((heading . auto) (plain-list-item . nil))) ; Control the insertion of blank line after M-Ret
  (org-fold-core-style 'overlays) ; Slower folding style to prevent some bugs when unfolding
  :config
  (unless (equal system-type 'darwin)
    (org-defkey org-cdlatex-mode-map "²" 'cdlatex-math-symbol))
  (setq org-format-latex-options
	(plist-put org-format-latex-options :scale 1.6))
  (org-babel-do-load-languages
   'org-babel-load-languages
   '((emacs-lisp . t)
     (python . t)
     (R . t)
     (shell . t)))
  :bind (:map org-mode-map
	      ("C-c o" . org-open-at-point)
	      ("C-c =" . imenu-list)))

Use org-appear for markup markers to appear automatically.

(use-package org-appear
  :hook
  (org-mode . org-appear-mode))

For a modern-looking org-mode, use org-modern.

(use-package org-modern
  :hook
  (org-mode . global-org-modern-mode))

org-fragtog for an automatic toggling of LaTeX fragments.

(use-package org-fragtog
  :hook
  (org-mode . org-fragtog-mode))

Bibliographic references and cross-references in org

org-cite for citations.

(use-package oc
  :ensure nil
  :after org
  :custom
  (org-cite-global-bibliography
   (list (substitute-in-file-name "${BIBINPUTS}/References.bib")))
  (org-cite-csl-styles-dir (substitute-in-file-name "${DROPBOX}/Bibliography/csl"))
  :bind (:map org-mode-map ("C-c [" . org-cite-insert)))

oxr to handle cross-references in org using the native org links.

(use-package oxr
  :after org
  :vc (:url "https://github.com/bdarcus/oxr")
  :bind (:map org-mode-map ("C-c ]" . oxr-insert-ref)))

Org export

(use-package ox
  :ensure nil
  :defer t
  :custom
  (org-odt-preferred-output-format "docx")) ; require soffice to be on the PATH

(use-package ox-beamer
  :ensure nil
  :after ox)

ox-reveal to export presentation to reveal.js.

(use-package ox-reveal
  :after ox
  :ensure htmlize) ; required for the fontification of code blocks

Preview of mathematical formulas

texfrag to have preview of LaTeX fragment outside LaTeX buffers

(use-package texfrag
  :hook
  (eww-mode . texfrag-mode))

The package math-preview has a problem under Windows, and some code should be commented out. See https://gitlab.com/matsievskiysv/math-preview/-/issues/29.

(use-package math-preview
  :bind
  ("C-c m d" . math-preview-all)
  ("C-c m p" . math-preview-at-point)
  ("C-c m r" . math-preview-region)
  ("C-c m c d" . math-preview-clear-all)
  ("C-c m c p" . math-preview-clear-at-point)
  ("C-c m c r" . math-preview-clear-region))

Spell checking

(use-package flyspell
  :hook (text-mode . flyspell-mode)
  :config
  (setq ispell-program-name (executable-find "hunspell")
	flyspell-issue-welcome-flag nil
	ispell-really-hunspell t
	ispell-dictionary "en_US"
	ispell-local-dictionary "en_US"
	ispell-local-dictionary-alist
	'(("en_US" "[[:alpha:]]" "[^[:alpha:]]" "[']" nil ("-d" "en_US") nil utf-8)
	  ("fr_FR" "[[:alpha:]]" "[^[:alpha:]]" "[']" nil ("-d" "fr_FR") nil utf-8))
	ispell-hunspell-dictionary-alist ispell-local-dictionary-alist
	ispell-personal-dictionary "~/.emacs.d/.hunspell_en_US"
	ispell-silently-savep t)
  :bind
  ("C-M-$" . ispell-word)
  :ensure-system-package hunspell)

(use-package flyspell-correct
  :after flyspell
  :bind (:map flyspell-mode-map
		  ("M-$" . flyspell-correct-at-point)))

Word wrapping and paragraph filling

(defun my-unfill-paragraph ()
  "Unfill paragraph."
  (interactive)
  (let ((fill-column (point-max)))
  (fill-paragraph nil)))

(defun my-unfill-region (start end)
  "Unfill region."
  (interactive "r")
  (let ((fill-column (point-max)))
    (fill-region start end nil)))

(setq-default fill-column 80)

Package to visually (not really) indent the filled lines following the first lines.

(use-package adaptive-wrap)

Use visual-fill-column for text modes

(use-package visual-fill-column
  :custom
  (visual-fill-column-width 100)
  :config
  (defun my-visual-fill ()
    "Toggle visual fill column, visual line mode, and adaptive wrap mode."
    (interactive)
    (visual-line-mode 'toggle)
    (visual-fill-column-mode 'toggle)
    ;; org-indent does not play nicely with adaptive-wrap-prefix-mode so we exclude
    ;; the later in org
    (unless (member major-mode '(org-mode))
      (adaptive-wrap-prefix-mode 'toggle)))

  (defun my-center-text ()
    "Center text in visual fill column."
    (interactive)
    (setq-local visual-fill-column-center-text t))

  (defun my-uncenter-text ()
    "Uncenter text in visual fill column."
    (interactive)
    (setq-local visual-fill-column-center-text nil))
  :bind ("C-c v" . my-visual-fill)
  :hook
  (bibtex-mode   . my-visual-fill)
  (text-mode     . (lambda()
		         (unless (member major-mode '(csv-mode))
			   (my-visual-fill)))))

YAML

(use-package yaml-mode
  :mode ("\\.yml$" "\\.dvc" "dvc.lock")
  :bind (:map yaml-mode-map
	      ("C-m" . newline-and-indent)))

Programming

Programming tools

Code linting

Use built-in flymake for linting.

(use-package flymake
  :ensure nil
  :custom
  (flymake-no-changes-timeout nil)
  :config
  (remove-hook 'flymake-diagnostic-functions 'flymake-proc-legacy-flymake)
  :bind
  (("M-n" . flymake-goto-next-error)
   ("M-p" . flymake-goto-prev-error)))

Code styling

(use-package format-all
  :defer t
  :config
  (setq-default
   format-all-formatters
   '(("LaTeX"
      (latexindent "-m" "--yaml=modifyLineBreaks:textWrapOptions:columns:-1,defaultIndent:'  ',indentAfterItems:itemize:0;enumerate:0;description:0")))))

Docker

(use-package dockerfile-mode
  :defer t)
(use-package docker
  :bind ("C-c d" . docker)
  :ensure-system-package docker)

Eldoc

Prevent eldoc from showing the function doc in the minibuffer when the cursor is on the function

(setq eldoc-echo-area-use-multiline-p nil)

GitHub copilot

Configuration from https://robert.kra.hn/posts/2023-02-22-copilot-emacs-setup/.

(use-package copilot
  :vc (:url "https://github.com/copilot-emacs/copilot.el"
	     :rev :newest
       :branch "main")
  :custom
  (copilot-indent-warning-suppress t)
  (copilot-indent-offset-warning-disable t)
  :config
  (add-to-list 'copilot-major-mode-alist '("ess-r" . "r"))
  (defun my-copilot-complete-or-accept ()
    "Command that either triggers a completion or accepts one if
 one is available."
    (interactive)
    ;; Check if the Copilot overlay is visible
    (if (copilot--overlay-visible)
	(progn
	  ;; Accept the completion
          (copilot-accept-completion)
          ;; ;; Open a new line
          ;; (open-line 1)
          ;; ;; Move to the next line
          ;; (next-line)
	  )
      ;; If the Copilot overlay is not visible, trigger completion
      (copilot-complete)))

  (defvar my-copilot-manual-mode nil
    "When `t' will only show completions when manually triggered,
 e.g. via M-C-<return>.")

  (defun my-copilot-disable-predicate ()
    "When copilot should not automatically show completions."
    my-copilot-manual-mode)

  (defun my-copilot-change-activation ()
    "Switch between three activation modes:
       - automatic: copilot will automatically overlay completions
       - manual: you need to press a key (M-C-<return>) to trigger completions
       - off: copilot is completely disabled."
    (interactive)
    (if (and copilot-mode my-copilot-manual-mode)
	(progn
          (message "deactivating copilot")
          (copilot-mode -1)
          (setq my-copilot-manual-mode nil))
      (if copilot-mode
          (progn
            (message "activating copilot manual mode")
            (setq my-copilot-manual-mode t))
	(message "activating copilot mode")
	(copilot-mode))))

  (add-to-list 'copilot-disable-predicates #'my-copilot-disable-predicate)
  :hook (prog-mode . (lambda() (setq my-copilot-manual-mode t)))
  :bind
  (("C-M-c"         . my-copilot-change-activation)
   :map copilot-mode-map
   (("M-C-<next>"   . copilot-next-completion)
    ("M-C-<prior>"  . copilot-previous-completion)
    ("M-C-<right>"  . copilot-accept-completion-by-word)
    ("M-C-<down>"   . copilot-accept-completion-by-line)
    ("M-C-<return>" . my-copilot-complete-or-accept)
    ("M-C-g"        . copilot-clear-overlay))))

Language Server Protocol

(use-package eglot
  :ensure nil
  :custom
  ;; Prevent eglot from reformatting code automatically
  (eglot-ignored-server-capabilities
   '(:documentFormattingProvider
     :documentRangeFormattingProvider
     :documentOnTypeFormattingProvider))
  ;; Set the buffer size to 0 to improve performances (https://www.gnu.org/software/emacs/manual/html_mono/eglot.html#Performance)
  ;; (eglot-events-buffer-config (:size 0 :format full))
  :bind
  ("C-c l" . eglot))

Literate programming

(use-package poly-markdown
  :bind (:map polymode-eval-map ("p" . quarto-preview)))

(use-package poly-R
  :mode ("\\.Rmd" . poly-markdown+r-mode))

(unless (package-installed-p 'quarto-mode)
  (package-vc-install
   '(quarto-mode
     :url "https://github.com/christophe-gouel/quarto-emacs"
     :branch "transient"
     :rev :last-release)))
(use-package quarto-mode
  :defer t
  ;; :load-path "~/Documents/git_projects/code/quarto-emacs"
  )

Package edit-indirect required to edit code blocks in indirect buffers in markdown-mode

(use-package edit-indirect
  :defer t)

Snippets

Use “M-C-TAB” for moving to next field to avoid conflict with autocompletion.

(use-package yasnippet
  :defer 1
  :custom
  (yas-use-menu nil)
  (unbind-key "<tab>" yas-minor-mode-map)
  (unbind-key "TAB" yas-minor-mode-map)
  :config
  (yas-global-mode 1)
  :bind (:map yas-minor-mode-map
	      ("M-C-TAB"   . yas-next-field-or-maybe-expand)
	      ("M-C-<tab>" . yas-next-field-or-maybe-expand)))

Tree sitter

Tree-sitter leads to a complete failure of font-locking with Rmd qnad Quarto files, so it is better to simply rely on ESS font-locking.

(use-package tree-sitter-ess-r
  :after ess
  :hook (ess-r-mode . tree-sitter-ess-r-mode-activate))

(use-package ts-fold
  :vc (:fetcher github :repo emacs-tree-sitter/ts-fold)
  :defer t)

Programming languages

Emacs Speaks Statistics (ESS)

(use-package ess-site
  :ensure ess
  :mode
  ("renv.lock"   . js-json-mode)
  (".Rhistory"   . ess-r-mode)
  (".lintr"      . conf-mode)
  ("\\.Rproj\\'" . conf-mode)
  :bind (:map ess-r-mode-map
	      ;; Shortcut for pipe |>
        ("C-S-m"   . " |>")
	      ;; Shortcut for pipe %>%
	      ("C-%"     . " %>%")
	      ;; Shortcut for assign <-
	      ("C--"     . ess-insert-assign)
	      ("C-c v" . ess-view-data-print)
        :map inferior-ess-r-mode-map
        ("C-S-m" . " |>")
        ("C-%"   . " %>%")
	      ("C--"   . ess-insert-assign)
	      ("C-c v" . ess-view-data-print)
	      :map inferior-ess-mode-map
	      ("<home>" . comint-bol)
	      :map ess-r-mode-map
	      :filter (not (eq system-type 'darwin))
	      ("M--"     . ess-insert-assign))
  :custom
  ;; Deactivate linter in ess because it does not seem to work well
  (ess-use-flymake nil)
  (ess-roxy-str "#'")
  (ess-roxy-template-alist
   '(("description" . ".. content for \\description{} (no empty lines) ..")
     ("details" . ".. content for \\details{} ..")
     ("param" . "")
     ("return" . "")))
  (ess-nuke-trailing-whitespace-p t)
  (ess-assign-list '(" <-" " <<- " " = " " -> " " ->> "))
  (ess-style 'RStudio)  ; Set code indentation
  (ess-ask-for-ess-directory nil) ; Do not ask what is the project directory
  (inferior-R-args "--no-restore-history --no-save ")
  ;; Font-locking
  (ess-R-font-lock-keywords
   '((ess-R-fl-keyword:keywords . t)
     (ess-R-fl-keyword:constants . t)
     (ess-R-fl-keyword:modifiers . t)
     (ess-R-fl-keyword:fun-defs . t)
     (ess-R-fl-keyword:assign-ops . t)
     (ess-R-fl-keyword:%op% . t)
     (ess-fl-keyword:fun-calls . t)
     (ess-fl-keyword:numbers . t)
     (ess-fl-keyword:operators . t)
     (ess-fl-keyword:delimiters . t)
     (ess-fl-keyword:= . t)
     (ess-R-fl-keyword:F&T . t)))
  :config
  (defun my-inferior-ess-init ()
    "Workaround for https://github.com/emacs-ess/ESS/issues/1193"
    (add-hook 'comint-preoutput-filter-functions #'xterm-color-filter -90 t)
    (setq-local ansi-color-for-comint-mode nil)
    (smartparens-mode 1))

  (defun my-ess-remove-project-hook ()
    "Remove a useless hook added by ess to use its own project functions"
    (make-local-variable 'project-find-functions)
    (setq project-find-functions '(project-try-vc)))
  :hook
  (inferior-ess-mode . my-inferior-ess-init)
  (inferior-ess-mode . my-ess-remove-project-hook)
  (ess-r-mode . my-ess-remove-project-hook)
  ;; Outlining like in RStudio
  (ess-r-mode . (lambda ()
    (setq outline-regexp "^#+ +.*\\(----\\|====\\|####\\)")
    (defun outline-level ()
           (cond ((looking-at "^# ") 1)
             ((looking-at "^## ") 2)
             ((looking-at "^### ") 3)
             ((looking-at "^#### ") 4)
             (t 1000))))))
(use-package ess-rscript
  :load-path "~/.emacs.d/lisp/"
  :after ess-site
  :bind (:map ess-r-mode-map ("<f9>" . ess-rscript)))

To interact easily with renv

(use-package rutils
  :defer t)

View R data.frame inside en Emacs buffer:

(use-package ess-view-data
  :bind
  (:map ess-view-data-mode-map
	("f" . ess-view-data-filter)
	("g" . ess-view-data-group)
	("m" . ess-view-data-mutate)
	("o" . ess-view-data-sort)
	("q" . ess-view-data-quit)
	("S" . ess-view-data-summarise)
	("s" . ess-view-data-select)
	("u" . ess-view-data-unique)
	("l 2 w" . ess-view-data-long2wide)
	("w 2 l" . ess-view-data-wide2long)
	("C-c C-p" . ess-view-data-goto-previous-page)
	("C-c C-n" . ess-view-data-goto-next-page))
  :custom
  (ess-view-data-current-update-print-backend 'kable)
  (ess-view-data-rows-per-page 1000))

To have R plots in emacs buffers:

(use-package essgd
  :if (member window-system '(pgtk ns))
  :commands (essgd-start essgd-toggle-plot-buffer)
  :config
  (defvar essgd-prev-buffer nil
    "The buffer used before switching to `*essgd*` buffer.")
  (defun essgd-toggle-plot-buffer ()
    "Switch to `*essgd*` buffer, and back to the previous buffer.
If already in the `*essgd*` buffer, return to the last buffer (either script or process).
The last key used will temporarily toggle the buffer. Assuming that it is bound to C-c
C-a, you can navigate back and forth between essgd and script
buffer with C-c C-a C-a C-a ...."
    (interactive)
    (let* ((essgd-buf-name "*essgd*")
           (essgd-buffer (get-buffer essgd-buf-name)))
      (if essgd-buffer
          ;; Switch to *essgd* buffer if it exists and we are not in it.
          (if (not (eq (current-buffer) essgd-buffer))
              (progn
		;; Store the current buffer as the 'previous' one.
		(setq essgd-prev-buffer (current-buffer))
		(pop-to-buffer essgd-buf-name))
            ;; If already in *essgd* buffer, switch back to the previous buffer.
            (if (and essgd-prev-buffer (buffer-live-p essgd-prev-buffer))
		(pop-to-buffer essgd-prev-buffer)
              (message "No previous buffer or *essgd* already the current buffer.")))
	(message "No existing *essgd* buffer.")))
    ;; Activate transient keymap to allow pressing the same key again
    (when (called-interactively-p 'any)
      (set-transient-map
       (let ((map (make-sparse-keymap))
             (key (vector last-command-event)))
         (define-key map key #'essgd-toggle-plot-buffer)
         map))))
  :bind
  (:map ess-r-mode-map ("C-c C-a" . essgd-toggle-plot-buffer)
	:map essgd-mode-map ("C-c C-a" . essgd-toggle-plot-buffer)
	:map inferior-ess-r-mode-map ("C-c C-a" . essgd-toggle-plot-buffer)))

GAMS

(use-package gams-mode
  ;; :load-path "~/Documents/git_projects/code/gams-mode"
  :vc (:url "https://github.com/christophe-gouel/gams-mode"
	    :rev :newest
	    :branch "auto-mode")
  :hook
  ;; (gams-mode . rainbow-delimiters-mode)
  ;; (gams-mode . smartparens-mode)
  ;; (gams-mode . display-fill-column-indicator-mode)
  ;; (gams-mode . (lambda ()
  ;;                (make-local-variable 'company-minimum-prefix-length)
  ;;                (setq company-minimum-prefix-length 1)))
  (gams-mode . (lambda ()
                 (outline-minor-mode)
                 (setq outline-regexp "^\*+ +.*----")
               (defun outline-level ()
                 (save-excursion
                   (looking-at outline-regexp)
                   (let ((match (match-string 0)))
                     (- (length match) (length (replace-regexp-in-string "\*" "" match))))))))
  :custom
  (gams-process-command-option "ll=0 lo=3 pw=153 ps=9999")
  (gams-fill-column 90)
  (gams-default-pop-window-height 20)
  ;; Remove the handling of parentheses by gams-mode to use smartparens instead
  (gams-close-paren-always nil)
  (gams-close-double-quotation-always nil)
  (gams-close-single-quotation-always nil)
  ;; Indent
  (gams-indent-number 2)
  (gams-indent-number-loop 2)
  (gams-indent-number-mpsge 2)
  (gams-indent-number-equation 2)
;  :mode ("\\.gms\\'" . gams-mode)
  ;; :config
  ;; (if (equal system-type 'windows-nt)
  ;;     (setq gams-system-directory "C:/GAMS/Last/"
  ;;                 gams-docs-directory "C:/GAMS/Last/docs")
  ;;   (setq gams-system-directory "/opt/gams/gamsLast_linux_x64_64_sfx"
  ;;         gams-docs-directory "/opt/gams/gamsLast_linux_x64_64_sfx/docs"))
  :bind (:map gams-mode-map
              ("C-c =" . gams-show-identifier-list)))

(use-package poly-gams
  :after gams-mode
  ;; :vc (:fetcher github :repo ShiroTakeda/poly-gams)
  ;; :load-path "~/Documents/git_projects/code/poly-gams"
  :mode ("\\.inc\\'" . poly-gams-mode))

Julia

(use-package julia-mode
  :defer t)

MATLAB

matlab-mode is a based on outdated major-mode programming, so it does not work that well, but this configuration seems to work.

(use-package matlab
  :ensure matlab-mode
  :commands (matlab-mode matlab-shell)
  :mode ("\\.m\\'" . matlab-mode)
  :custom
  (matlab-indent-function t)	; if you want function bodies indented
  (matlab-verify-on-save-flag nil) ; turn off auto-verify on save
  (matlab-indent-level 2)
  (matlab-comment-region-s "% ")
  (matlab-shell-command-switches '("-nodesktop -nosplash"))
  :config
  ;; mlint
  (cond
   ((eq system-type 'gnu/linux)
    (setq mlint-programs (quote ("/usr/local/MATLAB/RLast/bin/glnxa64/mlint"))))
    ((eq system-type 'darwin)
     (setq mlint-programs (quote ("/Applications/MATLAB_R2024b.app/bin/maca64/mlint"))))
    ((eq system-type 'windows-nt)
     (setq mlint-programs
	    (quote ("C:/Program Files/MATLAB/RLast/bin/win64/mlint.exe")))))
  (defun my-matlab-mode-hook ()
    "My matlab-mode hook"
    (setq matlab-show-mlint-warnings t)   ; Activate mlint
    (mlint-minor-mode))                   ; Activate mlint minor mode
  (defun my-matlab-shell-mode-hook ()
    '())
  (defalias 'my-matlab-three-dots
   (kmacro "SPC . . . <return>")
   "Add three dots and carriage return.")
  :bind
  (:map matlab-mode-map
	("C-c C-z" . matlab-show-matlab-shell-buffer)
	("C-c C-." . 'my-matlab-three-dots))
  :hook
  (matlab-mode . my-matlab-mode-hook)
  (matlab-shell-mode . my-matlab-shell-mode-hook))

Python

(use-package python
  :ensure nil
  :custom
  (python-shell-interpreter "ipython3")
  (python-shell-interpreter-args
   "-i --simple-prompt --InteractiveShell.display_page=True")
  (python-shell-prompt-detect-failure-warning nil)
  :config
;; Set encoding to utf-8 to allows utf-8 characters in Python REPL (from
;; https://stackoverflow.com/questions/14172576/why-unicodeencodeerror-raised-only-in-emacss-python-shell)
  (setenv "PYTHONIOENCODING" "utf-8")
  (defun my-python-mode-hook ()
    (add-to-list 'company-backends 'company-jedi))
  :hook
  (python-mode . my-python-mode-hook)
  (python-mode . flymake-mode))

(use-package conda
  :if (equal system-type 'windows-nt)
  :defer t
  :config
  (setq-default mode-line-format
		(cons '(:exec conda-env-current-name) mode-line-format)))

(use-package poetry
  :defer t)

(use-package pyvenv
  :custom
  (pyvenv-virtualenvwrapper-supported "ipython3")
  :config
  (if (equal system-type 'windows-nt)
      ;; Default virtualenv cache directory for poetry on Microsoft Windows
      (setenv "WORKON_HOME"
	      (substitute-in-file-name
	       "${LOCALAPPDATA}/pypoetry/Cache/virtualenvs"))
    ;; Default virtualenv cache directory for poetry on *nix
    (setenv "WORKON_HOME" "~/.cache/pypoetry/virtualenvs")))

(use-package pydoc)

(use-package numpydoc
  :bind (:map python-mode-map
              ("C-c C-n" . numpydoc-generate)))

Stata

(use-package ado-mode
  :defer t)

Epilogue

Custom file

Define a file in which any customization is saved

(setq custom-file (concat user-emacs-directory "custom.el"))
(when (file-exists-p custom-file)
  (load custom-file))

End message

;;; init.el ends here

To install manually

Fonts

Download and install fonts

Linters

LSP servers

pip3 install --user python-lsp-server[all]
Rscript -e "install.packages('languageserver')"
Curl --output %HOME%/.local/bin/digestif.cmd \
  https://raw.githubusercontent.com/astoff/digestif/master/scripts/digestif.cmd

Python

Install IPython to be able to launch it from Emacs

pip3 install --user ipython

Python requires the package pyreadline3 on Windows to enable auto-completion.

pip3 install --user pyreadline3

Install Jedi server for company-jedi:

(jedi:install-server)

Stylers

Rscript -e "install.packages('styler')"

Other installations

math-preview for LaTeX blocks in text buffers.

npm install -g git+https://gitlab.com/matsievskiysv/math-preview

Install

  • delta to have syntax highlighting in git diffs.
  • fd to have a fast alternative to find.
  • hunspell for spell checking.
  • ripgrep to have a fast alternative to grep.

On Windows, they can be installed with Chocolatey (requires admin rights):

choco install -y delta fd hunspell ripgrep

On Linux/Ubuntu

sudo snap install git-delta-snap
sudo snap alias git-delta-snap.delta delta

On Windows, one has to make sure that a recent version of grep and a POSIX version of find (not Windows version) are available in the PATH (both come with git). If it is not possible to move POSIX find before Windows find in the PATH, it is necessary to set the variable find-program in custom.el.