How to auto-suppress `#+RESULTS` for empty output? - org-mode

In the following org-mode source snippet, no result is generated:
#+BEGIN_SRC ipython :session
import numpy as np
#+END_SRC
However, upon execution #+RESULTS: is added to the document. How can I configure org-mode / babel to only insert results when there is something to display?
I know I can disable output using :results silent, but I'd prefer not to have to specify a different flag depending on whether a result is generated or not.

Using ob-ipython with Org 9, I got this to work with the following hack, provided that source blocks are configured to return values (i.e., the default header specifies :results value):
; don't label empty outputs, exclude empty result blocks
(advice-add 'ob-ipython--process-response :filter-return
(λ (contents)
(if (string-match-p "\\`# Out\[[0-9]+\]:\n\\'" contents)
"" contents)))
(advice-add 'org-babel-insert-result :filter-args
(λ (args)
(let ((result (car args))
(result-params (cadr args))
(others (cddr args)))
(apply 'list
result
(if (string-empty-p result) '("silent") result-params)
others))))
The first advice strips the output tag inserted by ob-ipython when there is nothing further in the output (making the result empty), while the second advice effectively applies :results silent when the result is empty. These pieces could be arranged differently depending on your use case.

I've added a check for a nil result to thcoffee-msft's answer to be compatible
with emacs-jupyter. It also deletes any previous result if the current one is empty:
(advice-add 'org-babel-insert-result :filter-args
(lambda (args)
(let ((result (car args))
(result-params (cadr args))
(others (cddr args)))
(apply 'list
result
(if (or
(string-empty-p result) (not result))
(progn (org-babel-remove-result) '("silent"))
result-params)
others))))

Related

Haskell with emacs org-mode: Variable not in scope

After wandering off in frustration from before, I've decided to try Haskell in Emacs org-mode again. I'm using Haskell stack-ghci (8.6.3), Emacs 26.2, org-mode 9.2.3 set up with intero. This code block
#+begin_src haskell :results raw :session *haskell*
pyth2 :: Int -> [(Int, Int, Int)]
pyth2 n =
[ (x, y, z)
| x <- [1 .. n]
, y <- [x .. n]
, z <- [y .. n]
, x ^ 2 + y ^ 2 == z ^ 2
]
#+end_src
produces this RESULTS:
*Main| *Main| *Main| *Main| *Main|
<interactive>:59:16: error: Variable not in scope: n
<interactive>:60:16: error: Variable not in scope: n
<interactive>:61:16: error: Variable not in scope: n
However, this
#+begin_src haskell :results raw
tripleMe x = x + x + x
#+end_src
works fine. I've added the :set +m to both ghci.conf and the individual code block to no effect. This code works fine in a separate hs file run in a separate REPL. The pyth2 code in a separate file also can be called from the org-mode started REPL and run just fine as well. Not sure how to proceed. Can include Emacs init info if necessary.
Over on the org-mode mailing list I got an answer that basically is saying the same as you, D. Gillis. He had a similar work-around that actually is more org-mode-centric. Under a heading where your code blocks will be put this "drawer"
:PROPERTIES:
:header-args:haskell: :prologue ":{\n" :epilogue ":}\n"
:END:
and then (possibly in a local variable) run
#+begin_src haskell :results output
:set prompt-cont ""
#+end_src
For reasons unknown I've had to include the :results output otherwise a cryptic error of "expecting a string" happens.
On a few other notes, haskell babel doesn't respond/care about the :session option, i.e., when you run a code block, a REPL *haskell* starts and that will be the sole REPL. Also, a haskell-mode started REPL doesn't play well with an existing org-mode initiated REPL, i.e., if you start a REPL from haskell-mode, it kills the original org-mode *haskkell*REPL, and any new attempt to run org-mode code blocks can't see this new, non-*haskell*REPL. Then if you kill the haskell-mode REPL and try to run org-mode blocks, you get
executing Haskell code block...
inferior-haskell-start-process: List contains a loop: ("--no-build" "--no-load" "--ghci-options=-ferror-spans" "--no-build" "--no-load" . #2)
... you're hosed -- and nothing seems to shake it, not any restart/refresh, nor killing, reloading the file, i.e., a complete restart of Emacs is necessary. Anyone knowing a better solution, please tells usses.
This is a GHCi issue.
The same error occurs when your code is copied directly into GHCi, which also gives a parse error when it encounters the new line after the equal sign. This first error isn't showing up here because org-babel only shows the value of the last expression (in this case, the error caused by the list comprehension).
I'm not entirely familiar with how Haskell-mode sends the code to GHCi, but it looks like it involves loading in the buffer into GHCi as a file, which may be why you didn't have this problem working from the hs file.
There are a few options to fix this, none of which are completely ideal:
Move some portion of the list into the first line (e.g. the first line could be pyth2 n = [).
Wrap the entire function definition with :{ and :}.
Write an Elisp function to modify what is being sent to GHCi and then changes it back after it is evaluated.
The first two options require you to format your code in a form that the GHCi will accept. In your example case, the first option may not be too bad, but this won't always be so trivial for all multi-line declarations (e.g. pattern-matching function declarations). The downside to the second option is that it requires adding brackets to the code that shouldn't be there in real source code.
To fix the issue of extraneous brackets being added, I've written an Elisp command (my-org-babel-execute-haskell-blocks) that places these brackets around code blocks that it finds, evaluates the region, and then deletes the brackets. Note that this function requires that blocks be separated from all other code with at least one empty line.
Calling my-org-babel-execute-haskell-blocks on your example declares the function without any errors.
EDIT: The previous function I gave failed to work on pattern matching declarations. I've rewritten the function to fix this issue as well as to be comment aware. This new function should be significantly more useful. However, it's worth noting that I didn't handle multi-line comments in a sophisticated manner, so code blocks with multi-line comments may not be wrapped properly.
(defun my-org-babel-execute-haskell-blocks ()
"Wraps :{ and :} around all multi-line blocks and then evaluates the source block.
Multi-line blocks are those where all non-indented, non-comment lines are declarations using the same token."
(interactive)
(save-excursion
;; jump to top of source block
(my-org-jump-to-top-of-block)
(forward-line)
;; get valid blocks
(let ((valid-block-start-ends (seq-filter #'my-haskell-block-valid-p (my-get-babel-blocks))))
(mapcar #'my-insert-haskell-braces valid-block-start-ends)
(org-babel-execute-src-block)
(mapcar #'my-delete-inserted-haskell-braces (reverse valid-block-start-ends)))))
(defun my-get-blocks-until (until-string)
(let ((block-start nil)
(block-list nil))
(while (not (looking-at until-string))
(if (looking-at "[[:space:]]*\n")
(when (not (null block-start))
(setq block-list (cons (cons block-start (- (point) 1))
block-list)
block-start nil))
(when (null block-start)
(setq block-start (point))))
(forward-line))
(when (not (null block-start))
(setq block-list (cons (cons block-start (- (point) 1))
block-list)))))
(defun my-get-babel-blocks ()
(my-get-blocks-until "#\\+end_src"))
(defun my-org-jump-to-top-of-block ()
(forward-line)
(org-previous-block 1))
(defun my-empty-line-p ()
(beginning-of-line)
(= (char-after) 10))
(defun my-haskell-type-declaration-line-p ()
(beginning-of-line)
(and (not (looking-at "--"))
(looking-at "^.*::.*$")))
(defun my-insert-haskell-braces (block-start-end)
(let ((block-start (car block-start-end))
(block-end (cdr block-start-end)))
(goto-char block-end)
(insert "\n:}")
(goto-char block-start)
(insert ":{\n")))
(defun my-delete-inserted-haskell-braces (block-start-end)
(let ((block-start (car block-start-end))
(block-end (cdr block-start-end)))
(goto-char block-start)
(delete-char 3)
(goto-char block-end)
(delete-char 3)))
(defun my-get-first-haskell-token ()
"Gets all consecutive non-whitespace text until first whitespace"
(save-excursion
(beginning-of-line)
(let ((starting-point (point)))
(re-search-forward ".*?[[:blank:]\n]")
(goto-char (- (point) 1))
(buffer-substring-no-properties starting-point (point)))))
(defun my-haskell-declaration-line-p ()
(beginning-of-line)
(or (looking-at "^.*=.*$") ;; has equals sign
(looking-at "^.*\n[[:blank:]]*|")
(looking-at "^.*where[[:blank:]]*$")))
(defun my-haskell-block-valid-p (block-start-end)
(let ((block-start (car block-start-end))
(block-end (cdr block-start-end))
(line-count 0))
(save-excursion
(goto-char block-start)
(let ((token 'nil)
(is-valid t))
;; eat top comments
(while (or (looking-at "--")
(looking-at "{-"))
(forward-line))
(when (my-haskell-type-declaration-line-p)
(progn
(setq token (my-get-first-haskell-token)
line-count 1)
(forward-line)))
(while (<= (point) block-end)
(let ((current-token (my-get-first-haskell-token)))
(cond ((string= current-token "") ; line with indentation
(when (null token) (setq is-valid nil))
(setq line-count (+ 1 line-count)))
((or (string= (substring current-token 0 2) "--") ;; skip comments
(string= (substring current-token 0 2) "{-"))
'())
((and (my-haskell-declaration-line-p)
(or (null token) (string= token current-token)))
(setq token current-token
line-count (+ 1 line-count)))
(t (setq is-valid nil)
(goto-char (+ 1 block-end))))
(forward-line)))
(and is-valid (> line-count 1))))))

Temporarily change a function variable in elisp

Update
This question is no longer feasible.
Turned out ivy-read doesn't return immediately as I expected. I used C-g to cancel completion which skips the restoring ivy-format-function part.
I'm writing an ivy extension with dynamic collection. I'd like to return a list of list of strings instead of a list of strings as candidates. The default value of ivy-format-function only supports list of strings so I decide to change it to my own function while calling ivy-read then change it back.
I defined the following macro and function:
(defun ivy-foo ()
(interactive)
(with--ivy-foo-format-function
(ivy-read "Foo: "
#'ivy-foo-function
:dynamic-collection t
:require-match t
:action #'ivy-foo--action
:unwind #'ivy-foo--unwind
:history 'ivy-foo-history
:caller 'ivy-foo)))
(defmacro with--ivy-foo-format-function (&rest body)
`(let ((original-function ivy-format-function))
(setq ivy-format-function (lambda (candidates) (ivy-foo--format-function candidates original-function)))
,#body
(setq ivy-format-function original-function)))
(defun ivy-foo--format-function (candidates original-format-function)
(funcall original-format-function
(mapcar
(lambda (cand)
(if (listp cand)
(car cand)
cand))
candidates)))
(defun ivy-foo-function (str)
(list (list "cand1" str) (list "cand2" str)))
with--ivy-foo-format-function doesn't set ivy-format-function to the original value. I got error "Symbol's value as a variable is void: original-function". What's wrong?
Update: ivy-format-function is a defvar. Its default value is #'ivy--format-function-default

Convert command line arguments to list in Emacs Lisp

In an automated Emacs Lisp --batch/--script script I need to process the command line arguments given to the script.
I've gotten as far as getting the arguments into a list of the the form:
("--aaa=bbb" "--ccc=ddd=eee" "--blah")
Now, I need to convert them to a list of the form:
(("aaa" "bbb") ("ccc" "ddd=eee") ("blah"))
In Python I'd write something like;
output = []
for v in input:
output.append(v[2:].split("=", 1))
But have been unable to convert that code to Emacs Lisp. I found Elisp split-string function to split a string by . character but wasn't able to figure out how to make it only split on the first equals.
I was heading down a route of using (substring "abcdefg" x x) with (search) from the cl package but it felt like there should be a better way? I think also want to use (mapc '<function> input) where function does the v[2:].split("=",1) part.
You can use split-string. See the following code example.
(setq cmd-line '("--aaa=bbb" "--ccc=ddd=eee" "--blah"))
(setq cmd-line (mapcar (lambda (argstr)
(when (string-match "^--" argstr)
(split-string (substring argstr 2) "=")))
cmd-line))
The output is (("aaa" "bbb") ("ccc" "ddd" "eee") ("blah")).
That is not exactly what you want because of "eee". Maybe you can use that and just neglect "eee".
If the "eee" is really a problem a small modification helps:
(setq cmd-line '("--aaa=bbb" "--ccc=ddd=eee" "--blah"))
(setq cmd-line (mapcar (lambda (arg)
(when (string-match "^--" arg)
(setq arg (split-string (substring arg 2) "="))
(if (cdr arg)
(setcdr (cdr arg) nil))
arg))
cmd-line))
The output is:
(("aaa" "bbb") ("ccc" "ddd") ("blah"))
Variant for the new requirement in the question:
(setq cmd-line '("--aaa=bbb" "--ccc=ddd=eee" "--blah"))
(setq cmd-line (mapcar (lambda (arg)
(when (string-match "^--\\([^=]*\\)\\(?:=\\(.*\\)\\)?" arg)
(let ((opt (match-string 1 arg))
(val (match-string 2 arg)))
(if val
(list opt val)
(list opt)))))
cmd-line))
The output is:
(("aaa" "bbb") ("ccc" "ddd=eee") ("blah"))

How do I code walk in elisp?

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")

Debugging code with a mix of functions, macros, and bash calls in elisp

I am trying to call a bash program herbstclient from Emacs via process-lines. I created a macro hc-call which actually calls herbstclient that is invoked by the function hc which is supposed to convert its numeric arguments into strings via stringify-numbers.
Needless to say it's not working. Calling hc with "keybind" "Mod4-Shift-r" "reload" gives the error:
*** Eval error *** Wrong type argument: listp, stringified-args
I tried using edebug on hc and the output suggested stringify-numbers was working properly. The function errored immediately on the hc-call. Yet, when I run:
(hc-call ("keybind" "Mod4-Shift-r" "reload"))
it works as expected. I then tried:
(setq sargs (list "keybind" "Mod4-Shift-r" "reload"))
(hc-call sargs)
and I got the same error. I don't know how to approach debugging this further. Below is all the code:
(defmacro hc-call (args)
"Call herbstclient to with the given arguments."
`(process-lines "herbstclient" ,#args))
(defun stringify-numbers (args)
"Take a list of random arguments with a mix of numbers and
strings and convert just the numbers to strings."
(let (stringified-args)
(dolist (arg args)
(if (numberp arg)
(setq stringified-args (cons (number-to-string arg) stringified-args))
(setq stringified-args (cons arg stringified-args))))
(nreverse stringified-args)))
(defun hc (&rest args)
"Pass arguments to herbstclient in a bash process."
(let ((stringified-args (stringify-numbers args)))
(hc-call stringified-args)))
Why would it complain stringified-args isn't a list?
Your hc-call should be a function, along the lines of
(defun hc-call (args)
"Call herbstclient to with the given arguments."
(apply #'process-lines "herbstclient" args))
BTW, while I'm here:
(if (numberp arg)
(setq stringified-args (cons (number-to-string arg) stringified-args))
(setq stringified-args (cons arg stringified-args))))
is better written
(setq stringified-args (cons (if (numberp arg) (number-to-string arg) arg) stringified-args))))
or
(push (if (numberp arg) (number-to-string arg) arg) stringified-args)))
Unlike most expressions, macro arguments are passed unevaluated.
This is why (hc-call ("keybind" "Mod4-Shift-r" "reload")) does not result in an error!
It therefore follows that (hc-call sargs) is passing the symbol sargs to the macro, rather than the list to which it would otherwise evaluate.
If you wish your macro to process a variable in this way, you could change ,#args to ,#(eval args), or else conditionally process args either way, depending on what it turns out to actually be.