2
votes

I wrote an interactive function which inserts the "character above the point" in to the current line. For instance, given a line containing "12345" followed by a line "abcdef" and the point sitting at the letter "c", copy-down would make the second line become "ab3cdef". copy-down again would make the second line become "ab34cdef".

My function fails (using GNU Emacs 23.3.1 under windows 7) the second time I invoke it by inserting the text from the first invocation and not advancing properly. If I put any emacs "manipulations" in-between invocations, it works fine. (For instance if I do a copy-down, "left arrow", "right arrow", copy-down it works fine for both invocations.)

Here's my function:

(defun copy-down ()
  "Grab the character in the line above and insert at the current location."
  (interactive)
  (let ((beg (progn (previous-line 1) (point)))
        (end (progn (forward-char) (point))))
    (backward-char)
    (next-line 1)
    (insert-buffer-substring (current-buffer) beg end)))

If it matters, I usually tie my function to a key: (global-set-key [f5] 'copy-down)

PS. I got used to using this capability in the editor I used before switching to emacs many years ago and I miss it in GNU Emacs. :-(

3

3 Answers

2
votes

What you have works just fine for me. That said, previous-line has interaction with other settings (specifically goal-column) and generally shouldn't be used when writing elisp. Instead you should use (forward-line -1). But, of course, your code relies on the goal-column... You can test this by running Emacs without your other configurations, ala emacs -q.

Here's a slightly different version of your code that doesn't rely on goal-column:

(defun copy-down ()
  "Grab the character in the line above and insert at the current location."
  (interactive)
  (let* ((col (current-column))
         (to-insert (save-excursion
                     (forward-line -1)
                     (move-to-column col)
                     (buffer-substring-no-properties (point) (1+ (point))))))
    (insert to-insert)))

If the problem isn't with using previous-line, then I don't imagine my code would make much of a difference.

Another option you have is to try running it in the debugger to see where your code breaks down. Move the point inside the defun for copy-down and type M-x edebug-defun, and the next time you run it you'll be able to step through the code. Docs for edebug can be found here.

0
votes

You need to use let* instead of let. The former allows you to use earlier values in later forms in the same statement.

BTW, that's an unconventional way to write elisp, you might want to look at some other code samples.

EDIT: Hey, someone completely rearranged your function! It might work now.

0
votes

Try

(defun copy-down (arg)
  (interactive "p")
  (let ((p (+ (current-column) (point-at-bol 0))))
    (insert-buffer-substring (current-buffer) p (+ p arg))))

which has the additional functionality of taking a prefix argument to copy n (default to 1) characters down.