From bdf9a71ab7baa2b1de9abcfd5df1a9107a55d141 Mon Sep 17 00:00:00 2001 From: Jack Jamison Date: Sun, 5 Jul 2026 01:19:30 -0400 Subject: add a bunch of emacs packages HELP --- .config/emacs/lisp/mwim.el | 472 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 472 insertions(+) create mode 100644 .config/emacs/lisp/mwim.el (limited to '.config/emacs/lisp/mwim.el') diff --git a/.config/emacs/lisp/mwim.el b/.config/emacs/lisp/mwim.el new file mode 100644 index 0000000..cd0b805 --- /dev/null +++ b/.config/emacs/lisp/mwim.el @@ -0,0 +1,472 @@ +;;; mwim.el --- Switch between the beginning/end of line or code -*- lexical-binding: t -*- + +;; Copyright © 2015–2026 Alex Kost + +;; Author: Alex Kost +;; Created: 9 Jan 2015 +;; Version: 0.4 +;; URL: https://github.com/alezost/mwim.el +;; Keywords: convenience +;; Package-Requires: ((seq "2.24")) + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. +;; +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;;; Commentary: + +;; MWIM stands for "Move Where I Mean". This package is inspired by +;; . It +;; provides commands to switch between various positions on the current +;; line (particularly, to move to the beginning/end of code, line or +;; comment). + +;; To install the package manually, add the following to your init file: +;; +;; (add-to-list 'load-path "/path/to/mwim-dir") +;; (autoload 'mwim "mwim" nil t) +;; (autoload 'mwim-beginning "mwim" nil t) +;; (autoload 'mwim-end "mwim" nil t) +;; (autoload 'mwim-beginning-of-code-or-line "mwim" nil t) +;; (autoload 'mwim-beginning-of-line-or-code "mwim" nil t) +;; (autoload 'mwim-beginning-of-code-or-line-or-comment "mwim" nil t) +;; (autoload 'mwim-end-of-code-or-line "mwim" nil t) +;; (autoload 'mwim-end-of-line-or-code "mwim" nil t) + +;; Then you can bind some keys to some of those commands and start +;; moving. See README in the source repo for more details. + +;;; Code: + +(require 'seq) + +(defgroup mwim nil + "Move Where I Mean. +Move the point to various line positions." + :group 'convenience) + +(defcustom mwim-beginning-of-line-function + '((t . beginning-of-line) + (message-mode . message-beginning-of-line) + (org-mode . org-beginning-of-line)) + "Function(s) used to move the point to the beginning of line. +Can either be a function or an alist of the following form: + + ((MODE-NAME . FUNCTION) ...) + +where MODE-NAME is either a `major-mode' name or `t' (used as a +default value for any unspecified mode), and FUNCTION is the +moving function for this mode." + :type '(choice (function-item beginning-of-visual-line) + (function-item beginning-of-line) + (alist :key-type symbol :value-type function) + (function :tag "Another function")) + :group 'mwim) + +(defcustom mwim-end-of-line-function + '((t . end-of-line) + (org-mode . org-end-of-line)) + "Function(s) used to move the point to the end of line. +See also `mwim-beginning-of-line-function'." + :type '(choice (function-item end-of-visual-line) + (function-item end-of-line) + (alist :key-type symbol :value-type function) + (function :tag "Another function")) + :group 'mwim) + +(defcustom mwim-next-position-function nil + "Function used to define the next position. + +This function is called with a list of functions returning +available point positions as a single argument. It should return +the next position where the point will be moved. + +There are 2 functions to choose from: `mwim-next-position' and +`mwim-next-unique-position'. + +`mwim-next-position' is faster as it calculates positions only +when needed, however there are some special cases when this +function will not return an expected position (when there are +more than 3 potential positions, and some of them are the same). + +With `mwim-next-unique-position' you will always switch between +all available positions, but it is slower as it calculates all +positions at once. Most likely, however, this slowness will be +insignificant as the number of potential positions is not big. + +If this variable is nil, an appropriate function will be chosen +automatically. This is the recommended value, as it provides the +speed when possible, and guaranteed cycling between all positions +for complex cases." + :type '(choice (const :tag "Choose automatically" nil) + (function-item mwim-next-position) + (function-item mwim-next-unique-position) + (function :tag "Another function")) + :group 'mwim) + +(defcustom mwim-beginning-position-functions + '(mwim-block-beginning + mwim-code-beginning + mwim-line-beginning + mwim-comment-beginning) + "List of functions used by `\\[mwim-beginning]' command." + :type '(repeat function) + :group 'mwim) + +(defcustom mwim-end-position-functions + '(mwim-block-end + mwim-code-end + mwim-line-end) + "List of functions used by `\\[mwim-end]' command." + :type '(repeat function) + :group 'mwim) + +(defcustom mwim-position-functions + '(mwim-line-beginning + mwim-code-beginning + mwim-comment-beginning + mwim-code-end + mwim-line-end) + "List of functions used by `\\[mwim]' command." + :type '(repeat function) + :group 'mwim) + +(defun mwim-function (fun-or-alist) + "Return function depending on FUN-OR-ALIST and the current `major-mode'. +FUN-OR-ALIST should have the same form as +`mwim-beginning-of-line-function' variable." + (cond + ((functionp fun-or-alist) + fun-or-alist) + ((listp fun-or-alist) + (or (cdr (assq major-mode fun-or-alist)) + (cdr (assq t fun-or-alist)))))) + + +;;; Calculating positions + +(defmacro mwim-point-at (&rest body) + "Return point position after evaluating BODY in `save-excursion'." + (declare (debug t) (indent 0)) + `(save-excursion ,@body (point))) + +(defun mwim-first-position (functions &optional position) + "Return the first point position that is not POSITION from +positions defined after calling FUNCTIONS. + +If POSITION is nil, use the current point position. + +Initially, the first function is called (without arguments). If +the resulting position is not the same as POSITION, return it. +Otherwise, call the second function, etc. + +If after calling all FUNCTIONS, all resulting positions are +the same as POSITION, return nil." + (or position (setq position (point))) + (pcase functions + (`(,first . ,rest) + (let ((pos (funcall first))) + (if (and pos (/= pos position)) + pos + (mwim-first-position rest position)))))) + +(defun mwim-next-position (functions &optional position fallback-position) + "Return the next point position after POSITION from positions +defined after calling FUNCTIONS. + +If POSITION is nil, use the current point position. + +Initially, the first function is called (without arguments). If +the resulting position is the same as POSITION, return position +defined after calling the second function. If it is not the +same, compare the second position with POSITION, etc. + +If after calling all FUNCTIONS, POSITION is not one of the +found positions, return FALLBACK-POSITION. If it is nil, return +the first position." + (or position (setq position (point))) + (if (null functions) + fallback-position + (pcase functions + (`(,first . ,rest) + ;; If the last function is reached, there is no point to call it, + ;; as the point should be moved to the first position anyway. + (if (and (null rest) fallback-position) + fallback-position + (let ((pos (funcall first))) + (if (and pos (= pos position)) + (or (mwim-first-position rest position) + fallback-position + pos) + (mwim-next-position rest position + (or fallback-position pos))))))))) + +(defun mwim-next-unique-position (functions &optional position + sort-predicate) + "Return the next point position after POSITION from positions +defined after calling FUNCTIONS. + +If POSITION is nil, use the current point position. + +Initially, all positions are calculated (all functions are +called). If POSITION is the same as one of the resulting +positions, return the next one, otherwise return the first +position. + +If SORT-PREDICATE is non-nil, it should be a function taken by +`sort'. It is used to sort available positions, so most likely +you want to use either `<' or `>' for SORT-PREDICATE." + (or position (setq position (point))) + (let* ((positions (seq-uniq (seq-keep #'funcall functions) + #'eq)) + (positions (if sort-predicate + (sort positions sort-predicate) + positions)) + (next-positions (cdr (memq position positions)))) + (car (or next-positions positions)))) + +(defun mwim-move-to-next-position (functions &optional sort-predicate) + "Move point to position returned by the first function. +If the point is already there, move to the position returned by +the second function, etc. + +FUNCTIONS are called without arguments and should return either a +number (point position) or nil (if this position should be +skipped). + +If SORT-PREDICATE is non-nil, `mwim-next-unique-position' is +called with it." + (let ((pos (if sort-predicate + (mwim-next-unique-position functions (point) + sort-predicate) + (funcall (or mwim-next-position-function + (if (> (length functions) 3) + #'mwim-next-unique-position + #'mwim-next-position)) + functions)))) + (when pos (goto-char pos)))) + +;; This macro is not really needed, it is an artifact from the past. It +;; is left in case some people use it to define their commands. +(defmacro mwim-goto-next-position (&rest expressions) + "Wrapper for `mwim-move-to-next-position'." + (declare (indent 0)) + `(mwim-move-to-next-position + (list ,@(mapcar (lambda (exp) `(lambda () ,exp)) + expressions)))) + + +;;; Position functions + +(defun mwim-current-comment-beginning () + "Return position of the beginning of the current comment. +Return nil, if not inside a comment." + (let ((syn (syntax-ppss))) + (and (nth 4 syn) + (nth 8 syn)))) + +(defun mwim-line-comment-beginning () + "Return position of the beginning of comment on the current line. +Return nil, if there is no comment beginning on the current line." + (let ((beg (save-excursion + (end-of-line) + (or (mwim-current-comment-beginning) + ;; There may be a marginal block comment, see + ;; . + (progn + (skip-chars-backward " \t") + (backward-char) + (mwim-current-comment-beginning)))))) + (and beg + (<= (line-beginning-position) beg) + beg))) + +(defun mwim-line-comment-text-beginning () + "Return position of a comment start on the current line. +Comment start means beginning of the text inside the comment. +Return nil, if there is no comment beginning on the current line." + ;; If `comment-start-skip' is not set by a major mode (this is the + ;; case for `sql-mode', for example), then `comment-search-forward' + ;; errors, so check that `comment-start-skip' is not nil. + (when comment-start-skip + (save-excursion + (goto-char (line-beginning-position)) + (let ((beg (comment-search-forward (line-end-position) t))) + (when beg (point)))))) + +(defalias 'mwim-comment-beginning #'mwim-line-comment-text-beginning) + +(defun mwim-line-beginning () + "Return position in the beginning of line. +Use `mwim-beginning-of-line-function'." + (mwim-point-at (mwim-beginning-of-line))) + +(defun mwim-code-beginning () + "Return position in the beginning of code." + (mwim-point-at (mwim-beginning-of-code))) + +(defun mwim-line-end () + "Return position in the end of line. +Use `mwim-end-of-line-function'." + (mwim-point-at (mwim-end-of-line))) + +(defun mwim-code-end () + "Return position in the end of code." + (mwim-point-at (mwim-end-of-code))) + +(defun mwim-block-beginning () + "Return position in the beginning of code or comment. +If the point is inside a comment, return beginning position of +the current comment, otherwise - of the code." + (if (mwim-current-comment-beginning) + (mwim-comment-beginning) + (mwim-code-beginning))) + +(defun mwim-block-end () + "Return position in the end of code or line. +If the point is inside a comment, return end position of +the current comment, otherwise - of the code." + (if (mwim-current-comment-beginning) + (mwim-line-end) + (mwim-code-end))) + + +;;; Moving commands + +(defun mwim-beginning-of-comment () + "Move point to the beginning of comment on the current line. +If the comment does not exist, do nothing." + (interactive "^") + (when-let* ((comment-beg (mwim-line-comment-beginning))) + (goto-char comment-beg))) + +(defun mwim-beginning-of-line () + "Move point to the beginning of line. +Use `mwim-beginning-of-line-function'." + (interactive "^") + (funcall (or (mwim-function mwim-beginning-of-line-function) + #'beginning-of-line))) + +(defun mwim-end-of-line () + "Move point to the end of line. +Use `mwim-end-of-line-function'." + (interactive "^") + (funcall (or (mwim-function mwim-end-of-line-function) + #'end-of-line))) + +(defun mwim-beginning-of-code () + "Move point to the first non-whitespace character on the current line." + (interactive "^") + (mwim-beginning-of-line) + (skip-syntax-forward " " (line-end-position))) + +(defun mwim-end-of-code () + "Move point to the end of code. + +\"End of code\" means before a possible comment and trailing +whitespaces. Comments are recognized in any mode that sets +`syntax-ppss' properly. + +If current line is fully commented (contains only comment), move +to the end of line." + (interactive "^") + (mwim-end-of-line) + (when-let* ((comment-beg (mwim-line-comment-beginning))) + (let ((eoc (mwim-point-at + (goto-char comment-beg) + (skip-chars-backward " \t")))) + (when (< (line-beginning-position) eoc) + (goto-char eoc)))) + (skip-chars-backward " \t")) + +(defmacro mwim-define-command (position &rest objects) + "Define `mwim-POSITION-of-OBJECT1-or-OBJECT2-or-...' command. +POSITION is either `beginning' or `end'. +OBJECT1 and OBJECT2 can be `line', `code' or `comment'." + (let* ((object1 (car objects)) + (direct-fun (intern (format "mwim-%S-of-%S" position object1))) + (fun-name (intern + (concat "mwim-" (symbol-name position) "-of-" + (mapconcat #'symbol-name objects "-or-"))))) + `(defun ,fun-name (&optional arg) + ,(concat (format "Move point to the %S of %S." + position object1) + (mapconcat (lambda (object) + (format " +If the point is already there, move to the %S of %S." + position object)) + (cdr objects) "") + "\n +If ARG is specified, move forward (or backward) this many lines. +See `forward-line' for details.") + (interactive + (progn + (handle-shift-selection) + (when current-prefix-arg + (list (prefix-numeric-value current-prefix-arg))))) + (if (or (null arg) (= 0 arg)) + (mwim-move-to-next-position + ',(mapcar (lambda (object) + (intern (format "mwim-%S-%S" object position))) + objects)) + (forward-line arg) + (,direct-fun))))) + +(mwim-define-command beginning line code) +(mwim-define-command beginning code line) +(mwim-define-command beginning code line comment) +(mwim-define-command end line code) +(mwim-define-command end code line) + +;;;###autoload (autoload 'mwim-beginning-of-line-or-code "mwim" nil t) +;;;###autoload (autoload 'mwim-beginning-of-code-or-line "mwim" nil t) +;;;###autoload (autoload 'mwim-beginning-of-code-or-line-or-comment "mwim" nil t) +;;;###autoload (autoload 'mwim-end-of-line-or-code "mwim" nil t) +;;;###autoload (autoload 'mwim-end-of-code-or-line "mwim" nil t) + +;;;###autoload +(defun mwim-beginning (&optional arg) + "Move point to the next beginning position +Available positions are defined by `mwim-beginning-position-functions'. +See `mwim-move-to-next-position' for details. +Interactively, with prefix argument, move to the previous position." + (interactive "^P") + (mwim-move-to-next-position + (if arg + (reverse mwim-beginning-position-functions) + mwim-beginning-position-functions))) + +;;;###autoload +(defun mwim-end (&optional arg) + "Move point to the next end position. +Available positions are defined by `mwim-end-position-functions'. +See `mwim-move-to-next-position' for details. +Interactively, with prefix argument, move to the previous position." + (interactive "^P") + (mwim-move-to-next-position + (if arg + (reverse mwim-end-position-functions) + mwim-end-position-functions))) + +;;;###autoload +(defun mwim (&optional arg) + "Switch between various positions on the current line. +Available positions are defined by `mwim-position-functions' +variable. +Interactively, with prefix argument, move to the previous position." + (interactive "^P") + (mwim-move-to-next-position mwim-position-functions + (if arg #'> #'<))) + +(provide 'mwim) + +;;; mwim.el ends here -- cgit v1.2.3