12

I am working on this little function that pulls up the next line to the current line. I want to add a functionality so that if the current line is a line comment and the next line is also a line comment, then the comment characters are removed after the "pull-up" action.

Example:

Before

;; comment 1▮
;; comment 2

Calling M-x modi/pull-up-line

After

;; comment 1▮comment 2

Note that the ;; characters are removed that were before comment 2.

(defun modi/pull-up-line ()
  "Join the following line onto the current one (analogous to `C-e', `C-d') or
`C-u M-^' or `C-u M-x join-line'.

If the current line is a comment and the pulled-up line is also a comment,
remove the comment characters from that line."
  (interactive)
  (join-line -1)
  ;; If the current line is a comment
  (when (nth 4 (syntax-ppss))
    ;; Remove the comment prefix chars from the pulled-up line if present
    (save-excursion
      (forward-char)
      (while (looking-at "/\\|;\\|#")
        (delete-forward-char 1))
      (when (looking-at "\\s-")
        (delete-forward-char 1)))))

The above function works but for now, regardless of the major-mode, it will consider / or ; or # as a comment character: (looking-at "/\\|;\\|#").

I'd like to make this line more intelligent; major-mode specific.

Solution

Thanks to @ericstokes' solution, I believe that the below now covers all my use cases :)

(defun modi/pull-up-line ()
  "Join the following line onto the current one (analogous to `C-e', `C-d') or
`C-u M-^' or `C-u M-x join-line'.

If the current line is a comment and the pulled-up line is also a comment,
remove the comment characters from that line."
  (interactive)
  (join-line -1)
  ;; If the current line is a comment
  (when (nth 4 (syntax-ppss))
    ;; Remove the comment prefix chars from the pulled-up line if present
    (save-excursion
      (forward-char)
      ;; Delete all comment-start or space characters
      (while (looking-at (concat "\\s<" ; comment-start char as per syntax table
                                 "\\|" (substring comment-start 0 1) ; first char of `comment-start'
                                 "\\|" "\\s-")) ; extra spaces
        (delete-forward-char 1)))))
6
  • Do you want this to be smart enough to handle different comment start and end characters and multi-character comments (e.g. C's /* ... */)?
    – erikstokes
    Commented Jan 18, 2015 at 18:31
  • Nope, that was all I needed. Thanks! Commented Jan 18, 2015 at 18:50
  • @erikstokes Out of curiosity, how would you handle C-style comments?
    – Pradhan
    Commented Jan 19, 2015 at 5:59
  • This solution works with multi line comments too right away, nothing special needs to be done because we don't need to delete any characters when joining multiline comments. Commented Jan 19, 2015 at 14:03
  • 2
    @Pradhan There are the comment-start and comment-end strings that are set to "/*" and "*/" in c-mode (but not c++-mode). And there's c-comment-start-regexp that matches both styles. You deleting the end characters then the beginning after joining. But I think my solution would be to uncomment-region, join-line the comment-region and let Emacs worry about what comment character is what.
    – erikstokes
    Commented Jan 19, 2015 at 16:08

3 Answers 3

11

You can check if the current character is a comment character by using the syntax table: (looking-at "\\s<"). The regexp \\s< will match any character with the "comment start" syntax; \\s> will match those with "comment end" syntax.

Another option is the variable comment-start, which is the string inserted by comment-dwim and friends. It typically is set to the comment start character plus some space.

2

There is a much simpler solution, please study my code at https://github.com/redguardtoo/evil-nerd-commenter/blob/master/evil-nerd-commenter.el

I won't copy/paste all the code here. but here are the key points:

  • comment has its own font face, search font-lock-comment-face and font-lock-comment-delimiter-face in my code

  • use Emacs own API to uncomment the second line, then join it with the first line

It should work on any sensible major modes.

This trick is not invented by me. It's actually from Emacs own code, (more specifically, flyspell). So the solution should support any major-modes flyspell supports

2
  • 3
    To highlight something as comment, it requires the syntax table to be set up appropriately, so I doubt this is smarter than the alternative of looking at the syntax.
    – wasamasa
    Commented Apr 22, 2015 at 14:32
  • I learned this trick by reading Emacs own code (actually, it's flyspell), as I documented in my code.
    – chen bin
    Commented Apr 22, 2015 at 14:37
0

If you are looking this feature for lisp code(Elisp, Clojure, Scheme and Common Lisp) , then you should try lispy, a function called lispy-fill does this kind of thing.

If you are looking this feature for other language, then you should rebox2(verified for C and Python), a function called rebox-fill or another function called rebox-dwim do this kind of thing.

They are both great packages, and they are not conflict with each other since they are used for different major-modes, and they are both(lispy-fill and rebox-dwim) bound to M-q, pretty neat.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.