What's wrong with this elisp function? - emacs

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))))

Related

Highlight a name throughout an Emacs buffer

I am starting to learn Emacs Lisp, and as a first project I would like to improve the fortran mode in Emacs. I would like to mark the name of a sub routine in the buffer, and then press a shortcut key. To bring up a buffer with all lines in the given source where the name of the subroutine is mentioned.
I found that I can get the marked text using:
(defun get-selected-text (beg end)
(interactive
(if (use-region-p)
(list (region-beginning) (region-end))
(list nil nil)))
(message "%s" (if (and beg end)
(buffer-substring-no-properties beg end) "")))
and can store the line numbers of the subroutines using:
(defun get-line-numbers (str)
(interactive "sEnter string: ")
(save-excursion
(goto-char 0)
(let (( sok 1) (list nil) pp)
(while sok
(setq pp (search-forward str nil t))
(if pp (push (line-number-at-pos pp) list)
(setq sok nil)))
(message "%s" list))))
I would now like to open a new buffer similar to when I use Ctrl-x Ctrl-b to execute list-buffers and then display each line number, together with the text on the line, and the user can select a given line, and press Enter to goto the given line in the original buffer..
Just wanted to show you my version of occur-dwim.
I remember spending some time to find out about the regexp-history variable.
The first function is similar to your get-selected-text.
(defun region-str-or-symbol ()
"Return the contents of region or current symbol."
(if (region-active-p)
(buffer-substring-no-properties
(region-beginning)
(region-end))
(thing-at-point 'symbol)))
(defun occur-dwim ()
"Call `occur' with a sane default."
(interactive)
(push (region-str-or-symbol) regexp-history)
(call-interactively 'occur))
To display the list-buffer you use get-buffer-create and clear it with erase-buffer (it might be that it already extisted).
To output the lines you search in the current buffer save the line in a string and put it into the list buffer via with-current-buffer and insert.
To make return special on the text or to make it clickable put a text-property with a local keymap on it.
With this guide you should be able to find everything you need in the elisp-manual.
Regarding your code, you get the beginning and end of the current region with (interactive "r"). Therewith you also get the error message if there is no active region.

Eclipse-like Line Commenting in Emacs

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))))

Emacs Shell mode: how to send region to shell?

Is there some module or command that'll let me send the current region to shell?
I want to have something like Python-mode's python-send-region which sends the selected region to the currently running Python shell.
Ok, wrote an easy bit. Will probably spend some time to write a complete minor mode.
For time being the following function will send current line (or region if the mark is active). Does quite a good job for me:
(defun sh-send-line-or-region (&optional step)
(interactive ())
(let ((proc (get-process "shell"))
pbuf min max command)
(unless proc
(let ((currbuff (current-buffer)))
(shell)
(switch-to-buffer currbuff)
(setq proc (get-process "shell"))
))
(setq pbuff (process-buffer proc))
(if (use-region-p)
(setq min (region-beginning)
max (region-end))
(setq min (point-at-bol)
max (point-at-eol)))
(setq command (concat (buffer-substring min max) "\n"))
(with-current-buffer pbuff
(goto-char (process-mark proc))
(insert command)
(move-marker (process-mark proc) (point))
) ;;pop-to-buffer does not work with save-current-buffer -- bug?
(process-send-string proc command)
(display-buffer (process-buffer proc) t)
(when step
(goto-char max)
(next-line))
))
(defun sh-send-line-or-region-and-step ()
(interactive)
(sh-send-line-or-region t))
(defun sh-switch-to-process-buffer ()
(interactive)
(pop-to-buffer (process-buffer (get-process "shell")) t))
(define-key sh-mode-map [(control ?j)] 'sh-send-line-or-region-and-step)
(define-key sh-mode-map [(control ?c) (control ?z)] 'sh-switch-to-process-buffer)
Enjoy.
(defun shell-region (start end)
"execute region in an inferior shell"
(interactive "r")
(shell-command (buffer-substring-no-properties start end)))
I wrote a package that sends/pipes lines or regions of code to shell processes, basically something similar that ESS is for R. It also allows for multiple shell processes to exist, and lets you choose which one to send the region to.
Have a look here: http://www.emacswiki.org/emacs/essh
M-x append-to-buffer RET
M-x shell-command-on-region
aka.
M-|
Modifying Jurgens answer above to operate on a specific buffer gives the following function, which will send the region and then switch to the buffer, displaying it in another window, the buffer named PYTHON is used for illustration. The target buffer should already be running a shell.
(defun p-send(start end)
(interactive "r") ;;Make the custom function interactive and operative on a region
(append-to-buffer (get-buffer "*PYTHON*") start end) ;;append to the buffer named *PYTHON*
(switch-to-buffer-other-window (get-buffer "*PYTHON*")) ;;switches to the buffer
(execute-kbd-macro "\C-m")) ;;sends the enter keystroke to the shell
Do you want the command to be executed automatically, or just entered into the command line in preparation?
M-x append-to-buffer RET will enter the selected text into the specified buffer at point, but the command would not be executed by the shell.
A wrapper function for that could automatically choose *shell* for the buffer (or more smartly select/prompt based on current buffers in shell-mode), and then call append-to-buffer.
You could trivially record a keyboard macro to copy the region, switch to *shell*, yank, and enter (if required).
F3M-wC-xb*shell*RETC-yRETF4
C-xC-knmy-execute-region-in-shellRET
M-xinsert-kbd-macroRETmy-execute-region-in-shellRET
(global-set-key (kbd "C-c e") 'my-execute-region-in-shell)
Update
The above (brilliant and useful) answers look a bit incomplete as of mid-2020: sh-mode has a function for sending shell region to non-interactive shell with output in the minibuffer called sh-send-line-or-region-and-step.
Alternatively: click Shell-script in the mode bar at the bottom of the window, then Mouse-1, then Execute region. The output is sent to the minibuffer and #<*Messages*>.
If minibuffer output is not enough, there are referenced techniques to redirect output to other buffers (not only the shell one, see for example "How to redirect message/echo output to a buffer in Emacs?").
You can also execute all the script with C-c C-x. VoilĂ .
Here is another solution from this post.
Just copying it for convenience. The print statement is key here.
(add-hook 'python-mode-hook
'my-python-send-statement)
(defun my-python-send-statement ()
(interactive)
(local-set-key [C-return] 'my-python-send-statement)
(end-of-line)
(set-mark (line-beginning-position))
(call-interactively 'python-shell-send-region)
(python-shell-send-string "; print()"))
I adapted the accepted answer for ansi-term / sane-term.
Changes:
change "shell" to "ansi-term" everywhere
The (with-current-buffer pbuff ...) form doesn't work, because of the way that line and char modes work in terminal mode. It'll give you a read-only buffer error. Wrapping the insert in mode commands to toggle toggle line on before inserting fixes that, but is not needed because...
You can just use the process-send-string form by itself evidently, and the term output and cursor location reflect the change.
(defun ansi-term-send-line-or-region (&optional step)
(interactive ())
(let ((proc (get-process "*ansi-term*"))
pbuf
min
max
command)
(unless proc
(let ((currbuff (current-buffer)))
(sane-term)
(switch-to-buffer currbuff)
(setq proc (get-process "*ansi-term*"))))
(setq pbuff (process-buffer proc))
(if (use-region-p)
(setq min (region-beginning)
max (region-end))
(setq min (point-at-bol)
max (point-at-eol)))
(setq command (concat (buffer-substring min max) "\n"))
(process-send-string proc command)
(display-buffer (process-buffer proc) t)
(when step
(goto-char max)
(next-line))))
(defun sh-send-line-or-region-and-step ()
(interactive)
(sh-send-line-or-region t))
(defun sh-switch-to-process-buffer ()
(interactive)
(pop-to-buffer (process-buffer (get-process "*ansi-term*")) t))
(define-key sh-mode-map [(control ?j)] 'sh-send-line-or-region-and-step)
(define-key sh-mode-map [(control ?c) (control ?z)] 'sh-switch-to-process-buffer)

emacs delete-trailing-whitespace except current line

I recently added Emacs (delete-trailing-whitespace) function to my 'before-save-hook for some programming modes, but I find it rather frustrating that it deletes whitespace from the line I am currently editing. Any suggestions as to how to fix this problem?
Since delete-trailing-whitespace respects narrowing, one solution is to narrow the buffer to the portion before the current line and call it, then narrow to the portion after the current line and call it again:
(defun delete-trailing-whitespace-except-current-line ()
(interactive)
(let ((begin (line-beginning-position))
(end (line-end-position)))
(save-excursion
(when (< (point-min) begin)
(save-restriction
(narrow-to-region (point-min) (1- begin))
(delete-trailing-whitespace)))
(when (> (point-max) end)
(save-restriction
(narrow-to-region (1+ end) (point-max))
(delete-trailing-whitespace))))))
Put this function on your before-save-hook instead of delete-trailing-whitespace.
This wrapper for delete-trailing-whitespace can be used to do what you want:
(defun delete-trailing-whitespace-except-current-line ()
"do delete-trailing-whitespace, except preserve whitespace of current line"
(interactive)
(let ((current-line (buffer-substring (line-beginning-position) (line-end-position)))
(backward (- (line-end-position) (point))))
(delete-trailing-whitespace)
(when (not (string-equal (buffer-substring (line-beginning-position) (line-end-position))
current-line))
(delete-region (line-beginning-position) (line-end-position))
(insert current-line)
(backward-char backward))))
I ran into the same problem, and found out that ws-butler perfectly solves it.
There is a simple sample config code:
;; autoload ws-butler on file open
(add-hook 'find-file-hook #'ws-butler-global-mode)
(setq require-final-newline t)
I simply have a wrapper to make two calls to `delete-trailing-whitespace':
(defun modi/delete-trailing-whitespace-buffer ()
"Delete trailing whitespace in the whole buffer, except on the current line.
The current line exception is because we do want to remove any whitespace
on the current line on saving the file (`before-save-hook') while we are
in-between typing something.
Do not do anything if `do-not-delete-trailing-whitespace' is non-nil."
(interactive)
(when (not (bound-and-true-p do-not-delete-trailing-whitespace))
(delete-trailing-whitespace (point-min) (line-beginning-position))
(delete-trailing-whitespace (line-end-position) (point-max))))
(add-hook 'before-save-hook #'modi/delete-trailing-whitespace-buffer)

How do I duplicate a whole line in Emacs?

I saw this same question for VIM and it has been something that I myself wanted to know how to do for Emacs. In ReSharper I use CTRL-D for this action. What is the least number of commands to perform this in Emacs?
I use
C-a C-SPACE C-n M-w C-y
which breaks down to
C-a: move cursor to start of line
C-SPACE: begin a selection ("set mark")
C-n: move cursor to next line
M-w: copy region
C-y: paste ("yank")
The aforementioned
C-a C-k C-k C-y C-y
amounts to the same thing (TMTOWTDI)
C-a: move cursor to start of line
C-k: cut ("kill") the line
C-k: cut the newline
C-y: paste ("yank") (we're back at square one)
C-y: paste again (now we've got two copies of the line)
These are both embarrassingly verbose compared to C-d in your editor, but in Emacs there's always a customization. C-d is bound to delete-char by default, so how about C-c C-d? Just add the following to your .emacs:
(global-set-key "\C-c\C-d" "\C-a\C- \C-n\M-w\C-y")
(#Nathan's elisp version is probably preferable, because it won't break if any of the key bindings are changed.)
Beware: some Emacs modes may reclaim C-c C-d to do something else.
In addition to the previous answers you can also define your own function to duplicate a line. For example, putting the following in your .emacs file will make C-d duplicate the current line.
(defun duplicate-line()
(interactive)
(move-beginning-of-line 1)
(kill-line)
(yank)
(open-line 1)
(next-line 1)
(yank)
)
(global-set-key (kbd "C-d") 'duplicate-line)
Place cursor on line, if not at beginning do a CTRL-A, then:
CTRL-K
CTRL-K
CTRL-Y
CTRL-Y
My version of a function to duplicate a line that works nice with undo and doesn't mess with the cursor position. It was the result of a discussion in gnu.emacs.sources from November 1997.
(defun duplicate-line (arg)
"Duplicate current line, leaving point in lower line."
(interactive "*p")
;; save the point for undo
(setq buffer-undo-list (cons (point) buffer-undo-list))
;; local variables for start and end of line
(let ((bol (save-excursion (beginning-of-line) (point)))
eol)
(save-excursion
;; don't use forward-line for this, because you would have
;; to check whether you are at the end of the buffer
(end-of-line)
(setq eol (point))
;; store the line and disable the recording of undo information
(let ((line (buffer-substring bol eol))
(buffer-undo-list t)
(count arg))
;; insert the line arg times
(while (> count 0)
(newline) ;; because there is no newline in 'line'
(insert line)
(setq count (1- count)))
)
;; create the undo information
(setq buffer-undo-list (cons (cons eol (point)) buffer-undo-list)))
) ; end-of-let
;; put the point in the lowest line and return
(next-line arg))
Then you can define CTRL-D to call this function:
(global-set-key (kbd "C-d") 'duplicate-line)
Instead of kill-line (C-k) as in C-a C-k C-k C-y C-y use the kill-whole-line command:
C-S-Backspace
C-y
C-y
The advantages over C-k include that it does not matter where point is on the line (unlike C-k which requires being at start of the line) and it also kills the newline (again something C-k does not do).
Here's yet another function for doing this. My version doesn't touch the kill ring, and the cursor ends up on the new line where it was on the original. It will duplicate the region if it's active (transient mark mode), or default to duplicating the line otherwise. It will also make multiple copies if given a prefix arg, and comment out the original line if given a negative prefix arg (this is useful for testing a different version of a command/statement while keeping the old one).
(defun duplicate-line-or-region (&optional n)
"Duplicate current line, or region if active.
With argument N, make N copies.
With negative N, comment out original line and use the absolute value."
(interactive "*p")
(let ((use-region (use-region-p)))
(save-excursion
(let ((text (if use-region ;Get region if active, otherwise line
(buffer-substring (region-beginning) (region-end))
(prog1 (thing-at-point 'line)
(end-of-line)
(if (< 0 (forward-line 1)) ;Go to beginning of next line, or make a new one
(newline))))))
(dotimes (i (abs (or n 1))) ;Insert N times, or once if not specified
(insert text))))
(if use-region nil ;Only if we're working with a line (not a region)
(let ((pos (- (point) (line-beginning-position)))) ;Save column
(if (> 0 n) ;Comment out original with negative arg
(comment-region (line-beginning-position) (line-end-position)))
(forward-line 1)
(forward-char pos)))))
I have it bound to C-c d:
(global-set-key [?\C-c ?d] 'duplicate-line-or-region)
This should never be re-assigned by a mode or anything because C-c followed by a single (unmodified) letter is reserved for user bindings.
Nathan's addition to your .emacs file is the way to go but it could be simplified slightly by replacing
(open-line 1)
(next-line 1)
with
(newline)
yielding
(defun duplicate-line()
(interactive)
(move-beginning-of-line 1)
(kill-line)
(yank)
(newline)
(yank)
)
(global-set-key (kbd "C-d") 'duplicate-line)
install duplicate-thing from melpa:
M-x package-install RET duplicate-thing
and add this keybinding to init file :
(global-set-key (kbd "M-c") 'duplicate-thing)
I don't quite remember how line duplication works anywhere else, but as a former SciTE user I liked one thing about SciTE-way: it doesn't touch the cursor position!
So all the recipies above weren't good enough for me, here's my hippie-version:
(defun duplicate-line ()
"Clone line at cursor, leaving the latter intact."
(interactive)
(save-excursion
(let ((kill-read-only-ok t) deactivate-mark)
(toggle-read-only 1)
(kill-whole-line)
(toggle-read-only 0)
(yank))))
Note that nothing gets actually killed in process, leaving marks and current selection intact.
BTW, why you guys so fond of jerking cursor around when there's this nice'n'clean kill-whole-line thingy (C-S-backspace)?
I have copy-from-above-command bound to a key and use that. It's provided with XEmacs, but I don't know about GNU Emacs.
`copy-from-above-command' is an
interactive compiled Lisp function
-- loaded from "/usr/share/xemacs/21.4.15/lisp/misc.elc"
(copy-from-above-command &optional
ARG)
Documentation: Copy characters from
previous nonblank line, starting just
above point. Copy ARG characters, but
not past the end of that line. If no
argument given, copy the entire rest
of the line. The characters copied are
inserted in the buffer before point.
something you might want to have in your .emacs is
(setq kill-whole-line t)
Which basically kills the entire line plus the newline whenever you invoke kill-line (i.e. via C-k). Then without extra code, you can just do C-a C-k C-y C-y to duplicate the line. It breaks down to
C-a go to beginning of line
C-k kill-line (i.e. cut the line into clipboard)
C-y yank (i.e. paste); the first time you get the killed line back;
second time gives the duplicated line.
But if you use this often then maybe a dedicated key binding might be a better idea, but the advantage of just using C-a C-k C-y C-y is you can duplicate the line elsewhere, instead of just below the current line.
' I wrote my own version of duplicate-line, cause I don't want to screw up the killing ring.
(defun jr-duplicate-line ()
"EASY"
(interactive)
(save-excursion
(let ((line-text (buffer-substring-no-properties
(line-beginning-position)
(line-end-position))))
(move-end-of-line 1)
(newline)
(insert line-text))))
(global-set-key "\C-cd" 'jr-duplicate-line)
There is package called Avy It has command avy-copy-line. When you use that command, every line in your window gets letter combination. Then you just have to type combination and you get that line. This also works for region. Then you just have to type two combination.
Here you can see interface:
because i don't know, i'll start this round of golf with a slowball:
ctrl-k, y, y
C-a C-k C-k C-y C-y
The defaults are horrible for this. However, you can extend Emacs to work like SlickEdit and TextMate, that is, copy/cut the current line when no text is selected:
(transient-mark-mode t)
(defadvice kill-ring-save (before slick-copy activate compile)
"When called interactively with no active region, copy a single line instead."
(interactive
(if mark-active (list (region-beginning) (region-end))
(message "Copied line")
(list (line-beginning-position)
(line-beginning-position 2)))))
(defadvice kill-region (before slick-cut activate compile)
"When called interactively with no active region, kill a single line instead."
(interactive
(if mark-active (list (region-beginning) (region-end))
(list (line-beginning-position)
(line-beginning-position 2)))))
Place the above in .emacs. Then, to copy a line, M-w. To delete a line, C-w. To duplicate a line, C-a M-w C-y C-y C-y ....
I liked FraGGod's version, except for two things: (1) It doesn't check whether the buffer is already read-only with (interactive "*"), and (2) it fails on the last line of the buffer if that last line is empty (as you cannot kill the line in that case), leaving your buffer read-only.
I made the following changes to resolve that:
(defun duplicate-line ()
"Clone line at cursor, leaving the latter intact."
(interactive "*")
(save-excursion
;; The last line of the buffer cannot be killed
;; if it is empty. Instead, simply add a new line.
(if (and (eobp) (bolp))
(newline)
;; Otherwise kill the whole line, and yank it back.
(let ((kill-read-only-ok t)
deactivate-mark)
(toggle-read-only 1)
(kill-whole-line)
(toggle-read-only 0)
(yank)))))
With recent emacs, you can use M-w anywhere in the line to copy it. So it becomes:
M-w C-a RET C-y
I saw very complex solutions, anyway...
(defun duplicate-line ()
"Duplicate current line"
(interactive)
(kill-whole-line)
(yank)
(yank))
(global-set-key (kbd "C-x M-d") 'duplicate-line)
This functionality should match up with JetBrains' implementation in terms of duplicating both by line or region, and then leaving the point and/ or active region as expected:
Just a wrapper to around the interactive form:
(defun wrx/duplicate-line-or-region (beg end)
"Implements functionality of JetBrains' `Command-d' shortcut for `duplicate-line'.
BEG & END correspond point & mark, smaller first
`use-region-p' explained:
http://emacs.stackexchange.com/questions/12334/elisp-for-applying-command-to-only-the-selected-region#answer-12335"
(interactive "r")
(if (use-region-p)
(wrx/duplicate-region-in-buffer beg end)
(wrx/duplicate-line-in-buffer)))
Which calls this,
(defun wrx/duplicate-region-in-buffer (beg end)
"copy and duplicate context of current active region
|------------------------+----------------------------|
| before | after |
|------------------------+----------------------------|
| first <MARK>line here | first line here |
| second item<POINT> now | second item<MARK>line here |
| | second item<POINT> now |
|------------------------+----------------------------|
TODO: Acts funky when point < mark"
(set-mark-command nil)
(insert (buffer-substring beg end))
(setq deactivate-mark nil))
Or this
(defun wrx/duplicate-line-in-buffer ()
"Duplicate current line, maintaining column position.
|--------------------------+--------------------------|
| before | after |
|--------------------------+--------------------------|
| lorem ipsum<POINT> dolor | lorem ipsum dolor |
| | lorem ipsum<POINT> dolor |
|--------------------------+--------------------------|
TODO: Save history for `Cmd-Z'
Context:
http://stackoverflow.com/questions/88399/how-do-i-duplicate-a-whole-line-in-emacs#answer-551053"
(setq columns-over (current-column))
(save-excursion
(kill-whole-line)
(yank)
(yank))
(let (v)
(dotimes (n columns-over v)
(right-char)
(setq v (cons n v))))
(next-line))
And then I have this bound to meta+shift+d
(global-set-key (kbd "M-D") 'wrx/duplicate-line-or-region)
When called interactively with no active region, COPY (M-w) a single line instead :
(defadvice kill-ring-save (before slick-copy activate compile)
"When called interactively with no active region, COPY a single line instead."
(interactive
(if mark-active (list (region-beginning) (region-end))
(message "Copied line")
(list (line-beginning-position)
(line-beginning-position 2)))))
When called interactively with no active region, KILL (C-w) a single line instead.
(defadvice kill-region (before slick-cut activate compile)
"When called interactively with no active region, KILL a single line instead."
(interactive
(if mark-active (list (region-beginning) (region-end))
(message "Killed line")
(list (line-beginning-position)
(line-beginning-position 2)))))
Also, on a related note:
(defun move-line-up ()
"Move the current line up."
(interactive)
(transpose-lines 1)
(forward-line -2)
(indent-according-to-mode))
(defun move-line-down ()
"Move the current line down."
(interactive)
(forward-line 1)
(transpose-lines 1)
(forward-line -1)
(indent-according-to-mode))
(global-set-key [(meta shift up)] 'move-line-up)
(global-set-key [(meta shift down)] 'move-line-down)
#[Kevin Conner]: Pretty close, so far as I know. The only other thing to consider is turning on kill-whole-line to include the newline in the C-k.
ctrl-k, ctrl-k, (position to new location) ctrl-y
Add a ctrl-a if you're not starting at the beginning of the line. And the 2nd ctrl-k is to grab the newline character. It can be removed if you just want the text.
If you're using Spacemacs, you can simply use duplicate-line-or-region, bound to:
SPC x l d
There's a package called 'move-dup' on Melpa that can help you with that.
Disclaimer: I'm the author of that package.
Here's a function for duplicating current line. With prefix arguments, it will duplicate the line multiple times. E.g., C-3 C-S-o will duplicate the current line three times. Doesn't change kill ring.
(defun duplicate-lines (arg)
(interactive "P")
(let* ((arg (if arg arg 1))
(beg (save-excursion (beginning-of-line) (point)))
(end (save-excursion (end-of-line) (point)))
(line (buffer-substring-no-properties beg end)))
(save-excursion
(end-of-line)
(open-line arg)
(setq num 0)
(while (< num arg)
(setq num (1+ num))
(forward-line 1)
(insert line))
)))
(global-set-key (kbd "C-S-o") 'duplicate-lines)
I write one for my preference.
(defun duplicate-line ()
"Duplicate current line."
(interactive)
(let ((text (buffer-substring-no-properties (point-at-bol) (point-at-eol)))
(cur-col (current-column)))
(end-of-line) (insert "\n" text)
(beginning-of-line) (right-char cur-col)))
(global-set-key (kbd "C-c d l") 'duplicate-line)
But I found this would have some problem when current line contains multi-byte characters (e.g. CJK characters). If you encounter this issue, try this instead:
(defun duplicate-line ()
"Duplicate current line."
(interactive)
(let* ((text (buffer-substring-no-properties (point-at-bol) (point-at-eol)))
(cur-col (length (buffer-substring-no-properties (point-at-bol) (point)))))
(end-of-line) (insert "\n" text)
(beginning-of-line) (right-char cur-col)))
(global-set-key (kbd "C-c d l") 'duplicate-line)
I cannot believe all these complicated solutions. This is two keystrokes:
<C-S-backspace> runs the command kill-whole-line
C-/ runs the command undo
So <C-S-backspace> C-/ to "copy" a whole line (kill and undo).
You can, of course, combine this with numeric and negative args to kill multiple lines either forward or backward.
As mentioned in other answers, binding key strokes to lisp code is a better idea than binding them to another key strokes. With #mw's answer, code duplicates the line and moves the mark to end of new line. This modification keeps the mark position at same column on the new line:
fun duplicate-line ()
(interactive)
(let ((col (current-column)))
(move-beginning-of-line 1)
(kill-line)
(yank)
(newline)
(yank)
(move-to-column col)))
With prefix arguments, and what is (I hope) intuitive behaviour:
(defun duplicate-line (&optional arg)
"Duplicate it. With prefix ARG, duplicate ARG times."
(interactive "p")
(next-line
(save-excursion
(let ((beg (line-beginning-position))
(end (line-end-position)))
(copy-region-as-kill beg end)
(dotimes (num arg arg)
(end-of-line) (newline)
(yank))))))
The cursor will remain on the last line.
Alternatively, you might want to specify a prefix to duplicate the next few lines at once:
(defun duplicate-line (&optional arg)
"Duplicate it. With prefix ARG, duplicate ARG times."
(interactive "p")
(save-excursion
(let ((beg (line-beginning-position))
(end
(progn (forward-line (1- arg)) (line-end-position))))
(copy-region-as-kill beg end)
(end-of-line) (newline)
(yank)))
(next-line arg))
I find myself using both often, using a wrapper function to switch the behavior of the prefix argument.
And a keybinding:
(global-set-key (kbd "C-S-d") 'duplicate-line)