I would like to provide completion for an emacs interactive function whose content would be based on a HTTP request's content.
Here, the TODO explains it all.
(defun sensei-record-flow (flow-type)
"Interactive function to record change in flow."
(interactive (list (completing-read
"Flow: "
;; TODO need to get the completion from a variable filled with user's
;; flow types
'(("Foo" Foo) ("Bar" Bar) ("Baz" Baz))
nil t)))
(let ((directory (projectile-project-root)))
(setq sensei-cur-directory directory)
(sensei-send-event-flow directory flow-type))
)
How can I do that? request.el provides a :completion key to block until completion of a request, but it's not clear to me how to do that? I think what I need is to make the interactive function a continuation of the call to senseiilist-flows but I don't know how to do that in elisp.
EDIT: Here is the code of sensei-list-flows based on request.el
(defun sensei-list-flows (on-success)
"List available flow types for the current user.
ON-SUCCESS is a function that's called upon successful completion of the call
and is passed a list of symbols listing user-defined flow names."
(let* ((config (sensei-read-config))
(auth-token (cdr (assoc 'authToken config)))
(username (cdr (assoc 'configUser config)))
(server-uri (cdr (assoc 'serverUri config))))
(request (concat server-uri "api/users/" username)
:headers `(("Content-Type" . "application/json")
("X-API-Version" . "0.38.0")
("Authorization" . ,(concat "Bearer " auth-token)))
:parser 'json-read
:error (cl-function (lambda (&rest args &key error-thrown &allow-other-keys)
(message "Got error: %S" error-thrown)))
:success (cl-function (lambda (&key data &allow-other-keys)
(funcall on-success
(map 'list 'car (cdr (assoc 'userFlowTypes data)))))))))
I don't know anything about request.el and you haven't included any call to a list-flows in your quoted code and so that's impossible to comment on; but OOTB you would use C-hf url-retrieve-synchronously to fetch something via http like that.
In the resulting buffer, the url-http-end-of-headers variable is of particular note (+1 to get to the start of the content); and you may wish to check the url-http-* vars in that buffer in general.
I was able to achieve what I want using url-insert-file-contents which is similar to url-retrieve-synchronously:
(defun sensei-list-flows ()
"List available flow types for the current user."
(let* ((config (sensei-read-config))
(auth-token (cdr (assoc 'authToken config)))
(username (cdr (assoc 'configUser config)))
(server-uri (cdr (assoc 'serverUri config)))
(url-request-extra-headers `(("Content-Type" . "application/json")
("X-API-Version" . "0.38.0")
("Authorization" . ,(concat "Bearer " auth-token)))))
(with-temp-buffer
(url-insert-file-contents (concat server-uri "api/users/" username))
(let ((flows (cdr (assoc 'userFlowTypes (json-parse-buffer :object-type 'alist)))))
(map 'list 'car flows)))))
Then interactive completion is trivial:
(defun sensei-record-flow (flow-type)
"Interactive function to record change in flow."
(interactive (list (completing-read
"Flow: "
(sensei-list-flows)
nil t)))
(let ((directory (projectile-project-root)))
(setq sensei-cur-directory directory)
(sensei-send-event-flow directory flow-type))
)
Related
At this site: http://www.gigamonkeys.com/book/practical-a-simple-database.html there is user entry function listed as follows:
(defun prompt-read (prompt)
(format *query-io* "~%~a: " prompt)
(force-output *query-io*)
(read-line *query-io*))
Are there any major advantages of above function as compared to following simpler form:
(defun prompt-read2 (prompt)
(format t "~%~a: " prompt)
(setf answer (read-line)))
Is it recommended to always use force-output and *query-io* all the time?
Setting the answer to a global variable like that is bad. You should just return the answer and let the caller do what it wants with it. If you do use special (~global) variables, you should put asterisks around the name (*ANSWER* instead of ANSWER).
FORCE-OUTPUT is needed to ensure that the user actually sees the prompt before having to answer. If I run the second version using SBCL in a terminal, the program just freezes to wait for input without saying anything.
*QUERY-IO* should be used for querying things from the user, because some environment might want to handle that differently from other output. For example, someone might write a GUI wrapper for your program that turns the queries into graphical dialogs. Or maybe they want to run it as a part of a script, providing the input from a string.
(defun prompt-read (prompt)
(format *query-io* "~%~a: " prompt)
(force-output *query-io*)
(read-line *query-io*))
(defun hello ()
(format t "~&Hello ~a!~%" (prompt-read "What's your name")))
(defmacro with-input ((input) &body body)
`(let ((*query-io* (make-two-way-stream (make-string-input-stream ,input)
(make-string-output-stream))))
,#body))
(defun test ()
(with-input ("jkiiski")
(hello))
(with-input ("rnso")
(hello)))
(test)
; Hello jkiiski!
; Hello rnso!
Edit
A more complex example using SBCLs gray streams.
(defclass foo-stream (sb-gray:fundamental-character-input-stream)
((output-input-script :initarg :script :accessor foo-stream-script)
(output-stream :initarg :out :accessor foo-stream-out)
(current-input :initform nil :accessor foo-stream-current-input)))
(defmethod sb-gray:stream-read-char ((stream foo-stream))
(with-accessors ((input foo-stream-current-input)
(out foo-stream-out)
(script foo-stream-script)) stream
(when (or (null input)
(not (listen input)))
(let ((output (string-trim '(#\space #\newline)
(get-output-stream-string out))))
(setf input (make-string-input-stream
(format nil "~a~%"
(cdr (assoc output script :test #'string=)))))))
(read-char input)))
(defun prompt-read (prompt)
(format *query-io* "~%~a: " prompt)
(force-output *query-io*)
(read-line *query-io*))
(defun hello ()
(format t "~&Hello ~a!~%" (prompt-read "What's your name"))
(format t "~&I'm ~a too!" (prompt-read "How are you"))
(format t "~&~a~%" (if (string-equal (prompt-read
"Do you want to delete all your files")
"yes")
"Deleting all files... (not really)"
"Not deleting anything.")))
(defmacro with-input-script ((script) &body body)
(let ((out-sym (gensym "out")))
`(let* ((,out-sym (make-string-output-stream))
(*query-io* (make-two-way-stream
(make-instance 'foo-stream
:out ,out-sym
:script ,script)
,out-sym)))
,#body)))
(defun test ()
(with-input-script ('(("What's your name:" . "jkiiski")
("How are you:" . "great")
("Do you want to delete all your files:" . "No")))
(hello))
(with-input-script ('(("What's your name:" . "Foo Bar")
("How are you:" . "fine")
("Do you want to delete all your files:" . "Yes")))
(hello)))
(test)
; Hello jkiiski!
; I'm great too!
; Not deleting anything.
; Hello Foo Bar!
; I'm fine too!
; Deleting all files... (not really)
Yes you code is easy but the first is more clarifying what are you doing:
*query-io* is a global variable (which you can tell because of the * naming convention for global variables) that contains the input stream
connected to the terminal. The return value of prompt-read will be the
value of the last form, the call to READ-LINE, which returns the
string it read (without the trailing newline.)
This is what they said about *query-io*
And about the streams that you can put there works as follow:
most other I/O functions also accept T and NIL as stream designators
but with a different meaning: as a stream designator, T designates the
bidirectional stream *TERMINAL-IO*, while NIL designates
*STANDARD-OUTPUT* as an output stream and *STANDARD-INPUT* as an input stream
in this case it seems that this is only pointing to *standard-input* and not to the bidirectional stream t
Does anyone have any ideas, please, regarding how to specify (within a function) switching to a specific tab group using Tabbar 2.0 and a current version of Emacs? For example, If the sky is blue, then switch to tab group "BLUE" (and/or the most recently viewed tab / buffer within that particular tab group).
I have written a few functions that permit me to organize tab groups by frames such that the tabs appear to be associated with a given frame. However, my function cycles through the various tab groups using tabbar-forward-group until the function finally stops at the correct group -- this method is very slow.
The function tabbar-current-tabset is used to determine the name of a current tab group that has focus. The result can be seen when placing it inside a message -- e.g., (message "%s" tabbar-current-tabset). It can also be used inside a function such as . . . (if (not (equal (format "%s" tabbar-current-tabset) "common")) . . . (tabbar-forward-group).
There is only one working function I have found that permits selecting a specific tab group, which is called ido-jump-to-tab-group (set forth below): https://github.com/bamanzi/dotemacs-full/blob/master/init.d/25-tabbar.el I am looking for a way to select a specific tab group (hard-coded into the function), without pausing to manually choose it using ido . . .. I mention this because it may help someone to resolve: If the sky is blue, then switch to tab group "BLUE" (and/or the most recently viewed tab / buffer within that particular tab group).
(defun ido-jump-to-tab-group ()
"Jump to a tabbar group."
(interactive)
(if (< emacs-major-version 24)
(ido-common-initialization))
(unless (and (featurep 'tabbar)
tabbar-mode)
(error "Error: tabbar-mode not turned on."))
(set tabbar-tabsets-tabset (tabbar-map-tabsets 'tabbar-selected-tab)) ;; refresh groups
(let* ( (groups (mapcar #'(lambda (group)
(format "%s" (cdr group)))
(tabbar-tabs tabbar-tabsets-tabset)))
(group-name (ido-completing-read "Groups: " groups)) )
(mapc #'(lambda (group)
(when (string= group-name (format "%s" (cdr group)))
(message "Switch to group '%s', current buffer: %s" (cdr group) (car group))
(switch-to-buffer (car group)) ))
(tabbar-tabs tabbar-tabsets-tabset))) )
During my Google searches, I came across an apparently broken function that does not work with Tabbar 2.0 and a current version of Emacs Trunk -- it is called tabbar+switch-group: https://gist.github.com/Johniel/4324127 I mention this function because it is the only one (other than ido-jump-to-tab-group) that is related to this issue.
(defun goto-tab-group (group-name)
"Jump to a specific tabbar group."
(unless (and (featurep 'tabbar)
tabbar-mode)
(error "Error: tabbar-mode not turned on."))
(set tabbar-tabsets-tabset (tabbar-map-tabsets 'tabbar-selected-tab)) ;; refresh groups
(let* ( (groups (mapcar #'(lambda (group)
(format "%s" (cdr group)))
(tabbar-tabs tabbar-tabsets-tabset))))
(mapc #'(lambda (group)
(when (string= group-name (format "%s" (cdr group)))
(message "Switch to group '%s', current buffer: %s" (cdr group) (car group))
(switch-to-buffer (car group)) ))
(tabbar-tabs tabbar-tabsets-tabset))) )
(defun example-using-goto-tab-group ()
(interactive)
(goto-tab-group "BLUE")) ;; predefined existing tab group
EDIT (September 27, 2014): The function ido-jump-to-tab-group (in the question above) and the function goto-tab-group (in the answer immediately above) are not compatible with a custom tabbar-buffer-groups-function that groups tabs based upon buffers associated with a particular frame, with that special list imbedded in the frame-parameter (independent of the general buffer-list and general buried-buffer-list). The following functions fix that imcompatibility.
(defun ido-switch-tab-group ()
"Switch tab groups using ido."
(interactive)
(let* (
(tab-buffer-list (mapcar
#'(lambda (b)
(with-current-buffer b
(list (current-buffer)
(buffer-name)
(funcall tabbar-buffer-groups-function) )))
(funcall tabbar-buffer-list-function)))
(groups (delete-dups
(mapcar #'(lambda (group)
(car (car (cdr (cdr group))))) tab-buffer-list)))
(group-name (ido-completing-read "Groups: " groups)) )
(catch 'done
(mapc
#'(lambda (group)
(when (equal group-name (car (car (cdr (cdr group)))))
(throw 'done (switch-to-buffer (car (cdr group))))))
tab-buffer-list) )))
(defun switch-tab-group (group-name)
"Switch to a specific tab group."
(let ((tab-buffer-list (mapcar
#'(lambda (b)
(with-current-buffer b
(list (current-buffer)
(buffer-name)
(funcall tabbar-buffer-groups-function) )))
(funcall tabbar-buffer-list-function))))
(catch 'done
(mapc
#'(lambda (group)
(when (equal group-name (format "%s" (car (car (cdr (cdr group))))))
(throw 'done (switch-to-buffer (car (cdr group))))))
tab-buffer-list) )))
(defun switch-to-tab-group-n ()
"Switch to a predefined existing tab group named `N`."
(interactive)
(switch-tab-group "N"))
(defun switch-to-tab-group-a ()
"Switch to a predefined existing tab group named `A`."
(interactive)
(switch-tab-group "A"))
I'm trying to open a file and read through the sexps. If the form has setq in its first position then traverse the rest of the form adding the in the setq form to an alist.
;;; File passwords.el.gpg
(setq twitter-password "Secret"
github-password "Sauce")
My goal is to able to construct an alist from the pairs in the setq forms in teh file. How I even start?
First, I second the recommendation that you store the passwords in an actual alist and, if necessary, set whatever variables you need to based on that.
That aside, here's another solution that tries to break things out a bit. The -partition function is from the dash.el library, which I highly recommend.
You don't really need to "walk" the code, just read it in and check if its car is setq. The remainder of the form should then be alternating symbols and strings, so you simply partition them by 2 and you have your alist. (Note that the "pairs" will be proper lists as opposed to the dotted pairs in Sean's solution).
(defun setq-form-p (form)
(eq (car form) 'setq))
(defun read-file (filename)
(with-temp-buffer
(insert-file-literally filename)
(read (buffer-substring-no-properties 1 (point-max)))))
(defun credential-pairs (form)
(-partition 2 (cdr form)))
(defun read-credentials-alist (filename)
(let ((form (read-file filename)))
(credential-pairs form)))
;; usage:
(read-credentials-alist "passwords.el")
Alternatively, here's how it would work if you already had the passwords in an alist, like so
(defvar *passwords*
'((twitter-password "Secret")
(github-password "Sauce")))
And then wanted to set the variable twitter-password to "Sauce" and so on. You would just map over it:
(mapcar #'(lambda (pair)
(let ((name (car pair))
(value (cadr pair)))
(set name value)))
*passwords*)
You can use streams to read in the files (read-from-string) and then do the usual elisp hacking. The below isn't robust, but you get the idea. On a file, pwd.el that has your file, it returns the alist ((github-password . "Sauce") (twitter-password . "Secret"))
(defun readit (file)
"Read file. If it has the form (sexp [VAR VALUE]+), return
an alist of the form ((VAR . VALUE) ...)"
(let* (alist
(sexp-len
(with-temp-buffer
(insert-file-contents file)
(read-from-string (buffer-substring 1 (buffer-size)))))
(sexp (car sexp-len)))
(when (equal (car sexp) 'setq)
(setq sexp (cdr sexp))
(while sexp
(let* ((l (car sexp))
(r (cadr sexp)))
(setq alist (cons (cons l r) alist)
sexp (cddr sexp)))))
alist))
(readit "pwd.el")
I have a alist of known variables and corresponding functions with nil parameter list, inside body it use that known non-parameter (or global) variable.
for e.g.
(defun funA ()
(message "%s" varA))
(defun funB ()
(message "%s" varB))
...
(setq alist
'((varA . funA)
(varB . funB)
...
))
Similar element in alist can be added / deleted dynamically.
I want to run all these function in another function where the value of known variable assigned dynamically in LET form.
(defun call-each-fun-of-alist ()
(dolist (e alist)
(let (( (car e) (read-from-minibuffer "value: ") ))
(funcall (cdr e)))))
(Yes it will throw error, but I wanted to similar thing, possible without EVAL)
For known element of alist (like first I could do
(let ((varA (read-from-minibuffer "value: ")))
(funcall (cdr (assoc 'varA alist))))
But alist is dynamically updated and I what to run all functions in alist
and the value for corresponding variable will come dynamically.
Please let me know how I can define
call-each-fun-of-alist
(not necessarily but without calling EVAL inside call-each-fun-of-alist, if not possible without EVAL than I like to know it also.)
You could do this with letf (cl-letf in recent Emacs). It binds values like let but allows 'places' or 'generalized variables' as well as simple variable names.
(defun call-each-fun-of-alist ()
(cl-dolist (e alist)
(cl-destructuring-bind (variable . function) e
(cl-letf (((symbol-value variable)
(read-from-minibuffer
(format "value for %s: "
variable))))
(funcall function)))))
Note that this will fail with an error unless the variables named in alist have previously been declared as dynamic variables using defvar. Look up 'generalized variables' in the Elisp manual for more information.
Another solution would be to use cl-progv, which takes parallel lists of variable names and values to bind dynamically:
(defun call-each-fun-of-alist ()
(cl-dolist (e alist)
(cl-destructuring-bind (variable . function) e
(cl-progv
(list variable)
(list (read-from-minibuffer
(format "value for %s: "
variable)))
(funcall function)))))
In Common Lisp this is provided by PROGV - dynamic binding of a variable given the symbol of the variable.
GNU Emacs should have PROGV in its Common Lisp emulation.
This is all you need -- no need for progv or destructing bind stuff:
(defun call-each-fun-of-alist (alist)
(dolist (e alist)
(set (car e) (read-from-minibuffer "value: "))
(funcall (cdr e))))
(defvar my-alist '((varA . funA) (varB . funB)))
(call-each-fun-of-alist my-alist)
Or if you really want to see a let binding for some reason:
(defun call-each-fun-of-alist (alist)
(dolist (e alist)
(eval `(let ((,(car e) (read-from-minibuffer "value: ")))
(funcall (cdr e))))))
I am using the openwith package in emacs. I would like to open .fig files with xfig with some additional options, for example:
xfig -specialtext -latexfont -startlatexFont default file.fig
openwith is working for me with other file associations where I don't need to pass additional options. I tried the following in my .emacs file
(setq
openwith-associations
'(("\\.fig\\'" "xfig" (file))))
which works, but
(setq
openwith-associations
'(("\\.fig\\'" "xfig -specialtext -latexfont -startlatexFont default" (file))))
does not work (error: Wrong type argument: arrayp, nil), also
(setq
openwith-associations
'(("\\.fig\\'" "xfig" (" -specialtext -latexfont -startlatexFont default " file))))
does not work, although here I don't get any error. It says "Opened file.fig in external program" but nothing happens. In this case, I notice that there is an xfig process running with all these options.
Could someone let me know how to fix this?
Thanks for the help.
I have no clue how this works, so I just document how one can figure it by reading the code:
The important code in openwith.el is the call to start-process in:
(dolist (oa openwith-associations)
(let (match)
(save-match-data
(setq match (string-match (car oa) (car args))))
(when match
(let ((params (mapcar (lambda (x)
(if (eq x 'file)
(car args)
(format "%s" x))) (nth 2 oa))))
(apply #'start-process "openwith-process" nil
(cadr oa) params))
(kill-buffer nil)
(throw 'openwith-done t))))
The in your case oa would have the following structure, and the cadr is "xfig":
(cadr '("\.fig\'" "xfig" (file))) ;; expands to => xfig
This is the definition and doc of start-process:
Function: start-process name buffer-or-name program &rest args
http://www.gnu.org/software/emacs/elisp/html_node/Asynchronous-Processes.html
args, are strings that specify command line arguments for the program.
An example:
(start-process "my-process" "foo" "ls" "-l" "/user/lewis/bin")
Now we need to figure out how params is constructed. With your example the argument to the mapcar is:
(nth 2 '("\.fig\'" "xfig" (file))) ;=> (file)
By the way you can write such lines in the scratch buffer in emacs and run them with C-M-x.
The (car args) refers to the parameter you give to openwith-association, note how the occurance of 'file in (nth 2 oa) is replaced by that. I'll just replace it with "here.txt" for now:
(mapcar (lambda (x)
(if (eq x 'file)
"here.txt"
(format "%s" x))) (nth 2 '("\.fig\'" "xfig" (file)))) ;=> ("here.txt")
Okay, now we see how the argument should be constructed:
(mapcar (lambda (x)
(if (eq x 'file)
"here.txt"
(format "%s" x)))
(nth 2 '("\.fig\'" "xfig"
("-specialtext" "-latexfont" "-startlatexFont" "default" file))))
; => ("-specialtext" "-latexfont" "-startlatexFont" "default" "here.txt")
Try this:
(setq openwith-associations
'(("\\.fig\\'" "xfig" ("-specialtext" "-latexfont" "-startlatexFont" "default" file))))
You have to supply each word as a single string in the list of parameters.