Xah Lee, , …,
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.
I want to be able to press a button, and have the current file executed or compiled. The file may be a Perl, Python, PHP, Bash, script, or Java code.
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 shell: perl process_log.pl. In emacs, to call a shell command, press the key 【Alt+!】. This is convenient, but if you write a lot different scripts in various languages, the typing gets tedious.
For example, when i program in Perl, i 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 Enter】 tens of times is no fun. Emacs's promp provides file name completion and command history feature, so that once i've run it once, i can just press ↑ key to get the previous command i typed in the promp. The full keystrokes to type now is: 【Alt+Shift+! ↑ Enter】. 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 F8, then have emacs automatically execute the current file in shell using the proper program. We proceed to write this.
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, Ruby, JavaScript, Bash, ocaml, vb, elisp. File suffix is used to determine what program to run." (interactive) (let (suffixMap fName suffix progName cmdStr) ;; a keyed list of file suffix to comand-line program path/name (setq suffixMap '( ("php" . "php") ("pl" . "perl") ("py" . "python") ("rb" . "ruby") ("js" . "js") ("sh" . "bash") ("ml" . "ocaml") ("vbs" . "cscript") ; ("pov" . "/usr/local/bin/povray +R2 +A0.1 +J1.2 +Am2 +Q9 +H480 +W640") ) ) (setq fName (buffer-file-name)) (setq suffix (file-name-extension fName)) (setq progName (cdr (assoc suffix suffixMap))) (setq cmdStr (concat progName " \"" fName "\"")) (if (string-equal suffix "el") ; special case for emacs lisp (load-file fName) (if progName (progn (message "Running…") (shell-command cmdStr "*run-current-file output*" ) ) (message "No recognized program file suffix for this file.") ) )))
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 “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:
(concat progName " " fName) is the string we want to execute in shell.(shell-command cmdStr) executes our cmdStr in a shell.(info "(elisp) Buffer File Name")
(info "(elisp) File Name Components")
Now, we can define a keyboard shortcut for this:
(global-set-key (kbd "<f8>") '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.
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