How to stay in the same buffer window after evaluation? - emacs

I'm trying to write a function R> that evaluates the code given as a string in the active ESS R process and returns basic conversions to elisp data structures or just nil. I'm not an experience elisp coder, so right now I'm just trying to get a really basic function working.
(defun R> (s)
"Evaluate simple R command."
(save-excursion
(with-temp-buffer
(setq ess-dialect "R")
(let (out list)
(setq tmpbuf (current-buffer))
(ess-command (s-concat s "\n") tmpbuf)
(setq list (s-split "\w" (R>--remove-numbered-brackets)))
(apply 'vector
(--map
(let ((it* (s-trim it)))
( if (s-numeric? it*)
(string-to-number it*)
(strip-inner-quotes it*)))
list))))))
(defun R>--remove-numbered-brackets ()
(replace-regexp-in-string "[\\[0-9\\]+]" "" (buffer-string)))
(defun strip-inner-quotes (s)
"If the string has inner quotes, remove them."
(replace-regexp-in-string "\"" "" s))
Unfortunately, running this causes the active windows to change. Not the desired behavior! I thought save-excursion was designed to prevent this, but I guess I'm not using properly. Is there some alternative way to use save-excursion that doesn't have this issue?

Use save-selected-window instead of/in addition to save-excursion.

Related

Auto-escaping yanked strings in emacs

I apparently have a powerful itch this weekend to add a ton of functionality to my Emacs environment. I can do some basics on my own, and hunt down other stuff, but I haven't been able to find a solution to this (and am not good enough at Lisp to do it on my own).
I frequently work with strings of HTML, and sometimes if I move them from one block to another (or one language to another) strings are broken where they aren't escaped. So, I want a function that does something like this:
(defun smart-yank-in-string()
(if (stringp) ; Check if the point is in a string
; Check if the region created from the point to the end of the yank ends the string
; (and there is more yank left that isn't ";")
; Escape quotes for those locations recursively by prepending \
; Insert result into buffer # mark
))
Any clever ideas? I think it involves using kill-new to stash a variable and walk through it, but I'm not conversant enough in elisp to solve it.
Next yank should insert the escaped string:
(defun escape-doublequotes-at-car-of-kill-ring ()
"Escape doublequotes in car of kill-ring "
(interactive)
(with-temp-buffer
(insert (car kill-ring))
(goto-char (point-min))
(while (search-forward "\"" nil t 1)
(replace-match "\\\\\""))
(kill-new (buffer-substring-no-properties (point-min) (point-max)))))
Here is an alternative
(defun my-yank()
(interactive)
(if (nth 3 (syntax-ppss)) ;; Checks if inside a string
(insert-for-yank (replace-regexp-in-string "[\\\"]"
"\\\\\\&"
(current-kill 0)
t))
(call-interactively 'yank)))
The command when invoked checks if the point is in a string, if so it escapes the yanked text otherwise it yanks normally. One disadvantage is that you cannot use yank-pop after yanking inside a string.
Maybe you could do it as follow (guaranteed 100% non-functional code ahead):
(defun my-kill-quoted-string (start end)
"Like kill-region but takes of unquoting/requoting."
(interactive "r")
(let ((str (buffer-extract-substring start end)))
(if (nth 3 (syntax-ppss))
;; Unquote according to mode and context. E.g. we should unquote " and things like that in HTML.
(setq str (replace-regexp-in-string "\\\\\"" "\"" str)))
(put-text-property 0 (length str) 'yank-handler
(list (lambda (str)
(if (not (nth 3 (syntax-ppss)))
(insert str)
;; Requote according to mode and context.
(insert (replace-regexp-in-string "[\\\"]" "\\\\\\&" str))))))
(kill-new str)))

How to kill a quoted string at point in emacs?

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.

Help writing emacs lisp for emacs etags search

I'm looking for some help developing what I think should be an easy program.
I want something similar to Emacs tags-search command, but I want to collect all search results into a buffer. (I want to see all results of M-,)
I'm thinking this python style pseudo code should work, but I have no idea how to do this in emacs lisp? Any help would be greatly appreciated.
def myTagsGrep(searchValue):
for aFile in the tag list:
result = grep aFile seachValue
if len(result) > 0:
print aFile # to the buffer
print result # to the buffer
I would like to be able to browse through the buffer with the same features tags-apropos does.
Note that a similar question has been asked before:
Is there a way to get emacs tag-search command to output all results to a buffer?
Since I'm such a fan of igrep, I'd use it as the building block. From there it's two simple routines and you're done. With that library and these two functions, all you have to do is:
M-x igrep-tags ^SomeRegexp.*Here RET
Here's the code:
(require 'igrep)
(defun igrep-tags (regex)
(interactive "sTAGS Regexp: ")
(igrep igrep-program regex (tags-file-names)))
(defun tags-file-names ()
(save-excursion
(visit-tags-table-buffer)
(mapcar (lambda (f) (file-truename f))
(tags-table-files))))
And, because the list of files can get really long, and you likely don't care what that list is, you can add these two pieces of code which will make the filenames invisible after the grep has finished:
(add-hook 'compilation-finish-functions 'igrep-tags-hide-filenames)
(defun igrep-tags-hide-filenames (buffer stat)
"hide the filenames b/c they can get long"
(save-excursion
(set-buffer buffer)
(save-match-data
(goto-char (point-min))
(if (search-forward (combine-and-quote-strings (tags-file-names))
nil
(save-excursion (forward-line 10) (point)))
(let ((display-string "..<files from TAGS>.."))
(put-text-property (match-beginning 0) (match-end 0) 'invisible t)
(put-text-property (match-beginning 0) (match-end 0) 'display display-string))))))
To avoid the really long command line, you can use the following code (which creates a temporary file containing all the names of files from TAGS file and uses that instead):
(defun igrep-tags (regex)
(interactive "sTAGS Regexp: ")
(let ((igrep-find t)
(igrep-use-file-as-containing-files t))
(igrep igrep-program regex nil)))
(defvar igrep-use-file-as-containing-files nil)
(defadvice igrep-format-find-command (around igrep-format-find-command-use-filename-instead activate)
"use the second argument as a file containing filenames"
(if igrep-use-file-as-containing-files
(progn (with-temp-file
(setq igrep-use-file-as-containing-files (make-temp-file "tags-files"))
(insert (combine-and-quote-strings (tags-file-names))))
(setq ad-return-value (format "cat %s | xargs -e %s"
igrep-use-file-as-containing-files
(ad-get-arg 0))))
ad-do-it))
And, for those using Emacs 22 or earlier, you'll need the routine that's shipped with Emacs 23 (from subr.el)
(defun combine-and-quote-strings (strings &optional separator)
"Concatenate the STRINGS, adding the SEPARATOR (default \" \").
This tries to quote the strings to avoid ambiguity such that
(split-string-and-unquote (combine-and-quote-strings strs)) == strs
Only some SEPARATORs will work properly."
(let* ((sep (or separator " "))
(re (concat "[\\\"]" "\\|" (regexp-quote sep))))
(mapconcat
(lambda (str)
(if (string-match re str)
(concat "\"" (replace-regexp-in-string "[\\\"]" "\\\\\\&" str) "\"")
str))
strings sep)))
Here is the code I use to create a tag system for my personal notes. It uses bookmarks and treats each word in a bookmark as a single tag. Its not quite what you're looking for but it might get you started.
The first couple of functions are probably already implemented in emacs, but I wrote my own for reasons that I no longer recall.
;; FILTER keeps only elements of li for which pred returns true
(defun filter (pred li)
(let (acc)
(dolist (elem li)
(if (funcall pred elem)
(setq acc (cons elem acc))))
(reverse acc)))
(defun string-match-all-p (str li)
(if li
(if (string-match-p (car li) str)
(string-match-all-p str (cdr li))
nil)
t))
;;bookmarks as tags
(defun lookup-bookmark-tags (tagstring)
(interactive "s")
(let ((taglist (split-string tagstring " ")))
(let ((bookmark-alist (filter
(lambda (elem)
(string-match-all-p (car elem) taglist))
bookmark-alist)))
(call-interactively 'list-bookmarks))))
I then bind the 'tagging' behavior to a key (F11) and the 'lookup' behavior to another (F12).
(global-set-key [f11] 'bookmark-set)
(global-set-key [f12] 'lookup-bookmark-tags))
Hope that is useful to you.
This is what you want:
http://www.emacswiki.org/emacs/Icicles_-_Emacs_Tags_Enhancements#icicle-tags-search
This is the doc string for icicle-tags-search:
Search all source files listed in tags tables for matches for REGEXP.
You are prompted for the REGEXP to match. Enter REGEXP with `RET'.
You do not need `M-,' - you see all matches as search hits to visit.
All tags in a tags file are used, including duplicate tags from the
same or different source files.
By default, all tags files are used, but if you provide a prefix
argument then only the current tag table is used.
If your TAGS file references source files that no longer exist, those
files are listed. In that case, you might want to update your TAGS
file.
You can alternatively choose to search, not the search contexts as
defined by the context regexp you provide, but the non-contexts, that
is, the text in the files that does not match the regexp. To do this,
use `C-M-~' during completion. (This is a toggle, and it affects only
future search commands, not the current one.)
See also this page for more explanation about Icicles search:
http://www.emacswiki.org/emacs/Icicles_-_Search_Commands%2c_Overview

matching keys in association lists in emacs lisp

I'm using folding-mode in emacs and was trying to make a function to insert the appropriate folding marker (start or end) depending on mode. So far I have
(defun insert-folding-mode-mark ()
(interactive)
(let ((st "##{{{")
(en "##}}}")
string-to-insert)
(save-excursion
(setq string-to-insert
(let ((here (point))
sp ep)
(setq sp (search-backward st))
(goto-char here)
(setq ep (search-backward en))
(if (< sp ep) st en))))
(insert string-to-insert)))
This inserts "##{{{" at (point) unless "##{{{" precedes it, in which case it inserts "##}}}".
I'd like to replace the first (let) assignment with something that determines the start and end markers with something like
(let* ((match (assoc (intern mode-name) folding-mode-marks-alist))
(st (nth 1 match))
(en (nth 2 match)))
[is (intern) meant to be called in this way?] A truncated version of my folding-mode-marks-alist looks something like
((ess-mode "##{{{" "##}}}")
(tex-mode "%{{{" "%}}}")
(python-mode "# {{{" "# }}}")
(emacs-lisp-mode ";;{{{" ";;}}}")
(TeX-mode "%{{{" "%}}}")
(LaTeX-mode "%{{{" "%}}}"))
while the mode-name returned from various modes are {"Emacs-Lisp", "ESS[S]", "PDFLaTeX", "Python", ...}. Seems like I might want to do some partial matching with strings using (downcase), (concat x "-mode"), and so on, but was wondering if there was an idiomatic way in emacs lisp to do this sort of matching with keys of an alist, or do I just have to have a separate block of code by which I extract the keys with (mapcar 'car folding-mode-marks-alist) and convert each symbol to string (how?) to do the matching?
Thanks much!
Emacs Lisp has a destructuring-bind facility which may be helpful here. Also taking advantage of the fact that the symbol naming the current major mode is available via the variable major-mode, you can write something like this:
(destructuring-bind (st en) (cdr (assoc major-mode folding-mode-marks-alist))
; do stuff
)
Note that this won't work if (assoc major-mode folding-mode-marks-alist) returns nil, so better replace that with a call to some custom function capable of returning a sensible default.
In addition to major-mode being more appropriate than mode-name here, the function insert-folding-mode-mark as listed above would throw an error if there were no folding marker between cursor and beginning of buffer. Here is a revision without that quirk:
(require 'cl)
(defun insert-folding-mode-mark ()
(interactive)
(flet ((fn (s) (save-excursion (or (search-backward s () t) 0))))
(destructuring-bind (mode st en)
(or (assoc major-mode folding-mode-marks-alist) '(nil "" ""))
(insert (if (<= (fn st) (fn en)) st en)))))
Edit: fix problem pointed out in comment.
You may be interested to know there are comment-start and comment-end variables which should already contain the information you need based on major-mode. Something like
(search-backward (concat comment-start "{{{"))
...
(insert comment-start "{{{" comment-end)
should be sufficient. Of course, for lisp modes comment-start is ";" so you may want to do what you are doing to get ";;" but fall back on comment-start for other modes. You can also (setq comment-start ";;") though I'm not entirely sure what difference that makes for the lisp modes.

Help with an interactive Emacs Lisp function for replacing text

I've been using Emacs for a couple months now, and I want to get started in elisp programming. Specifically, I'd like to write my own interactive function. However, I'm more than a bit lost. (interactive ...) has tons of options and I'm not sure which one I want. Then, I don't really know the names of the functions I need. If someone could kindly help me turn my pseudocode into real code, I would be mighty appreciative! (And as always, any links to informative places would be good. Right now I've just been reading this.)
Here is pseudocode for what I'd like to do:
(defun my-func (buffer) ; I think I need the buffer as an arg?
"does some replacements"
(interactive ???) ; ?
(let (replacements (list
'("a-regexp-string" . "a-replacement-string-with-backreferences")
...)) ; more of the above
(while replacements
(let (current (car replacements)) ; get a regexp-replacement pair
(some-regexp-replace-func buffer (car current) (cdr current)) ; do the replacement
(setq replacements (cdr replacements))))))
First, from the looks of your function you would probably be doing it in the current buffer, so no, you don't need to have a 'buffer' argument. If that's a bad assumption, I can change the code. Next, in a 'let' if you are assigning to variables you need another set of parens around each pair of var/value. Finally, when looping through a list I prefer to use functional-programming-like functions (mapcar, mapc, etc.). I'll try to inline some comments here:
(defun my-func ()
"Do some replacements"
(interactive)
(let ((replacements (list '("foo" . "bar")
'("baz" . "quux"))))
(save-excursion ; So point isn't moved after this function
(mapc (lambda (x) ; Go through the list, with this 'inline' function
; being called with each element as the variable 'x'
(goto-char (point-min)) ; Start at the beginning of the buffer
(while (re-search-forward (car x) nil t) ; Search for the car of the replacement
(replace-match (cdr x)))) ; And replace it with the cdr
replacements)))) ; The list we're mapc'ing through
As for what to read, I'd suggest the Elisp manual that comes with Emacs.