branch: master commit f857d3025261e8f323e00566059771feb47a3eba Author: Artur Malabarba <bruce.connor...@gmail.com> Commit: Artur Malabarba <bruce.connor...@gmail.com>
Add comment-or-uncomment-sexp command --- sotlisp.el | 90 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 89 insertions(+), 1 deletions(-) diff --git a/sotlisp.el b/sotlisp.el index c67ed8a..ad5dc40 100644 --- a/sotlisp.el +++ b/sotlisp.el @@ -443,8 +443,11 @@ If `speed-of-thought-mode' is already on, call ON." ;;; The local minor-mode (define-minor-mode sotlisp-mode nil nil " SoT" - '(([M-return] . sotlisp-newline-and-parentheses) + `(([M-return] . sotlisp-newline-and-parentheses) ([C-return] . sotlisp-downlist-newline-and-parentheses) + (,(kbd "C-M-;") . ,(if (boundp 'comment-or-uncomment-sexp) + #'comment-or-uncomment-sexp + #'sotlisp-comment-or-uncomment-sexp)) ("\C-cf" . sotlisp-find-or-define-function) ("\C-cv" . sotlisp-find-or-define-variable))) @@ -588,6 +591,91 @@ With a prefix argument, defines a `defvar' instead of a `defcustom'." (if prefix "" "\n :type 'boolean") ")\n\n"))))))) + +;;; Comment sexp +(defun sotlisp-uncomment-sexp (&optional n) + "Uncomment a sexp around point." + (interactive "P") + (let* ((initial-point (point-marker)) + (p) + (end (save-excursion + (when (elt (syntax-ppss) 4) + (re-search-backward comment-start-skip + (line-beginning-position) + t)) + (setq p (point-marker)) + (comment-forward (point-max)) + (point-marker))) + (beg (save-excursion + (forward-line 0) + (while (= end (save-excursion + (comment-forward (point-max)) + (point))) + (forward-line -1)) + (goto-char (line-end-position)) + (re-search-backward comment-start-skip + (line-beginning-position) + t) + (while (looking-at-p comment-start-skip) + (forward-char -1)) + (point-marker)))) + (unless (= beg end) + (uncomment-region beg end) + (goto-char p) + ;; Indentify the "top-level" sexp inside the comment. + (while (and (ignore-errors (backward-up-list) t) + (>= (point) beg)) + (skip-chars-backward (rx (syntax expression-prefix))) + (setq p (point-marker))) + ;; Re-comment everything before it. + (ignore-errors + (comment-region beg p)) + ;; And everything after it. + (goto-char p) + (forward-sexp (or n 1)) + (skip-chars-forward "\r\n[:blank:]") + (if (< (point) end) + (ignore-errors + (comment-region (point) end)) + ;; If this is a closing delimiter, pull it up. + (goto-char end) + (skip-chars-forward "\r\n[:blank:]") + (when (= 5 (car (syntax-after (point)))) + (delete-indentation)))) + ;; Without a prefix, it's more useful to leave point where + ;; it was. + (unless n + (goto-char initial-point)))) + +(defun sotlisp--comment-sexp-raw () + "Comment the sexp at point or ahead of point." + (pcase (or (bounds-of-thing-at-point 'sexp) + (save-excursion + (skip-chars-forward "\r\n[:blank:]") + (bounds-of-thing-at-point 'sexp))) + (`(,l . ,r) + (goto-char r) + (skip-chars-forward "\r\n[:blank:]") + (save-excursion + (comment-region l r)) + (skip-chars-forward "\r\n[:blank:]")))) + +(defun sotlisp-comment-or-uncomment-sexp (&optional n) + "Comment the sexp at point and move past it. +If already inside (or before) a comment, uncomment instead. +With a prefix argument N, (un)comment that many sexps." + (interactive "P") + (if (or (elt (syntax-ppss) 4) + (< (save-excursion + (skip-chars-forward "\r\n[:blank:]") + (point)) + (save-excursion + (comment-forward 1) + (point)))) + (sotlisp-uncomment-sexp n) + (dotimes (_ (or n 1)) + (sotlisp--comment-sexp-raw)))) + (provide 'sotlisp) ;;; sotlisp.el ends here