#+TITLE: Emacs configuration #+STARTUP: overview # note: if there is at least one block marked with =:tangle yes=, all # the other blocks won't be exported. * Personal Information #+BEGIN_SRC emacs-lisp (setq user-full-name "Louis Roché" user-mail-address "louis@louisroche.net") #+END_SRC * Customize settings Set up the customize file to its own separate file, instead of saving customize #+BEGIN_SRC emacs-lisp (setq custom-file (expand-file-name "custom.el" user-emacs-directory)) (load custom-file) #+END_SRC * Sane defaults Let's start with some sane defaults, shall we? Sources for this section include [[https://github.com/magnars/.emacs.d/blob/master/settings/sane-defaults.el][Magnars Sveen]], [[http://pages.sachachua.com/.emacs.d/Sacha.html][Sacha Chua]], [[https://github.com/danielmai/.emacs.d/blob/master/config.org][Daniel Mai]] and [[https://github.com/technomancy/better-defaults][Better defaults]]. #+BEGIN_SRC emacs-lisp ;; These functions are useful. Activate them. (put 'downcase-region 'disabled nil) (put 'upcase-region 'disabled nil) (put 'narrow-to-region 'disabled nil) (put 'dired-find-alternate-file 'disabled nil) ;; Answering just 'y' or 'n' will do (defalias 'yes-or-no-p 'y-or-n-p) ;; UTF-8 please (setq locale-coding-system 'utf-8) ; pretty (set-terminal-coding-system 'utf-8) ; pretty (set-keyboard-coding-system 'utf-8) ; pretty (set-selection-coding-system 'utf-8) ; please (prefer-coding-system 'utf-8) ; with sugar on top (when (display-graphic-p) (setq x-select-request-type '(UTF8_STRING COMPOUND_TEXT TEXT STRING))) ;; no tabs (setq-default indent-tabs-mode nil) ;; Turn off the blinking cursor (blink-cursor-mode -1) (setq-default indicate-empty-lines t) ;; delete the region when typing, just like as we expect nowadays. (delete-selection-mode t) (column-number-mode t) ;; (global-visual-line-mode) ;; (diminish 'visual-line-mode) (setq line-move-visual nil) (setq uniquify-buffer-name-style 'forward) ;; -i gets alias definitions from .bash_profile ;; (setq shell-command-switch "-ic") ;; Don't beep at me (setq visible-bell t) ;; Don't load old .elc files when the .el file is newer (setq load-prefer-newer t) (setq save-interprogram-paste-before-kill t) (autoload 'zap-up-to-char "misc" "Kill up to, but not including ARGth occurrence of CHAR." t) (bind-key "M-z" #'zap-up-to-char) (setq ediff-window-setup-function #'ediff-setup-windows-plain) (setq apropos-do-all t) (setq mouse-yank-at-point t) ;; all files must have a newline at the end ;; (setq require-final-newline t) (setq wdired-allow-to-change-permissions t) #+END_SRC *** Backups Keep all backup and auto-save files in one directory. Disk space is cheap. Save lots. #+BEGIN_SRC emacs-lisp (setq backup-directory-alist '(("." . "~/.emacs.d/backups"))) (setq auto-save-file-name-transforms '((".*" "~/.emacs.d/auto-save-list/" t))) (setq delete-old-versions -1) (setq version-control t) (setq vc-make-backup-files t) #+END_SRC *** Saveplace Save point position between sessions #+BEGIN_SRC emacs-lisp (use-package saveplace :custom (save-place-file (expand-file-name ".places" user-emacs-directory)) (save-place t) :config (save-place-mode 1)) #+END_SRC *** Autorevert Auto reload file when there is a change #+BEGIN_SRC emacs-lisp (global-auto-revert-mode t) (delight 'auto-revert-mode) #+END_SRC *** Compilation #+BEGIN_SRC emacs-lisp ;; (setq compilation-scroll-output t) (setq compilation-scroll-output 'first-error) #+END_SRC This package allows the hide the compilation buffer if compilation is successful. But its behavior is hard to predict. #+BEGIN_SRC emacs-lisp :tangle no (use-package bury-successful-compilation :config (bury-successful-compilation 1)) #+END_SRC Bucklescript prints compilation messages that are not formated correctly. And it doesn't want to disable colors. #+BEGIN_SRC emacs-lisp (require 'compile) (defconst bucklescript-compilation-regexp "^[ \t]*\\(?:We've found a bug for you!\\)?\\(?:Warning number \\([0-9]+\\)\\)?[\n ]*\ \\([^ ]+\\) +\\([0-9]+\\):\\([0-9]+\\)-?\\(?:\\([0-9]+\\):\\)?\\([0-9]+\\)?$" "The messages from -bs-super-errors are not standard") (add-to-list 'compilation-error-regexp-alist-alist `(bucklescript ,bucklescript-compilation-regexp 2 (3 . 5) (4 . 6) 1)) (add-to-list 'compilation-error-regexp-alist 'bucklescript) #+END_SRC Util function to help detecting which compilation regexp matched. #+begin_src emacs-lisp (defun test-compilation-error-regexps () (interactive) (save-excursion (let ((matched nil)) (dolist (rule compilation-error-regexp-alist) (let* ((item (if (symbolp rule) (cdr (assq rule compilation-error-regexp-alist-alist)) rule)) (pat (car item))) (goto-char (point-min)) (when (re-search-forward pat nil t) (push rule matched)))) (message "matched: %s" (nreverse matched))))) #+end_src #+BEGIN_SRC emacs-lisp (use-package xterm-color :ensure t :demand t :custom (compilation-environment '("TERM=xterm-256color" ; allow colors ;; "NINJA_ANSI_FORCED=0" ; but not from bsb or other ninja users )) :config (defun my/compilation-color (proc) ;; We need to differentiate between compilation-mode buffers ;; and running as part of comint (which at this point we assume ;; has been configured separately for xterm-color) (when (eq (process-filter proc) 'compilation-filter) ;; This is a process associated with a compilation-mode buffer. ;; We may call `xterm-color-filter' before its own filter function. (set-process-filter proc (lambda (proc string) (funcall 'compilation-filter proc (xterm-color-filter string)))))) :hook (compilation-start . my/compilation-color)) #+END_SRC *** Shell Set shell to something not fancy. Not sure it works... #+BEGIN_SRC emacs-lisp (setenv "SHELL" "/bin/bash") (setq-default explicit-shell-file-name "/bin/bash") (setq-default shell-file-name "/bin/bash") #+END_SRC *** Interlock files Interlock files are the ~.#*~ files created by emacs when a file is edited and has not been saved yet. It is possible to disable them using this snippet. #+BEGIN_SRC emacs-lisp :tangle no (setq create-lockfiles nil) #+END_SRC See https://www.gnu.org/software/emacs/manual/html_node/emacs/Interlocking.html *** Display Time When displaying the time with =display-time-mode=, I don't care about the load average. #+BEGIN_SRC emacs-lisp (setq display-time-default-load-average nil) #+END_SRC *** OS X scrolling #+BEGIN_SRC emacs-lisp (setq mouse-wheel-scroll-amount (quote (0.01))) #+END_SRC * Quelpa Install packages from github or local files. #+BEGIN_SRC emacs-lisp (use-package quelpa :ensure t :custom (quelpa-update-melpa-p nil) (quelpa-checkout-melpa-p nil)) (quelpa '(quelpa-use-package :fetcher github :repo "quelpa/quelpa-use-package")) (require 'quelpa-use-package) #+END_SRC * Theme ** Doom themes Theme of the [[https://github.com/hlissner/doom-emacs][doom emacs configuration]]. #+BEGIN_SRC emacs-lisp :tangle no (use-package doom-themes :ensure t :custom (doom-themes-enable-bold t) ; if nil, bold is universally disabled (doom-themes-enable-italic t) ; if nil, italics is universally disabled :config ; (doom-themes-visual-bell-config) ; Enable flashing mode-line on errors (doom-themes-org-config) ; Corrects (and improves) org-mode's native fontification. ) #+END_SRC ** Solarized theme Here's some configuration for [[https://github.com/bbatsov/solarized-emacs/][bbatsov's solarized themes]]. #+BEGIN_SRC emacs-lisp :tangle no (use-package solarized-theme :defer 10 :init (setq solarized-use-variable-pitch nil) :ensure t ) #+END_SRC ** Monokai theme #+BEGIN_SRC emacs-lisp :tangle no (use-package monokai-theme :if (window-system) :ensure t :init (setq monokai-use-variable-pitch nil)) #+END_SRC ** Zenburn theme Use zenburn in terminal by default #+BEGIN_SRC emacs-lisp :tangle no (use-package zenburn-theme :if (not window-system) :ensure t) #+END_SRC ** Zerodark theme A dark theme for Emacs, inspired from Niflheim and One Dark #+BEGIN_SRC emacs-lisp :tangle no (use-package zerodark-theme :ensure t) #+END_SRC ** Convenient theme functions #+BEGIN_SRC emacs-lisp (defun switch-theme (theme) "Disables any currently active themes and loads THEME." ;; This interactive call is taken from `load-theme' (interactive (list (intern (completing-read "Load custom theme: " (mapc 'symbol-name (custom-available-themes)))))) (let ((enabled-themes custom-enabled-themes)) (mapc #'disable-theme custom-enabled-themes) (load-theme theme t))) (defun disable-active-themes () "Disables any currently active themes listed in `custom-enabled-themes'." (interactive) (mapc #'disable-theme custom-enabled-themes)) #+END_SRC ** Choose theme Use environment variables to choose theme. #+BEGIN_SRC emacs-lisp (setq my/default-theme 'doom-one-light) (setq my/bad-id-theme 'whiteboard) (defun my/print-env-theme (kind expected) (message "Looking for theme %s `%s' detected from the env..." kind expected)) (defun my/load-theme () (let ((count-themes 1) (themes-ids (make-hash-table :size 30)) (themes-names (make-hash-table :size 30 :test #'equal)) (env-theme-name (getenv "EN")) (env-theme-id (getenv "EI"))) (dolist (theme (custom-available-themes)) (puthash count-themes theme themes-ids) (puthash (symbol-name theme) theme themes-names) (setq count-themes (+ 1 count-themes))) (when (or my/default-theme env-theme-name env-theme-id) (setq-local selected-theme (if env-theme-name (progn (my/print-env-theme "name" env-theme-name) (gethash env-theme-name themes-names my/bad-id-theme)) (if env-theme-id (progn (my/print-env-theme "id" env-theme-id) (gethash (string-to-number env-theme-id) themes-ids my/bad-id-theme)) my/default-theme))) (disable-active-themes) (message "Loading theme `%s'..." selected-theme) (load-theme selected-theme t)))) ;; (my/load-theme) #+END_SRC ** Solaire mode =solaire-mode= is an aesthetic plugin that helps visually distinguish file-visiting windows from other types of windows (like popups or sidebars) by giving them a slightly different -- often brighter -- background. #+BEGIN_SRC emacs-lisp :tangle no (use-package solaire-mode :ensure t :hook (after-change-major-mode . turn-on-solaire-mode) (after-revert . turn-on-solaire-mode) (minibuffer-setup . solaire-mode-in-minibuffer) :config (solaire-mode-swap-bg)) #+END_SRC ** Auto dim other buffers #+BEGIN_SRC emacs-lisp :tangle no (use-package auto-dim-other-buffers :ensure t :config (auto-dim-other-buffers-mode t)) #+END_SRC * Font And here's how we tell Emacs to use the font we want to use. #+BEGIN_SRC emacs-lisp (cond ((member "PragmataPro" (font-family-list)) (set-face-attribute 'default nil :font "PragmataPro-10")) ((member "Ubuntu Mono" (font-family-list)) (set-face-attribute 'default nil :font "Ubuntu Mono-10")) ((member "DejaVu Sans Mono" (font-family-list)) (set-face-attribute 'default nil :font "DejaVu Sans Mono-10"))) #+END_SRC Note: To view all fonts that are available to use, run the following: #+BEGIN_SRC emacs-lisp :tangle no (font-family-list) #+END_SRC * Mode line Move =(vc-mode vc-mode)= at the end of the mode line. #+begin_src emacs-lisp (setq-default mode-line-format '("%e" mode-line-front-space mode-line-mule-info mode-line-client mode-line-modified mode-line-remote mode-line-frame-identification mode-line-buffer-identification " " mode-line-position " " mode-line-misc-info mode-line-modes (vc-mode vc-mode) mode-line-end-spaces)) #+end_src * Terminal Disable hl-line-mode when in a terminal. #+BEGIN_SRC emacs-lisp :tangle no (add-hook 'after-change-major-mode-hook '(lambda () (hl-line-mode (if (equal major-mode 'term-mode) 0 1)))) #+END_SRC * List buffers ibuffer is the improved version of list-buffers. #+BEGIN_SRC emacs-lisp ;; make ibuffer the default buffer lister. (defalias 'list-buffers 'ibuffer) #+END_SRC source: http://ergoemacs.org/emacs/emacs_buffer_management.html #+BEGIN_SRC emacs-lisp (add-hook 'dired-mode-hook 'auto-revert-mode) ;; Also auto refresh dired, but be quiet about it (setq global-auto-revert-non-file-buffers t) (setq auto-revert-verbose nil) #+END_SRC * Projectile #+BEGIN_SRC emacs-lisp (use-package projectile :ensure t :delight :bind (:map projectile-mode-map ("C-c p" . projectile-command-map) ("" . projectile-compile-project) ("M-j" . projectile-find-file) ("M-C-j" . projectile-switch-project)) :custom (projectile-completion-system 'helm) (projectile-enable-caching t) (projectile-switch-project-action #'helm-projectile-find-file) :config (projectile-global-mode) (helm-projectile-on)) #+END_SRC #+BEGIN_SRC emacs-lisp (use-package projectile-ripgrep :ensure t) #+END_SRC * Flymake #+BEGIN_SRC emacs-lisp (use-package flymake :ensure t :bind ("" . flymake-goto-prev-error) ("" . flymake-goto-next-error) :config (defun flymake--transform-mode-line-format (ret) "Change the output of `flymake--mode-line-format'." (setf (seq-elt (car ret) 1) " FM") ret) (advice-add #'flymake--mode-line-format :filter-return #'flymake--transform-mode-line-format)) #+END_SRC #+BEGIN_SRC emacs-lisp :tangle no (use-package flymake-diagnostic-at-point :if (version<= "26.0" emacs-version) :after flymake :config (add-hook 'flymake-mode-hook #'flymake-diagnostic-at-point-mode)) #+END_SRC * Eldoc ~eldoc-mode~ provides information about the symbol at point in the echo area. Usually it is the signature of a function. #+BEGIN_SRC emacs-lisp (use-package eldoc :ensure t :delight :hook (eval-expression-minibuffer-setup . eldoc-mode)) #+END_SRC * Completion I use company mode as a completion backend #+BEGIN_SRC emacs-lisp (use-package company :ensure t :delight " C" :custom (company-quickhelp-delay 0) (company-idle-delay nil) (company-tooltip-align-annotations t) :hook (prog-mode . company-mode) :config (company-quickhelp-mode 1) :bind ("M-o" . company-complete)) #+END_SRC Popup for documentation or help #+BEGIN_SRC emacs-lisp (use-package company-quickhelp :commands (company-quickhelp-mode) :ensure t :bind (:map company-active-map ("M-h" . company-quickhelp-manual-begin))) #+END_SRC * Parentheses Rainbow delimiters, to have a different color for each level of =([{}])=. #+BEGIN_SRC emacs-lisp (use-package rainbow-delimiters :ensure t :hook (prog-mode . rainbow-delimiters-mode)) #+END_SRC #+BEGIN_SRC emacs-lisp (use-package paren :ensure t :custom (show-paren-delay 0.3) (blink-matching-paren t) (blink-matching-paren-on-screen t) (show-paren-style 'expression) (blink-matching-paren-dont-ignore-comments t) :config (show-paren-mode)) #+END_SRC It is a global minor mode. To enable it only for prog modes, this trick can be used. #+BEGIN_SRC emacs-lisp :tangle no (show-paren-mode) ;; activate the needed timer (setq show-paren-mode nil) ;; The timer will do nothing if this is nil (defun show-paren-local-mode () (interactive) (make-local-variable 'show-paren-mode) (setq show-paren-mode t)) (add-hook 'prog-mode-hook #'show-paren-local-mode) #+END_SRC Autopair is now replaced with electric-pair-mode. #+BEGIN_SRC emacs-lisp (use-package elec-pair :ensure t :config (electric-pair-mode)) #+END_SRC #+BEGIN_SRC emacs-lisp (use-package paredit :ensure t :delight :config (defun paredit-kill-region-or-word () "Call `paredit-kill-region' or `paredit-backward-kill-word' depending on whether or not a region is selected." (interactive) (if (and transient-mark-mode mark-active) (paredit-kill-region (point) (mark)) (paredit-backward-kill-word))) (unbind-key "M-?" paredit-mode-map) :hook ((emacs-lisp-mode dune-mode tuareg-mode) . paredit-mode)) #+END_SRC * Programming languages ** Ocaml/Reason *** Utils Util function to select where to load merlin from. #+BEGIN_SRC emacs-lisp (defun shell-cmd (cmd) "Returns the stdout output of a shell command or nil if the command returned an error" (car (ignore-errors (apply 'process-lines (split-string cmd))))) (defun reason-cmd-where (cmd) (let ((where (shell-cmd cmd))) (if (string-equal "unknown flag ----where" where) nil where))) (defun ocamlmerlin-where () (reason-cmd-where "ocamlmerlin ----where")) (defun refmt-where () (reason-cmd-where "refmt ----where")) (defun utop-where () (reason-cmd-where "utop ----where")) (defun opam-which (bin) (shell-cmd (format "opam exec -- which %s" bin))) (setq opam-p (shell-cmd "which opam")) (setq utop-p (shell-cmd "which utop")) (setq reason-npm-p (utop-where)) #+END_SRC *** Load opam Setup environment variables using opam. To be used if the env is not configured before to launch emacs. #+BEGIN_SRC emacs-lisp :tangle no (if opam-p (dolist (var (car (read-from-string (shell-command-to-string "opam config env --sexp")))) (setenv (car var) (cadr var)))) #+END_SRC Add opam libs. #+BEGIN_SRC emacs-lisp (if opam-p (let ((opam-share (ignore-errors (car (process-lines "opam" "config" "var" "share"))))) (when (and opam-share (file-directory-p opam-share)) (add-to-list 'load-path (expand-file-name "emacs/site-lisp" opam-share))))) #+END_SRC *** ocp tools Require ocp stuff first because of conflicts between shortcuts. It is installed from opam, ~ensure~ is not required. #+BEGIN_SRC emacs-lisp (use-package ocp-indent :if (shell-cmd "which ocp-indent")) (use-package ocp-index :if (shell-cmd "which ocp-index")) #+END_SRC *** caml, reasonml and tuareg modes If no face is setup for ~merlin-type-face~, it will be set to ~caml-types-expr-face~ which is provided by ~caml~. #+BEGIN_SRC emacs-lisp :tangle no (use-package caml :ensure t) #+END_SRC The ~comment-continue~ variable is set so that multi lines comments are not prefixed by ~*~. The length of ~comment-continue~ should be the same as the length of ~comment-start~ to preserve indentation. #+BEGIN_SRC emacs-lisp (use-package hideshow :ensure t :delight hs-minor-mode :config (defun deriving-inline-forward-sexp (&optional arg) (search-forward-regexp "\\[@@@end\\]") nil nil arg) (add-to-list 'hs-special-modes-alist '(tuareg-mode "\\[@@deriving_inline[^]]*\\]" "\\[@@@end\\]" nil deriving-inline-forward-sexp nil))) (use-package tuareg :ensure t :delight "O" :bind (:map tuareg-mode-map ("M-;" . comment-dwim) ; restore default comment command ("C-w" . paredit-kill-region-or-word)) :mode (("\\.ml[ily]?$" . tuareg-mode) ("\\.topml$" . tuareg-mode) ("\\.ocamlinit$" . tuareg-mode) ("^dune$" . dune-mode) ("^dune-project$" . dune-mode) ("^dune-workspace$" . dune-mode)) :config (unbind-key "" tuareg-mode-map) (load "tuareg-site-file") (defun my/setup-tuareg () (interactive) (setq-local comment-style 'indent)) (defconst my/ocaml-compilation-regexp "^ *\\(File \\(\"?\\)\\([^,\" \n\t<>]+\\)\\2, \ lines? \\([0-9]+\\)-?\\([0-9]+\\)?\ \\(?:, characters? \\([0-9]+\\)-?\\([0-9]+\\)?\\)?:\\)\ \\(?:\n[ \t]*\\(?:\\(?:[0-9]+ | .*\\|\\^+\\)\n[ \t]*\\)*\ \\(Warning\\(?: [0-9]+\\)?\\):\\)?" "Update the ocaml regexp to support >= 4.08") (add-to-list 'compilation-error-regexp-alist-alist `(my/ocaml ,my/ocaml-compilation-regexp 3 (4 . 5) (6 . 7) (8) 1)) (add-to-list 'compilation-error-regexp-alist 'my/ocaml) (defun setup-hide-deriving-inline () (inline) (hs-minor-mode t) (let ((hs-hide-comments-when-hiding-all nil)) (hs-hide-all))) :hook (tuareg-mode . my/setup-tuareg) (tuareg-mode . setup-hide-deriving-inline)) #+END_SRC When using ~reason-mode~, we want to load merlin from ~node_modules~ if it is available there. Otherwise it will come from opam. #+BEGIN_SRC emacs-lisp (use-package reason-mode :ensure t :bind (:map reason-mode-map ("C-M-\\" . refmt)) :custom (refmt-width-mode 'fill) (refmt-command 'opam) :config (defun my/setup-reason-npm () "When reason tools are only available from npm and not from an opam switch, the configuration must be different." (message "Loading reason tools from npm...") (let* ((refmt-reason (refmt-where)) (utop-reason (utop-where)) (utop-base-dir (when utop-reason (replace-regexp-in-string "bin/utop$" "" utop-reason))) (merlin-reason (ocamlmerlin-where)) (merlin-base-dir (when merlin-reason (replace-regexp-in-string "bin/ocamlmerlin$" "" merlin-reason)))) ;; Add npm merlin.el to the emacs load path and tell emacs where to find ocamlmerlin (when merlin-base-dir (message "loading merlin from %s" (concat merlin-base-dir "share/emacs/site-lisp/")) (add-to-list 'load-path (concat merlin-base-dir "share/emacs/site-lisp/"))) (when utop-base-dir (message "loading utop from %s" (concat utop-base-dir "share/emacs/site-lisp/")) (add-to-list 'load-path (concat utop-base-dir "share/emacs/site-lisp/"))) (when refmt-reason (setq refmt-command refmt-reason))) (message "Loading reason tools from npm...done")) (when reason-npm-p (my/setup-reason-npm)) (defun my/reason-npm-hook () (when reason-npm-p (setq-local merlin-command (ocamlmerlin-where)))) :hook (reason-mode . my/reason-npm-hook)) #+END_SRC *** merlin Configure merlin. Magical autocompletion and IDE features. #+BEGIN_SRC emacs-lisp (use-package merlin :custom (merlin-completion-with-doc t) (merlin-error-check-then-move nil) (merlin-command 'opam) (merlin-error-after-save t) (merlin-locate-preference 'mli) :custom-face ;; (merlin-type-face ((t (:background "firebrick")))) ;; doom-one ;; (merlin-type-face ((t (:background "#a0bcf8")))) ;; doom-one-light (merlin-type-face ((t (:inherit (highlight))))) :bind (:map merlin-mode-map ("M-." . merlin-locate) ("M-," . merlin-pop-stack) ("M-?" . merlin-occurrences) ("C-c m j" . merlin-jump) ("C-c m i" . merlin-locate-ident) ("C-c m e" . merlin-iedit-occurrences) ("C-c m d" . merlin-document)) :hook ;; Start merlin on ml files ((tuareg-mode reason-mode) . merlin-mode) :config (setq company-backends (remove 'merlin-company-backend company-backends))) #+END_SRC *** merlin-eldoc Enable automatic display of type and documentation of value at point after a small delay. Provides a merlin backend to ~eldoc-mode~. Works for OCaml and Reason. #+BEGIN_SRC emacs-lisp :tangle no (quelpa '(merlin-eldoc :repo "Khady/merlin-eldoc" :fetcher github)) (add-hook 'tuareg-mode-hook #'merlin-eldoc-setup) (add-hook 'reason-mode-hook #'merlin-eldoc-setup) #+END_SRC #+BEGIN_SRC emacs-lisp (use-package merlin-eldoc ;; :load-path "/home/louis/Code/github/merlin-eldoc" :ensure t :custom (eldoc-echo-area-use-multiline-p t) (merlin-eldoc-max-lines 8) (merlin-eldoc-max-lines-function-arguments 1) (merlin-eldoc-type-verbosity 'min) (merlin-eldoc-function-arguments nil) (merlin-eldoc-doc nil) :bind (:map merlin-mode-map ("C-c m p" . merlin-eldoc-jump-to-prev-occurrence) ("C-c m n" . merlin-eldoc-jump-to-next-occurrence)) :hook ((tuareg-mode reason-mode) . merlin-eldoc-setup)) #+END_SRC *** utop #+BEGIN_SRC emacs-lisp (use-package utop :if utop-p :delight utop-minor-mode :custom (utop-edit-command t) :commands (utop-minor-mode) :init (defun my/setup-utop (cmd &optional name) (setq my/utop-name (if name name cmd)) (setq utop-command (format "%s -emacs" cmd)) (setq utop-prompt (lambda () (let ((prompt (format "%s[%d]> " my/utop-name utop-command-number))) (add-text-properties 0 (length prompt) '(face utop-prompt) prompt) prompt))) (utop-minor-mode)) (defun my/setup-utop-tuareg () (my/setup-utop "opam exec -- utop" "utop")) (defun my/setup-utop-reason () (my/setup-utop "opam exec -- rtop" "rtop")) (defun my/setup-utop-dune (&optional path from) (let* ((from (if from from default-directory)) (path (if path path default-directory)) (rel-path-to (file-relative-name path from)) (cmd (format "dune-utop.sh %S %S" from rel-path-to)) (path-dirname (file-name-nondirectory (directory-file-name (file-name-directory path)))) (name (format "[dune %s]" path-dirname))) (my/setup-utop cmd name))) :hook (tuareg-mode . my/setup-utop-tuareg) (reason-mode . my/setup-utop-reason) (utop-minor-mode . (lambda () (setq company-backends (remove 'utop-company-backend company-backends))))) #+END_SRC *** Dune #+BEGIN_SRC emacs-lisp (use-package dune-flymake) (use-package dune) #+END_SRC *** ocamlformat #+BEGIN_SRC emacs-lisp (use-package ocamlformat :commands (ocamlformat ocamlformat-before-save) :bind (:map tuareg-mode-map ("M-" . ocamlformat))) #+END_SRC *** lsp #+begin_src emacs-lisp (defun my/merlin-lsp--current-font-among-fonts-p (pos fonts) "If current font at POS is among FONTS." (let* ((fontfaces (get-text-property pos 'face))) (when (not (listp fontfaces)) (setf fontfaces (list fontfaces))) (delq nil (mapcar (lambda (f) (member f fonts)) fontfaces)))) (defun my/merlin-lsp--in-comment-p (pos) "Return non-nil if character at POS is comment or documentation. This is done by comparing font face. So a mode such as `tuareg-mode' or `reason-mode' must be activated in the buffer before to call this function." (my/merlin-lsp--current-font-among-fonts-p pos '(font-lock-comment-face font-lock-comment-delimiter-face font-lock-doc-face))) (defun my/merlin-lsp--in-string-p (pos) "Return non-nil if character at POS is string. This is done by comparing font face. So a mode such as `tuareg-mode' or `reason-mode' must be activated in the buffer before to call this function." (my/merlin-lsp--current-font-among-fonts-p pos '(font-lock-string-face))) (defun my/merlin-lsp--in-keyword-p (pos) "Return non-nil if character at POS is keyword. This is done by comparing font face. So a mode such as `tuareg-mode' or `reason-mode' must be activated in the buffer before to call this function." (my/merlin-lsp--current-font-among-fonts-p pos '(tuareg-font-lock-governing-face font-lock-keyword-face))) (defun my/merlin-lsp--in-operator-p (pos) "Return non-nil if character at POS is operator. This is done by comparing font face. So a mode such as `tuareg-mode' or `reason-mode' must be activated in the buffer before to call this function." (my/merlin-lsp--current-font-among-fonts-p pos '(tuareg-font-lock-operator-face))) (defun my/merlin-lsp--valid-type-position-p (pos) "Return non-nil if POS is in a place valid to get a type." (let ((symbol (thing-at-point 'symbol)) (operator (my/merlin-lsp--in-operator-p pos)) (string (my/merlin-lsp--in-string-p pos)) (comment (my/merlin-lsp--in-comment-p pos)) (keyword (my/merlin-lsp--in-keyword-p pos))) (and (or symbol operator string) (not comment) (or (not keyword) string)))) (defun my/merlin-lsp--hover () "Call lsp-hover only in valid hover positions." (when (my/merlin-lsp--valid-type-position-p (point)) (lsp-hover))) (defun my/merlin-lsp--document-highlight () "Call lsp-document-highlight only in valid hover positions." (when (my/merlin-lsp--valid-type-position-p (point)) (lsp-document-highlight))) (defun my/merlin-lsp--setup-eldoc () "Replace default lsp-eldoc-hook with custom functions checking the validity of the position." (setq-local lsp-eldoc-hook '(my/merlin-lsp--hover my/merlin-lsp--document-highlight))) (defun my/merlin-lsp-register () "Register a lsp server for ocaml. This functions must be called only after lsp-mode has been loaded." (lsp-register-client (make-lsp-client :new-connection (lsp-stdio-connection '("opam" "exec" "--switch=ocaml-base-compiler.4.07.1" "--" "/home/louis/.opam/ocaml-base-compiler.4.07.1/bin/ocamlmerlin-lsp")) :major-modes '(caml-mode tuareg-mode reason-mode) :server-id 'O))) (defun my/merlin-lsp () "Setup and start merlin-lsp." (my/merlin-lsp--setup-eldoc) (lsp)) #+end_src For debug only #+begin_src emacs-lisp :tangle no (lsp-register-client (make-lsp-client :new-connection (lsp-stdio-connection '("/home/louis/Code/github/merlin/_build/default/src/frontend/lsp/ocamlmerlin_lsp.exe")) :major-modes '(caml-mode tuareg-mode reason-mode) :priority 1 :server-id 'merlin-test)) #+end_src *** dir locals Then it is possible to create a ~.dir-locals.el~ file to setup compilation and utop commands. #+BEGIN_SRC emacs-lisp :tangle no ((nil (eval progn (require 'projectile) (puthash (projectile-project-root) "make -C backend/api byte" projectile-compilation-cmd-map))) ("src" (tuareg-mode (eval progn (my/setup-utop-dune (format "%sbackend/api/src/" (projectile-project-root))))))) #+END_SRC ** Javascript #+BEGIN_SRC emacs-lisp (use-package web-mode :ensure t) #+END_SRC ** Json #+BEGIN_SRC emacs-lisp (use-package json-mode :ensure t) #+END_SRC #+BEGIN_SRC emacs-lisp (use-package json-reformat :ensure t :custom (json-reformat:indent-width 1)) #+END_SRC Based on [[https://emacs.cafe/emacs/guest-post/2017/06/26/hierarchy.html][hierarchy.el]]: #+BEGIN_SRC emacs-lisp (use-package tree-mode :ensure t) (use-package json-navigator :ensure t) #+END_SRC ** Rust #+BEGIN_SRC emacs-lisp (use-package rust-mode :ensure t :defer t) #+END_SRC ** Markdown #+BEGIN_SRC emacs-lisp (use-package markdown-mode :ensure t) #+END_SRC ** Protobuf #+BEGIN_SRC emacs-lisp (use-package protobuf-mode :ensure t) #+END_SRC ** Puppet #+BEGIN_SRC emacs-lisp (use-package puppet-mode :ensure t) #+END_SRC ** Yaml #+BEGIN_SRC emacs-lisp (use-package yaml-mode :ensure t) #+END_SRC ** Nginx #+BEGIN_SRC emacs-lisp (use-package nginx-mode :ensure t) #+END_SRC ** Lisp #+BEGIN_SRC emacs-lisp (use-package macrostep :ensure t :bind ("C-c e m" . macrostep-expand)) #+END_SRC #+BEGIN_SRC emacs-lisp (use-package elisp-def :ensure t :hook (emacs-lisp-mode . elisp-def-mode)) #+END_SRC ~package-lint~ to check files that are emacs libraries #+BEGIN_SRC emacs-lisp (use-package package-lint :ensure t) #+END_SRC ** Fish I use fish as a shell #+BEGIN_SRC emacs-lisp :tangle no (use-package fish-completion :ensure t) (when (and (executable-find "fish") (require 'fish-completion nil t)) (global-fish-completion-mode)) #+END_SRC #+BEGIN_SRC emacs-lisp (use-package fish-mode :ensure t) #+END_SRC * LSP #+begin_src emacs-lisp (defun my/yarn-bash-lsp-register () "Register a lsp server for bash installed globally with yarn. This functions must be called only after lsp-mode has been loaded." (lsp-register-client (make-lsp-client :new-connection (lsp-stdio-connection '("yarn-global.sh" "bash-language-server" "start")) :major-modes '(sh-mode) :server-id 'B))) (use-package helm-lsp :ensure t) (use-package lsp-ui ;; :load-path "/home/louis/Code/github/lsp-ui" :ensure t :custom (lsp-ui-doc-enable t) (lsp-ui-doc-position 'at-point) (lsp-ui-sideline-enable nil) (lsp-ui-peek-peek-height 5)) (use-package company-lsp :ensure t :custom (company-lsp-cache-candidates nil) :config (push 'company-lsp company-backends)) (use-package lsp-mode :ensure t :custom (lsp-log-max 100000) (lsp-eldoc-render-all nil) (lsp-eldoc-enable-hover nil) (lsp-enable-snippet nil) :config (my/merlin-lsp-register) (my/yarn-bash-lsp-register) :bind (:map lsp-ui-mode-map ([remap xref-find-references] . lsp-ui-peek-find-references) :map lsp-mode-map ("C-c r" . lsp-rename)) :hook (sh-mode . lsp) ;; (python-mode . lsp) ;; (tuareg-mode . my/merlin-lsp) ) #+end_src * Whitespace #+BEGIN_SRC emacs-lisp (use-package whitespace :ensure t :delight whitespace-mode :custom (whitespace-line-column nil) (whitespace-style '(face empty tabs lines-tail trailing)) ;; When it is enabled globally, there is a problem with magit ;; :config ;; (global-whitespace-mode t) :hook (prog-mode . whitespace-mode)) #+END_SRC * Editorconfig Same indentation rules everywhere. #+BEGIN_SRC emacs-lisp (use-package editorconfig :delight :ensure t :config (editorconfig-mode t)) #+END_SRC * Indentation #+BEGIN_SRC emacs-lisp (use-package aggressive-indent :ensure t ;; :config ;; (global-aggressive-indent-mode 1) ;; (dolist (mode '(tuareg-mode reason-mode)) ;; (add-to-list 'aggressive-indent-excluded-modes mode)) ) #+END_SRC * Git Number of columns in a git commit message #+BEGIN_SRC emacs-lisp (setq git-commit-summary-max-length 72) #+END_SRC ** Magit A great interface for git projects. It's much more pleasant to use than the git interface on the command line. Use an easy keybinding to access magit. #+BEGIN_SRC emacs-lisp (use-package magit :ensure t :custom (magit-display-buffer-function #'magit-display-buffer-fullframe-status-v1) (magit-log-margin '(t "%Y-%m-%d %H:%M " magit-log-margin-width t 18))) #+END_SRC ** Forge #+BEGIN_SRC emacs-lisp (use-package forge :ensure t :after magit :config (add-to-list 'forge-alist '("git.ahrefs.com" "git.ahrefs.com/api" "git.ahrefs.com" forge-github-repository))) #+END_SRC ** git links For magit buffers https://github.com/magit/orgit #+BEGIN_SRC emacs-lisp (use-package orgit :ensure t) #+END_SRC All git links https://orgmode.org/worg/org-contrib/org-git-link.html #+BEGIN_SRC emacs-lisp (use-package org-git-link) #+END_SRC ** Git gutter ~git-gutter~ is explicitely installed because it can't be diminished from ~git-gutter-fringe~. #+BEGIN_SRC emacs-lisp (use-package git-gutter :ensure t :delight) (use-package git-gutter-fringe :ensure t :custom (git-gutter-fr:side 'right-fringe) :config (set-face-foreground 'git-gutter-fr:modified "yellow") (set-face-foreground 'git-gutter-fr:added "blue") (set-face-foreground 'git-gutter-fr:deleted "white") (global-git-gutter-mode)) #+END_SRC ** Github *** git-link There are plenty of alternatives. This one seems to work, so why bother with other packages? #+BEGIN_SRC emacs-lisp (use-package git-link :ensure t :custom (git-link-use-commit 't) :config (add-to-list 'git-link-remote-alist '("git\\.ahrefs\\.com" git-link-github))) #+END_SRC ** Nice blame #+begin_src emacs-lisp (use-package vc-msg :ensure t) #+end_src * Which function #+begin_src emacs-lisp (use-package which-func :commands which-function-mode :init (which-function-mode t)) #+end_src * Helm https://github.com/emacs-helm/helm/wiki/Fuzzy-matching #+BEGIN_SRC emacs-lisp (use-package helm :ensure t :delight helm-mode :config (helm-mode t) :custom (helm-mode-fuzzy-match t) (helm-completion-in-region-fuzzy-match t) (helm-M-x-fuzzy-match t) (helm-recentf-fuzzy-match t) (helm-ff-fuzzy-matching t) (helm-buffers-fuzzy-matching t) :bind (("C-c h" . helm-command-prefix) ("C-x b" . helm-mini) ("C-`" . helm-resume) ("M-x" . helm-M-x) ("M-y" . helm-show-kill-ring) ("C-x C-f" . helm-find-files) ("M-n" . helm-imenu-in-all-buffers))) #+END_SRC #+BEGIN_SRC emacs-lisp (use-package helm-projectile :ensure t :config (helm-projectile-on)) #+END_SRC #+BEGIN_SRC emacs-lisp (use-package helm-swoop :ensure t :bind ("C-S-s" . helm-swoop) ("M-i" . helm-multi-swoop-projectile) ("M-I" . helm-swoop-back-to-last-point)) #+END_SRC #+BEGIN_SRC emacs-lisp :tangle no (use-package helm-ag :ensure t) #+END_SRC #+BEGIN_SRC emacs-lisp :tangle no (use-package org-projectile-helm :ensure t) #+END_SRC * Restclient See [[http://emacsrocks.com/e15.html][Emacs Rocks! Episode 15]] to learn how restclient can help out with testing APIs from within Emacs. The HTTP calls you make in the buffer aren't constrainted within Emacs; there's the =restclient-copy-curl-command= to get the equivalent =curl= call string to keep things portable. #+BEGIN_SRC emacs-lisp (use-package restclient :ensure t :mode ("\\.restclient\\'" . restclient-mode)) #+END_SRC * Elasticsearch It is convenient to be able to query elasticsearch from emacs. It can be done from restclient or ob-html. But ~es-mode~ comes with some nice things like tables when using ~ob-elasticsearch~. #+BEGIN_SRC emacs-lisp (use-package es-mode :ensure t) #+END_SRC * Org mode ** Installation Although Org mode ships with Emacs, the latest version can be installed externally. The configuration here follows the [[http://orgmode.org/elpa.html][Org mode ELPA installation instructions]]. #+BEGIN_SRC emacs-lisp (use-package org :pin "org" :ensure org-plus-contrib :bind (("C-c l" . org-store-link) ("C-c c" . org-capture) ("C-c a" . org-agenda))) #+END_SRC On Org mode version 9 I wasn't able to execute source blocks out of the box. [[https://emacs.stackexchange.com/a/28604][Others have ran into the same issue too]]. The solution is to remove the .elc files from the package directory: #+BEGIN_SRC sh :var ORG_DIR=(let* ((org-v (cadr (split-string (org-version nil t) "@"))) (len (length org-v))) (substring org-v 1 (- len 2))) echo "removing $(ls -1 ${ORG_DIR}/*.elc | wc -l) files from ${ORG_DIR}" rm -f ${ORG_DIR}/*.elc #+END_SRC ** Org extentions installation Enable =org-tempo= to enable ==\"" (my/date-n-days-ago 1) "\"")) (todo "IN-PROGRESS") (tags-todo "+TODO=\"TODO\"+@URGENT") (tags-todo (concat "+DEADLINE<=\"" (my/date-n-days-ago 0) "\"")) (tags-todo (concat "+DEADLINE>\"" (my/date-n-days-ago 0) "\"")) (tags-todo "@inbox") ) "yesterday") ("we" "Weekend" ((tags (concat "+TODO=\"DONE\"+CLOSED>=\"" (my/date-n-days-ago 3) "\"")) (todo "IN-PROGRESS") (tags-todo "+@URGENT+TODO=\"TODO\"") (tags-todo (concat "+DEADLINE<=\"" (my/date-n-days-ago 0) "\"")) (tags-todo (concat "+DEADLINE>\"" (my/date-n-days-ago 0) "\"")) (tags-todo "@inbox") ) "last 3 days") ("1" "ongoing work" tags-tree (concat "+TODO=\"DONE\"+CLOSED>=\"" (my/date-n-days-ago 1) "\"" "|+TODO=\"IN-PROGRESS\"" "|+@URGENT+TODO=\"TODO\"" )) )) #+END_SRC Some config #+BEGIN_SRC emacs-lisp (setq org-refile-targets (quote (("ahrefs.org" :tag . "done") ("ahrefs.org" :regexp . "Tasks")))) (setq org-outline-path-complete-in-steps nil) ; Refile in a single go (setq org-refile-use-outline-path t) ; Show full paths for refiling (setq org-agenda-tags-column -100) ; take advantage of the screen width (setq org-capture-templates '(("a" "Todo [inbox]" entry (file+headline "~/Notes/ahrefs.org" "Inbox") "** TODO %i%?") ("t" "Deadline [inbox]" entry (file+headline "~/Notes/ahrefs.org" "Inbox") "** TODO %i%? %^g\n DEADLINE:%^{Deadline}t") )) ;; Place tags close to the right-hand side of the window (add-hook 'org-finalize-agenda-hook 'place-agenda-tags) (defun place-agenda-tags () "Put the agenda tags by the right border of the agenda window." (setq org-agenda-tags-column (- 4 (window-width))) (org-agenda-align-tags)) #+END_SRC ** Org setup Speed commands are a nice and quick way to perform certain actions while at the beginning of a heading. It's not activated by default. See the doc for speed keys by checking out [[elisp:(info%20"(org)%20speed%20keys")][the documentation for speed keys in Org mode]]. #+BEGIN_SRC emacs-lisp (setq org-use-speed-commands t) #+END_SRC #+BEGIN_SRC emacs-lisp (setq org-image-actual-width 550) #+END_SRC #+BEGIN_SRC emacs-lisp (setq org-highlight-latex-and-related '(latex script entities)) #+END_SRC Disable =validate= link at the end of html export #+BEGIN_SRC emacs-lisp (setq org-html-validation-link nil) #+END_SRC #+BEGIN_SRC emacs-lisp (setq org-startup-folded 'fold) #+END_SRC #+BEGIN_SRC emacs-lisp (setq org-html-htmlize-output-type 'css) #+END_SRC #+BEGIN_SRC emacs-lisp (setq org-adapt-indentation nil) #+END_SRC ** Org babel languages #+BEGIN_SRC emacs-lisp (org-babel-do-load-languages 'org-babel-load-languages '((python . t) (C . t) (calc . t) (latex . t) (java . t) (ruby . t) (lisp . t) (scheme . t) (shell . t) (sqlite . t) (js . t) (http . t) (restclient . t) (elasticsearch . t))) #+END_SRC It is possible to skip the confimation evaluation for a set of languages. Not enabled. #+BEGIN_SRC emacs-lisp :tangle no (defun my-org-confirm-babel-evaluate (lang body) "Do not confirm evaluation for these languages." (not (or (string= lang "C") (string= lang "java") (string= lang "python") (string= lang "emacs-lisp") (string= lang "bash") (string= lang "sh") (string= lang "sqlite")))) (setq org-confirm-babel-evaluate #'my-org-confirm-babel-evaluate) #+END_SRC ** Org babel/source blocks I like to have source blocks properly syntax highlighted and with the editing popup window staying within the same window so all the windows don't jump around. Also, having the top and bottom trailing lines in the block is a waste of space, so we can remove them. I noticed that fontification doesn't work with markdown mode when the block is indented after editing it in the org src buffer---the leading =#s= for headers don't get fontified properly because they appear as Org comments. Setting ~org-src-preserve-indentation~ makes things consistent as it doesn't pad source blocks with leading spaces. #+BEGIN_SRC emacs-lisp (setq org-src-fontify-natively t org-src-window-setup 'current-window org-src-strip-leading-and-trailing-blank-lines t org-src-preserve-indentation t org-src-tab-acts-natively t) #+END_SRC ** Org publish #+BEGIN_SRC emacs-lisp (setq org-publish-project-alist '(("posts-org" :base-directory "~/Notes/posts" :base-extension "org" :publishing-directory "~/Notes/publish" :publishing-function org-html-publish-to-html :htmlized-source t :html-doctype "html5" :html-head-include-default-style nil :html-html5-fancy t :html-htmlized-css-url "/org.css" ; from https://github.com/gongzhitaao/orgcss :section-numbers nil :auto-sitemap t :html-postamble "%a %d %c" :html-link-home "archive.html" :sitemap-filename "archive.org" :sitemap-title "Archive" :sitemap-sort-files anti-chronologically :sitemap-style list :with-statistics-cookies nil) ("posts-static" :base-directory "~/Notes/posts/" :base-extension "css" :publishing-directory "~/Notes/publish/" :recursive t :publishing-function org-publish-attachment) ("posts" :components ("posts-org" "posts-static")))) #+END_SRC #+BEGIN_SRC emacs-lisp (add-to-list 'org-structure-template-alist '("b" . "#+TITLE: ? ,#+DATE: ? ,#+DESCRIPTION: ? ,#+KEYWORDS: ? ,#+LANGUAGE: en ,#+BEGIN_PREVIEW ,#+END_PREVIEW")) #+END_SRC * Iedit #+BEGIN_SRC emacs-lisp (use-package iedit :ensure t) #+END_SRC * Multiple cursors #+BEGIN_SRC emacs-lisp (use-package multiple-cursors :ensure t :bind ("C-<" . mc/mark-previous-like-this) ("C->" . mc/mark-next-like-this)) #+END_SRC * Linum ** Emacs 26 #+begin_src emacs-lisp :tangle no (use-package display-line-numbers :if (version<= "26.0" emacs-version) :config (global-display-line-numbers-mode) (defcustom display-line-numbers-disabled-modes-list '(eshell-mode wl-summary-mode compilation-mode org-mode text-mode dired-mode doc-view-mode) "List of modes disabled when global display-line-numbers mode is on" :type '(repeat (sexp :tag "Major mode")) :tag " Major modes where display-line-numbers is disabled: " :group 'display-line-numbers) (defcustom display-line-numbers-disable-starred-buffers 't "Disable buffers that have stars in them like *Gnu Emacs*" :type 'boolean :group 'display-line-numbers) (defun display-line-numbers--turn-on () "When display-line-numbers is running globally, disable line number in modes defined in `display-line-numbers-disabled-modes-list'. Also turns off numbering in starred modes like *scratch*" (unless (or (minibufferp) (and (daemonp) (null (frame-parameter nil 'client))) ;; additions to original `display-line-numbers--turn-on' (member major-mode display-line-numbers-disabled-modes-list) (and display-line-numbers-disable-starred-buffers (string-match "*" (buffer-name)))) (display-line-numbers-mode)))) #+end_src ** Emacs 25 It's seems to be a challenge to display line numbers. For performances reasons, people say to use ~nlinum~. But I can't do customization as with the normal linum mode. #+BEGIN_SRC emacs-lisp (use-package linum :if (version< emacs-version "26.0") :custom (linum-format " %2d") ; numbers in the line gutter don't touch the left :config (global-linum-mode) (defcustom linum-disabled-modes-list '(eshell-mode wl-summary-mode compilation-mode org-mode text-mode dired-mode doc-view-mode) "* List of modes disabled when global linum mode is on" :type '(repeat (sexp :tag "Major mode")) :tag " Major modes where linum is disabled: " :group 'linum ) (defcustom linum-disable-starred-buffers 't "* Disable buffers that have stars in them like *Gnu Emacs*" :type 'boolean :group 'linum) (defun linum-on () "* When linum is running globally, disable line number in modes defined in `linum-disabled-modes-list'. Changed by linum-off. Also turns off numbering in starred modes like *scratch*" (unless (or (minibufferp) (member major-mode linum-disabled-modes-list) (and linum-disable-starred-buffers (string-match "*" (buffer-name))) ) (linum-mode 1))) ) #+END_SRC #+BEGIN_SRC emacs-lisp :tangle no (use-package nlinum :ensure t) #+END_SRC #+BEGIN_SRC emacs-lisp :tangle no (use-package nlinum-relative :ensure t) #+END_SRC * Helpful #+BEGIN_SRC emacs-lisp (use-package helpful :ensure t :bind ("C-h f" . helpful-callable) ("C-h v" . helpful-variable) ("C-h k" . helpful-key)) #+END_SRC * Regexp I never took time to really use this... #+BEGIN_SRC emacs-lisp :tangle no (use-package visual-regexp :ensure t) #+END_SRC #+BEGIN_SRC emacs-lisp :tangle no (use-package visual-regexp-steroids :ensure t) #+END_SRC * Undo Tree #+BEGIN_SRC emacs-lisp (use-package undo-tree :ensure t :delight :config (global-undo-tree-mode)) #+END_SRC * Big files/lines #+BEGIN_SRC emacs-lisp (use-package vlf :ensure t) #+END_SRC * Scratch Convenient package to create =*scratch*= buffers that are based on the current buffer's major mode. This is more convienent than manually creating a buffer to do some scratch work or reusing the initial =*scratch*= buffer. #+BEGIN_SRC emacs-lisp (use-package scratch :ensure t :commands scratch) #+END_SRC * GDB #+BEGIN_SRC emacs-lisp (setq-default gdb-many-windows t) #+END_SRC * Frame/windows management ** Winner Keep an history of window positions and jump back to any previous configuration. #+BEGIN_SRC emacs-lisp (use-package winner :ensure t :config (winner-mode)) #+END_SRC ** Resize Convenient keybindings to resize windows. #+BEGIN_SRC emacs-lisp (bind-key "M-S-C-" #'shrink-window-horizontally) (bind-key "M-S-C-" #'enlarge-window-horizontally) (bind-key "M-S-C-" #'shrink-window) (bind-key "M-S-C-" #'enlarge-window) #+END_SRC ** Windmove Bind arrow keys to move between windows: #+BEGIN_SRC emacs-lisp :tangle no (bind-key "M-" #'windmove-up) (bind-key "M-" #'windmove-down) (bind-key "M-" #'windmove-right) (bind-key "M-" #'windmove-left) #+END_SRC It could be done like this too: #+BEGIN_SRC emacs-lisp :tangle no (use-package windmove :config (windmove-default-keybindings 'ctrl)) #+END_SRC Combine windmove with framemove #+BEGIN_SRC emacs-lisp :tangle no (use-package framemove :quelpa ((framemove :fetcher github :repo "emacsmirror/framemove")) :custom (framemove-hook-into-windmove t)) #+END_SRC ** Split Whenever I split windows, I usually do so and also switch to the other window as well, so might as well rebind the splitting key bindings to do just that to reduce the repetition. #+BEGIN_SRC emacs-lisp (defun vsplit-other-window () "Splits the window vertically and switches to that window." (interactive) (split-window-vertically) (other-window 1 nil)) (defun hsplit-other-window () "Splits the window horizontally and switches to that window." (interactive) (split-window-horizontally) (other-window 1 nil)) (bind-key "C-x 2" #'vsplit-other-window) (bind-key "C-x 3" #'hsplit-other-window) #+END_SRC Dedicated windows. #+BEGIN_SRC emacs-lisp ;; https://stackoverflow.com/questions/5151620/how-do-i-make-this-emacs-frame-keep-its-buffer-and-not-get-resized ;; Toggle window dedication (defun toggle-window-dedicated () "Toggle whether the current active window is dedicated or not" (interactive) (message (if (let (window (get-buffer-window (current-buffer))) (set-window-dedicated-p window (not (window-dedicated-p window)))) "Window '%s' is dedicated" "Window '%s' is normal") (current-buffer))) (define-minor-mode sticky-buffer-mode "Make the current window always display this buffer." nil " sticky" nil (set-window-dedicated-p (selected-window) sticky-buffer-mode)) (bind-key "C-c C-'" #'toggle-window-dedicated) #+END_SRC ** Ace window #+BEGIN_SRC emacs-lisp (use-package ace-window :ensure t :bind ("M-`" . ace-window) ("M-/" . aw-flip-window)) #+END_SRC * Tramp #+BEGIN_SRC emacs-lisp :tangle no (use-package tramp) #+END_SRC * Deadgrep #+BEGIN_SRC emacs-lisp (use-package deadgrep :ensure t :bind ("C-c g" . deadgrep)) #+END_SRC * SX – Stack Exchange #+BEGIN_SRC emacs-lisp (use-package sx :bind (:prefix "C-c e" :prefix-map my-sx-map :prefix-docstring "Global keymap for SX." ("q" . sx-tab-all-questions) ("i" . sx-inbox) ("o" . sx-open-link) ("u" . sx-tab-unanswered-my-tags) ("a" . sx-ask) ("s" . sx-search))) #+END_SRC * Utils #+BEGIN_SRC emacs-lisp (defun close-all-buffers () (interactive) (mapc 'kill-buffer (buffer-list))) (defun kill-region-or-word () "Call `kill-region' or `backward-kill-word' depending on whether or not a region is selected." (interactive) (if (and transient-mark-mode mark-active) (kill-region (point) (mark)) (backward-kill-word 1))) (defun buffer-same-mode (change-buffer-fun) (let ((current-mode major-mode) (next-mode nil)) (while (not (eq next-mode current-mode)) (funcall change-buffer-fun) (setq next-mode major-mode)))) (defun previous-buffer-same-mode () (interactive) (buffer-same-mode #'previous-buffer)) (defun next-buffer-same-mode () (interactive) (buffer-same-mode #'next-buffer)) #+END_SRC * Kill ring Interactively explore the kill ring. Display the kill ring in a small popup. #+BEGIN_SRC emacs-lisp (use-package popup-kill-ring :ensure t) #+END_SRC Display kill ring in another window and allow to search a pattern. #+BEGIN_SRC emacs-lisp (use-package browse-kill-ring :ensure t) #+END_SRC * Screencast Sometimes it is convenient to record a video of emacs. #+BEGIN_SRC emacs-lisp (use-package camcorder :ensure t) #+END_SRC * Emacsclient I want to be able to have multiple instances of emacs server running at the same time. To launch emacs client and specify the server name: ~emacsclient -s $EMACS_SERVER_NAME -c -n -a "" $@~. #+BEGIN_SRC emacs-lisp (use-package server :config (let ((env-server-name (getenv "EMACS_SERVER_NAME"))) (when env-server-name (message "Replacing original server name %s with %s...done" server-name env-server-name) (setq server-name env-server-name))) (add-to-list 'default-frame-alist `(title . ,server-name)) (server-start)) #+END_SRC I always forget which server is in use. #+BEGIN_SRC emacs-lisp (defun server-name () "Display the name of the server" (interactive) (message "%s" server-name)) #+END_SRC * Key bindings #+BEGIN_SRC emacs-lisp (use-package which-key :ensure t :delight :config (which-key-mode)) #+END_SRC #+BEGIN_SRC emacs-lisp (bind-key "C-c j" #'replace-string) (bind-key "C-c x" #'close-all-buffers) (bind-key "C-c k" #'kill-this-buffer) (bind-key "C-w" #'kill-region-or-word) (bind-key "C-S-" #'previous-buffer-same-mode) (bind-key "C-" #'next-buffer-same-mode) #+END_SRC * Todolist - full frame mode https://github.com/tomterl/fullframe - annoying arrows https://github.com/magnars/annoying-arrows-mode.el - visual regexp https://github.com/benma/visual-regexp-steroids.el/ - whole line or region https://github.com/purcell/whole-line-or-region - realgud debugger https://github.com/realgud/realgud - devdocs lookup https://github.com/skeeto/devdocs-lookup (doesn't work for ocaml) - sqlformat https://github.com/purcell/sqlformat - wgrep.el https://github.com/mhayashi1120/Emacs-wgrep * Tests #+begin_src emacs-lisp :tangle no (require eglot) (use-package eglot :config (add-to-list 'eglot-server-programs '(tuareg-mode . ("opam" "exec" "ocaml-base-compiler.4.07.1" "--" "/home/louis/.opam/ocaml-base-compiler.4.07.1/bin/ocamlmerlin-lsp")))) (defun my-projectile-project-find-function (dir) (let ((root (projectile-project-root dir))) (and root (cons 'transient root)))) (projectile-mode t) (with-eval-after-load 'project (add-to-list 'project-find-functions 'my-projectile-project-find-function)) #+end_src