mirror of
https://github.com/larstvei/dot-emacs.git
synced 2024-11-26 07:28:31 +00:00
1871 lines
56 KiB
Org Mode
1871 lines
56 KiB
Org Mode
#+TITLE: Emacs configuration file
|
|
#+AUTHOR: Lars Tveito
|
|
#+PROPERTY: header-args :tangle yes
|
|
#+STARTUP: overview
|
|
|
|
* About
|
|
|
|
This is an Emacs configuration file written in [[http://orgmode.org][Org mode]]. It is an attempt to
|
|
keep my =~/.emacs.d= tidy, but still be able to keep it all in one file. I
|
|
aim to briefly explain all my configurations as I go along!
|
|
|
|
I would not recommend using this configuration /as-is/, because it probably
|
|
contains a lot you don't really need. I do, however, hope people find some
|
|
golden nuggets that they can smuggle into their own configs.
|
|
|
|
If you really do want to try this config out, this is how I'd go about it:
|
|
|
|
Clone the repo.
|
|
|
|
#+begin_src sh :tangle no
|
|
|
|
git clone https://github.com/larstvei/dot-emacs
|
|
|
|
#+end_src
|
|
|
|
Backup your old =~/.emacs.d= (if necessary).
|
|
|
|
#+begin_src sh :tangle no
|
|
|
|
mv ~/.emacs.d ~/.emacs.d-bak
|
|
|
|
#+end_src
|
|
|
|
Backup your old =~/.emacs=-file (if necessary).
|
|
|
|
#+begin_src sh :tangle no
|
|
|
|
mv ~/.emacs ~/.emacs-bak
|
|
|
|
#+end_src
|
|
|
|
And finally
|
|
|
|
#+begin_src sh :tangle no
|
|
|
|
mv dot-emacs ~/.emacs.d
|
|
|
|
#+end_src
|
|
|
|
On first run it should install a bunch of packages (this might take a while),
|
|
and you might have to restart your Emacs the first time. If you experience
|
|
bugs, please let me know!
|
|
|
|
* Meta
|
|
|
|
All changes to the configuration should be done in =init.org=, *not* in =init.el=. Any changes in the =init.el= will be overwritten by saving =init.org=. The =init.el= in this repo should not be tracked by git, and is
|
|
replaced the first time Emacs is started (assuming it has been renamed to =~/.emacs.d=).
|
|
|
|
Emacs can't load =.org=-files directly, but =org-mode= provides functions to
|
|
extract the code blocks and write them to a file. There are multiple ways of
|
|
handling this; like suggested by [[http://emacs.stackexchange.com/questions/3143/can-i-use-org-mode-to-structure-my-emacs-or-other-el-configuration-file][this StackOverflow post]], one could just use =org-babel-load-file=, but I had problems with byte-compilation. Previously I
|
|
tracked both the =org.=- and =el.=-files, but the git commits got a little
|
|
messy. So here is a new approach.
|
|
|
|
When this configuration is loaded for the first time, the ~init.el~ is the
|
|
file that is loaded. It looks like this:
|
|
|
|
#+begin_src emacs-lisp :tangle no
|
|
|
|
;; This file replaces itself with the actual configuration at first run.
|
|
|
|
;; We can't tangle without org!
|
|
(require 'org)
|
|
;; Open the configuration
|
|
(find-file (concat user-emacs-directory "init.org"))
|
|
;; tangle it
|
|
(org-babel-tangle)
|
|
;; load it
|
|
(load-file (concat user-emacs-directory "init.el"))
|
|
;; finally byte-compile it
|
|
(byte-compile-file (concat user-emacs-directory "init.el"))
|
|
|
|
#+end_src
|
|
|
|
It tangles the org-file, so that this file is overwritten with the actual
|
|
configuration.
|
|
|
|
There is no reason to track the =init.el= that is generated; by running the
|
|
following command =git= will not bother tracking it:
|
|
|
|
#+begin_src sh :tangle no
|
|
|
|
git update-index --assume-unchanged init.el
|
|
|
|
#+end_src
|
|
|
|
If one wishes to make changes to the repo-version of =init.el= start tracking
|
|
again with:
|
|
|
|
#+begin_src sh :tangle no
|
|
|
|
git update-index --no-assume-unchanged init.el
|
|
|
|
#+end_src
|
|
|
|
I want lexical scoping for the init-file, which can be specified in the
|
|
header. The first line of the configuration is as follows:
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
;;; -*- lexical-binding: t -*-
|
|
|
|
#+end_src
|
|
|
|
The =init.el= should (after the first run) mirror the source blocks in the =init.org=. We can use =C-c C-v t= to run =org-babel-tangle=, which extracts
|
|
the code blocks from the current file into a source-specific file (in this
|
|
case a =.el=-file).
|
|
|
|
To avoid doing this each time a change is made we can add a function to the =after-save-hook= ensuring to always tangle and byte-compile the =org=-document after changes.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(defun tangle-init ()
|
|
"If the current buffer is init.org the code-blocks are
|
|
tangled, and the tangled file is compiled."
|
|
(when (equal (buffer-file-name)
|
|
(expand-file-name (concat user-emacs-directory "init.org")))
|
|
;; Avoid running hooks when tangling.
|
|
(let ((prog-mode-hook nil))
|
|
(org-babel-tangle)
|
|
(byte-compile-file (concat user-emacs-directory "init.el")))))
|
|
|
|
(add-hook 'after-save-hook 'tangle-init)
|
|
|
|
#+end_src
|
|
|
|
I'd like to keep a few settings private, so we load a =private.el= if it
|
|
exists after the init-file has loaded.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(add-hook
|
|
'after-init-hook
|
|
(lambda ()
|
|
(let ((private-file (concat user-emacs-directory "private.el")))
|
|
(when (file-exists-p private-file)
|
|
(load-file private-file))
|
|
(when custom-file
|
|
(load-file custom-file))
|
|
(server-start))))
|
|
|
|
#+end_src
|
|
|
|
A common optimization is to temporarily disable garbage collection during
|
|
initialization. Here, we set the ~gc-cons-threshold~ to a ridiculously large
|
|
number, and restore the default value after initialization.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(let ((old-gc-treshold gc-cons-threshold))
|
|
(setq gc-cons-threshold most-positive-fixnum)
|
|
(add-hook 'after-init-hook
|
|
(lambda () (setq gc-cons-threshold old-gc-treshold))))
|
|
|
|
#+end_src
|
|
|
|
* Packages
|
|
|
|
John Wiegley's extremely popular [[https://github.com/jwiegley/use-package][use-package]] was included in [[https://lists.gnu.org/archive/html/emacs-devel/2022-12/msg00261.html][Emacs 29]]. It
|
|
provides a powerful macro for isolating package configuration. After ignoring
|
|
this for a decade, I'll budge and give it a whirl.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(require 'use-package)
|
|
(setq use-package-always-ensure t)
|
|
|
|
#+end_src
|
|
|
|
Packages can be fetched from different mirrors, [[http://melpa.milkbox.net/#/][melpa]] is the largest archive
|
|
and is well maintained.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(setq package-archives
|
|
'(("GNU ELPA" . "https://elpa.gnu.org/packages/")
|
|
("MELPA Stable" . "https://stable.melpa.org/packages/")
|
|
("MELPA" . "https://melpa.org/packages/"))
|
|
package-archive-priorities
|
|
'(("GNU ELPA" . 10)
|
|
("MELPA" . 5)
|
|
("MELPA Stable" . 0)))
|
|
|
|
#+end_src
|
|
|
|
The configuration assumes that the packages listed below are installed. To
|
|
ensure we install missing packages if they are missing.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(let* ((package--builtins nil)
|
|
(packages
|
|
'(slime ; Superior Lisp Interaction Mode for Emacs
|
|
try ; Try out Emacs packages
|
|
vterm ; A terminal via libvterm
|
|
which-key ; Display available keybindings in popup
|
|
z3-mode))) ; z3/SMTLIBv2 interactive development
|
|
(when (memq window-system '(mac ns))
|
|
(push 'exec-path-from-shell packages)
|
|
(push 'reveal-in-osx-finder packages))
|
|
(let ((packages (seq-remove 'package-installed-p packages)))
|
|
(when packages
|
|
;; Install uninstalled packages
|
|
(package-refresh-contents)
|
|
(mapc 'package-install packages))))
|
|
|
|
#+end_src
|
|
|
|
* Mac OS X
|
|
|
|
I run this configuration mostly on Mac OS X, so we need a couple of settings
|
|
to make things work smoothly. In the package section =exec-path-from-shell=
|
|
is included (only if you're running OS X), this is to include
|
|
environment-variables from the shell. It makes using Emacs along with
|
|
external processes a lot simpler. I also prefer using the =Command=-key as
|
|
the =Meta=-key.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(when (memq window-system '(mac ns))
|
|
(setq ns-pop-up-frames nil
|
|
mac-option-modifier nil
|
|
mac-command-modifier 'meta
|
|
native-comp-async-report-warnings-errors nil)
|
|
(exec-path-from-shell-initialize)
|
|
(when (fboundp 'mac-auto-operator-composition-mode)
|
|
(mac-auto-operator-composition-mode 1))
|
|
(require 'ls-lisp)
|
|
(setq ls-lisp-use-insert-directory-program nil))
|
|
|
|
#+end_src
|
|
|
|
* Sane defaults
|
|
|
|
These are what /I/ consider to be saner defaults.
|
|
|
|
We can set variables to whatever value we'd like using =setq=.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(setq auto-revert-interval 1 ; Refresh buffers fast
|
|
default-input-method "TeX" ; Use TeX when toggling input method
|
|
echo-keystrokes 0.1 ; Show keystrokes asap
|
|
frame-inhibit-implied-resize 1 ; Don't resize frame implicitly
|
|
inhibit-startup-screen t ; No splash screen please
|
|
initial-scratch-message nil ; Clean scratch buffer
|
|
recentf-max-saved-items 10000 ; Show more recent files
|
|
ring-bell-function 'ignore ; Quiet
|
|
scroll-margin 1 ; Space between cursor and top/bottom
|
|
sentence-end-double-space nil ; No double space
|
|
custom-file ; Customizations in a separate file
|
|
(concat user-emacs-directory "custom.el"))
|
|
;; Some mac-bindings interfere with Emacs bindings.
|
|
(when (boundp 'mac-pass-command-to-system)
|
|
(setq mac-pass-command-to-system nil))
|
|
|
|
#+end_src
|
|
|
|
Some variables are buffer-local, so changing them using =setq= will only
|
|
change them in a single buffer. Using =setq-default= we change the
|
|
buffer-local variable's default value.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(setq-default tab-width 4 ; Smaller tabs
|
|
fill-column 79 ; Maximum line width
|
|
truncate-lines t ; Don't fold lines
|
|
indent-tabs-mode nil ; Use spaces instead of tabs
|
|
split-width-threshold 160 ; Split verticly by default
|
|
split-height-threshold nil ; Split verticly by default
|
|
frame-resize-pixelwise t ; Fine-grained frame resize
|
|
auto-fill-function 'do-auto-fill) ; Auto-fill-mode everywhere
|
|
|
|
#+end_src
|
|
|
|
The =load-path= specifies where Emacs should look for =.el=-files (or
|
|
Emacs lisp files). I have a directory called =site-lisp= where I keep all
|
|
extensions that have been installed manually (these are mostly my own
|
|
projects).
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(let ((default-directory (concat user-emacs-directory "site-lisp/")))
|
|
(when (file-exists-p default-directory)
|
|
(setq load-path
|
|
(append
|
|
(let ((load-path (copy-sequence load-path)))
|
|
(normal-top-level-add-subdirs-to-load-path)) load-path))))
|
|
|
|
#+end_src
|
|
|
|
Answering /yes/ and /no/ to each question from Emacs can be tedious, a single /y/ or /n/ will suffice.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(fset 'yes-or-no-p 'y-or-n-p)
|
|
|
|
#+end_src
|
|
|
|
To avoid file system clutter we put all auto saved files in a single
|
|
directory.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(defvar emacs-autosave-directory
|
|
(concat user-emacs-directory "autosaves/")
|
|
"This variable dictates where to put auto saves. It is set to a
|
|
directory called autosaves located wherever your .emacs.d/ is
|
|
located.")
|
|
|
|
;; Sets all files to be backed up and auto saved in a single directory.
|
|
(setq backup-directory-alist
|
|
`((".*" . ,emacs-autosave-directory))
|
|
auto-save-file-name-transforms
|
|
`((".*" ,emacs-autosave-directory t)))
|
|
|
|
#+end_src
|
|
|
|
Set =utf-8= as preferred coding system.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(set-language-environment "UTF-8")
|
|
|
|
#+end_src
|
|
|
|
By default the =narrow-to-region= command is disabled and issues a
|
|
warning, because it might confuse new users. I find it useful sometimes,
|
|
and don't want to be warned.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(put 'narrow-to-region 'disabled nil)
|
|
|
|
#+end_src
|
|
|
|
Automaticly revert =doc-view=-buffers when the file changes on disk.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(add-hook 'doc-view-mode-hook 'auto-revert-mode)
|
|
|
|
#+end_src
|
|
|
|
* Modes
|
|
|
|
There are some modes that are enabled by default that I don't find
|
|
particularly useful. We create a list of these modes, and disable all of
|
|
these.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(dolist (mode
|
|
'(tool-bar-mode ; No toolbars, more room for text
|
|
scroll-bar-mode ; No scroll bars either
|
|
blink-cursor-mode)) ; The blinking cursor gets old
|
|
(funcall mode 0))
|
|
|
|
#+end_src
|
|
|
|
Let's apply the same technique for enabling modes that are disabled by
|
|
default.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(dolist (mode
|
|
'(abbrev-mode ; E.g. sopl -> System.out.println
|
|
column-number-mode ; Show column number in mode line
|
|
delete-selection-mode ; Replace selected text
|
|
dirtrack-mode ; directory tracking in *shell*
|
|
global-so-long-mode ; Mitigate performance for long lines
|
|
recentf-mode ; Recently opened files
|
|
show-paren-mode ; Highlight matching parentheses
|
|
which-key-mode)) ; Available keybindings in popup
|
|
(funcall mode 1))
|
|
|
|
#+end_src
|
|
|
|
* Visual
|
|
|
|
I am using a lot from [[https://github.com/rougier/nano-emacs][rougier's N Λ N O Emacs]], starting with the theme.
|
|
|
|
** Theme
|
|
|
|
For the light theme, I keep the light background toned down a touch.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
;; N Λ N O theme
|
|
(use-package nano-theme
|
|
:init
|
|
(setq nano-light-background "#fafafa"
|
|
nano-light-highlight "#f5f7f8"))
|
|
|
|
#+end_src
|
|
|
|
The theme is set according to the system appearance (on macOS) if that is
|
|
available, defaulting to a light theme.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(defun load-nano-theme (variant)
|
|
(let ((theme (intern (concat "nano-" (symbol-name variant)))))
|
|
(load-theme theme t)))
|
|
|
|
(load-nano-theme (if (boundp 'ns-system-appearance) ns-system-appearance 'light))
|
|
|
|
#+end_src
|
|
|
|
Let's have Emacs change theme when the system appearance changes as well.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(when (boundp 'ns-system-appearance-change-functions)
|
|
(add-hook 'ns-system-appearance-change-functions 'load-nano-theme))
|
|
|
|
#+end_src
|
|
|
|
I want to be able to quickly switch between a light and a dark theme.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(defun cycle-themes ()
|
|
"Returns a function that lets you cycle your themes."
|
|
(let ((themes '(nano-light nano-dark)))
|
|
(lambda ()
|
|
(interactive)
|
|
;; Rotates the thme cycle and changes the current theme.
|
|
(let ((rotated (nconc (cdr themes) (list (car themes)))))
|
|
(load-theme (car (setq themes rotated)) t))
|
|
(message (concat "Switched to " (symbol-name (car themes)))))))
|
|
|
|
#+end_src
|
|
|
|
** Mode line
|
|
|
|
This is my setup for [[https://github.com/rougier/nano-modeline][N Λ N O Modeline]] after version 1.0.0:
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
;; N Λ N O modeline
|
|
(use-package nano-modeline
|
|
:hook ((prog-mode . nano-modeline-prog-mode)
|
|
(text-mode . nano-modeline-text-mode)
|
|
(org-mode . nano-modeline-org-mode)
|
|
(pdf-view-mode . nano-modeline-pdf-mode)
|
|
(mu4e-headers-mode . nano-modeline-mu4e-headers-mode)
|
|
(mu4e-view-mode . nano-modeline-mu4e-message-mode)
|
|
(elfeed-show-mode . nano-modeline-elfeed-entry-mode)
|
|
(elfeed-search-mode . nano-modeline-elfeed-search-mode)
|
|
(term-mode . nano-modeline-term-mode)
|
|
(xwidget-webkit-mode . nano-modeline-xwidget-mode)
|
|
(messages-buffer-mode . nano-modeline-message-mode)
|
|
(org-capture-mode . nano-modeline-org-capture-mode)
|
|
(org-agenda-mode . nano-modeline-org-agenda-mode))
|
|
|
|
;; Disable the default modeline and add a small margin around the frame
|
|
:init
|
|
(setq-default mode-line-format nil)
|
|
(add-to-list 'default-frame-alist '(internal-border-width . 24))
|
|
;; Set nano-modeline-text-mode as default
|
|
:config
|
|
(nano-modeline-text-mode 1))
|
|
|
|
#+end_src
|
|
|
|
** Font
|
|
|
|
Pick the first of the following fonts that is installed on the system.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(cond ((member "Source Code Pro" (font-family-list))
|
|
(set-face-attribute 'default nil :font "Source Code Pro-15"))
|
|
((member "Roboto Mono" (font-family-list))
|
|
(set-face-attribute 'default nil :font "Roboto Mono-14"))
|
|
((member "Fira Code" (font-family-list))
|
|
(set-face-attribute 'default nil :font "Fira Code-15"))
|
|
((member "Inconsolata" (font-family-list))
|
|
(set-face-attribute 'default nil :font "Inconsolata-14")))
|
|
|
|
#+end_src
|
|
|
|
New in Emacs 24.4 is the =prettify-symbols-mode=! It's neat.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(setq-default prettify-symbols-alist '(("lambda" . ?λ)
|
|
("delta" . ?Δ)
|
|
("gamma" . ?Γ)
|
|
("phi" . ?φ)
|
|
("psi" . ?ψ)))
|
|
|
|
#+end_src
|
|
|
|
** Centering with Olivetti
|
|
|
|
[[https://github.com/rnkn/olivetti][Olivetti]] is a package that simply centers the text of a buffer. It is very
|
|
simple and beautiful. The default width is just a bit short.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
;; Minor mode for a nice writing environment
|
|
(use-package olivetti
|
|
:defer t
|
|
:bind ("C-c o" . olivetti-mode)
|
|
:config
|
|
(setq-default olivetti-body-width (+ fill-column 3))
|
|
(remove-hook 'olivetti-mode-on-hook 'visual-line-mode))
|
|
|
|
#+end_src
|
|
|
|
** Focusing with focus
|
|
|
|
[[https://github.com/larstvei/Focus][Focus]] is my own package. It looks pretty nice, especially in combination
|
|
with Olivetti!
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
;; Dim color of text in surrounding sections
|
|
(use-package focus
|
|
:defer t
|
|
:bind ("C-c q" .
|
|
(lambda ()
|
|
(interactive)
|
|
(focus-mode 1)
|
|
(focus-read-only-mode 1))))
|
|
|
|
#+end_src
|
|
|
|
* Version control
|
|
|
|
Magit is the best.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
;; A Git porcelain inside Emacs.
|
|
(use-package magit
|
|
:bind ("C-c m" . magit-status))
|
|
|
|
#+end_src
|
|
|
|
Have some visual indication where there are uncommitted changes.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
;; Highlight uncommitted changes using VC
|
|
(use-package diff-hl
|
|
:config
|
|
(global-diff-hl-mode 1))
|
|
|
|
#+end_src
|
|
|
|
* Dashboard
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
;; A startup screen extracted from Spacemacs
|
|
(use-package dashboard
|
|
:config
|
|
(setq dashboard-banner-logo-title nil
|
|
dashboard-center-content t
|
|
dashboard-set-footer nil
|
|
dashboard-page-separator "\n\n\n"
|
|
dashboard-items '((projects . 15)
|
|
(recents . 15)
|
|
(bookmarks . 5)))
|
|
(dashboard-setup-startup-hook))
|
|
|
|
#+end_src
|
|
|
|
* EditorConfig
|
|
|
|
Using [[https://editorconfig.org/][EditorConfig]] is a must when collaborating with others. It is also a way
|
|
of having multiple tools that want to format your buffer to agree (e.g. both
|
|
the language's Emacs mode and some external formatter/prettifier).
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
;; EditorConfig Emacs Plugin
|
|
(use-package editorconfig
|
|
:config
|
|
(editorconfig-mode 1))
|
|
|
|
#+end_src
|
|
|
|
* Projectile
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
;; Manage and navigate projects in Emacs easily
|
|
(use-package projectile
|
|
:bind ("C-c p" . projectile-command-map))
|
|
|
|
#+end_src
|
|
|
|
* Ivy/Counsel
|
|
|
|
[[http://oremacs.com/swiper/][Ivy]] is a completion system, giving you completions and fuzzy search whenever
|
|
you interact with the minibuffer. I transitioned to Ivy from [[https://emacs-helm.github.io/helm/][Helm]], mainly due
|
|
to it being aesthetically noisy, and that I didn't fully take advantage of
|
|
all its features (which are numerous). Here are some customization's that
|
|
made the transition a bit easier.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
;; Incremental Vertical completion
|
|
(use-package ivy
|
|
:bind ("C-x b" . ivy-switch-buffer)
|
|
:config
|
|
(setq ivy-wrap t ; Easier access to the last candidate
|
|
ivy-height 25 ; Give me more candidates to look at
|
|
ivy-use-virtual-buffers t ; C-x b displays recents and bookmarks
|
|
ivy-count-format "(%d/%d) " ; Display both the index and the count
|
|
ivy-on-del-error-function 'ignore ; Lets me hold in backspace
|
|
ivy-posframe-min-width 100 ; Keep ivy reasonably narrow
|
|
ivy-posframe-height ivy-height ; Maintain the height given by ivy
|
|
ivy-virtual-abbreviate 'abbreviate) ; Disambiguate same file in different dirs
|
|
(ivy-mode 1))
|
|
|
|
#+end_src
|
|
|
|
The completions are centered in a posframe (a frame at point).
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
;; Using posframe to show Ivy
|
|
(use-package ivy-posframe
|
|
:config
|
|
(ivy-posframe-mode 1))
|
|
|
|
#+end_src
|
|
|
|
Use counsel for =M-x=, yanking and finding files.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
;; Various completion functions using Ivy
|
|
(use-package counsel
|
|
:bind
|
|
(("M-x" . counsel-M-x)
|
|
("M-y" . counsel-yank-pop)
|
|
("C-x C-f" . counsel-find-file)))
|
|
|
|
#+end_src
|
|
|
|
Use swiper for fancy search.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
;; Isearch with an overview. Oh, man!
|
|
(use-package swiper
|
|
:bind ("C-c i" . swiper-isearch))
|
|
|
|
#+end_src
|
|
|
|
Have Ivy play with nice with Projectile.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
;; Ivy integration for Projectile
|
|
(use-package counsel-projectile
|
|
:config
|
|
(counsel-projectile-mode 1))
|
|
|
|
#+end_src
|
|
|
|
Use smex to prioritize frequently used commands.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
;; M-x interface with Ido-style fuzzy matching
|
|
(use-package smex)
|
|
|
|
#+end_src
|
|
|
|
* PDF Tools
|
|
|
|
[[https://github.com/vedang/pdf-tools][PDF Tools]] makes a huge improvement on the built-in [[http://www.gnu.org/software/emacs/manual/html_node/emacs/Document-View.html][doc-view-mode]]! Removing
|
|
the =header-line-format= gives a very clean PDF-viewer; let's add that to a
|
|
key.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
;; Emacs support library for PDF files
|
|
(use-package pdf-tools
|
|
:defer t
|
|
:mode "\\.pdf\\'"
|
|
:init (pdf-loader-install)
|
|
:bind (:map pdf-view-mode-map
|
|
("c" . (lambda ()
|
|
(interactive)
|
|
(if header-line-format
|
|
(setq header-line-format nil)
|
|
(nano-modeline-pdf-mode))))
|
|
("j" . pdf-view-next-line-or-next-page)
|
|
("k" . pdf-view-previous-line-or-previous-page)))
|
|
|
|
#+end_src
|
|
|
|
* Completion
|
|
|
|
[[https://github.com/auto-complete/auto-complete][Auto-Complete]] has been a part of my config for years, but I want to try out
|
|
[[http://company-mode.github.io/][company-mode]]. If I code in an environment with good completion, I've made an
|
|
habit of trying to /guess/ function-names, and looking at the completions for
|
|
the right one. So I want a pretty aggressive completion system, hence the no
|
|
delay settings and short prefix length.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
;; Modular text completion framework
|
|
(use-package company
|
|
:bind (;; Note that a bug (probably) makes the quotes necessary here
|
|
:map company-active-map
|
|
("C-d" . 'company-show-doc-buffer)
|
|
("C-n" . 'company-select-next)
|
|
("C-p" . 'company-select-previous))
|
|
:config
|
|
(setq company-idle-delay 0
|
|
company-echo-delay 0
|
|
company-dabbrev-downcase nil
|
|
company-minimum-prefix-length 2
|
|
company-selection-wrap-around t
|
|
company-transformers '(company-sort-by-occurrence
|
|
company-sort-by-backend-importance))
|
|
(global-company-mode 1))
|
|
|
|
#+end_src
|
|
|
|
* Spelling
|
|
** Flyspell
|
|
|
|
Flyspell offers on-the-fly spell checking.
|
|
|
|
When working with several languages, we should be able to cycle through the
|
|
languages we most frequently use. Every buffer should have a separate cycle
|
|
of languages, so that cycling in one buffer does not change the state in a
|
|
different buffer (this problem occurs if you only have one global cycle). We
|
|
can implement this by using a [[http://www.gnu.org/software/emacs/manual/html_node/elisp/Closures.html][closure]].
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(defun cycle-languages ()
|
|
"Changes the ispell dictionary to the first element in
|
|
ISPELL-LANGUAGES, and returns an interactive function that cycles
|
|
the languages in ISPELL-LANGUAGES when invoked."
|
|
(let ((ispell-languages (list "american" "norsk")))
|
|
(lambda ()
|
|
(interactive)
|
|
;; Rotates the languages cycle and changes the ispell dictionary.
|
|
(let ((rotated (nconc (cdr ispell-languages) (list (car ispell-languages)))))
|
|
(ispell-change-dictionary (car (setq ispell-languages rotated)))))))
|
|
|
|
#+end_src
|
|
|
|
We enable =flyspell-mode= for all text-modes, and use =flyspell-prog-mode=
|
|
for spell checking comments and strings in all programming modes. We bind =C-c l= to a function returned from =cycle-languages=, giving a language
|
|
switcher for every buffer where flyspell is enabled.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(use-package flyspell
|
|
:defer t
|
|
:if (executable-find "aspell")
|
|
:hook ((text-mode . flyspell-mode)
|
|
(prog-mode . flyspell-prog-mode)
|
|
(flyspell-mode . (lambda ()
|
|
(local-set-key
|
|
(kbd "C-c l")
|
|
(cycle-languages)))))
|
|
:config
|
|
(ispell-change-dictionary "american" t))
|
|
|
|
#+end_src
|
|
|
|
** Define word
|
|
|
|
This super neat package looks up the word at point. I use it a lot!
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
;; display the definition of word at point
|
|
(use-package define-word
|
|
:defer t
|
|
:bind ("C-c D" . define-word-at-point))
|
|
|
|
#+end_src
|
|
|
|
* Org
|
|
|
|
I use Org mode extensively. Some of these configurations may be unfortunate,
|
|
but it is a bit impractical to change, as I have years worth of org-files and
|
|
want to avoid having to reformat a lot of files.
|
|
|
|
One example is =org-adapt-indentation=, which changed default value in
|
|
version 9.5 of Org mode. Another is that I for some unknown reason decided to
|
|
content within source content not be indented by two spaces (which is the
|
|
default).
|
|
|
|
Note that I disable some safety features, so please don't copy and paste
|
|
mindlessly (see the documentation for =org-confirm-babel-evaluate= and =org-export-allow-bind-keywords=).
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
;; Outline-based notes management and organizer
|
|
(use-package org
|
|
:defer t
|
|
:config
|
|
(setq org-adapt-indentation t
|
|
org-src-fontify-natively t
|
|
org-confirm-babel-evaluate nil
|
|
org-export-allow-bind-keywords t
|
|
org-edit-src-content-indentation 0))
|
|
|
|
#+end_src
|
|
|
|
** LaTeX export
|
|
|
|
For LaTeX export, I default to using XeLaTeX for compilation, and the [[https://www.ctan.org/pkg/minted][minted]]
|
|
package for syntax highlighting source blocks. I have PDFs open directly in
|
|
Emacs ([[PDF Tools]]). In addition, I have support for a couple of custom LaTeX
|
|
classes.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
;; LaTeX Back-End for Org Export Engine
|
|
(use-package ox-latex
|
|
:ensure nil
|
|
:after org
|
|
:config
|
|
;; Use Minted and XeLaTeX
|
|
(setq org-latex-src-block-backend 'minted
|
|
org-latex-packages-alist '(("newfloat" "minted"))
|
|
org-latex-pdf-process
|
|
'("latexmk -pdflatex='xelatex -shell-escape -interaction nonstopmode' -pdf -f %f"))
|
|
|
|
(add-to-list 'org-file-apps '("\\.pdf\\'" . emacs))
|
|
|
|
(add-to-list 'org-latex-classes
|
|
'("ifimaster"
|
|
"\\documentclass{ifimaster}
|
|
[DEFAULT-PACKAGES]
|
|
[PACKAGES]
|
|
[EXTRA]
|
|
\\usepackage{babel,csquotes,ifimasterforside,url,varioref}"
|
|
("\\chapter{%s}" . "\\chapter*{%s}")
|
|
("\\section{%s}" . "\\section*{%s}")
|
|
("\\subsection{%s}" . "\\subsection*{%s}")
|
|
("\\subsubsection{%s}" . "\\subsubsection*{%s}")
|
|
("\\paragraph{%s}" . "\\paragraph*{%s}")
|
|
("\\subparagraph{%s}" . "\\subparagraph*{%s}")))
|
|
|
|
(add-to-list 'org-latex-classes
|
|
'("easychair" "\\documentclass{easychair}"
|
|
("\\section{%s}" . "\\section*{%s}")
|
|
("\\subsection{%s}" . "\\subsection*{%s}")
|
|
("\\subsubsection{%s}" . "\\subsubsection*{%s}")
|
|
("\\paragraph{%s}" . "\\paragraph*{%s}")
|
|
("\\subparagraph{%s}" . "\\subparagraph*{%s}"))))
|
|
|
|
#+end_src
|
|
|
|
** Babel
|
|
|
|
Add a few languages for org-babel.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
;; Working with Code Blocks in Org
|
|
(use-package ob
|
|
:ensure nil
|
|
:after org
|
|
:config
|
|
(org-babel-do-load-languages
|
|
'org-babel-load-languages
|
|
'((emacs-lisp . t)
|
|
(python . t)
|
|
(clojure . t)
|
|
(chatgpt-shell . t))))
|
|
|
|
#+end_src
|
|
|
|
Default to use whatever interpreter is set by =python-shell-interpreter=.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
;; Babel Functions for Python
|
|
(use-package ob-python
|
|
:ensure nil
|
|
:after (ob python)
|
|
:config
|
|
(setq org-babel-python-command python-shell-interpreter))
|
|
|
|
#+end_src
|
|
|
|
** Tempo
|
|
|
|
Since version 9.2 of Org mode, typing =<s= to get a source block (and
|
|
similar variants) has been tucked away in the Org Tempo library, hoping that
|
|
users rather use =C-c C-,=. Hopefully I'll stop typing =<s= at some point,
|
|
and adapt the much saner =C-c C-,=.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
;; Template expansion for Org structures
|
|
(use-package org-tempo
|
|
:ensure nil
|
|
:after org)
|
|
|
|
#+end_src
|
|
|
|
** Bullets
|
|
|
|
Touch up the headings a bit, with some fancy UTF-8 characters.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
;; Show bullets in org-mode as UTF-8 characters
|
|
(use-package org-bullets
|
|
:after org
|
|
:hook (org-mode . (lambda () (org-bullets-mode 1))))
|
|
|
|
#+end_src
|
|
|
|
** Email with org mode
|
|
|
|
The package org-msg allows me to compose emails with Org mode. That means I
|
|
easily can add headings, tables, source code, etc. It is really neat.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
;; Org mode to send and reply to email in HTML
|
|
(use-package org-msg
|
|
:after (org mu4e)
|
|
:config
|
|
(add-to-list 'mu4e-compose-pre-hook 'org-msg-mode)
|
|
(setq org-msg-enforce-css (concat user-emacs-directory "email-style.css")
|
|
org-msg-options "html-postamble:nil toc:nil num:nil author:nil email:nil"
|
|
org-msg-default-alternatives '((new . (text html))
|
|
(reply-to-html . (text html))
|
|
(reply-to-text . (text)))
|
|
org-msg-signature "
|
|
|
|
,,#+begin_signature
|
|
,,#+begin_export html
|
|
|
|
- Lars
|
|
,,#+end_export
|
|
,,#+end_signature\n"))
|
|
|
|
#+end_src
|
|
|
|
** GitHub flavored markdown
|
|
|
|
I guess I have to include my (semi-abandoned) mode [[https://github.com/larstvei/ox-gfm][ox-gfm]] for exporting org
|
|
mode to GitHub Flavored Markdown.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
;; Export Github Flavored Markdown from Org
|
|
(use-package ox-gfm
|
|
:after (org))
|
|
|
|
#+end_src
|
|
|
|
* Markdown
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
;; Emacs Major mode for Markdown-formatted files
|
|
(use-package markdown-mode
|
|
:defer t)
|
|
|
|
#+end_src
|
|
|
|
* Direnv
|
|
|
|
I use [[https://direnv.net][direnv]] in combination with [[https://nixos.org][nix]] to allow for programs to only be
|
|
available in certain directories. The [[https://github.com/wbolster/emacs-direnv][emacs-direnv]] makes Emacs play nice with
|
|
direnv, so that it for instance can detect a language server that is only
|
|
available within some project. The =direnv-always-show-summary= is set to =nil= to avoid having long messages pop up in the messages buffer whenever I
|
|
enter a directory that interacts with direnv.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
;; direnv integration
|
|
(use-package direnv
|
|
:config
|
|
(setq direnv-always-show-summary nil)
|
|
(direnv-mode 1))
|
|
|
|
#+end_src
|
|
|
|
* Email
|
|
|
|
I've used Emacs for email in the past, where I've always had the need for a
|
|
more standard email client in addition. I'm going to give it another go.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(defvar load-mail-setup
|
|
(and (file-exists-p "~/Maildir")
|
|
(executable-find "mbsync")
|
|
(executable-find "msmtp")
|
|
(executable-find "mu")))
|
|
|
|
#+end_src
|
|
|
|
I use [[http://www.djcbsoftware.nl/code/mu/mu4e.html][mu4e]] (which is a part of [[http://www.djcbsoftware.nl/code/mu/][mu]]) along with [[https://isync.sourceforge.io/][mbsync]].
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(when load-mail-setup
|
|
(with-eval-after-load 'mu4e
|
|
(setq
|
|
mail-user-agent 'mu4e-user-agent
|
|
user-full-name "Lars Tveito" ; Your full name
|
|
user-mail-address "larstvei@ifi.uio.no" ; And email-address
|
|
|
|
sendmail-program (executable-find "msmtp")
|
|
send-mail-function 'smtpmail-send-it
|
|
|
|
message-sendmail-f-is-evil t
|
|
message-sendmail-extra-arguments '("--read-envelope-from")
|
|
message-send-mail-function 'message-send-mail-with-sendmail
|
|
message-kill-buffer-on-exit t
|
|
|
|
mu4e-get-mail-command (concat (executable-find "mbsync") " -a")
|
|
mu4e-change-filenames-when-moving t
|
|
mu4e-user-mail-address-list '("larstvei@ifi.uio.no")
|
|
mu4e-maildir-shortcuts '(("/Inbox" . ?i) ("/Sent Items" . ?s))
|
|
|
|
mu4e-sent-folder "/Sent Items"
|
|
mu4e-trash-folder "/Deleted Items"
|
|
mu4e-trash-folder "/Drafts"
|
|
|
|
mu4e-use-fancy-chars t))
|
|
(autoload 'mu4e "mu4e" nil t))
|
|
|
|
#+end_src
|
|
|
|
* ChatGPT
|
|
|
|
I have a line like this:
|
|
|
|
#+begin_example
|
|
|
|
machine api.openai.com password OPEN-AI-KEY
|
|
|
|
#+end_example
|
|
|
|
in my ~.authinfo~ file. Then the ~chatgpt-shell-openai-key~ can by set by:
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
;; Interaction mode for ChatGPT
|
|
(use-package chatgpt-shell
|
|
:defer t
|
|
:config
|
|
(setq chatgpt-shell-openai-key
|
|
(auth-source-pick-first-password
|
|
:host "api.openai.com")))
|
|
|
|
#+end_src
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
;; Org babel functions for ChatGPT evaluation
|
|
(use-package ob-chatgpt-shell
|
|
:after ob)
|
|
|
|
#+end_src
|
|
|
|
* Multiple cursors
|
|
|
|
I use this /all the time/. Perhaps more than I should?
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
;; Multiple cursors for Emacs
|
|
(use-package multiple-cursors
|
|
:defer t
|
|
:bind (("C-c e" . mc/edit-lines)
|
|
("C-c a" . mc/mark-all-like-this)
|
|
("C-c n" . mc/mark-next-like-this)))
|
|
|
|
#+end_src
|
|
|
|
* Expand region
|
|
|
|
This is neat, and I use it way less than I should.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
;; Increase selected region by semantic units
|
|
(use-package expand-region
|
|
:defer t
|
|
:bind ("C-=" . er/expand-region))
|
|
|
|
#+end_src
|
|
|
|
* Interactive functions
|
|
<<sec:defuns>> =just-one-space= removes all whitespace around a point - giving it a negative
|
|
argument it removes newlines as well. We wrap a interactive function around
|
|
it to be able to bind it to a key. In Emacs 24.4 =cycle-spacing= was
|
|
introduced, and it works like =just-one-space=, but when run in succession it
|
|
cycles between one, zero and the original number of spaces.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(defun cycle-spacing-delete-newlines ()
|
|
"Removes whitespace before and after the point."
|
|
(interactive)
|
|
(if (version< emacs-version "24.4")
|
|
(just-one-space -1)
|
|
(cycle-spacing -1)))
|
|
|
|
#+end_src
|
|
|
|
Often I want to find other occurrences of a word I'm at, or more specifically
|
|
the symbol (or tag) I'm at. The =isearch-forward-symbol-at-point= in Emacs
|
|
24.4 works well for this, but I don't want to be bothered with the =isearch=
|
|
interface. Rather jump quickly between occurrences of a symbol, or if non is
|
|
found, don't do anything.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(defun jump-to-symbol-internal (&optional backwardp)
|
|
"Jumps to the next symbol near the point if such a symbol
|
|
exists. If BACKWARDP is non-nil it jumps backward."
|
|
(let* ((point (point))
|
|
(bounds (find-tag-default-bounds))
|
|
(beg (car bounds)) (end (cdr bounds))
|
|
(str (isearch-symbol-regexp (find-tag-default)))
|
|
(search (if backwardp 'search-backward-regexp
|
|
'search-forward-regexp)))
|
|
(goto-char (if backwardp beg end))
|
|
(funcall search str nil t)
|
|
(cond ((<= beg (point) end) (goto-char point))
|
|
(backwardp (forward-char (- point beg)))
|
|
(t (backward-char (- end point))))))
|
|
|
|
(defun jump-to-previous-like-this ()
|
|
"Jumps to the previous occurrence of the symbol at point."
|
|
(interactive)
|
|
(jump-to-symbol-internal t))
|
|
|
|
(defun jump-to-next-like-this ()
|
|
"Jumps to the next occurrence of the symbol at point."
|
|
(interactive)
|
|
(jump-to-symbol-internal))
|
|
|
|
#+end_src
|
|
|
|
I sometimes regret killing the =*scratch*=-buffer, and have realized I never
|
|
want to actually kill it. I just want to get it out of the way, and clean it
|
|
up. The function below does just this for the =*scratch*=-buffer, and works
|
|
like =kill-this-buffer= for any other buffer. It removes all buffer content
|
|
and buries the buffer (this means making it the least likely candidate for =other-buffer=).
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(defun kill-this-buffer-unless-scratch ()
|
|
"Works like `kill-this-buffer' unless the current buffer is the
|
|
,*scratch* buffer. In witch case the buffer content is deleted and
|
|
the buffer is buried."
|
|
(interactive)
|
|
(if (not (string= (buffer-name) "*scratch*"))
|
|
(kill-this-buffer)
|
|
(delete-region (point-min) (point-max))
|
|
(switch-to-buffer (other-buffer))
|
|
(bury-buffer "*scratch*")))
|
|
|
|
#+end_src
|
|
|
|
To duplicate either selected text or a line we define this interactive
|
|
function.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(defun duplicate-thing (comment)
|
|
"Duplicates the current line, or the region if active. If an argument is
|
|
given, the duplicated region will be commented out."
|
|
(interactive "P")
|
|
(save-excursion
|
|
(let ((start (if (region-active-p) (region-beginning) (line-beginning-position)))
|
|
(end (if (region-active-p) (region-end) (line-end-position)))
|
|
(fill-column most-positive-fixnum))
|
|
(goto-char end)
|
|
(unless (region-active-p)
|
|
(newline))
|
|
(insert (buffer-substring start end))
|
|
(when comment (comment-region start end)))))
|
|
|
|
#+end_src
|
|
|
|
To tidy up a buffer we define this function borrowed from [[https://github.com/simenheg][simenheg]].
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(defun tidy ()
|
|
"Ident, untabify and unwhitespacify current buffer, or region if active."
|
|
(interactive)
|
|
(let ((beg (if (region-active-p) (region-beginning) (point-min)))
|
|
(end (if (region-active-p) (region-end) (point-max))))
|
|
(indent-region beg end)
|
|
(whitespace-cleanup)
|
|
(untabify beg (if (< end (point-max)) end (point-max)))))
|
|
|
|
#+end_src
|
|
|
|
Org mode does currently not support synctex (which enables you to jump from a
|
|
point in your TeX-file to the corresponding point in the pdf), and it [[http://comments.gmane.org/gmane.emacs.orgmode/69454][seems
|
|
like a tricky problem]].
|
|
|
|
Calling this function from an org-buffer jumps to the corresponding section
|
|
in the exported pdf (given that the pdf-file exists), using pdf-tools.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(defun org-sync-pdf ()
|
|
(interactive)
|
|
(let ((headline (nth 4 (org-heading-components)))
|
|
(pdf (concat (file-name-base (buffer-name)) ".pdf")))
|
|
(when (file-exists-p pdf)
|
|
(find-file-other-window pdf)
|
|
(pdf-links-action-perform
|
|
(cl-find headline (pdf-info-outline pdf)
|
|
:key (lambda (alist) (cdr (assoc 'title alist)))
|
|
:test 'string-equal)))))
|
|
|
|
#+end_src
|
|
|
|
The opposite of fill paragraph (from [[https://www.emacswiki.org/emacs/UnfillParagraph][EmacsWiki]]),
|
|
|
|
#+begin_src emacs-lisp
|
|
(defun unfill-paragraph ()
|
|
(interactive)
|
|
(let ((fill-column most-positive-fixnum))
|
|
(fill-paragraph nil (region-active-p))))
|
|
#+end_src
|
|
|
|
* Advice
|
|
|
|
An advice can be given to a function to make it behave differently. This
|
|
advice makes =eval-last-sexp= (bound to =C-x C-e=) replace the sexp with the
|
|
value.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(defadvice eval-last-sexp (around replace-sexp (arg) activate)
|
|
"Replace sexp when called with a prefix argument."
|
|
(if arg
|
|
(let ((pos (point)))
|
|
ad-do-it
|
|
(goto-char pos)
|
|
(backward-kill-sexp)
|
|
(forward-sexp))
|
|
ad-do-it))
|
|
|
|
#+end_src
|
|
|
|
When interactively changing the theme (using =M-x load-theme=), the current
|
|
custom theme is not disabled. This often gives weird-looking results; we can
|
|
advice =load-theme= to always disable themes currently enabled themes.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(defadvice load-theme
|
|
(before disable-before-load (theme &optional no-confirm no-enable) activate)
|
|
(mapc 'disable-theme custom-enabled-themes))
|
|
|
|
#+end_src
|
|
|
|
* global-scale-mode
|
|
|
|
These functions provide something close to ~text-scale-mode~, but for every
|
|
buffer, including the minibuffer and mode line.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(let* ((default (face-attribute 'default :height))
|
|
(size default))
|
|
|
|
(defun global-scale-default ()
|
|
(interactive)
|
|
(global-scale-internal (setq size default)))
|
|
|
|
(defun global-scale-up ()
|
|
(interactive)
|
|
(global-scale-internal (setq size (+ size 20))))
|
|
|
|
(defun global-scale-down ()
|
|
(interactive)
|
|
(global-scale-internal (setq size (- size 20))))
|
|
|
|
(defun global-scale-internal (arg)
|
|
(set-face-attribute 'default (selected-frame) :height arg)
|
|
(set-temporary-overlay-map
|
|
(let ((map (make-sparse-keymap)))
|
|
(define-key map (kbd "C-=") 'global-scale-up)
|
|
(define-key map (kbd "C-+") 'global-scale-up)
|
|
(define-key map (kbd "C--") 'global-scale-down)
|
|
(define-key map (kbd "C-0") 'global-scale-default) map))))
|
|
|
|
#+end_src
|
|
|
|
* Mode specific
|
|
** Eglot
|
|
|
|
I am using [[https://joaotavora.github.io/eglot/][eglot]], which is built in from [[https://git.savannah.gnu.org/cgit/emacs.git/tree/etc/NEWS?h=emacs-29#n3273][emacs 29.1]]. Some performance issues
|
|
led me to set =eglot-events-buffer-size= to 0.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(setq eglot-events-buffer-size 0)
|
|
(add-hook 'eglot-managed-mode-hook (lambda () (eglot-inlay-hints-mode -1)))
|
|
|
|
#+end_src
|
|
|
|
** Compilation
|
|
|
|
I often run ~latexmk -pdf -pvc~ in a compilation buffer, which recompiles
|
|
the latex-file whenever it is changed. This often results in annoyingly
|
|
large compilation buffers; the following snippet limits the buffer size in
|
|
accordance with ~comint-buffer-maximum-size~, which defaults to 1024 lines.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(add-hook 'compilation-filter-hook 'comint-truncate-buffer)
|
|
|
|
#+end_src
|
|
|
|
** vterm
|
|
|
|
Inspired by [[https://github.com/torenord/.emacs.d][torenord]], I maintain quick access to shell buffers with bindings ~M-1~ to ~M-9~. In addition, the ~M-z~ toggles between the last visited
|
|
shell, and the last visited non-shell buffer. The following functions
|
|
facilitate this, and are bound in the [[Key bindings]] section.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(let ((last-vterm ""))
|
|
(defun toggle-vterm ()
|
|
(interactive)
|
|
(cond ((string-match-p "^\\vterm<[1-9][0-9]*>$" (buffer-name))
|
|
(goto-non-vterm-buffer))
|
|
((get-buffer last-vterm) (switch-to-buffer last-vterm))
|
|
(t (vterm (setq last-vterm "vterm<1>")))))
|
|
|
|
(defun goto-non-vterm-buffer ()
|
|
(let* ((r "^\\vterm<[1-9][0-9]*>$")
|
|
(vterm-buffer-p (lambda (b) (string-match-p r (buffer-name b))))
|
|
(non-vterms (cl-remove-if vterm-buffer-p (buffer-list))))
|
|
(when non-vterms
|
|
(switch-to-buffer (car non-vterms)))))
|
|
|
|
(defun switch-vterm (n)
|
|
(let ((buffer-name (format "vterm<%d>" n)))
|
|
(setq last-vterm buffer-name)
|
|
(cond ((get-buffer buffer-name)
|
|
(switch-to-buffer buffer-name))
|
|
(t (vterm buffer-name)
|
|
(rename-buffer buffer-name))))))
|
|
|
|
#+end_src
|
|
|
|
Don't query whether or not the ~shell~-buffer should be killed, just kill
|
|
it.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(defadvice vterm (after kill-with-no-query nil activate)
|
|
(set-process-query-on-exit-flag (get-buffer-process ad-return-value) nil))
|
|
|
|
#+end_src
|
|
|
|
** Lisp
|
|
|
|
I use [[https://paredit.org/][Paredit]] when editing lisp code, we enable this for all lisp-modes.
|
|
Paredit version 25 [[https://paredit.org/cgit/paredit/plain/NEWS][seems to interfere]] with REPL-modes, and unbinding =RET=
|
|
is the proposed fix.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
;; minor mode for editing parentheses
|
|
(use-package paredit
|
|
:defer t
|
|
:bind (:map paredit-mode-map ("RET" . nil))
|
|
:hook ((cider-repl-mode
|
|
clojure-mode
|
|
ielm-mode
|
|
racket-mode
|
|
racket-repl-mode
|
|
slime-repl-mode
|
|
lisp-mode
|
|
emacs-lisp-mode
|
|
lisp-interaction-mode
|
|
scheme-mode)
|
|
. paredit-mode))
|
|
|
|
#+end_src
|
|
|
|
*** Emacs Lisp
|
|
|
|
In =emacs-lisp-mode= we can enable =eldoc-mode= to display information
|
|
about a function or a variable in the echo area.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(add-hook 'emacs-lisp-mode-hook 'turn-on-eldoc-mode)
|
|
(add-hook 'lisp-interaction-mode-hook 'turn-on-eldoc-mode)
|
|
|
|
#+end_src
|
|
|
|
*** Clojure
|
|
|
|
A very simple setup for Clojure. Cider works pretty much out of the box!
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
;; Clojure Interactive Development Environment
|
|
(use-package cider
|
|
:defer t
|
|
:bind (:map cider-repl-mode-map ("C-l" . cider-repl-clear-buffer)))
|
|
|
|
#+end_src
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
;; Commands for refactoring Clojure code
|
|
(use-package clj-refactor
|
|
:defer t)
|
|
|
|
#+end_src
|
|
|
|
*** Racket
|
|
|
|
A minimal setup for Racket.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
;; Major mode for Racket language
|
|
(use-package racket-mode
|
|
:defer t)
|
|
|
|
#+end_src
|
|
|
|
*** Common lisp
|
|
|
|
I use [[http://www.common-lisp.net/project/slime/][Slime]] along with =lisp-mode= to edit Common Lisp code. Slime provides
|
|
code evaluation and other great features, a must have for a Common Lisp
|
|
developer. [[http://www.quicklisp.org/beta/][Quicklisp]] is a library manager for Common Lisp, and you can
|
|
install Slime following the instructions from the site along with this
|
|
snippet.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(defun activate-slime-helper ()
|
|
(when (file-exists-p "~/.quicklisp/slime-helper.el")
|
|
(load (expand-file-name "~/.quicklisp/slime-helper.el"))
|
|
(define-key slime-repl-mode-map (kbd "C-l")
|
|
'slime-repl-clear-buffer))
|
|
(remove-hook 'common-lisp-mode-hook #'activate-slime-helper))
|
|
|
|
(add-hook 'common-lisp-mode-hook #'activate-slime-helper)
|
|
|
|
#+end_src
|
|
|
|
We can specify what Common Lisp program Slime should use (I use SBCL).
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(setq inferior-lisp-program "sbcl")
|
|
|
|
#+end_src
|
|
|
|
More sensible =loop= indentation, borrowed from [[https://github.com/simenheg][simenheg]].
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(setq lisp-loop-forms-indentation 6
|
|
lisp-simple-loop-indentation 2
|
|
lisp-loop-keyword-indentation 6)
|
|
|
|
#+end_src
|
|
|
|
** Python
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(setq python-shell-interpreter "python3.10")
|
|
(add-hook 'python-mode-hook
|
|
(lambda () (setq forward-sexp-function nil)))
|
|
|
|
#+end_src
|
|
|
|
** C
|
|
|
|
The =c-mode-common-hook= is a general hook that work on all C-like languages
|
|
(C, C++, Java, etc...). I like being able to quickly compile using =C-c C-c=
|
|
(instead of =M-x compile=), a habit from =latex-mode=.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(defun c-setup ()
|
|
(local-set-key (kbd "C-c C-c") 'compile))
|
|
|
|
(add-hook 'c-mode-hook 'c-setup)
|
|
|
|
#+end_src
|
|
|
|
** Java
|
|
|
|
Some statements in Java appear often, and become tedious to write out. We
|
|
can use abbrevs to speed this up.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(define-abbrev-table 'java-mode-abbrev-table
|
|
'(("psv" "public static void main(String[] args) {" nil 0)
|
|
("sopl" "System.out.println" nil 0)
|
|
("sop" "System.out.printf" nil 0)))
|
|
|
|
#+end_src
|
|
|
|
To be able to use the abbrev table defined above, =abbrev-mode= must be
|
|
activated.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(add-hook 'java-mode-hook 'eglot-ensure)
|
|
|
|
#+end_src
|
|
|
|
** Assembler
|
|
|
|
When writing assembler code I use =#= for comments. By defining =comment-start= we can add comments using =M-;= like in other programming
|
|
modes. Also in assembler should one be able to compile using =C-c C-c=.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(defun asm-setup ()
|
|
(setq comment-start "#")
|
|
(local-set-key (kbd "C-c C-c") 'compile))
|
|
|
|
(add-hook 'asm-mode-hook 'asm-setup)
|
|
|
|
#+end_src
|
|
|
|
** LaTeX
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
;; Integrated environment for *TeX*
|
|
(use-package auctex
|
|
:defer t)
|
|
|
|
#+end_src
|
|
|
|
** Erlang
|
|
|
|
Erlang mode works out of the box.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
;; Erlang major mode
|
|
(use-package erlang
|
|
:defer t)
|
|
|
|
#+end_src
|
|
|
|
** Nix
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
;; Major mode for editing .nix files
|
|
(use-package nix-mode
|
|
:defer t
|
|
:hook (nix-mode . eglot-ensure))
|
|
|
|
#+end_src
|
|
|
|
** Haskell =haskell-doc-mode= is similar to =eldoc=, it displays documentation in the
|
|
echo area. Haskell has several indentation modes - I prefer using =haskell-indent=.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
;; A Haskell editing mode
|
|
(use-package haskell-mode
|
|
:defer t
|
|
:hook ((haskell-mode . interactive-haskell-mode)
|
|
(haskell-mode . turn-on-haskell-doc-mode)
|
|
(haskell-mode . turn-on-haskell-indent)))
|
|
|
|
#+end_src
|
|
|
|
** Maude
|
|
|
|
Use =---= for comments in Maude.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
;; Emacs mode for the programming language Maude
|
|
(use-package maude-mode
|
|
:defer t
|
|
:hook (maude-mode . (lambda () (setq-local comment-start "---")))
|
|
:config
|
|
(add-to-list 'maude-command-options "-no-wrap"))
|
|
|
|
#+end_src
|
|
|
|
** Minizinc
|
|
|
|
Provide a default =compile-command=.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(defun minizinc-setup-compile-command ()
|
|
(let ((command (concat "minizinc " (buffer-file-name) " "))
|
|
(f (concat (file-name-base (buffer-file-name)) ".dzn")))
|
|
(local-set-key (kbd "C-c C-c") 'recompile)
|
|
(setq-local compile-command (concat command (if (file-exists-p f) f "")))))
|
|
|
|
#+end_src
|
|
|
|
Use =minizinc-mode=, and hook up the =minizinc-setup-compile-command= above.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
;; Major mode for MiniZinc code
|
|
(use-package minizinc-mode
|
|
:defer t
|
|
:mode "\\.mzn\\'"
|
|
:hook (minizinc-mode . minizinc-setup-compile-command))
|
|
|
|
#+end_src
|
|
|
|
** Coq
|
|
|
|
[[https://proofgeneral.github.io/][Proof General]] is really great for working with proof assistants. I have only
|
|
tried it with Coq.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
;; A generic Emacs interface for proof assistants
|
|
(use-package proof-general
|
|
:defer t)
|
|
|
|
#+end_src
|
|
|
|
For completions, I use [[https://github.com/cpitclaudel/company-coq][company-coq]].
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
;; A collection of extensions PG's Coq mode
|
|
(use-package company-coq
|
|
:defer t
|
|
:hook (coq-mode . company-coq-mode))
|
|
|
|
#+end_src
|
|
|
|
** Rust
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
;; Rust development environment
|
|
(use-package rustic
|
|
:defer t
|
|
:config
|
|
(setq rustic-lsp-client 'eglot))
|
|
|
|
#+end_src
|
|
|
|
** Go
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
;; Major mode for the Go programming language
|
|
(use-package go-mode
|
|
:defer t
|
|
:mode "\\.go\\'"
|
|
:hook (go-mode . eglot-ensure))
|
|
|
|
#+end_src
|
|
|
|
** Lua
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
;; a major-mode for editing Lua scripts
|
|
(use-package lua-mode
|
|
:defer t)
|
|
|
|
#+end_src
|
|
|
|
** Webdev
|
|
|
|
My webdev setup isn't much, but with eglot, I don't find myself missing
|
|
much. It depends on [[https://tree-sitter.github.io/tree-sitter/][Tree-sitter]], which was added in [[https://git.savannah.gnu.org/cgit/emacs.git/tree/etc/NEWS?h=emacs-29#n36][emacs 29.1]].
|
|
|
|
The following is what I use for plain Javascript:
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(add-to-list 'auto-mode-alist '("\\.jsx?\\'" . js-ts-mode))
|
|
(add-hook 'js-ts-mode-hook 'eglot-ensure)
|
|
|
|
#+end_src
|
|
|
|
Similarly for Typescript:
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(add-to-list 'auto-mode-alist '("\\.tsx?\\'" . tsx-ts-mode))
|
|
(add-hook 'tsx-ts-mode-hook 'eglot-ensure)
|
|
(setq typescript-ts-mode-indent-offset 4)
|
|
|
|
#+end_src
|
|
|
|
I am using [[https://svelte.dev][Svelte]] for some projects, where I find [[https://web-mode.org][web-mode]] along with the
|
|
[[https://github.com/sveltejs/language-tools][Svelte Language Server]] to work well.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(add-to-list 'auto-mode-alist '("\\.svelte\\'" . web-mode))
|
|
(add-hook 'web-mode-hook 'eglot-ensure)
|
|
(with-eval-after-load "web-mode"
|
|
(add-to-list 'web-mode-engines-alist '("svelte" . "\\.svelte\\'")))
|
|
(with-eval-after-load "eglot"
|
|
(add-to-list 'eglot-server-programs
|
|
'(web-mode . ("svelteserver" "--stdio"))))
|
|
|
|
#+end_src
|
|
|
|
* Key bindings
|
|
|
|
Inspired by [[http://stackoverflow.com/questions/683425/globally-override-key-binding-in-emacs][this StackOverflow post]] I keep a =custom-bindings-map= that holds
|
|
all my custom bindings. This map can be activated by toggling a simple =minor-mode= that does nothing more than activating the map. This inhibits
|
|
other =major-modes= to override these bindings. I keep this at the end of the
|
|
init-file to make sure that all functions are actually defined.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(defvar custom-bindings-map (make-keymap)
|
|
"A keymap for custom bindings.")
|
|
|
|
#+end_src
|
|
|
|
** Bindings for [[https://github.com/magnars/multiple-cursors.el][multiple-cursors]]
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(define-key custom-bindings-map (kbd "C-c e") 'mc/edit-lines)
|
|
(define-key custom-bindings-map (kbd "C-c a") 'mc/mark-all-like-this)
|
|
(define-key custom-bindings-map (kbd "C-c n") 'mc/mark-next-like-this)
|
|
|
|
#+end_src
|
|
|
|
** Bindings for mu4e
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(define-key custom-bindings-map (kbd "C-x m") 'mu4e)
|
|
|
|
#+end_src
|
|
|
|
** Bindings for built-ins
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(define-key custom-bindings-map (kbd "M-u") 'upcase-dwim)
|
|
(define-key custom-bindings-map (kbd "M-c") 'capitalize-dwim)
|
|
(define-key custom-bindings-map (kbd "M-l") 'downcase-dwim)
|
|
(define-key custom-bindings-map (kbd "M-]") 'other-frame)
|
|
(define-key custom-bindings-map (kbd "C-j") 'newline-and-indent)
|
|
(define-key custom-bindings-map (kbd "C-c s") 'ispell-word)
|
|
(define-key comint-mode-map (kbd "C-l") 'comint-clear-buffer)
|
|
|
|
#+end_src
|
|
|
|
** Bindings for functions defined [[sec:defuns][above]].
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(define-key global-map (kbd "M-p") 'jump-to-previous-like-this)
|
|
(define-key global-map (kbd "M-n") 'jump-to-next-like-this)
|
|
(define-key custom-bindings-map (kbd "M-,") 'jump-to-previous-like-this)
|
|
(define-key custom-bindings-map (kbd "M-.") 'jump-to-next-like-this)
|
|
(define-key custom-bindings-map (kbd "C-c .") (cycle-themes))
|
|
(define-key custom-bindings-map (kbd "C-x k") 'kill-this-buffer-unless-scratch)
|
|
(define-key custom-bindings-map (kbd "C-c C-0") 'global-scale-default)
|
|
(define-key custom-bindings-map (kbd "C-c C-=") 'global-scale-up)
|
|
(define-key custom-bindings-map (kbd "C-c C-+") 'global-scale-up)
|
|
(define-key custom-bindings-map (kbd "C-c C--") 'global-scale-down)
|
|
(define-key custom-bindings-map (kbd "C-c j") 'cycle-spacing-delete-newlines)
|
|
(define-key custom-bindings-map (kbd "C-c d") 'duplicate-thing)
|
|
(define-key custom-bindings-map (kbd "<C-tab>") 'tidy)
|
|
(define-key custom-bindings-map (kbd "C-z") 'toggle-vterm)
|
|
|
|
(dolist (n (number-sequence 1 9))
|
|
(let ((key (concat "M-" (int-to-string n))))
|
|
(with-eval-after-load 'vterm
|
|
(define-key vterm-mode-map (kbd key) nil))
|
|
(define-key custom-bindings-map (kbd key)
|
|
(lambda () (interactive) (switch-vterm n)))))
|
|
|
|
(with-eval-after-load 'org
|
|
(define-key org-mode-map (kbd "C-'") 'org-sync-pdf))
|
|
|
|
#+end_src
|
|
|
|
Lastly we need to activate the map by creating and activating the =minor-mode=.
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(define-minor-mode custom-bindings-mode
|
|
"A mode that activates custom-bindings."
|
|
t nil custom-bindings-map)
|
|
|
|
#+end_src
|
|
|
|
* License
|
|
|
|
My Emacs configurations written in Org mode.
|
|
|
|
Copyright (c) 2013 - 2023 Lars Tveito
|
|
|
|
This program is free software: you can redistribute it and/or modify it under
|
|
the terms of the GNU General Public License as published by the Free Software
|
|
Foundation, either version 3 of the License, or (at your option) any later
|
|
version.
|
|
|
|
This program is distributed in the hope that it will be useful, but WITHOUT
|
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
|
details.
|
|
|
|
You should have received a copy of the GNU General Public License along with
|
|
this program. If not, see <http://www.gnu.org/licenses/>.
|