Eclipse-like Line Commenting in Emacs - eclipse

In Eclipse, highlighting multiple rows and pressing Ctrl+/ comments each of the lines of the selection.
Emacs has a function comment-or-uncomment-region that is close what I want, but behaves differently if the region only partially covers the lines I'm trying to comment.
Is there any way I make a function similar to comment-or-uncomment-region, but have it comment each of the lines of the region regardless of how the region is selected?
In other words, I want the function to act as though the region occupies the whole line as long as the region includes that line, so it behaves as Eclipse's selection commenting does.
EDIT: I am actually using the comment-or-uncomment-region-or-line function mentioned as an answer instead of the function comment-or-uncomment-region that comes with Emacs.
I feel as though this is worth mentioning because the former seems to reflect how the line commenting works in Eclipse more. That is, the line the point is on is commented if no region exists.

I ended up combining parts from juanleon's and Ehvince's answers to get something just a little more like Eclipse's commenting.
Here is the final product:
(defun comment-eclipse ()
(interactive)
(let ((start (line-beginning-position))
(end (line-end-position)))
(when (or (not transient-mark-mode) (region-active-p))
(setq start (save-excursion
(goto-char (region-beginning))
(beginning-of-line)
(point))
end (save-excursion
(goto-char (region-end))
(end-of-line)
(point))))
(comment-or-uncomment-region start end)))
Please let me know if anything is wrong with it.

Note that emacs 25 has a new function comment-line bound to C-x C-;.

Here you have a function that do what you are describing:
(defun comment-or-uncomment-region-eclipse-style (beg end &optional arg)
(interactive "*r\nP")
(comment-or-uncomment-region
(save-excursion
(goto-char beg)
(beginning-of-line)
(point))
(save-excursion
(goto-char end)
(end-of-line)
(point)) arg))

FWIW, I don't use comment-or-uncomment-region. I use comment-region instead. It's similar, but it lets you decide whether to uncomment or comment. It lets you nest comments, instead of automatically uncommenting the region if it is already commented out. With a numeric prefix arg it uses that many comment-start chars (e.g., ;, ;;, ;;;,... in Lisp). With a plain C-u prefix arg it uncomments. I bind it to C-x C-;.
Anyway, I think this does what you want, using comment-region (see that for the general behavior):
(defun comment-region-lines (beg end &optional arg)
"Like `comment-region', but comment/uncomment whole lines."
(interactive "*r\nP")
(if (> beg end) (let (mid) (setq mid beg beg end end mid)))
(let ((bol (save-excursion (goto-char beg) (line-beginning-position)))
(eol (save-excursion (goto-char end) (line-end-position))))
(comment-region bol end arg)))
;; Suggested binding
(define-key ctl-x-map [(control ?\;)] 'comment-region-lines)
This saves and restores the region. And it works if only part of a single line is selected. I might even use it myself (which is saying quite a bit, since I have pretty set habits for this kind of thing).

Compared to Juanleon's solution, mine adds the fact that if you don't select a region it will (un)comment the current line and go the next line (instead of doing something based on marks you don't see):
(defun comment-or-uncomment-region-or-line ()
"Comments or uncomments the region or the current line if there's no active region."
(interactive)
(let (beg end)
(if (region-active-p)
(setq beg (region-beginning) end (region-end))
(setq beg (line-beginning-position) end (line-end-position)))
(comment-or-uncomment-region beg end)
(next-line)))
;; bind it to F7:
(global-set-key (kbd "<f7>")'comment-or-uncomment-region-or-line)
taken from: Emacs comment/uncomment current line

There is a file which provides the following
(defun ar-comment-or-uncomment-lor (&optional copy beg end)
"Comment line or region, unless it's already commented:
uncomment then.
..."
...
Afterwards cursor is at next line, which permits repeated execution.
With C-u the current line is copied and inserted as comment above - thus reminding the previous state when editing.
Get it here:
https://github.com/andreas-roehler/werkstatt/blob/master/ar-comment-lor.el

Here's a slight change to Ehvince's function which only advances to the next line if text was commented out. i.e., if uncommenting text, you usually want the cursor to remain.
(defun comment-or-uncomment-region-or-line ()
"Comments or uncomments the region or the current line if there's no active region."
(interactive)
(let (beg end)
(if (region-active-p)
(setq beg (region-beginning) end (region-end))
(setq beg (line-beginning-position) end (line-end-position)))
(comment-or-uncomment-region beg end)
(when (comment-only-p beg end)
(next-logical-line))))

Related

What's wrong with this elisp function?

I write a elisp function to copy the current line if no region has be selected, but it does not work on emacs 24.5. When I hit the "M-w" keystrokes , there comes a message "Mark set" in the minibuffer. Did I miss something?
(defun copy-region-or-current-line (beg end)
"copy current if no region selected, copy the region otherwise"
(interactive "r")
(let ((cur-pos (point)))
(if (region-active-p)
(kill-ring-save beg end)
(progn
(kill-whole-line)
(yank)
(goto-char cur-pos)))))
(global-set-key (kbd "M-w") 'copy-region-or-current-line)
Your function works: You're calling yank and that command sets the mark; hence the message.
That's a side effect you undoubtedly don't want, though, and the kill+yank sequence isn't necessary.
You already know about kill-ring-save, so just use that with (line-beginning-position) and (line-end-position).
FYI, on account of the optional REGION argument to kill-ring-save, you could rewrite this as:
(defun copy-region-or-current-line ()
"Copy the active region or the current line to the kill ring."
(interactive)
(if (region-active-p)
(kill-ring-save nil nil t)
(kill-ring-save (line-beginning-position) (line-end-position))))

How do I navigate efficiently through emacs buffer modifying lines

I am an elisp (but not programming) beginner and have some questions about the best practice to implement a function. I have written an elisp function that reformats assembler source code according to certain rules; this function currently works for a single line. It basically uses navigation within the line, looking-at and replace-match calls on subexpressions to achieve the goal.
Now I'd like to apply it to a marked region, processing the region line by line. The behaviour will be similar to the indent-region function.
What is the recommended (and efficient) way to implement this? I consider using (line-number-at-pos ...) applied to (region-beginning) and (region-end) to count line numbers and then move from top to bottom, working through the buffer line by line, modifying these.
Also, what would I need to preserve through this operation? I though about (save-match-data ...) and am not sure how to handle mark and point. I guess they will be useless because the text extent changed.
Use save-excursion to save and restore point and mark and save-restriction to narrow to the region.
The template would be something like this:
(defun my-process-region (beg end)
"Apply `my-process-line` to every line in region."
(interactive "r")
(save-restriction
(widen)
(save-excursion
(narrow-to-region beg end)
(goto-char (point-min))
(while (not (eobp))
(my-process-line)))))
I accept the answer of sds. In the end, I used the code below. The reason was that I wanted entire lines available for reformatting, not just the marked region. So (narrow-to-region) alone would not have done the job.
I am happy to learn more, and appreciate comments on pros/cons or missing things:
(defun x-mode-reformat-region (beg end)
"..."
(interactive "r")
(save-excursion
(let ((nlines (+ 1 (apply '- (mapcar 'line-number-at-pos `(,end ,beg)))))
bol
...)
(goto-char beg)
(dotimes (i nlines)
(setq bol (line-beginning-position))
(goto-char bol)
;; do reformatting for this line -- uses bol for calculations
(forward-line)))))
Next try -- modified based on comment. I did not find a simpler way to extend the selection to include the entire line... any idea whether the setq / narrow-to-region combination could be simplified further (except using (progn ...) directly as argument ?
(defun x-mode-reformat-region (beg end)
"..."
(interactive "r")
(save-restriction
(widen)
(save-excursion
(setq beg (progn (goto-char beg) (line-beginning-position))
end (progn (goto-char end) (line-end-position)))
(narrow-to-region beg end)
(goto-char (point-min))
(while (not (eobp))
(insert "*") ;; placeholder for fancy reformatting
(forward-line)))))

How to run a emacs command on a range of lines?

I want to run sort-lines command on the whole file.
C-xhM-xsort-linesEnter
I need to mark the region before running sort-lines command.
How to skip the C-xh. Can I provide a argument (like % in vim) to sort-lines command?
Here's what appears to be the simplest solution -- if no region is active, pass the extents of the buffer to sort-lines:
(defun my-sort-lines (reverse begin end)
(interactive "P\nr")
(sort-lines reverse
(if (region-active-p) begin (point-min))
(if (region-active-p) end (point-max))))
I'm not an expert, but I don't believe sort-lines can accept line numbers as arguments. That said, it shouldn't be difficult to write a function to do this. Here's a quick one I just hacked together:
(defun my-sort-lines (start end)
(interactive "nStart: \nnEnd: ")
(save-excursion
(save-restriction
(goto-line start)
(mark)
(goto-line (1+ end))
(narrow-to-region (mark) (point))
(sort-lines nil (point-min) (point-max))
(widen))))
From the limited amount of testing I've done, it appears to work. I'm sure there are improvements to be made, given that my experience with emacs lisp is fairly minimal.

emacs: what is C-c % doing in auctex and how can I make it behave better?

C-c % is supposed to be the emacs auctex mode shortcut for commenting out stuff. (There's also C-c ; which comments out the marked region, but that one works). Now sometimes it comments out a single line, sometimes it comments out a line and the ones above it. It doesn't seem to have very consistent behaviour.
What I'd really like it to do is comment out the line the cursor is on unless it's on a begin or end tag, in which case comment out the whole environment. (Actually, I'd settle for just understanding the slightly odd behaviour of the comment macro...)
C-c % runs TeX-comment-or-uncomment-paragraph. For what exactly is considered a paragraph here, see the manual:
Command: TeX-comment-or-uncomment-paragraph
(C-c %) Add or remove % from the beginning of each line in the current paragraph. When removing % characters the paragraph is considered to consist of all preceding and succeeding lines starting with a %, until the first non-comment line.
Here's a commenting function that does more or less what you want. Uncommenting an environment only works if LaTeX-syntactic-comments is t (and not always very well even then).
(defun LaTeX-comment-environment-or-line (arg)
"Comment or uncomment the current line.
If the current line is the \\begin or \\end line of an environment, comment
or uncomment the whole environment."
(interactive "*P")
(save-match-data
(save-excursion
(beginning-of-line)
(cond
((looking-at (concat "\\s-*\\(" TeX-comment-start-regexp "\\)?\\s-*"
(regexp-quote TeX-esc) "begin"))
(let ((begin (point)))
(goto-char (match-end 0))
(LaTeX-find-matching-end)
(TeX-comment-or-uncomment-region begin (point) arg)))
((looking-at (concat "\\s-*\\(" TeX-comment-start-regexp "\\)?\\s-*"
(regexp-quote TeX-esc) "end"))
(let ((end (save-excursion (end-of-line) (point))))
(LaTeX-find-matching-begin)
(beginning-of-line)
(TeX-comment-or-uncomment-region (point) end arg)))
(t
(TeX-comment-or-uncomment-region
(point) (save-excursion (end-of-line) (point)) arg))))))

Shift a region or line in emacs

I'm looking for a way in emacs to shift text to the right or to the left by n spaces. A similar functionality that it in vim << or >>. It should work on a region or if no region is selected on a current line and not move the cursor from its current location.
The solution from EmacsWiki does not work very well as the M-x indent-rigidly since it somewhat remembers the last region used and shifts that one instead. The closest seems to be the one here but I did not managed to make it work. I'm not a lisp developer so it's difficult to modify the code. I will appreciate any help.
Thanks!
You could select the region then C-u C-x <tab> will shift 4 spaces. You can type a number after C-u to change 4 to anything else.
Maybe this works the way you want.
(defun shift-text (distance)
(if (use-region-p)
(let ((mark (mark)))
(save-excursion
(indent-rigidly (region-beginning)
(region-end)
distance)
(push-mark mark t t)
(setq deactivate-mark nil)))
(indent-rigidly (line-beginning-position)
(line-end-position)
distance)))
(defun shift-right (count)
(interactive "p")
(shift-text count))
(defun shift-left (count)
(interactive "p")
(shift-text (- count)))
To achieve this I usually do a trick:
activate CUA mode
go to the beginning of line
C-RET, now if you move the cursor you should see a rectangular red region
Move the cursor down the lines and type space until you've obtained the correct shifting.
This can be done also programmatically in some way (in the same way).
EDIT:
I've just read the article in emacs wiki, it's the same solution except for the CUA mode that is infinitely more powerful than the "common" rectanguar selection (since it's visual).
As I use Evil (with Spacemacs), the Vim-like region shifting is already implemented in visual mode with S-v and </> properly.
I'm mostly using hybrid-mode though, and when it's active I also want to be able to shift the region, preferrably by the current language's shift-width.
To achieve this, here's an implementation that re-uses evil's shifting, but does it "properly" in hybrid-mode.
(defun jj/shift-text (beg end shift-block-fun shift-line-fun)
"shift text in region or line using evil like S-v with < and > do in Vim.
It takes special care of preserving or even extending the region to the moved text lines."
(if (use-region-p)
(progn
(let ((point-at-end (< (mark) (point))))
;; fix up current region end to grab the whole line
(if point-at-end
(end-of-line)
(beginning-of-line))
;; then fix up the other region end
(exchange-point-and-mark)
(if point-at-end
(beginning-of-line)
(end-of-line))
;; restore mark-point order
(exchange-point-and-mark)
(let ((linebeg (if point-at-end (mark) (point)))
(lineend (if point-at-end (point) (mark))))
;; shift the text
(save-mark-and-excursion
(funcall shift-block-fun linebeg lineend)
;; "In Transient Mark mode, every buffer-modifying primitive sets deactivate-mark"
;; but we wanna keep it active :)
(setq deactivate-mark nil)))))
(funcall shift-line-fun 1)))
(defun jj/shift-left (beg end)
(interactive "r")
(jj/shift-text beg end #'evil-shift-left #'evil-shift-left-line))
(defun jj/shift-right (beg end)
(interactive "r")
(jj/shift-text beg end #'evil-shift-right #'evil-shift-right-line))
and where your keybindings are defined:
;; text shifting. evil-normal-state-map has these anyway.
(define-key evil-hybrid-state-map (kbd "M-<") #'jj/shift-left)
(define-key evil-hybrid-state-map (kbd "M->") #'jj/shift-right)