My question comes from I expect a function with could convert org-mode table to ditaa diagram. What I try to do is:
find "|" if there's any "-" at left/right/above/below, change it to "+"
find "+" if there's no "|" at left/right/above/below, change it to "-"
Can anyone show me how to search the character above/below or the entire function?
You could use a function like this to check what's on the next line:
(defun get-char-on-next-line (&optional point)
"Return the character one line down from POINT.
If POINT is nil or omitted, use the current position of point."
(save-excursion
(when point
(goto-char point))
(let ((column (current-column))
(line-move-visual nil))
;; next-line preserves the current column if possible
(next-line)
(when (= column (current-column))
(char-after)))))
Replace next with previous for the opposite function.
First, say thank you to #legoscia for the original solution.
When I try the (next-line)/(previous-line) I got some problems like "end of buffer, begin of buffer". Then I searched in elisp manual. It suggests using forward-line. Then I got my solution here:
(defun get-char-below (&optional point)
"Return the character one line down from POINT.
If POINT is nil or omitted, use the current position of point."
(save-excursion
(when point
(goto-char point))
(let ((column (current-column)))
(if (= (forward-line 1) 0)
(if (= column (move-to-column column))
(char-after)
nil)
nil))))
Related
Is there any way to move point to the first currently visible character within a buffer? I would like to find this point because for a function it would really be faster to look from "first visible character" instead of "beginning of buffer".
EDIT: Actually, obtaining the value of the position would also be fine instead of moving the point actually there. PageUp and PageDown seem to have some weird behavior with this with big pieces of text.
If you want the point the current window is scrolled to:
(window-start)
If you want to skip over text that is hidden by text attributes, use invisible-p to skip past them:
(let ((pos (window-start)))
(while (and (invisible-p pos) (< pos (point-max)))
(setq pos (1+ pos)))
pos)
nschum gives the answer to obtaining the position value by: (window-start)
For moving point to this value use:
Based on nschum's answer:
(defun goto-window-start ()
(interactive)
(let ((pos (window-start)))
(while (and (invisible-p pos) (< pos (point-max) )
(setq pos (1+ pos)))
(goto-char pos))
)
Update: Handles empty buffer.
(defun goto-first-visible ()
(interactive)
(goto-char (point-min))
(save-match-data
(let ((pos (search-forward-regexp (rx graphic) nil t)))
(when pos
(goto-char (- pos 1))))))
Here is an efficient version that may work better for large chunks of invisible text. The hack here is to use the fact that invisible text is controlled by text properties, and emacs has built-in facilities for determining locations of text property changes:
(defun goto-first-visible ()
(interactive)
(goto-char (point-min))
(while (and (not (eobp)) (invisible-p (point)))
(goto-char (next-char-property-change (point)))))
Consider the following line of Lisp code:
(some-function 7 8 | 9) ;; some comment. note the extra indentation
The point is placed between '8' and '9'. If I perform (move-beginning-of-line), the point will be placed at the absolute beginning of the line, rather than at '('.
Same for move-end-of-line: I'd find it more desirable for it to place the point at ')' if I perform it once, and at the absolute end of the line if I perform it a second time. Some IDEs behave like that.
I tried to implement this but got stuck, my solution behaves particularly bad near the end of a buffer, and on the minibuffer as well. Is there a library that provides this functionality?
I don't know of any library, but it can be done in a few lines of Elisp.
For the beginning of line part, the bundled functions beginning-of-line-text and back-to-indentation (M-m) move to the beginning of the “interesting” part of the line. back-to-indentation ignores only whitespace whereas beginning-of-line-text skips over the fill prefix (in a programming language, this is typically the comment marker, if in a comment). See Smart home in Emacs for how to flip between the beginning of the actual and logical line.
For the end of line part, the following function implements what you're describing. The function end-of-line-code moves to the end of the line, except for trailing whitespace and an optional trailing comment. The function end-of-line-or-code does this, except that if the point was already at the target position, or if the line only contains whitespace and a comment, the point moves to the end of the actual line.
(defun end-of-line-code ()
(interactive "^")
(save-match-data
(let* ((bolpos (progn (beginning-of-line) (point)))
(eolpos (progn (end-of-line) (point))))
(if (comment-search-backward bolpos t)
(search-backward-regexp comment-start-skip bolpos 'noerror))
(skip-syntax-backward " " bolpos))))
(defun end-of-line-or-code ()
(interactive "^")
(let ((here (point)))
(end-of-line-code)
(if (or (= here (point))
(bolp))
(end-of-line))))
Some suggestions that almost do what you ask:
In lisp code, you can sort-of do what you want, with the sexp movement commands. To get to the beginning of the expression from somewhere in the middle, use backward-up-list, which is bound to M-C-u. In your example, that would bring you to the open parenthesis. To move backwards over individual elements in the list, use backward-sexp, bound to M-C-b; forward-sexp moves the other way, and is bound to M-C-f. From the beginning of an sexp, you can skip to the next with M-C-n; reverse with M-C-p.
None of these commands are actually looking at the physical line you are on, so they'll go back or forward over multiple lines.
Other options include Ace Jump mode, which is a very slick way to quickly navigate to the beginning of any word visible on the screen. That might eliminate your need to use line-specific commands. For quick movement within a line, I usually use M-f and M-b to jump over words. Holding the M key down while tapping on b or f is quick enough that I end up using that by default most of the time.
Edit:
Forgot one other nice command - back-to-indentation, bound to M-m. This will back you up to the first non-whitespace character in a line. You could advice this to behave normally on the first call, and then to back up to the beginning of the line on the second call:
(defadvice back-to-indentation (around back-to-back)
(if (eq last-command this-command)
(beginning-of-line)
ad-do-it))
(ad-activate 'back-to-indentation)
I just wrote these two functions that have the behavior you are looking for.
(defun move-beginning-indent ()
(interactive)
(if (eq last-command this-command)
(beginning-of-line)
(back-to-indentation))
)
(defun move-end-indent ()
(interactive)
(if (eq last-command this-command)
(end-of-line)
(end-of-line)
(search-backward-regexp "\\s)" nil t) ; searches backwards for a
(forward-char 1)) ; closed delimiter such as ) or ]
)
(global-set-key [f7] 'move-beginning-indent)
(global-set-key [f8] 'move-end-indent)
Just try them out, they should behave exactly the way you'd want them to.
I use this:
(defun beginning-of-line-or-text (arg)
"Move to BOL, or if already there, to the first non-whitespace character."
(interactive "p")
(if (bolp)
(beginning-of-line-text arg)
(move-beginning-of-line arg)))
(put 'beginning-of-line-or-text 'CUA 'move)
;; <home> is still bound to move-beginning-of-line
(global-set-key (kbd "C-a") 'beginning-of-line-or-text)
(defun end-of-code-or-line ()
"Move to EOL. If already there, to EOL sans comments.
That is, the end of the code, ignoring any trailing comment
or whitespace. Note this does not handle 2 character
comment starters like // or /*. Such will not be skipped."
(interactive)
(if (not (eolp))
(end-of-line)
(skip-chars-backward " \t")
(let ((pt (point))
(lbp (line-beginning-position))
(comment-start-re (concat (if comment-start
(regexp-quote
(replace-regexp-in-string
"[[:space:]]*" "" comment-start))
"[^[:space:]][[:space:]]*$")
"\\|\\s<"))
(comment-stop-re "\\s>")
(lim))
(when (re-search-backward comment-start-re lbp t)
(setq lim (point))
(if (re-search-forward comment-stop-re (1- pt) t)
(goto-char pt)
(goto-char lim) ; test here ->
(while (looking-back comment-start-re (1- (point)))
(backward-char))
(skip-chars-backward " \t"))))))
(put 'end-of-code-or-line 'CUA 'move)
;; <end> is still bound to end-of-visual-line
(global-set-key (kbd "C-e") 'end-of-code-or-line)
I would like to kill a quoted string in a source file
without having to mark the beginning of the string and kill-region,
but just by placing the point anywhere inside the quoted string and pressing a shortcut.
I tried to write a function in elisp for this, but I figured out that the file
would need to be parsed from the beginning up to point to determine whether the point is inside quoted string, and to find the bounds of the quoted string(also handle the \")...
But the file is already parsed by font-lock.
So now I can find out if I'm inside quoted string:
(defun inside-quoted-string? ()
(interactive)
(print (find 'font-lock-doc-face (text-properties-at (point)))))
But how do I get the bounds of the string?
font-lock knows it, since it nicely highlights it in blue, but how do I get it?
Edit:
Thanks for the answers. I came up with this code that does
exactly what I wanted - move code around without selecting region or even
moving to beginning of code.
(defun kill-at-point ()
"Kill the quoted string or the list that includes the point"
(interactive)
(let ((p (nth 8 (syntax-ppss))))
(if (eq (char-after p) ?\")
(progn
(goto-char p)
(kill-sexp))
(progn
(up-list)
(let ((beg (point)))
(backward-list)
(kill-region beg (point)))))))
(global-set-key (kbd "C-,") 'kill-at-point)
Any suggestions to improve it are welcome.
Rather than rely on font-lock, you can use the underlying parser's data. The start of the string around point (if any) is available as (nth 8 (syntax-ppss)). You can then use (forward-sexp 1) to jump over the string to find its end.
You can find the bounds of a property with previous-property-change and next-property-change. For example:
(defun kill-by-property (arg)
(interactive "d")
(kill-region
(previous-property-change arg nil (point-min))
(next-property-change arg nil (point-max))))
Building off of Stefan's suggestion of using syntax-ppss, the following should do the trick
(defun kill-string ()
(interactive)
(let ((string-start (nth 8 (syntax-ppss))))
(goto-char string-start)
(kill-sexp)))
It uses (nth 8 (syntax-ppss)) to find the beginning of the string, jumps there, then uses the built-in kill-sexp to kill the s-expression at point (in this case, the string we want gone). No need at all for any kind of region calculation on your part.
In Emacs, I sometimes want to kill or copy a rectangle of text such as this one:
XX first line
XX second line is longer
XX 3rd line short
I want to copy the three lines without the leading "XX" on every line. However, it is impossible to mark both corners of the required rectangle, because the first and third lines do not reach to the right edge of the rectangle that I'm after. So how would I copy the three lines above without the leading "XX" in emacs?
Start marking the rectangle from the first line and when you are at the end of the 3d line then just type spaces until the line is long enough. That's what I usually do.
After copying you can type M-\ (delete-horizontal-space) to remove the added spaces.
I see two options that jump out.
The first is to use M-x artist-mode,
this will let you move to the position on the first/last line you want.
At which point you cut the rectangle C-x r k, and then paste
it wherever you want. Don't forget to type C-c C-c to exit artist mode.
The second is to just cut all three lines using the regular C-w
and then when you paste, delete the rectangle of XXs at the beginning of the lines.
You can have a look at the cua-selection-mode (M-x cua-selection-mode).
It allows you to do what you want, but only in the graphical emacs (not in the Terminal). The key bindings are a bit different: you start the region with C-RET (instead of C-SPC) and cut/yank the rectangle simply with C-w/C-y (not using the rectangle commands).
You can find an example in this blog.
To permanently enable the mode just add
(cua-selection-mode t)
to your .emacs.
P.S. the full cua-mode will also change further key bindings. It is not necessary to use it.
simple modifications of inbulit defuns. kill-right-rectangle function does what you want.
I am sure there would much better way to do it. Nonetheless it works.
Two points about the following code.
- Since the idea is to get the text upto end of the line, you don't even need to specify the last column. simply mark at first line (in you example point at "f") and go down to third line to "3" call the function. (we only need one column, so don't need to move cursor to any right)
- this doesn't work if the buffer is read only. its just not implemented.
(defun end-column (POINT)
"returns end column"
(save-excursion
(goto-char POINT)
(move-end-of-line 1)
(current-column)))
(defun apply-on-right-rectangle (function start end &rest args)
(let (startcol startpt endcol endpt final-point)
(save-excursion
(goto-char start)
(setq startcol (current-column))
(beginning-of-line)
(setq startpt (point))
(goto-char end)
(setq endcol (current-column))
(forward-line 1)
(setq endpt (point-marker))
;; ensure the start column is the left one.
(if (< endcol startcol)
(let ((col startcol))
(setq startcol endcol endcol col)))
;; start looping over lines
(goto-char startpt)
(while (< (point) endpt)
(setq endcol (end-column (point)))
(apply function startcol endcol args)
(setq final-point (point))
(forward-line 1)))
final-point))
(defun delete-extract-right-rectangle (start end &optional fill)
(let ((lines (list nil)))
(apply-on-right-rectangle 'delete-extract-rectangle-line start end lines fill)
(nreverse (cdr lines))))
(defun kill-right-rectangle (start end &optional fill)
(interactive "r\nP")
(condition-case nil
(setq killed-rectangle (delete-extract-right-rectangle start end fill))
((buffer-read-only text-read-only)
(setq killed-rectangle (extract-rectangle start end))
(if kill-read-only-ok
(progn (message "Read only text copied to kill ring") nil)
(barf-if-buffer-read-only)
(signal 'text-read-only (list (current-buffer)))))))
After upgrading to emacs 23.2.1 with Fedora 15 one of my emacs functions is broken and I just can't seem to find the problem. Something has to have changed, but I sure can't find it in the elisp documentation.
I'd really appreciate any suggestions!
What it should do: Pressing Ctl-hyphen should copy the character above the cursor and move the point ahead by 1.
What it does: Keeps copying the character above without advancing the point. HOWEVER, calling it with "M-x insert-char-from-previous-line" or pressing RightArrow between Ctl-hypens works as it should. Adding (forward-char 1) to the end of the script doesn't help.
(defun insert-char-from-previous-line ()
"Copy previous line character by character"
(interactive)
(previous-line 1)
(let ((char-above (following-char) ))
(next-line 1)
(if (not (char-equal char-above ?\n ))
(insert char-above))))
(global-set-key [?\C--] 'insert-char-from-previous-line) ;copy char above
I think your getting caught in a goal column issue. You probably should not use next-line/previous-line for this, rather try this:
(defun insert-char-from-previous-line ()
"Copy previous line character by character"
(interactive)
(let ((curpoint (point))
(curcolumn (current-column)))
(forward-line -1)
(forward-char curcolumn)
(let ((char-above (following-char) ))
(goto-char curpoint)
(if (not (char-equal char-above ?\n ))
(insert char-above)))))
From the docs on pervious-line:
If you are thinking of using this in a Lisp program, consider using
`forward-line' with a negative argument instead. It is usually easier
to use and more reliable (no dependence on goal column, etc.).
(describe-function 'previous-line)
don't move the point for something like this:
(defun i-char (arg)
(interactive "*p")
(let ((start (+ (point-at-bol 0)
(current-column)))))
(insert (buffer-substring-no-properties start (+ start arg))))