21

I have elisp code I'd like to run in orgmode files when they load (different for different files, and defined in the file itself). Is there a way to do this? I didn't see anything in http://orgmode.org/manual/In_002dbuffer-settings.html

If I can add something to emacs initialization that runs a specially named code block whenever an orgmode file loads, that might be a solution, but I'm not sure how to do that, and ideally there is something built in.

avv
  • 1,563
  • 10
  • 24
  • 1
    File-local variables, perhaps? – Dan Jun 06 '15 at 07:01
  • That works! Do you want to make it into an answer that I can accept? – avv Jun 06 '15 at 08:01
  • On further thought that is a very messy environment to edit in, so a good solution would have the file local variable running a named code block. – avv Jun 06 '15 at 08:29
  • Wrap it in a progn or a lambda, perhaps? – Dan Jun 06 '15 at 12:38
  • 6
    You can also use `# -*- eval: (lisp code here) -*-` but you also have to be aware of the dangers. Even if you don't share these documents with anyone else, the interpreted nature of Emacs Lisp will mean that a change may accidentally result in loss of data. Also, mode hook sounds like a better option if you want to run the same code for more than one file. – wvxvw Jun 06 '15 at 13:51
  • wvxvw I believe that is exactly what Dan is referring to. Good points about the dangers! Dan - I am wrapping in a progn for now, the disadvantage is that the code can't be indented well, and I don't have much choice of where to put it (none of which are a problem if calling a separate code block from the file local variable eval). – avv Jun 06 '15 at 18:44

5 Answers5

20

This solution requires no change in init.el (with minor modifications). It involves file-local evaluations, though - but that's exactly what the OP asked for. Advantages of the solution are:

  • asks for confirmation to evaluate code
  • elisp code can be edited and tested within the org-babel environment
  • as the solution doesn't require modifications to init.el the orgmode file may be shared among (trusted) users

I'm rephrasing the solution here.

Add a src-block somewhere in your file:

#+NAME: startup
#+BEGIN_SRC emacs-lisp
(your-code-here)
#+END_SRC

Then, put this at the end of your orgmode-file:

# Local Variables:
# eval: (progn (org-babel-goto-named-src-block "startup") (org-babel-execute-src-block) (outline-hide-sublevels 1))
# End:

I've added (outline-hide-sublevels 1) because I like to hide the src-block inside a heading and want the sublevels to be hidden on startup. Without this statement, the sublevels will become expanded by (org-babel-goto-named-src-block "startup").

With this solution, emacs will ask 2 times for permission to execute (1st: apply Local Variables; 2nd: execute "startup"-src-block). As I have a lot of src-blocks in my file, I've set another file-local-variable, org-confirm-babel-evaluate, like this:

# Local Variables:
# org-confirm-babel-evaluate: nil
# eval: (progn (org-babel-goto-named-src-block "startup") (org-babel-execute-src-block) (outline-hide-sublevels 1))
# End:

Warning: With this addition, emacs will prompt only once for permission to execute - all src-blocks in that file may now be executed without further confirmation. As others have pointed out before, this behavior could be dangerous and you should be very careful with this setting.

However, I'd argue that this solution (especially the first version) is more secure than the one given by Joe Corneli because at least you will be asked for confirmation to execute. Joe's solution will evaluate the special block without confirmation, if it is found in the file. An attacker would have to guess the name of the special block, of course...

I'm using this approach to write large documents which require, e.g., adaptions to the org-export mechanisms.

ben
  • 311
  • 2
  • 4
5

org-mode ... In addition to any hooks its parent mode outline-mode might have run, this mode runs the hook org-mode-hook, as the final step during initialization.

So, in your init.el:

(defun function-that-finds-and-evaluates-special-block ()
;; DWIM :-)
)
(add-hook 'org-mode-hook 'function-that-finds-and-evaluates-special-block)
Joe Corneli
  • 1,786
  • 1
  • 14
  • 27
4

Since you ask for

(different for different files, and defined in the file itself)

then, try this solution.

Emacs User
  • 5,553
  • 18
  • 48
3

I have tried to improve the code from Joe Corneli:

You need that in your init.el file:

  (defun tdh/eval-startblock ()
    (if (member "startblock" (org-babel-src-block-names))
      (save-excursion
        (org-babel-goto-named-src-block "startblock")
        (org-babel-execute-src-block))
      nil
      )
    )
  (add-hook 'org-mode-hook 'tdh/eval-startblock)

Each time you open an org-mode buffer, it will search for a source block named startblock, if one is found, it will execute it.

In you org-mode files, you can then put:

#+NAME: startblock
#+BEGIN_SRC emacs-lisp
  ;; Any code you want
#+END_SRC
tdehaeze
  • 131
  • 2
1

I agree with @Joe Corneli's suggestion about using a hook.

It occurs to me too that you could leverage bookmarks here: put a specific bookmark jump on the hook. An advantage of a bookmark to the code block is that it typically gets relocated automatically (e.g. as the file content changes), so locating the block should typically be automatically taken care of.

[But it's not clear to me why you have the code in the Org-mode files, instead of elsewhere. We're taking that as a given, per the problem statement, but I wonder why you are doing that. Letting us know more about the design in that respect might lead to better help.]

Drew
  • 75,699
  • 9
  • 109
  • 225