X-Git-Url: https://code.delx.au/gnu-emacs-elpa/blobdiff_plain/d61a36e0409ee2dfad5f7523571753c648283ddc..37fc4433fb7e0f99a0ff74ddb23075127b7e4656:/packages/adjust-parens/adjust-parens.el diff --git a/packages/adjust-parens/adjust-parens.el b/packages/adjust-parens/adjust-parens.el index 003302d90..58a66323a 100644 --- a/packages/adjust-parens/adjust-parens.el +++ b/packages/adjust-parens/adjust-parens.el @@ -3,7 +3,7 @@ ;; Copyright (C) 2013 Free Software Foundation, Inc. ;; Author: Barry O'Reilly -;; Version: 1.1 +;; Version: 3.0 ;; 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 @@ -21,8 +21,8 @@ ;;; Commentary: ;; ;; This package provides commands for indenting and dedenting Lisp -;; code such that close parentheses are automatically adjusted to be -;; consistent with the new level of indentation. +;; code such that close parentheses and brackets are automatically +;; adjusted to be consistent with the new level of indentation. ;; ;; When reading Lisp, the programmer pays attention to open parens and ;; the close parens on the same line. But when a sexp spans more than @@ -36,6 +36,9 @@ ;; ;; To use: ;; (require 'adjust-parens) +;; (add-hook 'emacs-lisp-mode-hook #'adjust-parens-mode) +;; (add-hook 'clojure-mode-hook #'adjust-parens-mode) +;; ;; etc ;; ;; This binds two keys in Lisp Mode: ;; (local-set-key (kbd "TAB") 'lisp-indent-adjust-parens) @@ -101,7 +104,7 @@ ;; - Consider taking a region as input in order to indent a sexp and ;; its siblings in the region. Dedenting would not take a region. -(require 'cl) +(require 'cl-lib) (defun last-sexp-with-relative-depth (from-pos to-pos rel-depth) "Parsing sexps from FROM-POS (inclusive) to TO-POS (exclusive), @@ -116,7 +119,7 @@ Examples: Evaluate: (last-sexp-with-relative-depth pos-a (1+ pos-j) 0) Returns: position of j - Evaluate: (last-sexp-with-relative-depth pos-a (1+ pos-j) -1) + Evaluate: (last-sexp-with-relative-depth pos-a (1+ pos-j) 1) Returns: position of (h i) This function assumes FROM-POS is not in a string or comment." @@ -167,22 +170,25 @@ it moved to. If there's no close parens to move, either return nil or allow scan-error to propogate up." (save-excursion - (let ((deleted-paren-pos - (save-excursion - (beginning-of-line) - ;; Account for edge case when point has no sexp before it - ;; - ;; This is primarily to avoid funny behavior when there - ;; is no sexp between bob and point. - (if (not (adjust-parens-check-prior-sexp)) - nil - ;; If the sexp at point is a list, - ;; delete its closing paren - (when (eq (scan-lists (point) 1 0) - (scan-sexps (point) 1)) - (forward-sexp) - (delete-char -1) - (point)))))) + (let* ((deleted-paren-char nil) + (deleted-paren-pos + (save-excursion + (beginning-of-line) + ;; Account for edge case when point has no sexp before it + ;; + ;; This is primarily to avoid funny behavior when there + ;; is no sexp between bob and point. + (if (not (adjust-parens-check-prior-sexp)) + nil + ;; If the sexp at point is a list, + ;; delete its closing paren + (when (eq (scan-lists (point) 1 0) + (scan-sexps (point) 1)) + (forward-sexp) + (setq deleted-paren-char (char-before)) + (delete-char -1) + (point)))))) + ;; Invariant: deleted-paren-pos nil iff deleted-paren-char nil (when deleted-paren-pos (let ((sexp-to-close (save-excursion @@ -195,7 +201,7 @@ scan-error to propogate up." (forward-sexp)) ;; Note: when no sexp-to-close found, line is empty. So put ;; close paren after point. - (insert ")") + (insert deleted-paren-char) (list deleted-paren-pos (point))))))) (defun adjust-close-paren-for-dedent () @@ -209,13 +215,16 @@ it moved to. If there's no close parens to move, either return nil or allow scan-error to propogate up." (save-excursion - (let ((deleted-paren-pos - (save-excursion - (when (< (point) - (progn (up-list) - (point))) - (delete-char -1) - (point))))) + (let* ((deleted-paren-char nil) + (deleted-paren-pos + (save-excursion + (when (< (point) + (progn (up-list) + (point))) + (setq deleted-paren-char (char-before)) + (delete-char -1) + (point))))) + ;; Invariant: deleted-paren-pos nil iff deleted-paren-char nil (when deleted-paren-pos (let ((sexp-to-close ;; Needs to work when dedenting in an empty list, in @@ -230,7 +239,7 @@ scan-error to propogate up." (forward-sexp) (backward-up-list) (forward-char 1)) - (insert ")") + (insert deleted-paren-char) ;; The insertion makes deleted-paren-pos off by 1 (list (1+ deleted-paren-pos) (point))))))) @@ -240,59 +249,103 @@ scan-error to propogate up." (save-excursion (let ((orig-pos (point))) (back-to-indentation) - (and (not (use-region-p)) - (<= orig-pos (point)))))) - -(defun adjust-parens-and-indent (adjust-function parg) + (and (= orig-pos (point)) + (not (use-region-p)) + ;; Current line indented? + (let ((indent (calculate-lisp-indent))) + (and indent + (= (current-column) + (if (listp indent) + (car indent) + indent)))))))) + +(defun adjust-parens-and-indent (raw-parg + adjust-function + adjust-function-negative + fallback-function) "Adjust close parens and indent the region over which the parens moved." - (let ((region-of-change (list (point) (point)))) - (cl-loop for i from 1 to (or parg 1) - with finished = nil - while (not finished) - do - (condition-case err - (let ((close-paren-movement - (funcall adjust-function))) - (if close-paren-movement - (setq region-of-change - (list (min (car region-of-change) - (car close-paren-movement) - (cadr close-paren-movement)) - (max (cadr region-of-change) - (car close-paren-movement) - (cadr close-paren-movement)))) - (setq finished t))) - (scan-error (setq finished err)))) - (apply 'indent-region region-of-change)) - (back-to-indentation)) - -(defun lisp-indent-adjust-parens (&optional parg) + (if (adjust-parens-p) + (let* ((parg (prefix-numeric-value raw-parg)) + (adjust-function (if (and parg (< parg 0)) + adjust-function-negative + adjust-function)) + (region-of-change (list (point) (point)))) + (cl-loop for i from 1 to (or (and parg (abs parg)) 1) + with finished = nil + while (not finished) + do + (condition-case err + (let ((close-paren-movement + (funcall adjust-function))) + (if close-paren-movement + (setq region-of-change + (list (min (car region-of-change) + (car close-paren-movement) + (cadr close-paren-movement)) + (max (cadr region-of-change) + (car close-paren-movement) + (cadr close-paren-movement)))) + (setq finished t))) + (scan-error (setq finished err)))) + (apply 'indent-region region-of-change) + (back-to-indentation) + t) + (funcall fallback-function raw-parg))) + +(defcustom adjust-parens-fallback-indent-function 'indent-for-tab-command + "The function to call with prefix arg instead of +adjust-parens-and-indent when adjust-parens-p returns false." + :type 'function + :group 'adjust-parens) +(defun lisp-indent-adjust-parens (&optional raw-parg) "Indent Lisp code to the next level while adjusting sexp balanced expressions to be consistent. +Returns t if adjust-parens changed the buffer, else returns the +result of calling adjust-parens-fallback-indent-function. + This command can be bound to TAB instead of indent-for-tab-command. It potentially calls the latter." (interactive "P") - (if (adjust-parens-p) - (adjust-parens-and-indent 'adjust-close-paren-for-indent - parg) - (indent-for-tab-command parg))) - -(defun lisp-dedent-adjust-parens (&optional parg) + (adjust-parens-and-indent raw-parg + #'adjust-close-paren-for-indent + #'adjust-close-paren-for-dedent + adjust-parens-fallback-indent-function)) + +(defcustom adjust-parens-fallback-dedent-function 'indent-for-tab-command + "The function to call with prefix arg instead of +adjust-parens-and-indent when adjust-parens-p returns false." + :type 'function + :group 'adjust-parens) +(defun lisp-dedent-adjust-parens (&optional raw-parg) "Dedent Lisp code to the previous level while adjusting sexp balanced expressions to be consistent. +Returns t if adjust-parens changed the buffer, else returns the +result of calling adjust-parens-fallback-dedent-function. + Binding to (ie Shift-Tab) is a sensible choice." (interactive "P") - (when (adjust-parens-p) - (adjust-parens-and-indent 'adjust-close-paren-for-dedent - parg))) - -(add-hook 'emacs-lisp-mode-hook - (lambda () - (local-set-key (kbd "TAB") 'lisp-indent-adjust-parens) - (local-set-key (kbd "") 'lisp-dedent-adjust-parens))) + (adjust-parens-and-indent raw-parg + #'adjust-close-paren-for-dedent + #'adjust-close-paren-for-indent + adjust-parens-fallback-dedent-function)) + +(defgroup adjust-parens nil + "Indent and dedent Lisp code, automatically adjust close parens." + :prefix "adjust-parens-" + :group 'convenience) + +(defvar adjust-parens-mode-map (make-sparse-keymap) + "Keymap for `adjust-parens-mode'") +(define-key adjust-parens-mode-map (kbd "TAB") 'lisp-indent-adjust-parens) +(define-key adjust-parens-mode-map (kbd "") 'lisp-dedent-adjust-parens) + +(define-minor-mode adjust-parens-mode + "Indent and dedent Lisp code, automatically adjust close parens." + :group 'adjust-parens + :keymap adjust-parens-mode-map) (provide 'adjust-parens)