2

Consider the following elisp source code within a .org file:

#+BEGIN_SRC elisp :results silent
(delete-other-windows)
(split-window-right)
(switch-to-buffer-other-window '"*Python*")
#+END_SRC

This code is meant to delete all other windows, create a new window to the right (I work with 2 windows side by side), and then switch to that window and open an already running Python interpreter there.

It works if I go line by line and C-x C-e to evaluate the elisp code. However, running the code block via C-c C-c in org mode does not produce the desired effect.

Question: Is it possible to modify that code block so that running it with C-c C-c produces the desired effect?

2

The following lisp code defines the new emacs-lisp source block parameter :keep-windows. You can modify the window configuration through the source block if you set this parameter to t.

You can put the source code in your init file.

It replaces save-window-excursion in org-babel-execute:emacs-lisp by a newly defined macro save-window-excursion-if. The new macro needs a predicate as new first argument. The window configuration is only stored if that predicate is non-nil. That is where the new source block parameter :keep-windows is tested.

The function replace-in-fundef below needs access to the source file ob-emacs-lisp.el.

(require 'ob-emacs-lisp)
(defun transform-tree (tree trafo)
  "Transform TREE by TRAFO."
  (let ((next tree))
    (while next
      (let ((this next))
    (setq next (cdr next))
    (if (consp (car this))
        (transform-tree (car this) trafo)
      (funcall trafo this)))))
  tree)

(defun replace-in-fundef (fun sym &rest replacement)
  "In function FUN perform REPLACEMENT."
  (setq fun (or
         (condition-case err
         (let* ((pos (find-function-noselect fun t))
            (buf (car pos))
            (pt (cdr pos)))
           (with-current-buffer buf
             (save-excursion
               (goto-char pt)
               (read buf))))
         (error nil))
         (and (symbolp fun) (symbol-function fun))
         fun))
  (transform-tree fun
   (lambda (this)
     (when (eq (car this) sym)
       (let ((copy-repl (cl-copy-list replacement)))
     (setcdr (last copy-repl) (cdr this))
     (setcdr this (cdr copy-repl))
     (setcar this (car copy-repl)))))))

(defmacro save-window-excursion-if (pred &rest body)
  "Act like `save-window-excursion' if PRED is non-nil."
  (declare (indent 1) (debug t))
  (let ((c (make-symbol "wconfig")))
    `(let ((,c (and ,pred (current-window-configuration))))
       (unwind-protect (progn ,@body)
         (when ,c (set-window-configuration ,c))))))

(advice-remove 'org-babel-execute:emacs-lisp #'ad-org-babel-execute:emacs-lisp)
;; make sure we have access to the source code of `org-babel-execute:emacs-lisp'
(find-function-noselect 'org-babel-execute:emacs-lisp t)
;; (defun ad-org-babel-execute:emacs-lisp ...):
(eval (replace-in-fundef 'org-babel-execute:emacs-lisp 'org-babel-execute:emacs-lisp 'ad-org-babel-execute:emacs-lisp))
;; Use `save-window-excursion-if' in `ad-org-babel-execute:emacs-lisp':
(declare-function 'ad-org-babel-execute:emacs-lisp " ")
(eval (replace-in-fundef 'ad-org-babel-execute:emacs-lisp
             'save-window-excursion 'save-window-excursion-if '(null (member (cdr (assoc :keep-windows params)) '("yes" "t")))))
;; Replace `org-babel-execute:emacs-lisp':
(advice-add 'org-babel-execute:emacs-lisp :override #'ad-org-babel-execute:emacs-lisp)

Your example source block can be augmented with :keep-windows as follows:

#+BEGIN_SRC elisp :results silent :keep-windows t
(delete-other-windows)
(split-window-right)
(switch-to-buffer-other-window '"*Python*")
#+END_SRC

Some details about the source code:

We want to replace save-window-excursion in org-babel-execute:emacs-lisp. save-window-excursion is a macro. That means we need the source code to replace the macro before its expansion. We look org-babel-execute:emacs-lisp up with find-function and read it through the elisp reader. The function definition can be interpreted as a tree constructed from lisp-lists (if the function is not byte-compiled). Modifying that definition is a tree-transformation.

  • Wow, this is amazing. Works flawlessly. Thank you! In terms of understanding your code, any advice? I'm fairly new to elisp (haven't finished the tutorial yet). – Guilherme Salomé Jun 19 '18 at 11:35
  • 1
    @GuilhermeSalomé I added some short overview about the source code. Nevertheless you will need some elisp basics to understand the code. Maybe the intro and the doc help. – Tobias Jun 21 '18 at 21:16
1

I'm pretty sure the window-modifying behavior you want is not supported. When you press C-c C-c on the source block it ultimately calls org-babel-execute:emacs-lisp, and that function wraps everything in a save-window-excursion form.

Are you just trying to easily reset your windows if they get out of alignment? Consider defining your own function like

(defun my-window-fixup ()
  (interactive)
  (delete-other-windows)
  (split-window-right)
  (switch-to-buffer-other-window "*Python*"))

and bind my-window-fixup to a key in the org-mode-map with something like

(define-key org-mode-map (kbd "C-c f") #'my-window-fixup)
  • Thanks for your suggestion. I'm trying to understand how to produce something akin to a Jupyter notebook for Python but in orgmode. And these commands were just a quick way to open the Python interpreter side by side with my org buffer. – Guilherme Salomé Jun 18 '18 at 19:41
  • 1
    Oh! Have you checked out ob-ipython or EIN (specifically the ob-ein part)? – purple_arrows Jun 18 '18 at 19:50
  • 1
    Tiny self-promotion … I found ob-ipython too slow and EIN too complicated, so I wrote my own replacement jupyter-mode. It’s still very much a WIP, though – purple_arrows Jun 18 '18 at 19:51
  • I have checked EIN, but somehow I couldn't find it on MELPA. I am not sure if I want to dive into these notebooks right now, since orgmode seems capable of delivering similar results in a simpler fashion. Thanks for sharing your work, I'll check it out too! – Guilherme Salomé Jun 18 '18 at 19:59
  • 1
    Oh I gotcha. Looking to use Org as a replacement for Jupyter? Makes sense. Be sure to check out the :session header argument for your Python blocks. Also, C-c C-v C-z (org-babel-switch-to-session) should switch to the Python interpreter buffer from a Python source block. – purple_arrows Jun 18 '18 at 20:20

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.