diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 956bf8b6..e74e4324 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,6 +14,7 @@ jobs: - '29.1' - '29.2' - '29.3' + - '29.4' - 'snapshot' include: - emacs_version: 'snapshot' diff --git a/README.md b/README.md index 3ed43502..0cdbee65 100644 --- a/README.md +++ b/README.md @@ -67,10 +67,10 @@ You may want to try `smartparens-strict-mode`. This enforces that pairs are always balanced, so commands like `kill-line` keep your code well-formed. -You can also use `use-package` to install and setup `smartparens`. A minimal config is: +You can also use `use-package` to install and setup `smartparens`. An example config is: ``` elisp -(use-package smartparens-mode +(use-package smartparens :ensure smartparens ;; install the package :hook (prog-mode text-mode markdown-mode) ;; add `smartparens-mode` to these hooks :config diff --git a/smartparens-clojure.el b/smartparens-clojure.el index 4408e8ae..a38ebce7 100644 --- a/smartparens-clojure.el +++ b/smartparens-clojure.el @@ -44,7 +44,15 @@ (defvar sp-clojure-prefix "\\(?:[@`'#~,_?^]+\\)" "Prefix used in `sp-sexp-prefix' for clojure modes.") -(dolist (mode '(clojure-mode clojurescript-mode clojurec-mode cider-repl-mode)) +(dolist (mode '( + cider-repl-mode + clojure-mode + clojure-ts-mode + clojurec-mode + clojurec-ts-mode + clojurescript-mode + clojurescript-ts-mode + )) (add-to-list 'sp-sexp-prefix `(,mode regexp ,sp-clojure-prefix))) ;; Match "`" with "`" in strings and comments diff --git a/smartparens-config.el b/smartparens-config.el index c639e5e9..981641f5 100644 --- a/smartparens-config.el +++ b/smartparens-config.el @@ -70,9 +70,21 @@ ID, ACTION, CONTEXT." ;; do not consider punctuation (not (looking-at "[?.,;!]")))))))) +(defun sp-lisp-insert-space-after-slurp (_id action _context) + (-let (((&plist :ok-orig :next-thing) sp-handler-context)) + (when (and (eq action 'slurp-forward) + (sp-get ok-orig (/= :beg-in :end-in))) + (save-excursion + (sp-get ok-orig (goto-char :end-in)) + (skip-syntax-backward " ") + (unless (looking-at-p (rx (or whitespace eol))) + (insert " ")))))) + ;; emacs is lisp hacking environment, so we set up some most common ;; lisp modes too (sp-with-modes sp-lisp-modes + (sp-local-pair "(" nil :post-handlers '(:add sp-lisp-insert-space-after-slurp)) + (sp-local-pair "[" nil :post-handlers '(:add sp-lisp-insert-space-after-slurp)) ;; disable ', it's the quote character! (sp-local-pair "'" nil :actions nil)) @@ -116,8 +128,11 @@ ID, ACTION, CONTEXT." ;; automatically. If you want to call sp-local-pair outside this ;; macro, you MUST supply the major mode argument. -(eval-after-load 'cc-mode '(require 'smartparens-c)) -(eval-after-load 'clojure-mode '(require 'smartparens-clojure)) +(--each '(cc-mode c-ts-mode) + (eval-after-load it '(require 'smartparens-c))) +(--each '(clojure-mode clojure-ts-mode) + (eval-after-load it '(require 'smartparens-clojure))) +(eval-after-load 'coq-mode '(require 'smartparens-coq)) (eval-after-load 'crystal-mode '(require 'smartparens-crystal)) (eval-after-load 'elixir-mode '(require 'smartparens-elixir)) (eval-after-load 'elixir-ts-mode '(require 'smartparens-elixir)) diff --git a/smartparens-coq.el b/smartparens-coq.el new file mode 100644 index 00000000..c84a6cfc --- /dev/null +++ b/smartparens-coq.el @@ -0,0 +1,60 @@ +;;; smartparens-coq.el --- Additional configuration for Coq proof assistant -*- lexical-binding: t; -*- + +;; Copyright (C) 2024 Matus Goljer + +;; Author: Matus Goljer +;; Maintainer: Matus Goljer +;; Created: 29 June 2024 +;; Keywords: smartparens, coq +;; URL: https://github.com/Fuco1/smartparens + +;; This file is not part of GNU Emacs. + +;;; License: + +;; This file is part of Smartparens. + +;; Smartparens 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. + +;; Smartparens 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 Smartparens. If not, see . + +;;; Commentary: + +;; This file provides some additional configuration for Coq proof +;; assistant. To use it, simply add: +;; +;; (require 'smartparens-coq) +;; +;; into your configuration. You can use this in conjunction with the +;; default config or your own configuration. +;; +;; If you have good ideas about what should be added please file an +;; issue on the github tracker. +;; +;; For more info, see github readme at +;; https://github.com/Fuco1/smartparens + +;;; Code: + +(require 'smartparens) + +(sp-with-modes '(coq-mode) + ;; Disable ' because it is used in pattern-matching + (sp-local-pair "'" nil :actions nil) + ;; Disable ` because it is used in polymorphic variants + (sp-local-pair "`" nil :actions nil) + (sp-local-pair "(*" "*)" + :post-handlers '(("| " "SPC") + (" | " "*")))) + +(provide 'smartparens-coq) +;;; smartparens-coq.el ends here diff --git a/smartparens-ruby.el b/smartparens-ruby.el index 31267c7d..27b9c365 100644 --- a/smartparens-ruby.el +++ b/smartparens-ruby.el @@ -56,14 +56,14 @@ (defun sp-ruby-forward-sexp () "Wrapper for `ruby-forward-sexp' based on `enh-ruby-mode'." (interactive) - (if (boundp 'enh-ruby-forward-sexp) + (if (fboundp 'enh-ruby-forward-sexp) (enh-ruby-forward-sexp) (ruby-forward-sexp))) (defun sp-ruby-backward-sexp () "Wrapper for `ruby-backward-sexp' based on `enh-ruby-mode'." (interactive) - (if (boundp 'enh-ruby-backward-sexp) + (if (fboundp 'enh-ruby-backward-sexp) (enh-ruby-backward-sexp) (ruby-backward-sexp))) diff --git a/smartparens.el b/smartparens.el index f6251907..7ea05734 100644 --- a/smartparens.el +++ b/smartparens.el @@ -565,6 +565,9 @@ Symbol is defined as a chunk of text recognized by clojurec-mode clojurescript-mode clojurex-mode + clojure-ts-mode + clojurescript-ts-mode + clojurec-ts-mode common-lisp-mode emacs-lisp-mode eshell-mode @@ -597,6 +600,9 @@ Symbol is defined as a chunk of text recognized by clojurec-mode clojurescript-mode clojurex-mode + clojure-ts-mode + clojurescript-ts-mode + clojurec-ts-mode inf-clojure-mode ) "List of Clojure-related modes." @@ -606,6 +612,8 @@ Symbol is defined as a chunk of text recognized by (defcustom sp-c-modes '( c-mode c++-mode + c-ts-mode + c++-ts-mode ) "List of C-related modes." :type '(repeat symbol) @@ -737,6 +745,8 @@ You can enable pre-set bindings by customizing (add-hook 'pre-command-hook 'sp--save-pre-command-state nil 'local) (add-hook 'post-command-hook 'sp--post-command-hook-handler nil 'local) (run-hooks 'smartparens-enabled-hook)) + (when smartparens-strict-mode + (smartparens-strict-mode -1)) (remove-hook 'self-insert-uses-region-functions 'sp-wrap--can-wrap-p 'local) (remove-hook 'post-self-insert-hook 'sp--post-self-insert-hook-handler 'local) (remove-hook 'pre-command-hook 'sp--save-pre-command-state 'local) @@ -1527,6 +1537,15 @@ kill \"subwords\" when `subword-mode' is active." :type 'boolean :group 'smartparens) +(defcustom sp-delete-blank-sexps nil + "If non-nil, automatically delete enclosing pair when it only +contains whitespace. + +This setting only has effect if `smartparens-strict-mode' is +active." + :type 'boolean + :group 'smartparens) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Selection mode handling @@ -1821,14 +1840,19 @@ If optional argument P is present test this instead of point." If optional argument P is present test this instead of point. +If the sexp around the point is blank, the return value is a cons +containing the beginning and end of the expression. + Warning: it is only safe to call this when point is inside a sexp, otherwise the call may be very slow." (save-excursion (when p (goto-char p)) (-when-let (enc (sp-get-enclosing-sexp)) - (sp-get enc (string-match-p - "\\`[ \t\n]*\\'" - (buffer-substring-no-properties :beg-in :end-in)))))) + (sp-get enc + (when (string-match-p + "\\`[ \t\n]*\\'" + (buffer-substring-no-properties :beg-in :end-in)) + (cons :beg :end)))))) (defun sp-char-is-escaped-p (&optional point) "Test if the char at POINT is escaped or not. @@ -5831,7 +5855,7 @@ Pair object is anything delimited by pairs from `sp-pair-list'." (execute-kbd-macro cmd)))) (defun sp-prefix-symbol-object (&optional _arg) - "Read the command and invoke it on the next pair object. + "Read the command and invoke it on the next symbol object. If you specify a regular emacs prefix argument this is passed to the executed command. Therefore, executing @@ -7362,7 +7386,12 @@ Examples: (let ((n (abs (prefix-numeric-value arg))) (enc (sp-get-enclosing-sexp)) (in-comment (sp-point-in-comment)) - next-thing ok) + next-thing ok + ;; At some places we mutate the value of `ok' + ;; destructively (updating the end). The original value + ;; is useful in the handlers for manipulating the + ;; surroundings, so we copy it here. + ok-orig) (when enc (save-excursion (if (sp--raw-argument-p arg) @@ -7387,6 +7416,7 @@ Examples: (goto-char (sp-get next-thing :end-suf)) (setq ok next-thing) (setq next-thing (sp-get-thing nil))) + (setq ok-orig (copy-sequence ok)) ;; do not allow slurping into a different context from ;; inside a comment (if (and in-comment @@ -7427,14 +7457,14 @@ Examples: (insert " ")))) (sp--run-hook-with-args (sp-get enc :op) :pre-handlers 'slurp-forward - (list :arg arg :enc enc :ok ok :next-thing next-thing)) + (list :arg arg :enc enc :ok ok :ok-orig ok-orig :next-thing next-thing)) (sp-get ok (insert :cl :suffix)) (sp--indent-region (sp-get ok :beg-prf) (point)) ;; HACK: update the "enc" data structure if ok==enc (when (= (sp-get enc :beg) (sp-get ok :beg)) (plist-put enc :end (point))) (sp--run-hook-with-args (sp-get enc :op) :post-handlers 'slurp-forward - (list :arg arg :enc enc :ok ok :next-thing next-thing))) + (list :arg arg :enc enc :ok ok :ok-orig ok-orig :next-thing next-thing))) (setq n (1- n))) (sp-message :cant-slurp) (setq n -1)))))))) @@ -9127,6 +9157,8 @@ If on a closing delimiter, move backward into balanced expression. If on a opening delimiter, refuse to delete unless the balanced expression is empty, in which case delete the entire expression. +If `sp-delete-blank-sexps' is non-nil, the expression is also +considered empty if it contains only white-space. If the delimiter does not form a balanced expression, it will be deleted normally. @@ -9172,6 +9204,13 @@ Examples: ok) ;; make this customizable (setq n (1- n))) + ((and sp-delete-blank-sexps + (sp--looking-back (sp--get-opening-regexp (sp--get-pair-list-context 'navigate))) + (-when-let ((beg . end) (sp-point-in-blank-sexp)) + (goto-char beg) + (delete-char (- end beg)) + t)) + (setq n (1- n))) ((and (sp-point-in-string) (save-excursion (backward-char) (not (sp-point-in-string)))) (setq n 0)) diff --git a/test/smartparens-commands-test.el b/test/smartparens-commands-test.el index 3e43b7f7..32111e2a 100644 --- a/test/smartparens-commands-test.el +++ b/test/smartparens-commands-test.el @@ -725,7 +725,10 @@ be." ((nil ("[foo]|" "[foo|]") ("\\{foo\\}|" "\\{foo|\\}") - ("\"foo\\\\\"|" "\"foo\\\\|\"")))) + ("\"foo\\\\\"|" "\"foo\\\\|\"") + ("(|)" "|")) + (((sp-delete-blank-sexps t)) + ("[| ]" "|")))) (ert-deftest sp-test-command-sp-backward-delete-char-hungry-delete-mode () "In `hungry-delete-mode' we should kill all whitespace." diff --git a/test/smartparens-elisp-test.el b/test/smartparens-elisp-test.el index 9e1036e2..334725c7 100644 --- a/test/smartparens-elisp-test.el +++ b/test/smartparens-elisp-test.el @@ -25,3 +25,54 @@ punctuation was considered invalid." (sp-backward-delete-char) (sp-backward-delete-char) (sp-buffer-equals ";; `a-symbol-name'? I|"))) + +(prog1 "#781" + (ert-deftest sp-test-slurp-insert-space-for-style--next-sexp-paren-no-space () + (sp-test-with-temp-elisp-buffer "(foo|)(bar)" + (call-interactively 'sp-forward-slurp-sexp) + (sp-buffer-equals "(foo| (bar))") + (call-interactively 'sp-forward-barf-sexp) + (sp-buffer-equals "(foo|) (bar)") + (call-interactively 'sp-forward-slurp-sexp) + (sp-buffer-equals "(foo| (bar))"))) + + (ert-deftest sp-test-slurp-insert-space-for-style--next-sexp-paren-space () + (sp-test-with-temp-elisp-buffer "(foo|) (bar)" + (call-interactively 'sp-forward-slurp-sexp) + (sp-buffer-equals "(foo| (bar))") + (call-interactively 'sp-forward-barf-sexp) + (sp-buffer-equals "(foo|) (bar)") + (call-interactively 'sp-forward-slurp-sexp) + (sp-buffer-equals "(foo| (bar))"))) + + (ert-deftest sp-test-slurp-insert-space-for-style--next-sexp-symbol-no-space () + (sp-test-with-temp-elisp-buffer "(foo|)bar" + (call-interactively 'sp-forward-slurp-sexp) + (sp-buffer-equals "(foo| bar)") + (call-interactively 'sp-forward-barf-sexp) + (sp-buffer-equals "(foo|) bar") + (call-interactively 'sp-forward-slurp-sexp) + (sp-buffer-equals "(foo| bar)"))) + + (ert-deftest sp-test-slurp-insert-space-for-style--next-sexp-symbol-space () + (sp-test-with-temp-elisp-buffer "(foo|) bar" + (call-interactively 'sp-forward-slurp-sexp) + (sp-buffer-equals "(foo| bar)") + (call-interactively 'sp-forward-barf-sexp) + (sp-buffer-equals "(foo|) bar") + (call-interactively 'sp-forward-slurp-sexp) + (sp-buffer-equals "(foo| bar)"))) + + (ert-deftest sp-test-slurp-insert-space-for-style--no-extra-space-from-empty-sexp () + (sp-test-with-temp-elisp-buffer "(|)foo" + (call-interactively 'sp-forward-slurp-sexp) + (sp-buffer-equals "(|foo)") + (call-interactively 'sp-forward-barf-sexp) + (sp-buffer-equals "(|)foo"))) + + (ert-deftest sp-test-slurp-insert-space-for-style--next-on-new-line () + (sp-test-with-temp-elisp-buffer "(foo|)\n(bar)" + (call-interactively 'sp-forward-slurp-sexp) + (sp-buffer-equals "(foo|\n (bar))") + (call-interactively 'sp-forward-barf-sexp) + (sp-buffer-equals "(foo|)\n(bar)"))))