Skip to content

Add #_ reader form #411

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

Closed
benedekfazekas opened this issue Oct 16, 2016 · 7 comments
Closed

Add #_ reader form #411

benedekfazekas opened this issue Oct 16, 2016 · 7 comments

Comments

@benedekfazekas
Copy link
Member

Migrated over from clojure-emacs/clj-refactor.el#334 originally created by @agzam
Agreement was that this should rather come here. see more discussion on the original issue.

the original description is as follows:
Clojure's #_ (Ignore next form reader macro) is very useful. Would be very cool to have a function and a dedicated key binding to toggle it.

My initial attempt looks like this:

(defun cljr-toggle-ignore-next-form ()
  "Ignore next form (#_)"
  (interactive)
  (if (search-backward "#_" 2 t 1)
  (delete-char 2)
(progn
  (let ((fc (following-char)))
    (cond ((-contains? '( ?\) ?\] ?\} ) fc) (paredit-backward-up))
      ((-contains? '( ?\( ?\[ ?\: ?\{ ) fc) nil)
      (t (beginning-of-thing 'sexp)))
    (insert "#_")))))
@vspinu
Copy link
Contributor

vspinu commented Jun 19, 2017

Two notes to consider if someone ever implements this:

  • When deleting #_ don't search beyond current top level form.
  • Would be nice to have cycling behavior. That is, if cljr-toggle-ignore-next-form is called multiple times in a row, the #_ mark gets pushed one enclosing sexp up.

Without the second item, I don't see much use of this, it's straitforward to get one level up and type #_ anyways.

@yuhan0
Copy link
Contributor

yuhan0 commented Dec 3, 2018

Hi, I wrote these functions a while ago to toggle the ignore form for the surrounding list or symbol below the cursor:

(defun clojure--toggle-ignore-next-sexp ()
  "Insert or delete the `#_' characters at the current point.
Assumes point is directly before a sexp or the #_ characters"
  (if (or (looking-at "#_")
          (search-backward "#_" (- (point) 2) t 1)
          (and (looking-at-p "_") (search-backward "#" (- (point) 2) t 1)))
      (delete-char 2)
    (insert "#_")))

(defun clojure-toggle-ignore-form (&optional arg)
  "Toggle the #_ ignore reader form for the surrounding form at point.
With optional ARG, move up by ARG surrounding lists first.
When called with C-u, act on the current top level defun."
  (interactive "P")
  (save-excursion
    (if (consp arg) ;; called with C-u
        (beginning-of-defun)
      (unless (looking-at-p "[({[]")
        ;; guard against moving past top level
        (condition-case nil
            (backward-up-list arg t t)
          (scan-error nil))))
    (clojure--toggle-ignore-next-sexp)))

(defun clojure-toggle-ignore-symbol (&optional arg)
  "Toggle the #_ ignore reader form for current symbol.
With optional prefix ARG, toggle ARG symbols ahead of cursor position."
  (interactive "p")
  (save-excursion
    (dotimes (i arg)
      (beginning-of-thing 'symbol)
      (clojure--toggle-ignore-next-sexp)
      (forward-symbol 2))))

It doesn't have the "cycling" property mentioned above, but you can specify a numeric prefix for how many levels to escape before commenting, or C-u to operate on the top-level form. I've been using it regularly for a few months now, some slight idiosyncracies might be the save-excursion (lots of vanilla Emacs commands like to move the cursor to the operating point, which I find disruptive), and the smartparens inspired conventions for numeric and universal prefix args.

Let me know if it's acceptable and I can prepare a PR for it :)

@j-cr
Copy link
Contributor

j-cr commented Dec 25, 2019

Another (simpler/barebones) implementation, based on other clojure-cycle- functions:

(defun clojure-cycle-comment ()
  "Add or remove #_ literal before the current form."
  ;; code adapted from clojure-mode's `clojure-cycle-not'
  (interactive)
  (save-excursion
    (condition-case nil
        (backward-up-list)
      (scan-error (user-error "`clojure-cycle-comment' must be invoked inside a form")))
    (if (looking-back "#_")
        (delete-char -2) 
      (insert "#_"))))

@andreyorst
Copy link
Contributor

This version will ignore next sexp if used outside of expression instead of echoing error message:

(defun clojure-toggle-ignore ()
  "Add or remove #_ literal before the current form or at next form if used at top level."
  (interactive)
  (save-excursion
    (condition-case nil
        (progn
          (backward-up-list)
          (if (looking-back "#_")
              (delete-char -2)
            (insert "#_")))
      (scan-error
       (progn
         (forward-sexp)
         (if (looking-back "#_")
             (delete-char -2)
           (backward-sexp)
           (insert "#_")))))))

Although maybe finding next sexp should be improved

@dotemacs
Copy link

I do have this in the works: https://github.com/dotemacs/clojure-comment-dwim.el/ just in case that you might be interested

@Bost
Copy link
Contributor

Bost commented Oct 31, 2020

I hammered myself following code. I wrote it some time ago so:

  • it's rather well tested! :)
  • IIRC it works for any lisp language where you a sexp comment sequence.
;; TODO Implement using the `spacemacs/toggle'
(defun my=toggle-reader-comment-fst-sexp-on-line (cmtstr)
  "If line starts with a line comment, toggle the comment.
Otherwise toggle the reader comment."
  (if (and (current-line-empty-p) (my=end-of-file-p))
      (progn
        (message "Point at the end-of-file. Doing nothing."))
    (let* ((point-pos1 (point)))
      ;; Switch to insert state at beginning of current line.
      ;; 0 means: don't insert any line
      (evil-insert-line 0)
      (let* ((point-pos2 (point))
             (is-comment-only (comment-only-p point-pos2
                                              (save-excursion
                                                (move-end-of-line 1)
                                                (point)))))
        (if is-comment-only
            ;; (evilnc-comment-or-uncomment-lines 1)
            (spacemacs/comment-or-uncomment-lines 1)
          (let* ((cmtstr-len (length cmtstr))
                 (line-start (buffer-substring-no-properties
                              point-pos2 (+ point-pos2 cmtstr-len))))
            ;; (message "line-start %s" line-start)
            (if (string= cmtstr line-start)
                (progn
                  (delete-char cmtstr-len)
                  (goto-char (- point-pos1 cmtstr-len)))
              (progn
                (insert cmtstr)
                (goto-char (+ point-pos1 cmtstr-len))))))))))

(defun my=racket-toggle-reader-comment-fst-sexp-on-line ()
  (interactive)
  (my=toggle-reader-comment-fst-sexp-on-line "#;"))

(defun my=clj-toggle-reader-comment-fst-sexp-on-line (&optional arg)
  "When invoked with prefix <C u 2> it toggles two forms - for key-value pair"
  (interactive "p")
  (my=toggle-reader-comment-fst-sexp-on-line
   (if (eq 2 arg)
       "#_#_"
     "#_")))

(defun my=racket-toggle-reader-comment-current-sexp ()
  (interactive)
  (newline-and-indent)
  (my=racket-toggle-reader-comment-fst-sexp-on-line))

(defun my=clj-toggle-reader-comment-current-sexp ()
  (interactive)
  (newline-and-indent)
  (my=clj-toggle-reader-comment-fst-sexp-on-line))

With keybindings:

(dolist (state-map `(,clojure-mode-map ,cider-repl-mode-map))
    (bind-keys :map state-map
               ;; on the german keyboard the '#' is next to Enter
               ("C-s-\\" . my=clj-toggle-reader-comment-current-sexp)
               ("s-\\"   . my=clj-toggle-reader-comment-fst-sexp-on-line)
               ))

(add-hook
   'racket-mode-hook
   (lambda ()
     (bind-keys :map racket-mode-map
                ("C-s-\\" . my=racket-toggle-reader-comment-fst-sexp-on-line)
                ("s-\\"   . my=racket-toggle-reader-comment-fst-sexp-on-line))))

@yuhan0
Copy link
Contributor

yuhan0 commented Feb 26, 2021

This should be closed by #583

@bbatsov bbatsov closed this as completed Feb 26, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

8 participants