summaryrefslogtreecommitdiff
path: root/.config/emacs/modules/mm-editing.el
diff options
context:
space:
mode:
Diffstat (limited to '.config/emacs/modules/mm-editing.el')
-rw-r--r--.config/emacs/modules/mm-editing.el376
1 files changed, 376 insertions, 0 deletions
diff --git a/.config/emacs/modules/mm-editing.el b/.config/emacs/modules/mm-editing.el
new file mode 100644
index 0000000..5b6c7a4
--- /dev/null
+++ b/.config/emacs/modules/mm-editing.el
@@ -0,0 +1,376 @@
+;;; mm-editing.el --- Text editing configuation -*- lexical-binding: t; -*-
+
+;;; Delete Region When Typing
+
+(use-package delsel
+ :hook (after-init . delete-selection-mode))
+
+
+;;; Capitalize ‘ß’ into ‘ẞ’
+
+;; https://lists.gnu.org/archive/html/bug-gnu-emacs/2024-11/msg00030.html
+(set-case-syntax-pair ?ẞ ?ß (standard-case-table))
+(put-char-code-property ?ß 'special-uppercase nil)
+
+
+;;; Force Spaces For Alignment
+
+(defun mm-editing-force-space-indentation (function &rest arguments)
+ "Call FUNCTION with ARGUMENTS in an environment in which
+`indent-tabs-mode' is nil."
+ (let (indent-tabs-mode)
+ (apply function arguments)))
+
+(dolist (command #'(align-region
+ c-backslash-region
+ comment-dwim
+ makefile-backslash-region
+ sh-backslash-region))
+ (advice-add command :around #'mm-editing-force-space-indentation))
+
+
+;;; Indentation Settings
+
+(setq-default
+ tab-width 4
+ indent-tabs-mode (not mm-humanwave-p))
+
+(defvar mm-editing-indentation-settings-alist
+ '((awk-ts-mode . (:extras awk-ts-mode-indent-level))
+ (c-mode . (:extras c-basic-offset))
+ (c-ts-mode . (:extras c-ts-mode-indent-offset))
+ (css-mode . (:extras css-indent-offset))
+ (elixir-ts-mode . (:width 2 :extras elixir-ts-indent-offset))
+ (emacs-lisp-mode . (:width 8 :spaces t)) ; GNU code uses 8-column tabs
+ (go-mod-ts-mode . (:extras go-ts-mode-indent-offset))
+ (go-ts-mode . (:extras go-ts-mode-indent-offset))
+ (gsp-ts-mode . (:width 2 :extras gsp-ts-mode-indent-rules))
+ (helpful-mode . (:width 8)) ; GNU code uses 8-column tabs
+ (json-ts-mode . (:extras json-ts-mode-indent-offset))
+ (latex-mode . (:width 2))
+ (lisp-data-mode . (:spaces t))
+ (lisp-interaction-mode . (:spaces t))
+ (lisp-mode . (:spaces t))
+ (mhtml-mode . (:extras sgml-basic-offset))
+ (org-mode . (:width 8 :spaces t))
+ (python-mode . (:extras python-indent-offset))
+ (python-ts-mode . (:extras python-indent-offset))
+ (sgml-mode . (:extras sgml-basic-offset))
+ (sh-mode . (:extras sh-basic-offset))
+ (sql-mode . (:extras sqlind-basic-offset))
+ (tex-mode . (:width 2))
+ (typescript-ts-mode . (:extras typescript-ts-mode-indent-offset))
+ (vimscript-ts-mode . (:extras vimscript-ts-mode-indent-level))
+ (vue-ts-mode . (:extras (typescript-ts-mode-indent-offset
+ vue-ts-mode-indent-offset))))
+ "Alist of indentation settings.
+Each pair in this alist is of the form (MODE . SETTINGS) where MODE
+specifies the mode for which the given SETTINGS should apply.
+
+SETTINGS is a plist of one-or-more of the following keys:
+
+ `:spaces' -- If nil force tabs for indentation, if non-nil for spaces
+ for indentation. If this key is not provided then the
+ value of `indent-tabs-mode' is used.
+ `:width' -- Specifies a non-negative number to be used as the tab
+ width and indentation offset. If this key is not
+ provided then the default value of `tab-width' is used.
+ `:extras' -- A list of mode-specific variables which control
+ indentation settings that need to be set for
+ configurations to properly be applied.")
+
+(defun mm-editing-set-indentation-settings ()
+ "Set indentation settings for the current major mode.
+The indentation settings are set based on the configured values in
+`mm-editing-indentation-settings-alist'."
+ (let* ((plist (alist-get major-mode mm-editing-indentation-settings-alist))
+ (spaces (plist-member plist :spaces))
+ (width (plist-member plist :width))
+ (extras (plist-member plist :extras)))
+ ;; Some modes like ‘python-mode’ explicitly set ‘tab-width’ and
+ ;; ‘indent-tabs-mode’ so we must override them explicitly.
+ (setq-local indent-tabs-mode (if spaces (not (cadr spaces))
+ (default-value 'indent-tabs-mode))
+ tab-width (or (cadr width) (default-value 'tab-width)))
+ (when extras
+ (setq extras (cadr extras))
+ (when (symbolp extras)
+ (setq extras (list extras)))
+ (dolist (extra extras)
+ (set extra tab-width)))))
+
+(add-hook 'after-change-major-mode-hook #'mm-editing-set-indentation-settings)
+
+(defun mm-editing-set-tabsize ()
+ "Set the tabsize for the current buffer.
+If the current buffer’s major mode requires setting additional variables,
+those should be listed in `mm-editing-indentation-settings-alist'."
+ (declare (interactive-only t))
+ (interactive)
+ (let* ((prompt-default (default-value 'tab-width))
+ (prompt (format-prompt "Tabsize" prompt-default))
+ (tabsize (mm-as-number (read-string prompt nil nil prompt-default))))
+ (setq-local tab-width tabsize)
+ (when-let* ((plist (alist-get major-mode mm-editing-indentation-settings))
+ (extras (plist-get plist :extras)))
+ (dolist (extra (if (symbolp extras)
+ (list extras)
+ extras))
+ (set (make-local-variable extra) tabsize)))))
+
+(use-package sh-mode
+ :custom
+ (sh-indent-for-case-label 0)
+ (sh-indent-for-case-alt #'+))
+
+;; Indent with tabs and align with spaces
+;; PKG-INTERN
+(use-package smart-tabs-mode
+ :load-path (lambda () (expand-file-name "site-lisp/smart-tabs-mode.el"
+ mm-config-directory))
+ :hook after-init)
+
+
+;;; Code Commenting
+
+(defvar mm-editing-comment-settings-alist
+ '(((c-mode c++-mode) . ("/* " " " " */"))
+ ;; rustfmt doesn’t play nice, so we need the ‘*’ comment
+ ;; continuation
+ (rust-mode . ("/* " " * " " */")))
+ "TODO")
+
+(defun mm-newcomment-rust-config ()
+ (setq-local comment-quote-nested nil))
+
+(use-package newcomment
+ :custom
+ (comment-style 'multi-line)
+ :config
+ (dolist (record mm-editing-comment-settings-alist)
+ (let* ((modes (car record))
+ (modes (if (listp modes) modes (list modes)))
+ (config (cdr record))
+ (set-comment-settings
+ (lambda ()
+ (setq-local comment-start (nth 0 config)
+ comment-continue (nth 1 config)
+ comment-end (nth 2 config)))))
+ (dolist (mode modes)
+ (let ((ts-mode (mm-mode-to-ts-mode mode)))
+ (when (fboundp mode)
+ (add-hook (mm-mode-to-hook mode) set-comment-settings))
+ (when (fboundp ts-mode)
+ (add-hook (mm-mode-to-hook ts-mode) set-comment-settings)))))))
+
+
+;;; Multiple Cursors
+
+;; PKG-INTERN
+(use-package multiple-cursors-extensions
+ :after multiple-cursors
+ :bind (("C-M-@" . #'mce-add-cursor-to-next-word)
+ ("C-M-o" . #'mce-add-cursor-to-next-symbol)
+ :map search-map
+ ("$" . #'mce-mark-all-in-region)
+ ("M-$" . #'mce-mark-all-in-region-regexp))
+ :commands (mce-add-cursor-to-next-symbol
+ mce-add-cursor-to-next-word
+ mce-mark-all-in-region
+ mce-mark-all-in-region-regexp
+ mce-sort-regions
+ mce-transpose-cursor-regions))
+
+;; PKG-EXTERN
+(use-package multiple-cursors
+ :ensure t
+ :demand t
+ :bind (("C->" . #'mc/mark-next-like-this)
+ ("C-<" . #'mc/mark-previous-like-this)
+ ("C-M-<" . #'mc/mark-all-like-this-dwim)
+ ("C-M->" . #'mc/edit-lines))
+ :commands ( mm-editing-mark-all-in-region mm-editing-mark-all-in-region-regexp
+ mm-add-cursor-to-next-thing mm-transpose-cursor-regions)
+ :init
+ (with-eval-after-load 'multiple-cursors-core
+ (keymap-unset mc/keymap "<return>" :remove)))
+
+
+;;; Increment Numbers
+
+;; PKG-INTERN
+(use-package increment
+ :bind (("C-c C-a" . #'increment-number-at-point)
+ ("C-c C-x" . #'decrement-number-at-point))
+ :commands (increment-number-at-point decrement-number-at-point))
+
+
+;;; Move Line or Region
+
+(defun mm-editing-move-text-indent (&rest _)
+ (let ((deactivate deactivate-mark))
+ (if (region-active-p)
+ (indent-region (region-beginning) (region-end))
+ (indent-region (line-beginning-position) (line-end-position)))
+ (setq deactivate-mark deactivate)))
+
+;; PKG-EXTERN
+(use-package move-text
+ :ensure t
+ :bind (("M-n" . move-text-down)
+ ("M-p" . move-text-up))
+ :config
+ (dolist (command #'(move-text-up move-text-down))
+ (advice-add command :after #'mm-editing-move-text-indent)))
+
+
+;;; Surround With Delimeters
+
+(defun mm-editing-surround-with-spaces (char)
+ "Surrounds region or current symbol with a pair defined by CHAR.
+This is the same as `surround-insert' except it pads the contents of the
+surround with spaces."
+ (interactive
+ (list (char-to-string (read-char "Character: "))))
+ (let* ((pair (surround--make-pair char))
+ (left (car pair))
+ (right (cdr pair))
+ (bounds (surround--infer-bounds t)))
+ (save-excursion
+ (goto-char (cdr bounds))
+ (insert " " right)
+ (goto-char (car bounds))
+ (insert left " "))
+ (when (eq (car bounds) (point))
+ (forward-char))))
+
+;; TODO: Implement this manually
+;; PKG-EXTERN
+(use-package surround
+ :ensure t
+ :bind-keymap ("M-'" . surround-keymap)
+ :bind (:map surround-keymap
+ ("S" . #'mm-editing-surround-with-spaces))
+ :config
+ (dolist (pair '(("‘" . "’")
+ ("“" . "”")
+ ("»" . "«")
+ ("⟮" . "⟯")))
+ (push pair surround-pairs))
+ (make-variable-buffer-local 'surround-pairs)
+ (add-hook 'emacs-lisp-mode-hook
+ (defun mm-editing-add-elisp-quotes-pair ()
+ (push '("`" . "'") surround-pairs))))
+
+
+;;; Insert Webpage Contents
+
+(defun mm-editing-insert-from-url (url)
+ "Insert the contents of URL at point."
+ (interactive
+ (progn
+ (barf-if-buffer-read-only)
+ (let ((url-at-point (thing-at-point 'url)))
+ (list (read-string
+ (format-prompt "URL" url-at-point)
+ nil nil url-at-point)))))
+ (call-process "curl" nil '(t nil) nil url))
+
+
+;;; Emmet Mode
+
+(defun mm-editing-emmet-dwim (arg)
+ "Do-What-I-Mean Emmet expansion.
+If the region is active then the region will be surrounded by an emmet
+expansion read from the minibuffer. Otherwise the emmet expression
+before point is expanded. When provided a prefix argument the behaviour
+is as described by `emmet-expand-line'."
+ (interactive "*P")
+ (if (region-active-p)
+ (call-interactively #'emmet-wrap-with-markup)
+ (emmet-expand-line arg)))
+
+;; PKG-EXTERN
+(use-package emmet-mode
+ :ensure t
+ :bind ("C-," . mm-editing-emmet-dwim)
+ :custom
+ (emmet-self-closing-tag-style ""))
+
+(defun mm-editing-set-closing-tag-style ()
+ (setq-local emmet-self-closing-tag-style " /"))
+
+(use-package emmet-mode
+ :hook (vue-ts-mode . mm-editing-set-closing-tag-style)
+ :after vue-ts-mode)
+
+
+;;; JQ Manipulation in JSON Mode
+
+;; PKG-INTERN
+(use-package jq
+ :commands (jq-filter-region jq-live))
+
+
+;;; Number Formatting
+
+;; PKG-INTERN
+(use-package number-format-mode
+ :commands ( number-format-buffer number-format-region
+ number-unformat-buffer number-unformat-region
+ number-format-mode))
+
+
+;;; Additional Major Modes
+
+(use-package awk-ts-mode :ensure t) ; PKG-EXTERN
+(use-package cmake-mode :ensure t) ; PKG-EXTERN
+(use-package git-modes :ensure t) ; PKG-EXTERN
+(use-package kdl-mode :ensure t) ; PKG-EXTERN
+(use-package po-mode :ensure t) ; PKG-EXTERN
+(use-package sed-mode :ensure t) ; PKG-EXTERN
+
+;; PKG-EXTERN
+(use-package csv-mode
+ :ensure t
+ :custom
+ (csv-align-style 'auto)
+ (csv-align-padding 2))
+
+(use-package csv-mode
+ :hook (csv-mode . number-format-mode)
+ :after number-format-mode)
+
+;; PKG-INTERN
+(use-package xcompose-mode
+ :vc ( :url "https://git.thomasvoss.com/xcompose-mode"
+ :branch "master"
+ :rev :newest
+ :vc-backend Git)
+ :ensure t)
+
+
+;;; Mode-Specific Configurations
+
+(use-package make-mode
+ :custom
+ (makefile-backslash-column 80))
+
+(use-package python-mode
+ :custom
+ (python-indent-def-block-scale 1)
+ (python-indent-guess-indent-offset-verbose nil))
+
+
+;;; Add Missing Extensions
+
+(dolist (pattern '("\\.tmac\\'" "\\.mom\\'"))
+ (add-to-list 'auto-mode-alist (cons pattern #'nroff-mode)))
+
+
+;;; Subword Navigation
+
+(use-package subword
+ :hook prog-mode)
+
+(provide 'mm-editing)