diff --git a/smartparens.el b/smartparens.el index 98e111a1..692b3e82 100644 --- a/smartparens.el +++ b/smartparens.el @@ -1011,6 +1011,34 @@ See also `sp-skip-closing-pair'." :type 'boolean :group 'smartparens) +(defcustom sp-auto-narrow-kill-region nil + "If non-nil, `sp-kill-region' will always try to kill as much as possible. + +When user selects a region which deletion would make the buffer +unbalanced, try to find the most permissible +region (i.e. largest) that when removed leaves the buffer +balanced. + +This allows for more sloppy region selection when trying to clean +up some code. + +Example: + + (let (|(foo bar) + (one two)) + body here)M + +With this setting enabled calling `sp-kill-region' will result in + + (let (|M) + body here) + +instead of an error. + +The same applies to `sp-delete-region'." + :type 'boolean + :group 'smartparens) + (defcustom sp-undo-pairs-separately nil "If non-nil, put an `undo-boundary' before each inserted pair. @@ -9030,7 +9058,13 @@ If that text is unbalanced, signal an error instead. With a prefix argument, skip the balance check." (interactive "r") (when (or current-prefix-arg - (sp-region-ok-p beg end) + (-let [(&plist :ok ok :last-good-sexp last-good-sexp) (sp-get-region-info beg end)] + (if (and (not ok) + sp-auto-narrow-kill-region) + (when last-good-sexp + (setq end (sp-get last-good-sexp :end-suf)) + (goto-char end)) + ok)) (user-error (sp-message :unbalanced-region :return))) (setq this-command 'kill-region) (kill-region beg end))) @@ -9054,28 +9088,47 @@ of the point." (indent-sexp)) (sp--back-to-indentation column indentation)))) -(cl-defun sp-region-ok-p (start end) - "Test if region between START and END is balanced. +(defun sp-get-region-info (start end) + "Get information about region between START and END. -A balanced region is one where all opening delimiters are matched -by closing delimiters. +See `sp-region-ok-p' for explanation of what constitutes a +balanced region. -This function does *not* check that the delimiters are correctly -ordered, that is [(]) is correct even though it is not logically -properly balanced." - (interactive "r") +The return value is a plist with the following keys: + + :ok - non-nil if the region is balanced + :last-good-sexp - last sexp in the region which is part of a + balanced region starting at START" (save-excursion (save-restriction (when (eq (sp-point-in-string start) (sp-point-in-string end)) (narrow-to-region start end) - (let ((regex (sp--get-allowed-regexp (-difference sp-pair-list (sp--get-allowed-pair-list))))) + (let ((regex (sp--get-allowed-regexp (-difference sp-pair-list (sp--get-allowed-pair-list)))) + (last-good-sexp)) (goto-char (point-min)) - (while (or (prog1 (sp-forward-sexp) + (while (or (prog1 (let ((ok (sp-forward-sexp))) + (when ok + (setq last-good-sexp ok)) + ok) (sp-skip-forward-to-symbol)) ;; skip impossible delimiters (when (looking-at-p regex) (goto-char (match-end 0))))) - (looking-at-p "[[:blank:]\n]*\\'")))))) + (list + :ok (looking-at-p "[[:blank:]\n]*\\'") + :last-good-sexp last-good-sexp)))))) + +(defun sp-region-ok-p (start end) + "Test if region between START and END is balanced. + +A balanced region is one where all opening delimiters are matched +by closing delimiters. + +This function does *not* check that the delimiters are correctly +ordered, that is [(]) is correct even though it is not logically +properly balanced." + (interactive "r") + (plist-get (sp-get-region-info start end) :ok)) (defun sp-newline () "Insert a newline and indent it.