Skip to content

Fix auto-insertion of spaces in paredit #566

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Apr 19, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
3 changes: 2 additions & 1 deletion Cask
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@

(development
(depends-on "s")
(depends-on "buttercup"))
(depends-on "buttercup")
(depends-on "paredit"))
50 changes: 10 additions & 40 deletions clojure-mode.el
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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)
Expand All @@ -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 ()
Expand Down
134 changes: 134 additions & 0 deletions test/clojure-mode-external-interaction-test.el
Original file line number Diff line number Diff line change
@@ -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 <bozhidar@batsov.com>

;; 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 <http://www.gnu.org/licenses/>.

;;; 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"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get the point of using this macro here, but probably we need some alias for it, otherwise the tests read a bit weird. Probably not a big deal, though, as I can't suggest a great name right now - something like with-edits/actions.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I couldn't think of a name as well, but at least the console output makes sense -

Interactions with Paredit:
  it should insert a space
    before lists (0.19ms)
    before vectors (0.17ms)
    before maps (0.16ms)
    before strings (0.15ms)
    after gensym (0.20ms)
    after symbols ending with ' (0.19ms)
  it should not insert a space
    for anonymous fn syntax (0.19ms)
    for hash sets (0.17ms)
    for regexes (0.16ms)
    for quoted collections (0.17ms)
    for reader conditionals (0.17ms)

"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
9 changes: 0 additions & 9 deletions test/clojure-mode-syntax-test.el
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down