;; -*- lexical-binding: t; -*- ;; General text editing configuration ;; Better isearch (use-package ctrlf :config (ctrlf-mode 1)) (use-package avy :config (with-eval-after-load 'embark (defun avy-action-embark (point) (unwind-protect (save-excursion (goto-char point) (embark-act)) (select-window (cdr (ring-ref avy-ring 0)))) t) (setf (alist-get ?. avy-dispatch-alist) #'avy-action-embark) (add-to-list 'avy-dispatch-alist '(?\C-. . avy-action-embark))) :bind ("M-f" . avy-goto-char-timer) :custom (avy-style 'pre)) ;; "pair" management, where pairs are parentheses, braces, etc. (use-package smartparens :init (leader-def-key "s" '(nil :which-key "smartparens")) :config (require 'smartparens-config) (smartparens-global-mode) (defun sp-wrap-double-quote () (interactive) (sp-wrap-with-pair "\"")) (defun sp-wrap-single-quote () (interactive) (sp-wrap-with-pair "'")) (defun sp-wrap-angle-bracket () (interactive) (sp-wrap-with-pair "<")) (defun sp-wrap-backtick () (interactive) (sp-wrap-with-pair "`")) (defun sp-after-equals-p (_id action _context) (when (memq action '(insert navigate)) (sp--looking-back-p "=>" 2))) ;; Enable ES6 arrow functions in web mode (defun sp-after-equals-skip-p (ms mb _me) (when (eq ms ">") (save-excursion (goto-char mb) (sp--looking-back-p "=" 1)))) (defun sp-web-mode-is-code-context (id action context) (and (eq action 'insert) (not (or (get-text-property (point) 'part-side) (get-text-property (point) 'block-side))))) (sp-local-pair '(web-mode) "<" nil :when '(sp-web-mode-is-code-context) :unless '(:add sp-after-equals-p) :skip-match 'sp-after-equals-skip-p) :hook (prog-mode . smartparens-strict-mode) (eshell-mode . smartparens-strict-mode) :general ;; Wrapping (leader-map "s(" #'sp-wrap-round "s{" #'sp-wrap-curly "s'" #'sp-wrap-single-quote "s\"" #'sp-wrap-double-quote "sW" #'sp-unwrap-sexp "sl" #'sp-next-sexp "sh" #'sp-backward-sexp "sj" #'sp-down-sexp "sk" #'sp-backward-up-sexp "sL" #'sp-forward-symbol "sH" #'sp-backward-symbol "s^" #'sp-beginning-of-sexp "s$" #'sp-end-of-sexp "st" #'sp-transpose-sexp "su" #'undo-tree-undo "sy" #'sp-copy-sexp "sd" #'sp-kill-sexp "ss" #'sp-forward-slurp-sexp "sS" #'sp-backward-slurp-sexp "sb" #'sp-forward-barf-sexp "sB" #'sp-backward-barf-sexp "sv" #'sp-select-next-thing "sV" #'sp-select-previous-thing) (normal "g(" #'sp-wrap-round "g[" #'sp-wrap-square "g{" #'sp-wrap-curly "g\"" #'sp-wrap-double-quote "g'" #'sp-wrap-single-quote "g<" #'sp-wrap-angle-bracket "g`" #'sp-wrap-backtick)) (use-package evil-smartparens :after (evil smartparens) :hook (smartparens-enabled . evil-smartparens-mode)) ;; Automagical indent (use-package aggressive-indent :hook ((lisp-mode . aggressive-indent-mode) (emacs-lisp-mode . aggressive-indent-mode) (clojure-mode . aggressive-indent-mode))) ;; Highlight indent level for whitespace-sensitive languages (use-package highlight-indent-guides :commands highlight-indent-guides-mode :custom (highlight-indent-guides-method 'character) (highlight-indent-guides-auto-character-face-perc 7) (highlight-indent-guides-responsive 'stack) (highlight-indent-guides-auto-stack-character-face-perc 10)) ;; Handy mode for prose writing and reading (use-package olivetti :general ("C-c o" #'olivetti-mode) :custom (olivetti-body-width 120)) ;; Multiple cursors (use-package evil-multiedit :defer 2 :config (evil-multiedit-default-keybinds)) ;; Don't choke on files with long lines (use-package so-long :straight (:type built-in) :demand t :config (global-so-long-mode)) ;; Polite white-space trimming (use-package ws-butler :hook (prog-mode . ws-butler-mode)) ;; Inc/dec numbers (use-package evil-numbers :straight (:host github :repo "juliapath/evil-numbers") :general (normal "g+" #'evil-numbers/inc-at-pt) (normal "g-" #'evil-numbers/dec-at-pt) (normal "g M-+" #'evil-numbers/inc-at-pt-incremental) (normal "g M--" #'evil-numbers/dec-at-pt-incremental)) ;; Manipulate string inflection, e.g. camelCase -> snake_case (use-package evil-string-inflection :general (normal "gc" #'evil-operator-string-inflection)) (use-package origami :hook ((prog-mode . origami-mode) (text-mode . origami-mode)) :config (defun origami-toggle-all-nodes-same-level (buffer point) (interactive (list (current-buffer) (point))) (when-let ((tree (origami-get-fold-tree buffer))) (when-let ((path (origami-fold-find-path-containing tree point))) (let ((parent (origami-fold-parent path))) (dolist (fold (origami-fold-children parent)) (origami-toggle-node buffer (origami-fold-beg fold))))))) (defun origami-setup () (evil-local-set-key 'normal (kbd "zM") #'origami-toggle-all-nodes-same-level)) (add-hook 'origami-mode-hook #'origami-setup)) ;; Language detection of arbitrary strings/buffers (use-package language-detection :commands (language-detection-buffer language-detection-string) :init (defun language-detection-detect-mode (string) (let* ((map '((ada ada-mode) (awk awk-mode) (c c-mode) (cpp c++-mode) (clojure clojure-mode lisp-mode) (csharp csharp-mode java-mode) (css css-mode) (dart dart-mode) (delphi delphi-mode) (emacslisp emacs-lisp-mode) (erlang erlang-mode) (fortran fortran-mode) (fsharp fsharp-mode) (go go-mode) (groovy groovy-mode) (haskell haskell-mode) (html html-mode) (java java-mode) (javascript javascript-mode) (json json-mode javascript-mode) (latex latex-mode) (lisp lisp-mode) (lua lua-mode) (matlab matlab-mode octave-mode) (objc objc-mode c-mode) (perl perl-mode) (php php-mode) (prolog prolog-mode) (python python-mode) (r r-mode) (ruby ruby-mode) (rust rust-mode) (scala scala-mode) (shell shell-script-mode) (smalltalk smalltalk-mode) (sql sql-mode) (swift swift-mode) (visualbasic visual-basic-mode) (xml sgml-mode))) (language (language-detection-string string)) (modes (cdr (assoc language map))) (mode (cl-loop for mode in modes when (fboundp mode) return mode))) (when (fboundp mode) mode))) (defun fontify-with-mode (mode text) (with-temp-buffer (insert text) (delay-mode-hooks (funcall mode)) (font-lock-default-function mode) (font-lock-default-fontify-region (point-min) (point-max) nil) (buffer-string))) (defun fontify-using-faces (text) (let ((pos 0)) (while (setq next (next-single-property-change pos 'face text)) (put-text-property pos next 'font-lock-face (get-text-property pos 'face text) text) (setq pos next)) (add-text-properties 0 (length text) '(fontified t) text) text)) (defun language-detection-fontify-region (start end) (interactive "r") (let* ((text (buffer-substring-no-properties start end)) (mode (language-detection-detect-mode text)) (fontified (fontify-using-faces (fontify-with-mode mode text)))) (delete-region start end) (insert fontified))) (defun language-detection-fontify-buffer () (interactive) (language-detection-fontify-region (point-min) (point-max)))) (provide 'init-editing)