;; -*- lexical-binding: t; -*- (use-package elisp-mode :straight (:type built-in) :general (emacs-lisp-mode-map "C-c C-c" #'eval-defun "C-c C-k" #'eval-buffer) :config ;; Indent quoted lists in a nicer way (advice-add #'calculate-lisp-indent :override #'void~calculate-lisp-indent) (defun void~calculate-lisp-indent (&optional parse-start) "Add better indentation for quoted and backquoted lists." ;; This line because `calculate-lisp-indent-last-sexp` was defined with `defvar` ;; with it's value ommited, marking it special and only defining it locally. So ;; if you don't have this, you'll get a void variable error. (defvar calculate-lisp-indent-last-sexp) (save-excursion (beginning-of-line) (let ((indent-point (point)) state ;; setting this to a number inhibits calling hook (desired-indent nil) (retry t) calculate-lisp-indent-last-sexp containing-sexp) (cond ((or (markerp parse-start) (integerp parse-start)) (goto-char parse-start)) ((null parse-start) (beginning-of-defun)) (t (setq state parse-start))) (unless state ;; Find outermost containing sexp (while (< (point) indent-point) (setq state (parse-partial-sexp (point) indent-point 0)))) ;; Find innermost containing sexp (while (and retry state (> (elt state 0) 0)) (setq retry nil) (setq calculate-lisp-indent-last-sexp (elt state 2)) (setq containing-sexp (elt state 1)) ;; Position following last unclosed open. (goto-char (1+ containing-sexp)) ;; Is there a complete sexp since then? (if (and calculate-lisp-indent-last-sexp (> calculate-lisp-indent-last-sexp (point))) ;; Yes, but is there a containing sexp after that? (let ((peek (parse-partial-sexp calculate-lisp-indent-last-sexp indent-point 0))) (if (setq retry (car (cdr peek))) (setq state peek))))) (if retry nil ;; Innermost containing sexp found (goto-char (1+ containing-sexp)) (if (not calculate-lisp-indent-last-sexp) ;; indent-point immediately follows open paren. ;; Don't call hook. (setq desired-indent (current-column)) ;; Find the start of first element of containing sexp. (parse-partial-sexp (point) calculate-lisp-indent-last-sexp 0 t) (cond ((looking-at "\\s(") ;; First element of containing sexp is a list. ;; Indent under that list. ) ((> (save-excursion (forward-line 1) (point)) calculate-lisp-indent-last-sexp) ;; This is the first line to start within the containing sexp. ;; It's almost certainly a function call. (if (or ;; Containing sexp has nothing before this line ;; except the first element. Indent under that element. (= (point) calculate-lisp-indent-last-sexp) ;; First sexp after `containing-sexp' is a keyword. This ;; condition is more debatable. It's so that I can have ;; unquoted plists in macros. It assumes that you won't ;; make a function whose name is a keyword. ;; (when-let (char-after (char-after (1+ containing-sexp))) ;; (char-equal char-after ?:)) ;; Check for quotes or backquotes around. (let* ((positions (elt state 9)) (last (car (last positions))) (rest (reverse (butlast positions))) (any-quoted-p nil) (point nil)) (or (when-let (char (char-before last)) (or (char-equal char ?') (char-equal char ?`))) (progn (while (and rest (not any-quoted-p)) (setq point (pop rest)) (setq any-quoted-p (or (when-let (char (char-before point)) (or (char-equal char ?') (char-equal char ?`))) (save-excursion (goto-char (1+ point)) (looking-at-p "\\(?:back\\)?quote[\t\n\f\s]+("))))) any-quoted-p)))) ;; Containing sexp has nothing before this line ;; except the first element. Indent under that element. nil ;; Skip the first element, find start of second (the first ;; argument of the function call) and indent under. (progn (forward-sexp 1) (parse-partial-sexp (point) calculate-lisp-indent-last-sexp 0 t))) (backward-prefix-chars)) (t ;; Indent beneath first sexp on same line as ;; `calculate-lisp-indent-last-sexp'. Again, it's ;; almost certainly a function call. (goto-char calculate-lisp-indent-last-sexp) (beginning-of-line) (parse-partial-sexp (point) calculate-lisp-indent-last-sexp 0 t) (backward-prefix-chars))))) ;; Point is at the point to indent under unless we are inside a string. ;; Call indentation hook except when overridden by lisp-indent-offset ;; or if the desired indentation has already been computed. (let ((normal-indent (current-column))) (cond ((elt state 3) ;; Inside a string, don't change indentation. nil) ((and (integerp lisp-indent-offset) containing-sexp) ;; Indent by constant offset (goto-char containing-sexp) (+ (current-column) lisp-indent-offset)) ;; in this case calculate-lisp-indent-last-sexp is not nil (calculate-lisp-indent-last-sexp (or ;; try to align the parameters of a known function (and lisp-indent-function (not retry) (funcall lisp-indent-function indent-point state)) ;; If the function has no special alignment ;; or it does not apply to this argument, ;; try to align a constant-symbol under the last ;; preceding constant symbol, if there is such one of ;; the last 2 preceding symbols, in the previous ;; uncommented line. (and (save-excursion (goto-char indent-point) (skip-chars-forward " \t") (looking-at ":")) ;; The last sexp may not be at the indentation ;; where it begins, so find that one, instead. (save-excursion (goto-char calculate-lisp-indent-last-sexp) ;; Handle prefix characters and whitespace ;; following an open paren. (Bug#1012) (backward-prefix-chars) (while (not (or (looking-back "^[ \t]*\\|([ \t]+" (line-beginning-position)) (and containing-sexp (>= (1+ containing-sexp) (point))))) (forward-sexp -1) (backward-prefix-chars)) (setq calculate-lisp-indent-last-sexp (point))) (> calculate-lisp-indent-last-sexp (save-excursion (goto-char (1+ containing-sexp)) (parse-partial-sexp (point) calculate-lisp-indent-last-sexp 0 t) (point))) (let ((parse-sexp-ignore-comments t) indent) (goto-char calculate-lisp-indent-last-sexp) (or (and (looking-at ":") (setq indent (current-column))) (and (< (line-beginning-position) (prog2 (backward-sexp) (point))) (looking-at ":") (setq indent (current-column)))) indent)) ;; another symbols or constants not preceded by a constant ;; as defined above. normal-indent)) ;; in this case calculate-lisp-indent-last-sexp is nil (desired-indent) (t normal-indent))))))) (provide 'init-elisp)