Elisp: get quotation marks in format/echo - email

In .mailrc, I would like to get the first row, but I get the second row
alias UIF "UIF Boxning <uifboxning#boxing.se>" # cool (written manually by me)
alias test_alias test_name <test_mail> # no quotation marks (written by below defun)
I am unsure if the problem is with format or echo. Have a look:
(defun save-mail-address (address name alias)
"add an alias to the .mailrc file"
(interactive "sMail address: \nsFull name: \nsAlias: ")
(let ((compl-alias (format "alias %s \"%s \<%s\>\"" alias name address)))
(shell-command (concat "echo " compl-alias " >> .mailrc")) ))
(defalias 'sma 'save-mail-address)
Edit:
OK, the goal is keep it like this:
alias long "Long Long <long#long.long>" # 123456
alias s "S S <s#s.s>" # 1
Also, made I check so not to include duplicates:
(defun append-blanks (str len)
(concat str (make-string (- len (length str)) ? )) )
(defun save-mail-address (mail name alias phone)
"add an alias to the .mailrc file"
(interactive "sMail mail: \nsFull name: \nsAlias: \nnPhone: ")
(let*(
(alias-alias (format "alias %s" alias))
(alias-alias-blanks (append-blanks alias-alias 16))
(mail-str (append-blanks (format "\"%s \<%s\>\"" name mail) 48))
(line (format "%s%s\# %d\n" alias-alias-blanks mail-str phone))
(file "~/.mailrc") )
(with-temp-buffer
(insert-file file)
(if
(search-forward alias-alias (point-max) t) ; t = return nil on fail
(message (format "Error: The alias %s is already in use." alias))
(progn
(insert line)
(sort-columns nil (point-min) (point-max)) ; nil = not reversed
(write-file file nil) ))))) ; nil = inhibit confirm

You need to ensure that variable arguments passed to a shell command are always appropriately escaped.
(shell-command (concat "echo " (shell-quote-argument compl-alias)))

Just appending the text through Elisp would look like this:
(defun save-mail-address (address name alias)
"add an alias to the .mailrc file"
(interactive "sMail address: \nsFull name: \nsAlias: ")
(let ((compl-alias (format "alias %s \"%s \<%s\>\"" alias name address)))
(with-temp-buffer
(insert compl-alias "\n")
(write-region (point-min) (point-max) "~/.mailrc" t))))

Related

How to copy a string and paste a substring in Emacs?

Found this on the interwebs:
(defun clipboard/set (astring)
"Copy a string to clipboard"
(with-temp-buffer
(insert astring)
(clipboard-kill-region (point-min) (point-max))))
I want to make it interactive, run the string through substring, and then copy it to the clipboard
(defun clipboard/set (astring)
"Copy a string to clipboard"
(interactive)
(let (bstring (substring astring -11)))
(with-temp-buffer
(insert bstring)
(clipboard-kill-region (point-min) (point-max))))
How would one do this?
You need to tell interactive how to populate the arguments:
(interactive "sAstring: ")
Also, the syntax of let is different, it starts with a list of lists of variables and values, i.e.
(let ((bstring (substring astring -11)))
; ^^
i.e.
(defun clipboard/set (astring)
"Copy a string to clipboard"
(interactive "sAstring: ")
(let ((bstring (substring astring -11)))
(with-temp-buffer
(insert bstring)
(clipboard-kill-region (point-min) (point-max)))))
and close it at the very end.

How to edit a Gist filename using `gist.el` with Emacs

Can anyone please steer me in the right direction regarding how to edit the filename of an existing Gist using gist.el. I've tried modifying gist-edit-current-description to handle filename modification, but my attempted variations haven't worked. Here is the gist-edit-current-description function -- I assume editing the filename would be something similar to the description:
(defun gist-edit-current-description ()
(interactive)
(let* ((id (tabulated-list-get-id))
(gist (gist-list-db-get-gist id))
(old-descr (oref gist :description))
(new-descr (read-from-minibuffer "Description: " old-descr)))
(let* ((g (clone gist
:files nil
:description new-descr))
(api (gist-get-api t))
(resp (gh-gist-edit api g)))
(gh-url-add-response-callback resp
(lambda (gist)
(gist-list-reload))))))
This may help give someone some ideas -- it is a method of setting the filename at the time of Gist creation (and it is based on a prior answer by #Jordon Biondo -- https://stackoverflow.com/a/22973794/2112489 ):
(defun gist-region-with-filename-description (begin end &optional filename description private callback)
"Post the current region as a new paste at gist.github.com
Copies the URL into the kill ring.
With a prefix argument, makes a private paste."
(interactive "r\nsGist Description: \nP") ;; we handle the prompt here!
(let* ((file (or (buffer-file-name) (buffer-name)))
(name (file-name-nondirectory file))
(ext (or (cdr (assoc major-mode gist-supported-modes-alist))
(file-name-extension file)
"txt"))
(fname (if filename filename (concat (file-name-sans-extension name) "." ext)))
(files (list
(gh-gist-gist-file "file"
:filename fname
:content (buffer-substring begin end)))))
;; finally we use our new arg to specify the description in the internal call
(gist-internal-new files private description callback)))
(defun gist-buffer-with-filename-description (&optional filename description private)
"Post the current buffer as a new paste at gist.github.com.
Copies the URL into the kill ring.
With a prefix argument, makes a private paste."
(interactive "P")
(let* (
(filename (if filename filename (read-string "Filename: " (buffer-name))))
(description (if description description (read-string "Description: " (buffer-name)))))
(gist-region-with-filename-description (point-min) (point-max) filename description private nil)))
The developer added this feature on December 29, 2014 -- the function is gist-mode-write-file, which is used when visiting the file. Issue number 55: https://github.com/defunkt/gist.el/issues/55

Asking emacs for default directory path "once"

I want to have a variable that keeps the default directory a user enters and keep using it throughout the run of emacs.
Basically, when the user executes a custom command, the prompt will ask for a default directory path to execute the command (only once) and whenever the user calls the same command emacs uses the same path onward.
How can I program that snippet of code in lisp?
I basically want this code in the igrep library to accept the input from user once and not ask again:
(defvar default-files-string-new "*.[sch]")
(defun igrep-read-files (&optional prompt-prefix)
"Read and return a file name pattern from the minibuffer.
If `current-prefix-arg' is '(16) or '(64), read multiple file name
patterns and return them in a list. Optional PROMPT-PREFIX is
prepended to the \"File(s): \" prompt."
(let* ((default-files (igrep-default-files))
(default-files-string (mapconcat 'identity default-files " "))
(insert-default-directory igrep-insert-default-directory)
(file (igrep-read-file-name
(igrep-prefix prompt-prefix
(if default-files
(format "File(s) [default: %s]: "
default-files-string)
"File(s): "))
nil (if default-files default-files-string "") nil nil
'igrep-files-history))
(files (list file)))
(if (or igrep-read-multiple-files
(and (consp current-prefix-arg)
(memq (prefix-numeric-value current-prefix-arg)
'(16 64))))
(let* ((key (igrep-default-key 'exit-minibuffer
minibuffer-local-completion-map
"\r"))
(prompt
(igrep-prefix prompt-prefix
(if igrep-verbose-prompts
(format "File(s): [Type `%s' when done] "
(key-description key))
"File(s): "))))
(while (and (setq file
(igrep-read-file-name prompt
nil "" nil nil
'igrep-files-history))
(not (equal file "")))
(setq files (cons file files)))))
(mapcar (lambda (file)
(if (file-directory-p file)
;; really should map expand-file-name over default-files:
(expand-file-name (if default-files default-files-string-new "*")
file)
file))
(nreverse files))))
You could use advices to do that:
(defvar wd-alist nil)
(mapc
(lambda (function)
(eval
`(defadvice ,function (around ,(intern (format "%s-wd" function)) activate)
(let ((wd (cdr (assoc ',function wd-alist))))
(unless wd
(setq wd (read-file-name "Default directory: "))
(push (cons ',function wd) wd-alist))
(let ((default-directory wd))
ad-do-it)))))
'(grep-find))
The variable wd-list stores the association (FUNCTION . PATH). The list mapc iterate over are the advised functions. Now, when calling find-grep, it asks for the working directory (after interactive arguments, so you first have to type the pattern and enter...) and stores it in wd-list for further use. Now your find-grep are always done in that directory.
You could have a custom variable for the sane default, and then have the user enter the path or accept the default on the first call.
(defcustom default-path "/tmp/foo" "Path")
(setq current-path nil)
(defun foo ()
(interactive)
(unless current-path
(setq current-path
(read-from-minibuffer
(format "Path [%s]" default-path) nil nil t nil default-path)))
(message "Path is: %s" current-path))
The first time you do M-x foo, it prompts for the path. A common idiom is to allow the user to specify a prefix argument when they want to change the value (after the first time.) This code will have the desired effect:
(defun foo (choose)
(interactive "P")
(when (or choose (not current-path))
(setq current-path
(read-from-minibuffer
(format "Path [%s]" default-path) nil nil t nil default-path)))
(message "Path is: %s" current-path))
Now doing M-x foo is the same as before, but C-0 M-x foo will prompt for a new value.
In your example, something like this will work.
(defun igrep-read-files (&optional prompt-prefix)
(interactive "P")
(when (or prompt-prefix (not current-path ))
(setq current-path
(read-file-name "Dir: " default-path nil t)))
(message (expand-file-name default-files-string-new current-path)))
Have a look at the code of sendmail-query-once.
Although it's not very fashionable to do this sort of thing.
Usually package writers pick a sane default and let the user
customize it as they want.

Has anyone used elisp as a script language? [duplicate]

In Python, you might do something like
fout = open('out','w')
fin = open('in')
for line in fin:
fout.write(process(line)+"\n")
fin.close()
fout.close()
(I think it would be similar in many other languages as well).
In Emacs Lisp, would you do something like
(find-file 'out')
(setq fout (current-buffer)
(find-file 'in')
(setq fin (current-buffer)
(while moreLines
(setq begin (point))
(move-end-of-line 1)
(setq line (buffer-substring-no-properties begin (point))
;; maybe
(print (process line) fout)
;; or
(save-excursion
(set-buffer fout)
(insert (process line)))
(setq moreLines (= 0 (forward-line 1))))
(kill-buffer fin)
(kill-buffer fout)
which I got inspiration (and code) from Emacs Lisp: Process a File line-by-line. Or should I try something entirely different? And how to remove the "" from the print statement?
If you actually want batch processing of stdin and sending the result to stdout, you can use the --script command line option to Emacs, which will enable you to write code that reads from stdin and writes to stdout and stderr.
Here is an example program which is like cat, except that it reverses each line:
#!/usr/local/bin/emacs --script
;;-*- mode: emacs-lisp;-*-
(defun process (string)
"just reverse the string"
(concat (nreverse (string-to-list string))))
(condition-case nil
(let (line)
;; commented out b/c not relevant for `cat`, but potentially useful
;; (princ "argv is ")
;; (princ argv)
;; (princ "\n")
;; (princ "command-line-args is" )
;; (princ command-line-args)
;; (princ "\n")
(while (setq line (read-from-minibuffer ""))
(princ (process line))
(princ "\n")))
(error nil))
Now, if you had a file named stuff.txt which contained
abcd
1234
xyz
And you invoked the shell script written above like so (assuming it is named rcat):
rcat < stuff.txt
you will see the following printed to stdout:
dcba
4321
zyx
So, contrary to popular belief, you can actually do batch file processing on stdin and not actually have to read the entire file in at once.
Here's what I came up with. Looks a lot more idiomatic to me:
(with-temp-buffer
(let ((dest-buffer (current-buffer)))
(with-temp-buffer
(insert-file-contents "/path/to/source/file")
(while (search-forward-regexp ".*\n\\|.+" nil t)
(let ((line (match-string 0)))
(with-current-buffer dest-buffer
(insert (process line)))))))
(write-file "/path/to/dest/file" nil))
Emacs Lisp is not suitable for processing file-streams. The whole file must be read at once:
(defun my-line-fun (line)
(concat "prefix: " line))
(let* ((in-file "in")
(out-file "out")
(lines (with-temp-buffer
(insert-file-contents in-file)
(split-string (buffer-string) "\n\r?"))))
(with-temp-file out-file
(mapconcat 'my-line-fun lines "\n")))

macro for making a template

I am new to emacs and trying to figure out if there is an "easy" way to write a macro that will create a template for a standard function specification (see lines with # below). For example, I would like to execute a command to extract the input and output variables and place them in this standard format above the function (using the R language):
#This function does something
#Input:
# var1 - h
# var2 -
# var3 -
# var4 -
# Output:
# myoutput -
MyFunction <- function(var1,var2,var3=13,var4=NULL){
...
...
return(myoutput)
}
I don't know R so I'm just guessing how it looks from your one example. Add this to your Emacs init file (and evaluate it or restart), go to a function definition line and M-x my-r-insert-function-template:
(defun my-r-insert-function-template ()
"Insert a function template."
(interactive)
(let (name inputs output pos)
(beginning-of-line)
(save-excursion
(when (re-search-forward "\\([a-zA-Z0-9_\\.]+\\)\\s-*<-\\s-*function\\s-*(" nil t)
(setq name (match-string-no-properties 1))
(backward-char)
(forward-sexp)
(setq pos (1- (point)))
(backward-sexp)
(while (re-search-forward "[a-zA-Z0-9_\\.]+" pos 'go)
(push (match-string-no-properties 0) inputs)
(search-forward "," pos 'go))
(search-forward "{")
(setq pos (point))
(backward-char)
(forward-sexp)
(when (re-search-backward "return\\s-*(\\s-*\\([a-zA-Z0-9\\.]+\\)" pos t)
(setq output (match-string-no-properties 1)))))
(when name
(insert "# " name " : This function does something\n")
(when inputs
(insert "# Input:\n")
(setq inputs (nreverse inputs))
(dolist (input inputs)
(insert "# " input " -\n")))
(when output
(insert "# Output:\n")
(insert "# " output " -\n")))))
I don't use R but it looks like ESSr-autoyas could be used to do what you want. It makes use of YASnippet (a template package for Emacs).