Emacs Lisp: Execute/Compile Current File

, , …,

This page shows a example of writing a emacs lisp function that execute or compile the current file. If you don't know elisp, first take a look at Emacs Lisp Basics.

Problem Description

I want to be able to press a button, and have the current file executed or compiled. The file may be a Perl, Python, Ruby, PHP, Bash, or any shell script.

If you work with scripting languages often, it'd be nice to have a command to run the current buffer directly, even without going into shell in emacs.

This way is even better than using a interactive command line (aka REPL), because you can write and edit normally with full emacs power. And you can run it by just pressing a key, such as F8.


Here's the solution:

(defun xah-run-current-file ()
  "Execute the current file.
For example, if the current buffer is the file xx.py,
then it'll call “python xx.py” in a shell.
The file can be php, perl, python, ruby, javascript, bash, ocaml, vb, elisp.
File suffix is used to determine what program to run.

If the file is modified, ask if you want to save first.

If the file is emacs lisp, run the byte compiled version if exist."
  (let* (
            ("php" . "php")
            ("pl" . "perl")
            ("py" . "python")
            ("py3" . ,(if (string-equal system-type "windows-nt") "c:/Python32/python.exe" "python3"))
            ("rb" . "ruby")
            ("js" . "node")             ; node.js
            ("sh" . "bash")
            ("ml" . "ocaml")
            ("vbs" . "cscript")
         (fName (buffer-file-name))
         (fSuffix (file-name-extension fName))
         (progName (cdr (assoc fSuffix suffixMap)))
         (cmdStr (concat progName " \""   fName "\""))

    (when (buffer-modified-p)
      (when (y-or-n-p "Buffer modified. Do you want to save first?")
          (save-buffer) ) )

    (if (string-equal fSuffix "el") ; special case for emacs lisp
        (load (file-name-sans-extension fName))
      (if progName
            (message "Running…")
            (shell-command cmdStr "*xah-run-current-file output*" )
        (message "No recognized program file suffix for this file.")
        ) ) ))

The code does a very few things. It gets the current file's name, current file's suffix, and use the suffix to determine which shell command to run. Then, it generate the shell command string, then run it in a shell.

First, it defines a “suffixMap” which is a list of pairs. In each pair, the first element is the file suffix, and the second element is the language's command line path or name. Such a list of pairs is called “association list” in lisp. (a similar concept is hash table, but we don't need it here since our list only has a handful of elements. See: Emacs Lisp Tutorial: Hash Table.)

To extract the value of a given key in a association list, use the assoc function like this: (cdr (assoc myKey myAList)). This is used in our code to obtain a command-line program name of a given file extention, in this line: (setq progName (cdr (assoc suffix suffixMap))).

(info "(elisp) Association Lists")

The rest of the program is rather simple:

(info "(elisp) Buffer File Name")

(info "(elisp) File Name Components")

Now, we can define a keyboard shortcut for this:

(global-set-key (kbd "<f8>") 'xah-run-current-file)

So now, doesn't matter we are writing in Perl, Python, elisp, …, we can just press a button and have the file executed or compiled.

Determine Language by Major Mode

If you don't have file extension, you might use the current major mode to determine what language it is.

The major mode's name is stored in the variable major-mode.

You also need to redefine your associative list to store major mode names corresponding to program paths.

(thanks to Matic Bojan for suggesting major mode for determining language.)

emacs ♥

blog comments powered by Disqus