From 0eedc37603c4b6f16ed5d6cf83184090ee100bce Mon Sep 17 00:00:00 2001 From: Jim Myhrberg Date: Sun, 16 Apr 2023 03:13:14 +0100 Subject: [PATCH] feat(editor): add custom yank-indent package When yank-indent-mode is enabled, yanked (pasted) text is indented based on the indent rules of the current major mode. It has a global-yank-indent-mode too which by default excludes a long list of known indentation sensitive modes with which this approach does not work very well. It is based on some random hacky snippets elisp I've been using for over a decade. Said snippets are themselves based on some random snippets I found online, and since morphed into the weird monster they had become. --- core/siren-core-custom.el | 19 --- core/siren-core-editor.el | 26 ----- core/siren-core-modules.el | 1 + modules/editor/siren-yank-indent.el | 17 +++ modules/languages/siren-makefile.el | 5 +- vendor/yank-indent/yank-indent.el | 173 ++++++++++++++++++++++++++++ 6 files changed, 192 insertions(+), 49 deletions(-) create mode 100644 modules/editor/siren-yank-indent.el create mode 100644 vendor/yank-indent/yank-indent.el diff --git a/core/siren-core-custom.el b/core/siren-core-custom.el index 3d1711b..acc3f43 100644 --- a/core/siren-core-custom.el +++ b/core/siren-core-custom.el @@ -12,25 +12,6 @@ "Basic settings for Siren." :group 'tools) -(defcustom siren-yank-indent-threshold 1000 - "Threshold (# chars) over which indentation does not automatically occur." - :type 'number - :group 'siren) - -(defcustom siren-indent-sensitive-modes - '(coffee-mode conf-mode haml-mode makefile-automake-mode makefile-bsdmake-mode - makefile-gmake-mode makefile-imake-mode makefile-makepp-mode - makefile-mode python-mode slim-mode yaml-mode) - "Major modes for which auto-indenting is suppressed." - :type '(repeat symbol) - :group 'siren) - -(defcustom siren-yank-indent-modes '(LaTeX-mode TeX-mode) - "Major modes in which to indent regions that are yanked (or yank-popped). -Only modes that don't derive from `prog-mode' should be listed here." - :type '(repeat symbol) - :group 'siren) - (defcustom siren-transparency-level 99 "The default frame transparency level for Emacs frames." :type 'number diff --git a/core/siren-core-editor.el b/core/siren-core-editor.el index d512ca2..0d22165 100644 --- a/core/siren-core-editor.el +++ b/core/siren-core-editor.el @@ -95,32 +95,6 @@ (put 'upcase-region 'disabled nil) (put 'downcase-region 'disabled nil) -;; automatically indenting yanked text if in programming-modes -(defun yank-advised-indent-function (beg end) - "Do indentation, as long as the region isn't too large." - (if (<= (- end beg) siren-yank-indent-threshold) - (indent-region beg end nil))) - -(defmacro advise-commands (advice-name commands class &rest body) - "Apply advice named ADVICE-NAME to multiple COMMANDS. - -The body of the advice is in BODY." - `(progn - ,@(mapcar (lambda (command) - `(defadvice ,command (,class ,(intern (concat (symbol-name command) "-" advice-name)) activate) - ,@body)) - commands))) - -(advise-commands "indent" (yank yank-pop) after - "If current mode is one of `siren-yank-indent-modes', -indent yanked text (with prefix arg don't indent)." - (if (and (not (ad-get-arg 0)) - (not (member major-mode siren-indent-sensitive-modes)) - (or (derived-mode-p 'prog-mode) - (member major-mode siren-yank-indent-modes))) - (let ((transient-mark-mode nil)) - (yank-advised-indent-function (region-beginning) (region-end))))) - ;; make a shell script executable automatically on save (add-hook 'after-save-hook 'executable-make-buffer-file-executable-if-script-p) diff --git a/core/siren-core-modules.el b/core/siren-core-modules.el index 7d105da..28b7e5c 100644 --- a/core/siren-core-modules.el +++ b/core/siren-core-modules.el @@ -52,6 +52,7 @@ (require 'siren-vundo) (require 'siren-which-key) (require 'siren-whitespace) +(require 'siren-yank-indent) ;; Completion Systems and Interfaces (require 'siren-vertico) diff --git a/modules/editor/siren-yank-indent.el b/modules/editor/siren-yank-indent.el new file mode 100644 index 0000000..911eeab --- /dev/null +++ b/modules/editor/siren-yank-indent.el @@ -0,0 +1,17 @@ +;;; siren-yank-indent.el --- jimeh's Emacs Siren: yank-indent configuration. + +;;; Commentary: + +;; Basic configuration for yank-indent. + +;;; Code: + +(use-package yank-indent + :straight (:type built-in) ;; from vendor directory + :demand + + :config + (global-yank-indent-mode t)) + +(provide 'siren-yank-indent) +;;; siren-yank-indent.el ends here diff --git a/modules/languages/siren-makefile.el b/modules/languages/siren-makefile.el index 8315057..a8c42d0 100644 --- a/modules/languages/siren-makefile.el +++ b/modules/languages/siren-makefile.el @@ -21,10 +21,7 @@ :preface (defun siren-makefile-mode-setup () (siren-display-indentation -1) - (setq-local tab-width 4)) - - :init - (add-to-list 'siren-indent-sensitive-modes 'makefile-mode)) + (setq-local tab-width 4))) (provide 'siren-makefile) ;;; siren-makefile.el ends here diff --git a/vendor/yank-indent/yank-indent.el b/vendor/yank-indent/yank-indent.el new file mode 100644 index 0000000..5c42810 --- /dev/null +++ b/vendor/yank-indent/yank-indent.el @@ -0,0 +1,173 @@ +;;; yank-indent.el --- Automatically indent yanked text -*- lexical-binding: t -*- + +;; Version: 0.1.0 +;; Author: Jim Myhrberg +;; Keywords: editor, yank, indent +;; Package-Requires: ((emacs "25.1")) + +;; This file is not part of GNU Emacs. + +;;; License: +;; +;; Copyright (c) 2023 Jim Myhrberg. +;; +;; Redistribution and use in source and binary forms, with or without +;; modification, are permitted provided that the following conditions are +;; met: +;; +;; * Redistributions of source code must retain the above copyright +;; notice, this list of conditions and the following disclaimer. +;; * Redistributions in binary form must reproduce the above +;; copyright notice, this list of conditions and the following disclaimer +;; in the documentation and/or other materials provided with the +;; distribution. +;; * Neither the name of the copyright holder nor the names of its +;; contributors may be used to endorse or promote products derived from +;; this software without specific prior written permission. +;; +;; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +;; "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +;; LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +;; A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +;; OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +;; SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +;; LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +;; DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +;; THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +;; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +;; OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +;;; Commentary: +;; +;; Automatically yank indented regions when yank-indent-mode is enabled. + +;;; Code: + +(defgroup yank-indent nil + "Customization options for the yank-indent package. + +The yank-indent package provides functionality to automatically +indent yanked text according to the current mode's indentation +rules. This group contains customization options for controlling +the behavior of `yank-indent-mode' and `global-yank-indent-mode'." + :group 'editing) + +(defcustom yank-indent-threshold 5000 + "Max chars in yanked region to trigger auto indentation. + +If the yanked region contains more characters than the value +specified by `yank-indent-threshold', the automatic indentation +will not occur. This helps prevent performance issues when +working with large blocks of text." + :type 'number + :group 'yank-indent) + +(defcustom yank-indent-derived-modes '(prog-mode tex-mode) + "Major modes from which derived modes should enable `yank-indent-mode'. + +When `global-yank-indent-mode' is enabled, it activates +`yank-indent-mode' in buffers with major modes derived from those +listed in this variable. This is useful when you want to enable +`yank-indent-mode' for all modes that inherit from a specific +mode, such as `prog-mode' for programming modes or `text-mode' +for text editing modes." + :type '(repeat symbol) + :group 'yank-indent) + +(defcustom yank-indent-exact-modes '() + "Major modes where `yank-indent-mode' should be enabled. + +When `global-yank-indent-mode' is enabled, it activates +`yank-indent-mode' in buffers with major modes listed in this +variable. Unlike `yank-indent-derived-modes', `yank-indent-mode' +will not be activated in modes derived from those listed here. +Use this variable to list specific modes where you want +`yank-indent-mode' to be enabled without affecting their derived +modes." + :type '(repeat symbol) + :group 'yank-indent) + +(defcustom yank-indent-excluded-modes '(cmake-ts-mode + coffee-mode + conf-mode + haml-mode + makefile-automake-mode + makefile-bsdmake-mode + makefile-gmake-mode + makefile-imake-mode + makefile-makepp-mode + makefile-mode + python-mode + python-ts-mode + slim-mode + yaml-mode + yaml-ts-mode) + "Major modes where `yank-indent-mode' should not be enabled. + +`global-yank-indent-mode' will not activate `yank-indent-mode' in +buffers with major modes listed in this variable or their derived +modes. This list takes precedence over +`yank-indent-derived-modes' and `yank-indent-exact-modes'. Use +this variable to exclude specific modes and their derived modes +from having `yank-indent-mode' enabled." + :type '(repeat symbol) + :group 'yank-indent) + +(defun yank-indent--should-indent-p () + "Return non-nil if current mode should be indented." + (and (not (minibufferp)) + (not (member major-mode yank-indent-excluded-modes)) + (or (member major-mode yank-indent-exact-modes) + (apply #'derived-mode-p yank-indent-derived-modes)))) + +(defun yank-indent--maybe-indent-advice (&optional _) + "Conditionally indent the current region after yanking. + +Indentation is applied only if the region size is less than or +equal to `yank-indent-threshold' and no prefix argument is +provided. This function is used as advice for `yank' and +`yank-pop' functions." + (if (and (not current-prefix-arg)) + (let ((beg (region-beginning)) + (end (region-end)) + (mark-even-if-inactive transient-mark-mode)) + (if (<= (- end beg) yank-indent-threshold) + (indent-region beg end))))) + +;;;###autoload +(define-minor-mode yank-indent-mode + "Minor mode for automatically indenting yanked text. + +When enabled, this mode indents the yanked region according to +the current mode's indentation rules, provided that the region +size is less than or equal to `yank-indent-threshold' and no +prefix argument is given during yanking." + :lighter " YI" + :group 'yank-indent + (if yank-indent-mode + (progn + (advice-add #'yank :after #'yank-indent--maybe-indent-advice) + (advice-add #'yank-pop :after #'yank-indent--maybe-indent-advice)) + (progn + (advice-remove #'yank #'yank-indent--maybe-indent-advice) + (advice-remove #'yank-pop #'yank-indent--maybe-indent-advice)))) + +(defun yank-indent--enable () + "Enable `yank-indent-mode' if the current buffer meets the criteria. + +This function checks if the current buffer's major mode should +have `yank-indent-mode' enabled based on the settings in +`yank-indent-derived-modes', `yank-indent-exact-modes', and +`yank-indent-excluded-modes'. If the criteria are met, it enables +`yank-indent-mode'." + (when (yank-indent--should-indent-p) + (yank-indent-mode 1))) + +;;;###autoload +(define-globalized-minor-mode global-yank-indent-mode + yank-indent-mode + yank-indent--enable + :group 'yank-indent) + +(provide 'yank-indent) +;;; yank-indent.el ends here