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)))))
Kaushal Modi
  • 25,203
  • 3
  • 74
  • 179
  • 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 Jan 18 '15 at 18:31
  • Nope, that was all I needed. Thanks! – Kaushal Modi Jan 18 '15 at 18:50
  • @erikstokes Out of curiosity, how would you handle C-style comments? – Pradhan Jan 19 '15 at 05: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. – Kaushal Modi Jan 19 '15 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 Jan 19 '15 at 16:08
  • @kaushalmodi You don't need to remove the characters but you probably should otherwise funny things might happen if you try to uncomment. `comment-dwim` does the right thing but doing it "by hand" doesn't. – erikstokes Jan 19 '15 at 16:13

3 Answers3

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.

erikstokes
  • 12,686
  • 2
  • 34
  • 56
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

chen bin
  • 4,781
  • 18
  • 36
  • 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 Apr 22 '15 at 14:32
  • I learned this trick by reading Emacs own code (actually, it's flyspell), as I documented in my code. – chen bin Apr 22 '15 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.

CodyChan
  • 2,599
  • 1
  • 19
  • 33