1181 lines
40 KiB
Org Mode
1181 lines
40 KiB
Org Mode
#+TITLE: Emacs configuration
|
|
#+AUTHOR: Oreodave
|
|
#+DESCRIPTION: My new Emacs configuration
|
|
#+PROPERTY: header-args :tangle config.el :comment link
|
|
#+OPTIONS: toc:nil
|
|
|
|
#+BEGIN_center
|
|
My configuration for vanilla Emacs
|
|
#+END_center
|
|
#+LATEX: \clearpage
|
|
#+TOC: headlines
|
|
#+LATEX: \clearpage
|
|
|
|
* Initial
|
|
** Who am I?
|
|
Set full name and mail address for use in a variety of applications,
|
|
including encryption.
|
|
#+BEGIN_SRC emacs-lisp
|
|
(setq user-full-name "Aryadev Chavali"
|
|
user-mail-address "aryadev@aryadevchavali.com")
|
|
#+END_SRC
|
|
** All yes or no questions to y or n
|
|
Sets yes or no questions to single letter responses.
|
|
#+BEGIN_SRC emacs-lisp
|
|
(fset 'yes-or-no-p 'y-or-n-p)
|
|
#+END_SRC
|
|
** Hs Minor mode
|
|
Turn on hs minor mode for all prog-mode.
|
|
#+BEGIN_SRC emacs-lisp
|
|
(add-hook 'prog-mode-hook #'hs-minor-mode)
|
|
#+END_SRC
|
|
** Saving files
|
|
Setup backup files at =user-emacs-directory/saves=, auto-revert mode
|
|
for everything and save my place in a file if possible
|
|
#+BEGIN_SRC emacs-lisp
|
|
(setq backup-directory-alist `(("." . "~/.config/emacs/saves")))
|
|
(global-auto-revert-mode 1)
|
|
(setq global-auto-revert-non-file-buffers nil
|
|
auto-revert-verbose nil)
|
|
#+END_SRC
|
|
** Themes
|
|
Load my custom "Grayscale" theme (look at [[file:Grayscale-theme.el][this file]]).
|
|
#+BEGIN_SRC emacs-lisp
|
|
(load-theme 'Grayscale t)
|
|
#+END_SRC
|
|
** Turn off startup buffer and turn off bells
|
|
#+BEGIN_SRC emacs-lisp
|
|
(setq inhibit-startup-screen t
|
|
ring-bell-function 'ignore)
|
|
#+END_SRC
|
|
* Emacs Mode-line
|
|
Firstly, declare a variable for the separator between each module
|
|
#+BEGIN_SRC emacs-lisp
|
|
(defconst +modeline/separator " " "Separator between modules.")
|
|
#+END_SRC
|
|
|
|
Then declare a variable for the number of separators between each
|
|
module in the modeline.
|
|
#+BEGIN_SRC emacs-lisp
|
|
(defconst +modeline/sep-count 4 "Number of +modline/separator instances separating modules.")
|
|
#+END_SRC
|
|
|
|
Then, declare a list of reserved characters for which the previously
|
|
declared seperator won't be applied when placed at the end of a module
|
|
string.
|
|
#+BEGIN_SRC emacs-lisp
|
|
(defconst +modeline/reserved-chars (list "[" "(")
|
|
"Characters that, when at the end of a module string, won't have the separator applied to them.")
|
|
#+END_SRC
|
|
|
|
Now declare a function that applies the separator with respect to the
|
|
reserved characters to any one string.
|
|
#+BEGIN_SRC emacs-lisp
|
|
(defun +modeline/handle-string (STR)
|
|
(condition-case nil
|
|
(progn
|
|
(string-blank-p STR)
|
|
(cond ((cl-member (car (last (split-string STR "" t))) +modeline/reserved-chars :test #'string=) STR)
|
|
(t (concat STR (cl-reduce #'concat (cl-loop for i from 1 to +modeline/sep-count collect +modeline/separator))))))
|
|
(error STR)))
|
|
#+END_SRC
|
|
|
|
Finally, set the mode-line-format.
|
|
#+BEGIN_SRC emacs-lisp
|
|
(setq-default
|
|
mode-line-format
|
|
(mapcar #'+modeline/handle-string
|
|
(list "%l:%c"
|
|
"%p["
|
|
'(:eval (upcase
|
|
(substring
|
|
(format "%s" (if (bound-and-true-p evil-state) evil-state ""))
|
|
0 1)))
|
|
"]"
|
|
"%+%b("
|
|
'(:eval (format "%s" major-mode))
|
|
")"
|
|
"%I"
|
|
vc-mode
|
|
mode-line-end-spaces)))
|
|
#+END_SRC
|
|
* Custom Functions
|
|
These are custom functions I have defined
|
|
** New line function
|
|
Vim bindings don't have a nice way of adding new lines before or after
|
|
the current line while staying in normal mode. You can use =o/O= to
|
|
enter insert mode at a new line, but this isn't the same as being able
|
|
to stay in normal mode while opening newlines and only adds extra
|
|
keypresses if your only purpose was to open up some lines.
|
|
|
|
As this is Emacs I can extend it as I wish, so I decided to define a
|
|
new line function that won't remove me from normal state.
|
|
|
|
The logic is pretty simple:
|
|
- Use the predefined vim functions for opening new lines above and
|
|
below with insert mode
|
|
- Given the argument =BACKWARD= to assess whether to open lines
|
|
above or below
|
|
- Return to previous location
|
|
- Enter normal state
|
|
|
|
#+BEGIN_SRC emacs-lisp
|
|
(with-eval-after-load "evil"
|
|
(defun dx:newline (&optional BACKWARD)
|
|
(interactive)
|
|
(let ((old (point)))
|
|
(cond ((and BACKWARD (= BACKWARD 1)) (evil-open-below 1))
|
|
(t (evil-open-above 1)))
|
|
(goto-char (+ old 1))
|
|
(evil-normal-state))))
|
|
#+END_SRC
|
|
** Toggle buffer
|
|
For some buffer with name =buf-name= with a creation function
|
|
=buf-create=, toggle it via this function.
|
|
#+BEGIN_SRC emacs-lisp
|
|
(defun +dx/toggle-buffer (buf-name buf-create)
|
|
(interactive)
|
|
(let* ((buffer (or (get-buffer buf-name) (funcall buf-create)))
|
|
(displayed (get-buffer-window buffer))) ; Get window when displayed, nil otherwise
|
|
(cond (displayed ; already displayed thus delete
|
|
(select-window displayed)
|
|
(delete-window))
|
|
(t ; not displayed thus show and select
|
|
(display-buffer buffer)
|
|
(select-window (get-buffer-window buffer))))))
|
|
#+END_SRC
|
|
* General
|
|
Setup general, a good package for defining keys. In this case, I
|
|
generate a new definer for the "LEADER" keys. Leader is bound to SPC
|
|
and it's functionally equivalent the doom/spacemacs leader.
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package general
|
|
:config
|
|
(general-def 'normal global-map "SPC" nil)
|
|
(general-def 'normal global-map
|
|
"M-V" #'dx:newline
|
|
"M-v" #'(lambda () (interactive) (dx:newline 1))
|
|
"M--" #'whitespace-cleanup
|
|
"C--" #'text-scale-decrease
|
|
"C-=" #'text-scale-increase)
|
|
|
|
(general-create-definer leader
|
|
:states 'normal
|
|
:keymaps 'override
|
|
:prefix "SPC")
|
|
|
|
(leader
|
|
"!" #'async-shell-command
|
|
";" #'eval-expression
|
|
"SPC" #'execute-extended-command
|
|
"q" #'kill-emacs
|
|
"u" #'universal-argument
|
|
"cF" #'(lambda () (interactive) (find-file "~/Code/"))
|
|
"cc" #'compile
|
|
"si" #'imenu
|
|
"h" #'help-command)
|
|
|
|
(leader
|
|
:infix "b"
|
|
"d" #'kill-this-buffer
|
|
"i" #'ibuffer
|
|
"b" #'switch-to-buffer)
|
|
|
|
(leader
|
|
:infix "f"
|
|
"f" #'find-file
|
|
"s" #'save-buffer
|
|
"p" #'(lambda () (interactive) (find-file (concat user-emacs-directory "config.org")))))
|
|
#+END_SRC
|
|
* Evil
|
|
** Evil default
|
|
Setup the evil package, with some basic keybinds. Also declare a
|
|
leader-map at "SPC".
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package evil
|
|
:init
|
|
(setq evil-want-keybinding nil
|
|
evil-split-window-below t
|
|
evil-vsplit-window-right t)
|
|
:config
|
|
(evil-mode +1)
|
|
(evil-define-key 'normal global-map
|
|
"TAB" #'evil-jump-item)
|
|
(evil-define-key 'visual 'emacs-lisp-mode-map "gr" #'eval-region)
|
|
(leader
|
|
:infix "w"
|
|
"h" #'evil-window-left
|
|
"j" #'evil-window-down
|
|
"k" #'evil-window-up
|
|
"l" #'evil-window-right))
|
|
#+END_SRC
|
|
** Evil surround
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package evil-surround
|
|
:after evil
|
|
:config
|
|
(global-evil-surround-mode))
|
|
#+END_SRC
|
|
** Evil commentary
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package evil-commentary
|
|
:after evil
|
|
:config
|
|
(evil-commentary-mode))
|
|
#+END_SRC
|
|
** Evil mc
|
|
Setup for multicursors in Evil mode. Don't let evil-mc setup it's own
|
|
keymap because it uses 'gr' as its prefix, which I don't like.
|
|
|
|
Instead, bind some useful functions to my personal =dx:evil-mc-map=
|
|
which is bound to 'gz'. Furthermore, define a function
|
|
=dx:evil-mc-cursor-here= which pauses cursors upon placing a cursor at
|
|
the current position.
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package evil-mc
|
|
:after evil
|
|
:bind (("M-p" . evil-mc-skip-and-goto-prev-cursor)
|
|
:map dx:evil-mc-map
|
|
("q" . evil-mc-undo-all-cursors)
|
|
("d" . evil-mc-make-and-goto-next-match)
|
|
("j" . evil-mc-make-cursor-move-next-line)
|
|
("k" . evil-mc-make-cursor-move-prev-line)
|
|
("j" . evil-mc-make-cursor-move-next-line)
|
|
("m" . evil-mc-make-all-cursors)
|
|
("z" . dx:evil-mc-cursor-here)
|
|
("r" . evil-mc-resume-cursors)
|
|
("s" . evil-mc-pause-cursors))
|
|
:init
|
|
(define-prefix-command 'dx:evil-mc-map)
|
|
(bind-key "gz" dx:evil-mc-map evil-normal-state-map)
|
|
:config
|
|
(dolist (fn '((delete-char)
|
|
(backward-kill-word)
|
|
(company-complete-common . evil-mc-execute-default-complete)
|
|
(evil-delete-back-to-indentation . evil-mc-execute-default-call)
|
|
;; Have evil-mc work with explicit `evil-escape' (on C-g)
|
|
(evil-escape . evil-mc-execute-default-evil-normal-state)
|
|
;; Add `evil-org' support
|
|
(evil-org-delete . evil-mc-execute-default-evil-delete)
|
|
(evil-org-delete-char . evil-mc-execute-default-evil-delete)
|
|
(evil-org-delete-backward-char . evil-mc-execute-default-evil-delete)))
|
|
(cl-pushnew `(,(car fn) (:default . ,(or (cdr fn) #'evil-mc-execute-default-call-with-count)))
|
|
evil-mc-custom-known-commands
|
|
:test #'eq
|
|
:key #'car))
|
|
|
|
(global-evil-mc-mode +1)
|
|
(defun dx:evil-mc-cursor-here ()
|
|
(interactive)
|
|
(evil-mc-make-cursor-here)
|
|
(evil-mc-pause-cursors)))
|
|
#+END_SRC
|
|
** Evil collection
|
|
Setup evil collection, but don't turn on the mode. Instead, I'll turn
|
|
on setups for specific modes I think benefit from it.
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package evil-collection
|
|
:after evil)
|
|
#+END_SRC
|
|
* Ivy
|
|
Ivy is a completion framework for Emacs, and my preferred (sometimes
|
|
second favourite) one. It has a great set of features with little to
|
|
no pain with setting up.
|
|
** Ivy
|
|
Setup for ivy, in preparation for counsel. Turn on ivy-mode just
|
|
after init.
|
|
|
|
Setup vim-like bindings for the minibuffer ("C-(j|k)" for down|up the
|
|
selection list). Also setup evil-collection for ivy.
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package ivy
|
|
:hook (after-init . ivy-mode)
|
|
:after evil-collection
|
|
:bind (:map ivy-minibuffer-map
|
|
("M-j" . ivy-next-line-or-history)
|
|
("M-k" . ivy-previous-line-or-history)
|
|
:map ivy-switch-buffer-map
|
|
("M-j" . ivy-next-line-or-history)
|
|
("M-k" . ivy-previous-line-or-history))
|
|
:general
|
|
(:keymaps 'ivy-minibuffer-map
|
|
"C-c C-e" #'ivy-occur)
|
|
:config
|
|
(require 'counsel nil t)
|
|
(setq ivy-height 10
|
|
ivy-wrap t
|
|
ivy-fixed-height-minibuffer t
|
|
ivy-use-virtual-buffers nil
|
|
ivy-virtual-abbreviate 'full
|
|
ivy-on-del-error-function #'ignore
|
|
ivy-use-selectable-prompt t
|
|
ivy-initial-inputs-alist nil)
|
|
(evil-collection-ivy-setup))
|
|
#+END_SRC
|
|
** Counsel
|
|
Setup for counsel. Load after ivy and helpful.
|
|
|
|
Bind:
|
|
- Swiper to "C-s"
|
|
- Switch buffer to "C-x b"
|
|
- Counsel ripgrep to "M-s r" (search namespace)
|
|
|
|
Along with that, set the help function and variable functions to their
|
|
helpful counterparts.
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package counsel
|
|
:defer t
|
|
:general
|
|
(leader
|
|
"ss" #'counsel-grep-or-swiper)
|
|
:init
|
|
(general-def
|
|
[remap describe-function] #'counsel-describe-function
|
|
[remap describe-variable] #'counsel-describe-variable
|
|
[remap describe-bindings] #'counsel-descbinds
|
|
[remap describe-face] #'counsel-faces
|
|
[remap execute-extended-command] #'counsel-M-x
|
|
[remap find-file] #'counsel-find-file
|
|
[remap imenu] #'counsel-imenu
|
|
[remap load-theme] #'counsel-load-theme)
|
|
:config
|
|
(setq ivy-initial-inputs-alist nil)
|
|
(setq counsel-describe-function-function #'helpful-callable
|
|
counsel-describe-variable-function #'helpful-variable))
|
|
#+END_SRC
|
|
** Counsel etags
|
|
Counsel etags allows me to search generated tag files for tags. I
|
|
already have a function defined to generate the tags, so it's just
|
|
searching them which I find to be a bit of a hassle, and where this
|
|
package comes in.
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package counsel-etags
|
|
:after counsel
|
|
:general
|
|
(leader "st" #'counsel-etags-find-tag))
|
|
#+END_SRC
|
|
* Prompt buffer switch
|
|
Essentially add advice to the window split functions so that they run
|
|
ivy-switch-buffer once they're finished.
|
|
#+BEGIN_SRC emacs-lisp
|
|
(with-eval-after-load "ivy"
|
|
(with-eval-after-load "evil"
|
|
(advice-add #'evil-window-vsplit :after #'ivy-switch-buffer)
|
|
(advice-add #'evil-window-split :after #'ivy-switch-buffer)))
|
|
#+END_SRC
|
|
* Xwidget
|
|
Xwidget is a package (that must be compiled at source) which allows
|
|
for the insertion of arbitrary xwidgets into Emacs through
|
|
buffers. One of its premier uses is in navigating the web which it
|
|
provides through the function =xwidget-webkit-browse-url=. This
|
|
renders a fully functional web browser within Emacs.
|
|
|
|
Though I am not to keen on using Emacs to browse the web /via/ xwidget
|
|
(EWW does a good job on its own), I am very interested in its
|
|
capability to render full fledged HTML documents, as it may come of
|
|
use when doing web development. I can see the results of work very
|
|
quickly without switching windows or workspaces.
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package xwidget
|
|
:straight nil
|
|
:general
|
|
(leader "au" #'xwidget-webkit-browse-url)
|
|
(general-def
|
|
:states 'normal
|
|
:keymaps 'xwidget-webkit-mode-map
|
|
"q" #'quit-window
|
|
"h" #'xwidget-webkit-scroll-backward
|
|
"j" #'xwidget-webkit-scroll-up
|
|
"k" #'xwidget-webkit-scroll-down
|
|
"l" #'xwidget-webkit-scroll-forward
|
|
(kbd "C-f") #'xwidget-webkit-scroll-up
|
|
(kbd "C-b") #'xwidget-webkit-scroll-down
|
|
"H" #'xwidget-webkit-back
|
|
"L" #'xwidget-webkit-forward
|
|
"gu" #'xwidget-webkit-browse-url
|
|
"gr" #'xwidget-webkit-reload
|
|
"gg" #'xwidget-webkit-scroll-top
|
|
"G" #'xwidget-webkit-scroll-bottom))
|
|
#+END_SRC
|
|
|
|
* Avy
|
|
Setup avy with leader.
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package avy
|
|
:after evil
|
|
:general
|
|
(leader
|
|
:infix "s"
|
|
"l" #'avy-goto-line
|
|
"g" #'avy-goto-char-2))
|
|
#+END_SRC
|
|
* Projectile
|
|
Setup projectile, along with the tags command. Also bind "C-c C-p" to
|
|
the projectile command map for quick access.
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package projectile
|
|
:after evil
|
|
:hook (prog-mode . projectile-mode)
|
|
:general
|
|
(leader "p" #'projectile-command-map)
|
|
:init
|
|
(setq projectile-tags-command "ctags -Re -f \"%s\" %s \"%s\"")
|
|
:config
|
|
(projectile-global-mode))
|
|
#+END_SRC
|
|
** Counsel projectile
|
|
Counsel projectile provides the ivy interface to projectile commands, which is really useful.
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package counsel-projectile
|
|
:after (projectile counsel)
|
|
:config
|
|
(counsel-projectile-mode +1))
|
|
#+END_SRC
|
|
* Mail
|
|
** Notmuch
|
|
#+BEGIN_SRC emacs-lisp
|
|
(setq +mail/signature "---------------\nAryadev Chavali")
|
|
(use-package notmuch
|
|
:commands notmuch
|
|
:general
|
|
(leader "am" #'notmuch)
|
|
:custom
|
|
((notmuch-show-logo nil)
|
|
(message-signature +mail/signature)
|
|
(mail-signature +mail/signature))
|
|
:init
|
|
(defun +mail/sync-mail ()
|
|
"Sync mail via mbsync."
|
|
(interactive)
|
|
(start-process-shell-command "" nil "mbsync -a"))
|
|
:config
|
|
(evil-define-key 'normal notmuch-hello-mode-map "M" #'+mail/sync-mail)
|
|
(evil-collection-notmuch-setup))
|
|
#+END_SRC
|
|
** Smtpmail
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package smtpmail
|
|
:commands mail-send
|
|
:after notmuch
|
|
:custom
|
|
((smtpmail-smtp-server "mail.aryadevchavali.com")
|
|
(smtpmail-smtp-user "aryadev")
|
|
(smtpmail-smtp-service 587)
|
|
(smtpmail-stream-type 'starttls))
|
|
:init
|
|
(setq send-mail-function #'smtpmail-send-it
|
|
message-send-mail-function #'smtpmail-send-it))
|
|
#+END_SRC
|
|
** Org message
|
|
Org message allows for the use of org mode when composing mails,
|
|
generating HTML multipart emails. This integrates the WYSIWYG
|
|
experience into mail in Emacs while also providing powerful text
|
|
features with basically no learning curve (as long as you've already
|
|
learnt the basics of org).
|
|
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package org-msg
|
|
:after 'notmuch
|
|
:hook (message-mode . org-msg-mode))
|
|
#+END_SRC
|
|
* Dired
|
|
Setup for dired. Firstly, as it's an inbuilt package don't let
|
|
straight try and download it. Make dired-hide-details-mode the
|
|
default mode when dired-mode, as it removes the clutter. Create a
|
|
keymap =dx:dired-map= which is bound to the prefix "C-c d", binding
|
|
useful dired functions. Setup evil collection for dired (even though
|
|
dired doesn't really conflict with evil, there are some black corners
|
|
I'd like to adjust)
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package dired
|
|
:straight nil
|
|
:hook (dired-mode . dired-hide-details-mode)
|
|
:after evil-collection
|
|
:general
|
|
(leader
|
|
:infix "d"
|
|
"f" #'find-dired
|
|
"D" #'dired-other-window
|
|
"d" #'dired-jump)
|
|
:config
|
|
(evil-collection-dired-setup))
|
|
#+END_SRC
|
|
* Hydra
|
|
Use hydras for stuff that I use often, currently buffer manipulation
|
|
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package hydra
|
|
:after evil
|
|
:init
|
|
(defun dx:kill-defun ()
|
|
"Mark defun then kill it."
|
|
(interactive)
|
|
(mark-defun)
|
|
(delete-active-region t))
|
|
|
|
(defun dx:paste-section ()
|
|
"Paste the current kill-region content above section."
|
|
(interactive)
|
|
(open-line 1)
|
|
(yank))
|
|
|
|
:config
|
|
(defhydra hydra-buffer (evil-normal-state-map "SPC b")
|
|
"buffer-hydra"
|
|
("l" next-buffer)
|
|
("h" previous-buffer)
|
|
("c" kill-this-buffer))
|
|
|
|
(defhydra hydra-code-manipulator (global-map "C-x c")
|
|
"code-manip"
|
|
("j" evil-forward-section-begin)
|
|
("k" evil-backward-section-begin)
|
|
("m" mark-defun)
|
|
("d" dx:kill-defun)
|
|
("p" dx:paste-section)
|
|
("TAB" evil-toggle-fold)))
|
|
#+END_SRC
|
|
* IBuffer
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package ibuffer
|
|
:after evil-collection
|
|
:config
|
|
(evil-collection-ibuffer-setup))
|
|
#+END_SRC
|
|
* Helpful
|
|
Basic setup, will be fully integrated in counsel.
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package helpful
|
|
:commands (helpful-callable helpful-variable)
|
|
:config
|
|
(evil-define-key 'normal helpful-mode-map "q" #'quit-window))
|
|
#+END_SRC
|
|
* Which-key
|
|
Pretty simple, just activate after init.
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package which-key
|
|
:hook (after-init . which-key-mode))
|
|
#+END_SRC
|
|
* Yasnippet
|
|
Yasnippet is a great package for snippets, which I use heavily in
|
|
programming and org-mode. I setup here the global mode for yasnippet
|
|
and a collection of snippets for ease of use.
|
|
** Yasnippet default
|
|
Setup global mode after evil mode has been loaded
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package yasnippet
|
|
:after evil
|
|
:hook ((prog-mode . yas-minor-mode)
|
|
(text-mode . yas-minor-mode))
|
|
:general
|
|
(leader
|
|
"i" #'yas-insert-snippet)
|
|
:config
|
|
(yas-load-directory (concat user-emacs-directory "snippets")))
|
|
#+END_SRC
|
|
** Yasnippet snippets
|
|
Collection of snippets, activate after yasnippet has been loaded.
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package yasnippet-snippets
|
|
:after yasnippet)
|
|
#+END_SRC
|
|
* Yatemplate
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package yatemplate
|
|
:after yasnippet
|
|
:config
|
|
(yatemplate-fill-alist))
|
|
#+END_SRC
|
|
* Keychord
|
|
Keychord is only really here for this one chord I wish to define: "jk"
|
|
for exiting insert state. Otherwise, I don't really need it.
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package key-chord
|
|
:after evil
|
|
:config
|
|
(key-chord-define evil-insert-state-map "jk" #'evil-normal-state)
|
|
(key-chord-mode +1))
|
|
#+END_SRC
|
|
* Ripgrep
|
|
The ripgrep package provides utilities to grep projects and files for
|
|
strings via the rg tool. Though [[*Ivy][ivy]] comes with =counsel-rg= using it
|
|
makes me dependent on the ivy framework, and this configuration is
|
|
intentionally built to be modular and switchable.
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package rg
|
|
:after evil
|
|
:general
|
|
(leader "r" #'rg)
|
|
(:keymaps 'rg-mode-map
|
|
"]]" #'rg-next-file
|
|
"[[" #'rg-prev-file
|
|
"q" #'quit-window)
|
|
:init
|
|
(setq rg-group-result t
|
|
rg-hide-command t
|
|
rg-show-columns nil
|
|
rg-show-header t
|
|
rg-custom-type-aliases nil
|
|
rg-default-alias-fallback "all"))
|
|
#+END_SRC
|
|
* Magit
|
|
Magit is *the* git porcelain for Emacs, which perfectly encapsulates
|
|
the git cli. In this case, I just need to setup the bindings for it.
|
|
As magit will definitely load after evil (as it must be run by a
|
|
binding, and evil will load after init), I can use evil-collection
|
|
freely.
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package magit
|
|
:general
|
|
(leader "g" #'magit-status))
|
|
|
|
(use-package evil-magit
|
|
:after magit)
|
|
#+END_SRC
|
|
* Company
|
|
Company is the auto complete system I use. I don't like having heavy
|
|
setups for company, as it only makes it worse to use. In this case,
|
|
just setup some evil binds for company
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package company
|
|
:hook (prog-mode . company-mode)
|
|
:bind (("C-SPC" . company-complete)
|
|
:map company-active-map
|
|
("M-j" . company-select-next)
|
|
("M-k" . company-select-previous)))
|
|
#+END_SRC
|
|
* Elfeed
|
|
Elfeed is the perfect RSS feed reader, integrated into Emacs
|
|
perfectly. I've got a set of feeds that I use for a large variety of
|
|
stuff, mostly media and entertainment. I've also bound "C-c r" to
|
|
elfeed for loading the system.
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package elfeed
|
|
:general
|
|
(leader "ar" #'elfeed)
|
|
:init
|
|
(setq +rss/feed-urls
|
|
'(("Arch Linux" "https://www.archlinux.org/feeds/news/" Linux)
|
|
("LEMMiNO" "https://www.youtube.com/feeds/videos.xml?channel_id=UCRcgy6GzDeccI7dkbbBna3Q" YouTube Stories)
|
|
("Dark Sominium" "https://www.youtube.com/feeds/videos.xml?channel_id=UC_e39rWdkQqo5-LbiLiU10g" YouTube Stories)
|
|
("Dark Sominium Music" "https://www.youtube.com/feeds/videos.xml?channel_id=UCkLiZ_zLynyNd5fd62hg1Kw" YouTube Music)
|
|
("Nexpo" "https://www.youtube.com/feeds/videos.xml?channel_id=UCpFFItkfZz1qz5PpHpqzYBw" YouTube)
|
|
("Techquickie" "https://www.youtube.com/feeds/videos.xml?channel_id=UC0vBXGSyV14uvJ4hECDOl0Q" YouTube)
|
|
("Captain Sinbad" "https://www.youtube.com/feeds/videos.xml?channel_id=UC8XKyvQ5Ne_bvYbgv8LaIeg" YouTube)
|
|
("3B1B" "https://www.youtube.com/feeds/videos.xml?channel_id=UCYO_jab_esuFRV4b17AJtAw" YouTube)
|
|
("Fredrik Knusden" "https://www.youtube.com/feeds/videos.xml?channel_id=UCbWcXB0PoqOsAvAdfzWMf0w" YouTube Stories)
|
|
("Barely Sociable" "https://www.youtube.com/feeds/videos.xml?channel_id=UC9PIn6-XuRKZ5HmYeu46AIw" YouTube Stories)
|
|
("Atrocity Guide" "https://www.youtube.com/feeds/videos.xml?channel_id=UCn8OYopT9e8tng-CGEWzfmw" YouTube Stories)
|
|
("Philip Defranco" "https://www.youtube.com/feeds/videos.xml?channel_id=UClFSU9_bUb4Rc6OYfTt5SPw" YouTube News)
|
|
("Hacker News" "http://morss.aryadevchavali.com/news.ycombinator.com/rss" Social)
|
|
("Hacker Factor" "https://www.hackerfactor.com/blog/index.php?/feeds/index.rss2" Social)
|
|
("BBC Top News" "http://morss.aryadevchavali.com/feeds.bbci.co.uk/news/rss.xml" News)
|
|
("BBC Tech News" "http://morss.aryadevchavali.com/feeds.bbci.co.uk/news/technology/rss.xml" News)))
|
|
(setq elfeed-db-directory (concat user-emacs-directory "elfeed"))
|
|
:config
|
|
(evil-collection-elfeed-setup)
|
|
(evil-define-key 'normal elfeed-search-mode-map "gr" #'elfeed-update)
|
|
(evil-define-key 'normal elfeed-search-mode-map "s" #'elfeed-search-live-filter)
|
|
(evil-define-key 'normal elfeed-search-mode-map "<return>" #'elfeed-search-show-entry)
|
|
(setq elfeed-feeds (cl-map 'list #'(lambda (item) (append (list (nth 1 item)) (cdr (cdr item)))) +rss/feed-urls)))
|
|
#+END_SRC
|
|
* Eshell
|
|
Eshell is the integrated shell environment for Emacs. Though it isn't
|
|
necessarily *the best* shell, it really suits the 'integrated
|
|
computing environment' moniker that Emacs gets.
|
|
|
|
It may be argued that Emacs integrates within itself many of the
|
|
functionalities that one would use within a shell or terminal. Stuff
|
|
like compilation, file management, large scale text manipulation could
|
|
be done through Emacs' own tools (=compile=, =dired= and =occur= come
|
|
to mind).
|
|
|
|
However, the Eshell is still a useful tool even if you don't use it
|
|
for classical shell tasks. As it is integrated with Emacs, it actually
|
|
has two language parsers: one for standard shell scripting [echo
|
|
"Hello, world"] and one for Emacs lisp [(message "Hello,
|
|
world!")]. This means that eshell is essentially just a REPL for Emacs
|
|
lisp with extra shell capabilities. You can use programs defined in
|
|
any language (as long as it's in path and executable) and also run
|
|
lisp functions. This allows for mix-and-match capabilities when
|
|
needed, so cognitive load decreases as you can rely on either parsers
|
|
when necessary.
|
|
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package eshell
|
|
:general
|
|
(leader
|
|
"t" #'eshell)
|
|
:init
|
|
(setq eshell-cmpl-ignore-case t
|
|
eshell-cd-on-directory t))
|
|
#+END_SRC
|
|
* Window management
|
|
Window management is really important. I find the default window
|
|
handling of Emacs incredibly annoying: sometimes consuming my windows,
|
|
sometimes creating new ones. So, as Emacs is the ultimate editor, I
|
|
want to configure and fine tune the window management of Emacs.
|
|
#+BEGIN_SRC emacs-lisp
|
|
(setq display-buffer-alist
|
|
'(("\\*e?shell\\*"
|
|
(display-buffer-in-side-window)
|
|
(window-height . 0.25)
|
|
(side . bottom)
|
|
(slot . 0))
|
|
("\\*[Hh]elp.*"
|
|
(display-buffer-in-side-window)
|
|
(window-height . 0.25)
|
|
(side . bottom)
|
|
(slot . 1))
|
|
("magit:.*"
|
|
(display-buffer-in-side-window)
|
|
(side . right)
|
|
(slot . -1)
|
|
(window-width . 0.5))
|
|
("magit-diff:.*"
|
|
(display-buffer-in-side-window)
|
|
(side . right)
|
|
(window-width . 0.5))
|
|
("magit-log:.*"
|
|
(display-buffer-in-side-window)
|
|
(side . right)
|
|
(window-width . 0.5))
|
|
("\\*compilation\\*"
|
|
(display-buffer-in-side-window)
|
|
(side . bottom)
|
|
(slot . -1)
|
|
(window-height . 0.25))
|
|
("\\*Flycheck.*"
|
|
(display-buffer-in-side-window)
|
|
(side . bottom)
|
|
(window-height . 0.25)
|
|
(slot . 0))
|
|
("\\*rg.*"
|
|
(display-buffer-in-side-window)
|
|
(side . bottom)
|
|
(window-height . 0.25)
|
|
(slot . 1))
|
|
("\\*Python\\*"
|
|
(display-buffer-in-side-window)
|
|
(side . bottom)
|
|
(window-height . 0.25))
|
|
("\\*Org Export.*"
|
|
(display-buffer-in-side-window)
|
|
(side . bottom)
|
|
(window-height . 0.25)
|
|
(slot . 0))
|
|
("\\*Async Shell Command\\*"
|
|
(display-buffer-in-side-window)
|
|
(side . bottom)
|
|
(window-height . 0.25))
|
|
))
|
|
#+END_SRC
|
|
* Text modes
|
|
** Flyspell
|
|
Flyspell allows me to quickly spell check text documents. I use
|
|
flyspell primarily in org mode, as that is my preferred prose writing
|
|
software, but I also need it in commit messages and so on. So
|
|
flyspell-mode should be hooked to text-mode.
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package flyspell
|
|
:hook (text-mode . flyspell-mode))
|
|
#+END_SRC
|
|
|
|
As I use ivy I'd like the flyspell correct interface (which allow for
|
|
corrections to real words) to use ivy.
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package flyspell-correct-ivy
|
|
:after flyspell
|
|
:general
|
|
(general-def
|
|
:states '(normal insert)
|
|
:map flyspell-mode-map
|
|
"M-A" #'flyspell-correct-at-point
|
|
"M-a" #'ispell-word))
|
|
#+END_SRC
|
|
** Set auto-fill-mode for all text-modes
|
|
Auto fill mode is nice for most text modes, 80 char limit is great.
|
|
|
|
#+BEGIN_SRC emacs-lisp
|
|
(add-hook 'text-mode-hook #'auto-fill-mode)
|
|
#+END_SRC
|
|
** Delete a sentence in auto fill
|
|
In long lines via truncate lines, deleting till the end of the
|
|
sentence was easy via vim motions. However, the same action is
|
|
difficult with auto-fill-mode where sentences are separated through
|
|
(potentially several) newlines which makes vim motions
|
|
difficult. Thus, I propose some form of functionality which allows you
|
|
to:
|
|
|
|
- Find the next closest period denoting the end of the sentence
|
|
- Delete the region between the point of invocation and the found period
|
|
|
|
This essentially does the same task as vim motion based deletion, but
|
|
can handle the newlines. To not trample on the toes of any package,
|
|
I'll set it to "M-d" (kill-word), the most inoffensive binding
|
|
possible which is still mnemonic.
|
|
|
|
First, the function. I'll use search-forward (from zap* lib) to find
|
|
the period. Then auto-fill to make it look nice.
|
|
#+BEGIN_SRC emacs-lisp
|
|
(defun +text/delete-till-sentence ()
|
|
"Delete all text from current point to the next closest period."
|
|
(interactive)
|
|
(set-mark-command nil)
|
|
(search-forward ". ")
|
|
(kill-region (region-beginning) (region-end))
|
|
(fill-paragraph))
|
|
#+END_SRC
|
|
|
|
Now, the binding
|
|
#+BEGIN_SRC emacs-lisp
|
|
(general-def
|
|
:states '(normal insert)
|
|
(kbd "M-d") #'+text/delete-till-sentence)
|
|
#+END_SRC
|
|
* Org
|
|
** Org default with evil
|
|
Setup for org mode, currently basically nothing. Has evil-org for
|
|
evil bindings.
|
|
|
|
Also setup a lot of variables, particularly for latex exports.
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package org
|
|
:hook (org-mode . yas-minor-mode)
|
|
:bind (:map org-mode-map
|
|
([remap imenu] . counsel-org-goto))
|
|
:custom
|
|
((org-edit-src-content-indentation 0)
|
|
(org-src-window-setup 'current-window)
|
|
(org-indirect-buffer-display 'current-window)
|
|
(org-eldoc-breadcrumb-separator " → ")
|
|
(org-enforce-todo-dependencies t)
|
|
(org-fontify-quote-and-verse-blocks t)
|
|
(org-fontify-whole-heading-line t)
|
|
(org-footnote-auto-label 'plain)
|
|
(org-hide-leading-stars t)
|
|
(org-hide-emphasis-markers nil)
|
|
(org-image-actual-width nil)
|
|
(org-priority-faces '((?A . error) (?B . warning) (?C . success)))
|
|
(org-startup-indented t)
|
|
(org-tags-column 0)
|
|
(org-use-sub-superscripts '{})
|
|
(org-latex-listings 'minted)
|
|
(org-babel-load-languages '((emacs-lisp . t)
|
|
(C . t)))
|
|
(org-latex-packages-alist '(("" "minted")))
|
|
(org-latex-pdf-process '("%latex -interaction nonstopmode -shell-escape -output-directory %o %f"
|
|
"%latex -interaction nonstopmode -shell-escape -output-directory %o %f"
|
|
"%latex -interaction nonstopmode -shell-escape -output-directory %o %f"))
|
|
(org-latex-minted-options '(("style" "xcode")
|
|
("linenos")
|
|
("frame" "single")
|
|
("mathescape")
|
|
("fontfamily" "courier")
|
|
("samepage" "false")
|
|
("breaklines" "true")
|
|
("breakanywhere" "true")
|
|
))))
|
|
|
|
(use-package evil-org
|
|
:hook (org-mode . evil-org-mode))
|
|
#+END_SRC
|
|
** Org fragtog
|
|
Toggle latex fragments in org mode so you get fancy maths symbols. I
|
|
use latex a bit in org mode as it is the premier way of getting
|
|
mathematical symbols and text rendered and compiled, but org mode >
|
|
latex.
|
|
|
|
As Org mode has the ability to accept arbitrary inputs of Latex
|
|
(through escaped (square) brackets), allowing me to observe how they
|
|
look is nice to have.
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package org-fragtog
|
|
:hook (org-mode . org-fragtog-mode))
|
|
#+END_SRC
|
|
** Org pretty tables
|
|
Make the default ASCII tables of org mode pretty with
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package org-pretty-table-mode
|
|
:straight (org-pretty-table-mode :type git :host github :repo "Fuco1/org-pretty-table")
|
|
:hook org-mode)
|
|
#+END_SRC
|
|
** Org superstar
|
|
Org superstar adds cute little unicode symbols for headers, much
|
|
better than the default asterisks.
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package org-superstar
|
|
:hook (org-mode . org-superstar-mode))
|
|
#+END_SRC
|
|
* Major modes and Programming
|
|
Setups for common major modes and languages. Here are some basic
|
|
packages for programming first
|
|
** Smartparens
|
|
Smartparens is a smarter electric-parens, it's much more aware of
|
|
stuff and easier to use.
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package smartparens
|
|
:hook (prog-mode . smartparens-mode)
|
|
:hook (text-mode . smartparens-mode)
|
|
:after evil
|
|
:config
|
|
(setq sp-highlight-pair-overlay nil
|
|
sp-highlight-wrap-overlay t
|
|
sp-highlight-wrap-tag-overlay t)
|
|
|
|
(let ((unless-list '(sp-point-before-word-p
|
|
sp-point-after-word-p
|
|
sp-point-before-same-p)))
|
|
(sp-pair "'" nil :unless unless-list)
|
|
(sp-pair "\"" nil :unless unless-list))
|
|
(sp-local-pair sp-lisp-modes "(" ")" :unless '(:rem sp-point-before-same-p))
|
|
(require 'smartparens-config))
|
|
#+END_SRC
|
|
** Show-paren-mode
|
|
Show parenthesis for Emacs
|
|
#+BEGIN_SRC emacs-lisp
|
|
(add-hook 'prog-mode-hook #'show-paren-mode)
|
|
#+END_SRC
|
|
** Eldoc
|
|
Eldoc presents documentation to the user upon placing ones cursor upon
|
|
any symbol. This is very useful when programming as it:
|
|
- presents the arguments of functions while writing calls for them
|
|
- presents typing and documentation of variables
|
|
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package eldoc
|
|
:hook (prog-mode . eldoc-mode))
|
|
|
|
(use-package eldoc-box
|
|
:hook (eldoc-mode . eldoc-box-hover-mode)
|
|
:custom
|
|
((eldoc-box-position-function #'eldoc-box--default-upper-corner-position-function)
|
|
(eldoc-box-clear-with-C-g t))
|
|
:config
|
|
(advice-add #'evil-force-normal-state :before #'eldoc-box-quit-frame))
|
|
#+END_SRC
|
|
** Eglot
|
|
Eglot is a library of packages to communicate with LSP servers for
|
|
better programming capabilities. Interactions with a server provide
|
|
results to the client, done through JSON.
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package eglot
|
|
:hook (c++-mode . eglot-ensure)
|
|
:hook (c-mode . eglot-ensure)
|
|
:bind (:map eglot-mode-map
|
|
("<f2>" . eglot-rename))
|
|
:general
|
|
(leader
|
|
:keymaps '(eglot-mode-map)
|
|
:infix "c"
|
|
"f" #'eglot-format
|
|
"a" #'eglot-code-actions
|
|
"r" #'eglot-rename)
|
|
:config
|
|
(add-to-list 'eglot-server-programs '((c-mode c++-mode) "clangd")))
|
|
#+END_SRC
|
|
** Flycheck
|
|
Flycheck is the checking system for Emacs. I don't necessarily like
|
|
having all my code checked all the time, so I haven't added a hook to
|
|
prog-mode as it would be better for me to decide when I want checking
|
|
and when I don't.
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package flycheck
|
|
:commands flycheck-mode
|
|
:config
|
|
(defun +flycheck/list-errors-load-flycheck ()
|
|
"Load flycheck if not available, then list errors."
|
|
(interactive)
|
|
(when (not (or flycheck-mode global-flycheck-mode))
|
|
(flycheck-mode))
|
|
(flycheck-list-errors)))
|
|
#+END_SRC
|
|
** Activate tabs
|
|
Set tabs to nil by default, with normal tab size set to 2.
|
|
#+BEGIN_SRC emacs-lisp
|
|
(setq-default indent-tabs-mode nil
|
|
tab-width 2)
|
|
#+END_SRC
|
|
|
|
Add a function to activate tabs mode for any modes you want tabs in.
|
|
#+BEGIN_SRC emacs-lisp
|
|
(defun dx:activate-tabs ()
|
|
(interactive)
|
|
(setq indent-tabs-mode t))
|
|
#+END_SRC
|
|
** C/C++
|
|
Setup for C and C++ modes via the cc-mode package.
|
|
|
|
C and C++ are great languages for general purpose programming. Though
|
|
lisp is more aesthetically and mentally pleasing, they get the job
|
|
done. Furthermore, they provide speed and finer control in trade of
|
|
aesthetics and security-based abstractions.
|
|
|
|
When writing C/C++ code, I use folds and section manipulation quite a
|
|
bit so observing folds is quite important for me when considering a
|
|
codebase. Thus, I observed the two main styles of brace placement and
|
|
how they do folds.
|
|
|
|
#+begin_src c :tangle no
|
|
if (cond) {...}
|
|
#+end_src
|
|
vs
|
|
#+begin_src c :tangle no
|
|
if (cond)
|
|
{....}
|
|
#+end_src
|
|
|
|
I don't print my code, nor am I absolutely pressed for screen real
|
|
estate in terms of height (such that newlines matter). Width matters
|
|
to me as I do use Emacs multiplexing capabilities often. Thus, with
|
|
these in mind the open brace style is a better option than the
|
|
opposing style.
|
|
|
|
Also, with large code bases consistency is important. I personally use
|
|
tabs as they are more accessible: anyone can set their tab width such
|
|
that it best suits them. Furthermore, tabs produce smaller source
|
|
files. However, this isn't set in stone and I will return to no tabs
|
|
when needed in projects.
|
|
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package cc-mode
|
|
:hook (c-mode . dx:activate-tabs)
|
|
:hook (c++-mode . dx:activate-tabs)
|
|
:init
|
|
(setq-default c-basic-offset 2)
|
|
(setq c-default-style '((java-mode . "java")
|
|
(awk-mode . "awk")
|
|
(other . "user")))
|
|
:config
|
|
(c-add-style
|
|
"user"
|
|
'((c-basic-offset . 2)
|
|
(c-comment-only-line-offset . 0)
|
|
(c-hanging-braces-alist (brace-list-open)
|
|
(brace-entry-open)
|
|
(substatement-open after)
|
|
(block-close . c-snug-do-while)
|
|
(arglist-cont-nonempty))
|
|
(c-cleanup-list brace-else-brace)
|
|
(c-offsets-alist
|
|
(statement-block-intro . +)
|
|
(knr-argdecl-intro . 0)
|
|
(substatement-open . 0)
|
|
(substatement-label . 0)
|
|
(access-label . 0)
|
|
(label . 0)
|
|
(statement-cont . +)))))
|
|
#+END_SRC
|
|
*** Clang format
|
|
Clang format for when:
|
|
- eglot isn't working/I'm not running it
|
|
- eglot format is bad
|
|
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package clang-format
|
|
:after cc-mode
|
|
:config
|
|
(bind-key "C-c '" #'clang-format-region c-mode-map)
|
|
(bind-key "C-c '" #'clang-format-region c++-mode-map))
|
|
#+END_SRC
|
|
** HTML/CSS/JS
|
|
Firstly, web mode for consistent colouring of syntax.
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package web-mode
|
|
:mode ("\\.html" . web-mode)
|
|
:mode ("\\.js" . web-mode)
|
|
:mode ("\\.css" . web-mode)
|
|
:custom
|
|
((web-mode-markup-indent-offset 2)
|
|
(web-mode-css-indent-offset 2)))
|
|
#+END_SRC
|
|
|
|
Then emmet for super speed
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package emmet-mode
|
|
:hook (web-mode . emmet-mode)
|
|
:general
|
|
(general-def
|
|
:states 'insert
|
|
:keymaps 'emmet-mode-keymap
|
|
"TAB" #'emmet-expand-line
|
|
"M-j" #'emmet-next-edit-point
|
|
"M-k" #'emmet-prev-edit-point))
|
|
#+END_SRC
|
|
** Emacs lisp
|
|
Add a new lisp indent function which indents newline lists more
|
|
appropriately.
|
|
#+BEGIN_SRC emacs-lisp
|
|
(with-eval-after-load "lisp-mode"
|
|
(defun +modded/lisp-indent-function (indent-point state)
|
|
"This function is the normal value of the variable `lisp-indent-function'.
|
|
The function `calculate-lisp-indent' calls this to determine
|
|
if the arguments of a Lisp function call should be indented specially.
|
|
INDENT-POINT is the position at which the line being indented begins.
|
|
Point is located at the point to indent under (for default indentation);
|
|
STATE is the `parse-partial-sexp' state for that position.
|
|
If the current line is in a call to a Lisp function that has a non-nil
|
|
property `lisp-indent-function' (or the deprecated `lisp-indent-hook'),
|
|
it specifies how to indent. The property value can be:
|
|
,* `defun', meaning indent `defun'-style
|
|
\(this is also the case if there is no property and the function
|
|
has a name that begins with \"def\", and three or more arguments);
|
|
,* an integer N, meaning indent the first N arguments specially
|
|
(like ordinary function arguments), and then indent any further
|
|
arguments like a body;
|
|
,* a function to call that returns the indentation (or nil).
|
|
`lisp-indent-function' calls this function with the same two arguments
|
|
that it itself received.
|
|
This function returns either the indentation to use, or nil if the
|
|
Lisp function does not specify a special indentation."
|
|
(let ((normal-indent (current-column))
|
|
(orig-point (point)))
|
|
(goto-char (1+ (elt state 1)))
|
|
(parse-partial-sexp (point) calculate-lisp-indent-last-sexp 0 t)
|
|
(cond
|
|
;; car of form doesn't seem to be a symbol, or is a keyword
|
|
((and (elt state 2)
|
|
(or (not (looking-at "\\sw\\|\\s_"))
|
|
(looking-at ":")))
|
|
(if (not (> (save-excursion (forward-line 1) (point))
|
|
calculate-lisp-indent-last-sexp))
|
|
(progn (goto-char calculate-lisp-indent-last-sexp)
|
|
(beginning-of-line)
|
|
(parse-partial-sexp (point)
|
|
calculate-lisp-indent-last-sexp 0 t)))
|
|
;; Indent under the list or under the first sexp on the same
|
|
;; line as calculate-lisp-indent-last-sexp. Note that first
|
|
;; thing on that line has to be complete sexp since we are
|
|
;; inside the innermost containing sexp.
|
|
(backward-prefix-chars)
|
|
(current-column))
|
|
((and (save-excursion
|
|
(goto-char indent-point)
|
|
(skip-syntax-forward " ")
|
|
(not (looking-at ":")))
|
|
(save-excursion
|
|
(goto-char orig-point)
|
|
(looking-at ":")))
|
|
(save-excursion
|
|
(goto-char (+ 2 (elt state 1)))
|
|
(current-column)))
|
|
(t
|
|
(let ((function (buffer-substring (point)
|
|
(progn (forward-sexp 1) (point))))
|
|
method)
|
|
(setq method (or (function-get (intern-soft function)
|
|
'lisp-indent-function)
|
|
(get (intern-soft function) 'lisp-indent-hook)))
|
|
(cond ((or (eq method 'defun)
|
|
(and (null method)
|
|
(> (length function) 3)
|
|
(string-match "\\`def" function)))
|
|
(lisp-indent-defform state indent-point))
|
|
((integerp method)
|
|
(lisp-indent-specform method state
|
|
indent-point normal-indent))
|
|
(method
|
|
(funcall method indent-point state))))))))
|
|
(add-hook 'emacs-lisp-mode-hook #'(lambda () (interactive) (setq-local lisp-indent-function #'+modded/lisp-indent-function))))
|
|
#+END_SRC
|