diff --git a/CHANGELOG.md b/CHANGELOG.md index e7357327..86379da8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ ### Bugs fixed +* [#565](https://github.com/clojure-emacs/clojure-mode/issues/565): Fix extra spaces being inserted after quote in paredit-mode. * [#544](https://github.com/clojure-emacs/clojure-mode/issues/544): Fix docstring detection when string contains backslash. * [#547](https://github.com/clojure-emacs/clojure-mode/issues/547): Fix font-lock regex for character literals for uppercase chars and other symbols. * [#556](https://github.com/clojure-emacs/clojure-mode/issues/556): Fix renaming of ns aliases containing regex characters. diff --git a/Cask b/Cask index d6771c81..abcd75fd 100644 --- a/Cask +++ b/Cask @@ -5,4 +5,5 @@ (development (depends-on "s") - (depends-on "buttercup")) + (depends-on "buttercup") + (depends-on "paredit")) diff --git a/clojure-mode.el b/clojure-mode.el index 4abc7015..994c8bc6 100644 --- a/clojure-mode.el +++ b/clojure-mode.el @@ -456,21 +456,13 @@ The command will prompt you to select one of the available sections." "Prevent paredit from inserting useless spaces. See `paredit-space-for-delimiter-predicates' for the meaning of ENDP and DELIM." - (or endp - (not (memq delim '(?\" ?{ ?\( ))) - (not (or (derived-mode-p 'clojure-mode) - (derived-mode-p 'cider-repl-mode))) - (save-excursion - (backward-char) - (cond ((eq (char-after) ?#) - (and (not (bobp)) - (or (char-equal ?w (char-syntax (char-before))) - (char-equal ?_ (char-syntax (char-before)))))) - ((and (eq delim ?\() - (eq (char-after) ??) - (eq (char-before) ?#)) - nil) - (t))))) + (and (not endp) + ;; don't insert after opening quotes, auto-gensym syntax, or reader tags + (not (looking-back + (if (member delim clojure-omit-space-between-tag-and-delimiters) + "\\_<\\(?:'+\\|#.*\\)" + "\\_<\\(?:'+\\|#\\)") + (point-at-bol))))) (defconst clojure--collection-tag-regexp "#\\(::[a-zA-Z0-9._-]*\\|:?\\([a-zA-Z0-9._-]+/\\)?[a-zA-Z0-9._-]+\\)" "Collection reader macro tag regexp. @@ -479,29 +471,8 @@ collection literal (e.g. '[]' or '{}'), as reader macro tags. This includes #fully.qualified/my-ns[:kw val] and #::my-ns{:kw val} as of Clojure 1.9.") -(defun clojure-no-space-after-tag (endp delimiter) - "Prevent inserting a space after a reader-literal tag. - -When a reader-literal tag is followed be an opening delimiter -listed in `clojure-omit-space-between-tag-and-delimiters', this -function returns t. - -This allows you to write things like #db/id[:db.part/user] -and #::my-ns{:some \"map\"} without inserting a space between -the tag and the opening bracket. - -See `paredit-space-for-delimiter-predicates' for the meaning of -ENDP and DELIMITER." - (if endp - t - (or (not (member delimiter clojure-omit-space-between-tag-and-delimiters)) - (save-excursion - (let ((orig-point (point))) - (not (and (re-search-backward - clojure--collection-tag-regexp - (line-beginning-position) - t) - (= orig-point (match-end 0))))))))) +(make-obsolete-variable 'clojure--collection-tag-regexp nil "5.12.0") +(make-obsolete #'clojure-no-space-after-tag #'clojure-space-for-delimiter-p "5.12.0") (declare-function paredit-open-curly "ext:paredit" t t) (declare-function paredit-close-curly "ext:paredit" t t) @@ -525,10 +496,9 @@ replacement for `cljr-expand-let`." (let ((keymap (or keymap clojure-mode-map))) (define-key keymap "{" #'paredit-open-curly) (define-key keymap "}" #'paredit-close-curly)) + (make-local-variable 'paredit-space-for-delimiter-predicates) (add-to-list 'paredit-space-for-delimiter-predicates #'clojure-space-for-delimiter-p) - (add-to-list 'paredit-space-for-delimiter-predicates - #'clojure-no-space-after-tag) (advice-add 'paredit-convolute-sexp :after #'clojure--replace-let-bindings-and-indent))) (defun clojure-mode-variables () diff --git a/test/clojure-mode-external-interaction-test.el b/test/clojure-mode-external-interaction-test.el new file mode 100644 index 00000000..8451abe1 --- /dev/null +++ b/test/clojure-mode-external-interaction-test.el @@ -0,0 +1,134 @@ +;;; clojure-mode-external-interaction-test.el --- Clojure Mode interactions with external packages test suite -*- lexical-binding: t; -*- + +;; Copyright (C) 2014-2020 Bozhidar Batsov + +;; This file is not part of GNU Emacs. + +;; 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 . + +;;; Code: + +(require 'clojure-mode) +(require 'buttercup) +(require 'paredit) + +(describe "Interactions with Paredit:" + ;; reuse existing when-refactoring-it macro + (describe "it should insert a space" + (when-refactoring-it "before lists" + "foo" + "foo ()" + (paredit-mode) + (paredit-open-round)) + (when-refactoring-it "before vectors" + "foo" + "foo []" + (paredit-mode) + (paredit-open-square)) + (when-refactoring-it "before maps" + "foo" + "foo {}" + (paredit-mode) + (paredit-open-curly)) + (when-refactoring-it "before strings" + "foo" + "foo \"\"" + (paredit-mode) + (paredit-doublequote)) + (when-refactoring-it "after gensym" + "foo#" + "foo# ()" + (paredit-mode) + (paredit-open-round)) + (when-refactoring-it "after symbols ending with '" + "foo'" + "foo' ()" + (paredit-mode) + (paredit-open-round))) + (describe "it should not insert a space" + (when-refactoring-it "for anonymous fn syntax" + "foo #" + "foo #()" + (paredit-mode) + (paredit-open-round)) + (when-refactoring-it "for hash sets" + "foo #" + "foo #{}" + (paredit-mode) + (paredit-open-curly)) + (when-refactoring-it "for regexes" + "foo #" + "foo #\"\"" + (paredit-mode) + (paredit-doublequote)) + (when-refactoring-it "for quoted collections" + "foo '" + "foo '()" + (paredit-mode) + (paredit-open-round)) + (when-refactoring-it "for reader conditionals" + "foo #?" + "foo #?()" + (paredit-mode) + (paredit-open-round))) + (describe "reader tags" + (when-refactoring-it "should insert a space before strings" + "#uuid" + "#uuid \"\"" + (paredit-mode) + (paredit-doublequote)) + (when-refactoring-it "should not insert a space before namespaced maps" + "#::my-ns" + "#::my-ns{}" + (paredit-mode) + (paredit-open-curly)) + (when-refactoring-it "should not insert a space before namespaced maps 2" + "#::" + "#::{}" + (paredit-mode) + (paredit-open-curly)) + (when-refactoring-it "should not insert a space before namespaced maps 3" + "#:fully.qualified.ns123.-$#.%*+!" + "#:fully.qualified.ns123.-$#.%*+!{}" + (paredit-mode) + (paredit-open-curly)) + (when-refactoring-it "should not insert a space before tagged vectors" + "#tag123.-$#.%*+!" + "#tag123.-$#.%*+![]" + (paredit-mode) + (paredit-open-square)))) + + +(describe "Interactions with delete-trailing-whitespace" + (when-refactoring-it "should not delete trailing commas" + "(def foo + \\\"foo\\\": 1, + \\\"bar\\\": 2} + +(-> m + (assoc ,,, + :foo 123))" + "(def foo + \\\"foo\\\": 1, + \\\"bar\\\": 2} + +(-> m + (assoc ,,, + :foo 123))" + (delete-trailing-whitespace))) + +(provide 'clojure-mode-external-interaction-test) + + +;;; clojure-mode-external-interaction-test.el ends here diff --git a/test/clojure-mode-syntax-test.el b/test/clojure-mode-syntax-test.el index af00c1a7..f3699f67 100644 --- a/test/clojure-mode-syntax-test.el +++ b/test/clojure-mode-syntax-test.el @@ -75,15 +75,6 @@ (backward-prefix-chars) (expect (bobp)))))) -(describe "clojure-no-space-after-tag" - (it "should allow allow collection tags" - (dolist (tag '("#::ns" "#:ns" "#ns" "#:f.q/ns" "#f.q/ns" "#::")) - (with-clojure-buffer tag - (expect (clojure-no-space-after-tag nil ?{) :to-be nil))) - (dolist (tag '("#$:" "#/f" "#:/f" "#::f.q/ns" "::ns" "::" "#f:ns")) - (with-clojure-buffer tag - (expect (clojure-no-space-after-tag nil ?{)))))) - (describe "fill-paragraph" (it "should work within comments"