Skip to content

Toggle ignore reader forms #583

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 6 commits into from
Feb 26, 2021
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## master (unreleased)

### New features

* [#567](https://github.com/clojure-emacs/clojure-mode/issues/567): Add new commands `clojure-toggle-ignore`, `clojure-toggle-ignore-surrounding-form`, and `clojure-toggle-defun` for inserting/deleting #_ ignore forms.

### Changes

* [#571](https://github.com/clojure-emacs/clojure-mode/issues/571): Remove `project.el` integration.
Expand Down
57 changes: 57 additions & 0 deletions clojure-mode.el
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,10 @@ Out-of-the box `clojure-mode' understands lein, boot, gradle,
(define-key map (kbd "s b") #'clojure-let-backward-slurp-sexp)
(define-key map (kbd "C-a") #'clojure-add-arity)
(define-key map (kbd "a") #'clojure-add-arity)
(define-key map (kbd "-") #'clojure-toggle-ignore)
(define-key map (kbd "C--") #'clojure-toggle-ignore)
(define-key map (kbd "_") #'clojure-toggle-ignore-surrounding-form)
(define-key map (kbd "C-_") #'clojure-toggle-ignore-surrounding-form)
map)
"Keymap for Clojure refactoring commands.")
(fset 'clojure-refactor-map clojure-refactor-map)
Expand All @@ -264,6 +268,8 @@ Out-of-the box `clojure-mode' understands lein, boot, gradle,
["Cycle if, if-not" clojure-cycle-if]
["Cycle when, when-not" clojure-cycle-when]
["Cycle not" clojure-cycle-not]
["Toggle #_ ignore form" clojure-toggle-ignore]
["Toggle #_ ignore of surrounding form" clojure-toggle-ignore-surrounding-form]
["Add function arity" clojure-add-arity]
("ns forms"
["Insert ns form at the top" clojure-insert-ns-form]
Expand Down Expand Up @@ -2822,6 +2828,57 @@ Assumes cursor is at beginning of function."
(clojure--add-arity-reify-internal)))
(indent-region beg end-marker))))


;;; Toggle Ignore forms

(defun clojure--toggle-ignore-next-sexp (&optional n)
"Insert or delete N `#_' ignore macros at the current point.
Point must be directly before a sexp or the #_ characters.
When acting on a top level form, insert #_ on a new line
preceding the form to prevent indentation changes."
(let ((rgx (rx-to-string `(repeat ,(or n 1) (seq "#_" (* (in "\r\n" blank)))))))
(backward-prefix-chars)
(skip-chars-backward "#_ \r\n")
(skip-chars-forward " \r\n")
(if (looking-at rgx)
(delete-region (point) (match-end 0))
(dotimes (_ (or n 1)) (insert-before-markers "#_"))
(when (zerop (car (syntax-ppss)))
(insert-before-markers "\n")))))

(defun clojure-toggle-ignore (&optional n)
"Toggle the #_ ignore reader form for the sexp at point.
With numeric argument, toggle N number of #_ forms at the same point.

e.g. with N = 2:
|a b c => #_#_a b c"
(interactive "p")
(save-excursion
(ignore-errors
(goto-char (or (nth 8 (syntax-ppss)) ;; beginning of string
(beginning-of-thing 'sexp))))
(clojure--toggle-ignore-next-sexp n)))

(defun clojure-toggle-ignore-surrounding-form (&optional arg)
"Toggle the #_ ignore reader form for the surrounding form at point.
With optional ARG, move up by ARG surrounding forms first.
With universal argument \\[universal-argument], act on the \"top-level\" form."
(interactive "P")
(save-excursion
(if (consp arg)
(clojure-toggle-ignore-defun)
(condition-case nil
(backward-up-list arg t t)
(scan-error nil)))
(clojure--toggle-ignore-next-sexp)))

(defun clojure-toggle-ignore-defun ()
"Toggle the #_ ignore reader form for the \"top-level\" form at point."
(interactive)
(save-excursion
(beginning-of-defun)
(clojure--toggle-ignore-next-sexp)))


;;; ClojureScript
(defconst clojurescript-font-lock-keywords
Expand Down
25 changes: 0 additions & 25 deletions test/clojure-mode-refactor-add-arity-test.el
Original file line number Diff line number Diff line change
Expand Up @@ -25,31 +25,6 @@
(require 'clojure-mode)
(require 'buttercup)

(defmacro when-refactoring-with-point-it (description before after &rest body)
"Return a buttercup spec.

Like when-refactor-it but also checks whether point is moved to the expected
position.

BEFORE is the buffer string before refactoring, where a pipe (|) represents
point.

AFTER is the expected buffer string after refactoring, where a pipe (|)
represents the expected position of point.

DESCRIPTION is a string with the description of the spec."
`(it ,description
(let* ((after ,after)
(expected-cursor-pos (1+ (s-index-of "|" after)))
(expected-state (delete ?| after)))
(with-clojure-buffer ,before
(goto-char (point-min))
(search-forward "|")
(delete-char -1)
,@body
(expect (buffer-string) :to-equal expected-state)
(expect (point) :to-equal expected-cursor-pos)))))

(describe "clojure-add-arity"

(when-refactoring-with-point-it "should add an arity to a single-arity defn with args on same line"
Expand Down
13 changes: 0 additions & 13 deletions test/clojure-mode-sexp-test.el
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,6 @@
(require 'clojure-mode)
(require 'buttercup)

(defmacro with-clojure-buffer-point (text &rest body)
"Run BODY in a temporary clojure buffer with TEXT.

TEXT is a string with a | indicating where point is. The | will be erased
and point left there."
(declare (indent 2))
`(progn
(with-clojure-buffer ,text
(goto-char (point-min))
(re-search-forward "|")
(delete-char -1)
,@body)))

(describe "clojure-top-level-form-p"
(it "should return true when passed the correct form"
(with-clojure-buffer-point
Expand Down
87 changes: 86 additions & 1 deletion test/clojure-mode-util-test.el
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@
(clojure-mode)
(clojure-sort-ns)
(expect (buffer-string) :to-equal
"\n(ns my-app.core
"\n(ns my-app.core
(:require [my-app.state :refer [state]] ; Comments too.
my-app.util.api
[my-app.views [front-page :as front-page]]
Expand All @@ -149,6 +149,91 @@
(:import [clojure.lang AFunction Atom MultiFn Namespace]
java.io.Writer))"))))

(describe "clojure-toggle-ignore"
(when-refactoring-with-point-it "should add #_ to literals"
"[1 |2 3]" "[1 #_|2 3]"
(clojure-toggle-ignore))
(when-refactoring-with-point-it "should work with point in middle of symbol"
"[foo b|ar baz]" "[foo #_b|ar baz]"
(clojure-toggle-ignore))
(when-refactoring-with-point-it "should remove #_ after cursor"
"[1 |#_2 3]" "[1 |2 3]"
(clojure-toggle-ignore))
(when-refactoring-with-point-it "should remove #_ before cursor"
"[#_:fo|o :bar :baz]" "[:fo|o :bar :baz]"
(clojure-toggle-ignore))
(when-refactoring-with-point-it "should insert multiple #_"
"{:foo| 1 :bar 2 :baz 3}"
"{#_#_#_#_:foo| 1 :bar 2 :baz 3}"
(clojure-toggle-ignore 4))
(when-refactoring-with-point-it "should remove multiple #_"
"{#_#_#_#_:foo| 1 :bar 2 :baz 3}"
"{#_#_:foo| 1 :bar 2 :baz 3}"
(clojure-toggle-ignore 2))
(when-refactoring-with-point-it "should handle spaces and newlines"
"[foo #_ \n #_ \r\n b|ar baz]" "[foo b|ar baz]"
(clojure-toggle-ignore 2))
(when-refactoring-with-point-it "should toggle entire string"
"[:div \"lorem ips|um text\"]"
"[:div #_\"lorem ips|um text\"]"
(clojure-toggle-ignore))
(when-refactoring-with-point-it "should toggle regexps"
"[|#\".*\"]"
"[#_|#\".*\"]"
(clojure-toggle-ignore))
(when-refactoring-with-point-it "should toggle collections"
"[foo |[bar baz]]"
"[foo #_|[bar baz]]"
(clojure-toggle-ignore))
(when-refactoring-with-point-it "should toggle hash sets"
"[foo #|{bar baz}]"
"[foo #_#|{bar baz}]"
(clojure-toggle-ignore))
(when-refactoring-with-point-it "should work on last-sexp"
"[foo '(bar baz)| quux]"
"[foo #_'(bar baz)| quux]"
(clojure-toggle-ignore))
(when-refactoring-with-point-it "should insert newline before top-level form"
"|[foo bar baz]"
"#_
|[foo bar baz]"
(clojure-toggle-ignore)))

(describe "clojure-toggle-ignore-surrounding-form"
(when-refactoring-with-point-it "should toggle lists"
"(li|st [vector {map #{set}}])"
"#_\n(li|st [vector {map #{set}}])"
(clojure-toggle-ignore-surrounding-form))
(when-refactoring-with-point-it "should toggle vectors"
"(list #_[vector| {map #{set}}])"
"(list [vector| {map #{set}}])"
(clojure-toggle-ignore-surrounding-form))
(when-refactoring-with-point-it "should toggle maps"
"(list [vector #_ \n {map #{set}|}])"
"(list [vector {map #{set}|}])"
(clojure-toggle-ignore-surrounding-form))
(when-refactoring-with-point-it "should toggle sets"
"(list [vector {map #{set|}}])"
"(list [vector {map #_#{set|}}])"
(clojure-toggle-ignore-surrounding-form))
(when-refactoring-with-point-it "should work with numeric arg"
"(four (three (two (on|e)))"
"(four (three #_(two (on|e)))"
(clojure-toggle-ignore-surrounding-form 2))
(when-refactoring-with-point-it "should remove #_ with numeric arg"
"(four #_(three (two (on|e)))"
"(four (three (two (on|e)))"
(clojure-toggle-ignore-surrounding-form 3)))

(describe "clojure-toggle-ignore-defun"
(when-refactoring-with-point-it "should ignore defun with newline"
"(defn foo [x]
{:nested (in|c x)})"
"#_
(defn foo [x]
{:nested (in|c x)})"
(clojure-toggle-ignore-defun)))

(provide 'clojure-mode-util-test)

;;; clojure-mode-util-test.el ends here
39 changes: 39 additions & 0 deletions test/utils/test-helper.el
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,19 @@
(clojure-mode)
,@body))

(defmacro with-clojure-buffer-point (text &rest body)
"Run BODY in a temporary clojure buffer with TEXT.

TEXT is a string with a | indicating where point is. The | will be erased
and point left there."
(declare (indent 2))
`(progn
(with-clojure-buffer ,text
(goto-char (point-min))
(re-search-forward "|")
(delete-char -1)
,@body)))

(defmacro when-refactoring-it (description before after &rest body)
"Return a buttercup spec.

Expand All @@ -56,4 +69,30 @@ DESCRIPTION is the description of the spec."
,@body
(expect (buffer-string) :to-equal ,after))))

(defmacro when-refactoring-with-point-it (description before after &rest body)
"Return a buttercup spec.

Like when-refactor-it but also checks whether point is moved to the expected
position.

BEFORE is the buffer string before refactoring, where a pipe (|) represents
point.

AFTER is the expected buffer string after refactoring, where a pipe (|)
represents the expected position of point.

DESCRIPTION is a string with the description of the spec."
(declare (indent 1))
`(it ,description
(let* ((after ,after)
(expected-cursor-pos (1+ (s-index-of "|" after)))
(expected-state (delete ?| after)))
(with-clojure-buffer ,before
(goto-char (point-min))
(search-forward "|")
(delete-char -1)
,@body
(expect (buffer-string) :to-equal expected-state)
(expect (point) :to-equal expected-cursor-pos)))))

;;; test-helper.el ends here