Elisp Lesson: Execute/Compile Current File

Xah Lee, 2007-11

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.

The Problem

Summary

I want to be able to press a button, and have the current file executed or compiled. The file may be a perl, python, bash, php script, or java code.

Detail

I work in Perl, Python, PHP, bash a lot. For example, suppose i'm currently editing “process_log.pl”. Once i'm done, i'd like to run this in a command line call: “perl process_log.pl”. In emacs, to call a shell command, press the key “Alt+!”, then type “perl process_log.pl”. This is convenient, but if one writes a lot different scripts in various languages, the typing gets tedious.

For example, when i program in perl, i may often create a temp file test.pl that tests some snippet of construct or function before i actually put it into a program i'm working on. It is common, that the edit-then-run cycle may reach 5 or 10 times. Typing out “Alt+Shift+! perl test.pl RET” tens of times is no fun. Emacs's promp provides a history feature, so that once i've run it once, i can just press the up-arrow to get the previous command i typed in the promp. The full keystrokes to type now is: “Alt+Shift+! ↑ RET”. But then, sometimes while working on a function in “test.pl”, i want to branch out into alternatives. So i may have “test2.pl” and “test3.pl”. To execute these different files, i'll either have to type their full commands again, or use the up-arrow and eyeball.

It would be better, if i can just press a button such as F7, then have emacs automatically execute the current file in shell using the proper program (e.g. perl, python, bash, java). We proceed to write this.

Solution

Here's the solution:

(defun run-current-file ()
  "Execute or compile the current file.
For example, if the current buffer is the file x.pl,
then it'll call “perl x.pl” in a shell.
The file can be php, perl, python, bash, java.
File suffix is used to determine what program to run."
(interactive)
  (let (ext-map file-name file-ext prog-name cmd-str)
; get the file name
; get the program name
; run it
    (setq ext-map
          '(
            ("php" . "php")
            ("pl" . "perl")
            ("py" . "python")
            ("sh" . "bash")
            ("java" . "javac")
            )
          )
    (setq file-name (buffer-file-name))
    (setq file-ext (file-name-extension file-name))
    (setq prog-name (cdr (assoc file-ext ext-map)))
    (setq cmd-str (concat prog-name " " file-name))
    (shell-command cmd-str)))

This is really just a simple program. The program 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 “ext-map” 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.)

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 prog-name (cdr (assoc file-ext ext-map)))”.

Reference: Elisp Manual: Association-Lists.

Reference: Elisp Manual: Hash-Tables.

The rest of the program is rather simple:

Reference: Elisp Manual: Buffer-File-Name.

Reference: Elisp Manual: File-Name-Components.

Now, we can define a keyboard shortcut for this:

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

So now, doesn't matter we are writting in perl, python, php, bash, or even Java, we can just press a button and have the file executed or compiled.

WOOT! Emacs is beautiful!


Related essays:


Page created: 2007-11.
© 2007 by Xah Lee.
Xah Signet