I wanted to rename all of the files in a directory that ended in .txt so that they ended in .xml . Clearly some sort of script was needed, but what language to use? I chose elisp, because I was working in emacs and the interactive help made it easy to find the functions I wanted. Thinking about it later I wondered how I'd do this in other languages so here also are python and perl versions with the same functionality. None are heavily tested but all work if run in the directory containing the files to rename. My final impression? The elisp one was the quickest to write and I think is the easiest to manage for this job.
In elisp:
(defun rename-some-files (dir) "Rename some files." (interactive "DDirectory name ") (mapc (lambda (f) (rename-file (concat f ".txt") (concat f ".xml"))) (mapcar 'file-name-sans-extension (directory-files dir nil "\.txt$")))
In python:
import os
import re
pat = re.compile('\.txt$')
map(lambda f: os.rename(f + '.txt', f + '.xml'),
[os.path.splitext(f)[0] for f in os.listdir(os.getcwd()) \
if pat.search(f)])
In perl:
use Cwd;
use File::Basename;
opendir(DIR, cwd());
map(rename($_ . '.txt', $_ . '.xml'),
map(basename($_, '.txt'),
grep(/\.txt$/, readdir(DIR))));
closedir(DIR);
The best thing about Emacs is the ease with which it can be extended. Here's a simple function that I wrote in a couple of minutes to add <span> tags round the currently selected region. Saved me a lot of boring copy and paste or typing.
(defun ajm-ref (s e) "Add <span class=\"ref\"> </span> round the text in the region and then call fill-paragraph to reformat." (interactive "r") (goto-char e) (let ((end (point-marker))) (goto-char s) (insert "<span class=\"ref\">") (goto-char end) (insert "</span>")) (fill-paragraph nil))
I've just finished adding incremental search by java method name to the elisp minor mode jxminor that I maintain. When I started I thought it would be nearly impossible to do a decent job but it turned out much better than I could have hoped.
If your an elisp expert you may find what follows rather obvious. If so do you have any suggestions for improvement?
Anyway, the code that provides incremental search is 2074 lines of code and comments. There is no way I was going to be able to reproduce that functionality so I thought at first that my best option would be to patch it somehow. The actual work of searching is done by one function isearch-search and it chooses between word-search-forward/backward, re-search-forward/backward and search-forward/backward depending on various variable settings. If you just press C-s or C-r you'll end up calling search-forward or search-backward. If another pair of functions could be added that would solve the problem.
However, when I looked at the code though I had the thought "if I could just rebind search-forward and search-backward so that my functions got called instead that would solve the problem". This is easy in scheme as there's only one namespace. Looking around I found the function fset that changes the function that a symbol points to. Now the problem is reduced to being able to save the functions search-forward/backward are bound to, rebind them to the method name search functions, execute isearch-search and restore the original bindings.
Turns out this is easy as well. Using the defadvice macro you can arrange for code you write to be "wrapped around" any other function. So the wrapper I wrote does the save, execute and restore described. What's even better is it's easy to turn the wrapper function on and off so you don't have to have a complex one that can deal with being called in all sorts of situations.
The final solution is as simple as having the java incremental search function turn the wrapper on for isearch-search and then call the normal incremental search function. This gives you the full mini buffer editing capability that isearch provides but with a different underlying function to do the actual searching.
I was very impressed, as I always am, with what you can do in emacs and how very easy it is to do it.
This elips stuff is getting out of hand! The original single file with a few functions in it has become four files that define an emacs minor mode to help when editing java code. Available for download here.
I find these quite useful and I spend my work days writing java so I hope they will be useful to others also. Functions are provided for managing import statements, inserting log4j debugging statements, and creating skeleton javadoc comments for methods.
In the future I want to add some sort of template system to make generation of repetitive code easier. The scenario I have in mind goes like this...
I see a piece of code I want to make into a reusable template so I set the emacs region to surround it and call the templatize command. This copies the code to a new buffer in templatize mode. Now, in templatize mode I can select regions of the buffer corresponding to the variables sections I want to make variable when the template is instantiated to create code. I think that there are only two sorts of regions in the basic system, those that get their values by prompting the user, and those whose values are provided by applying some sort of function to an input value. In addition to the standard elisp functions some java specific ones, such as initial-upcase and initial-downcase should be provided as well.
Once the code is marked up it is used to generate a data structure for saving to a file. I'm still debating how to save the data, as a simple structure or as some sort of function definition.
One of the benefits of using emacs is that as well as using a great tool you get to modify and customize it to make it even better. Just as carpenters can built jigs to make repetitive or difficult table saw cuts quicker or easier emacs users can add bits on to emacs for the same reasons. Besides, hacking on elisp is a great change from writing java code.
With that intro unfortunately the only change I've made recently is to my java stuff package. The import checking function now does a better job of commenting out imports that are duplicates or that are only referenced in commented out code. Here's the code and here's an htmlized version.
I made yet more changes to my package of elisp functions for helping with java editing in emacs. This set adds a function to insert log4j debugging messages into the code.
The formatting of the inserted code is controlled by the java mode so it should end up looking the same as the stuff you write by hand. You can change logCat to some other value by modifying the value of the java-log4j-statement variable. For example:
if (logCat.isDebugEnabled())
logCat.debug("a" +
b);
Here's the code for downloading and for easy reading in a browser.
java-sort-imports was modifying the kill ring because I was using kill-region and yank instead of the recommended delete-region. I also got tired (after doing it about three times) of manually deleting import statements after running java-check-imports so I decided to add a funtion java-delete-commented-imports to do that automatically as well.
Of course when I sat down to make those changes it all got a bit out of hand and I found myself adding a menu to get to the functions. So, the package now lets you add an Extras submenu to the Java menu in java mode.
Here's the modified code for downloading and here it is formatted for easy reading in a browser. As before the formatting is courtesy of the htmlize package.
import java.foo.*;. In emacs I can use M-x java-sort-imports to order the statements the way I like to see them.
The second is to make sure that I'm not importing more classes than I need to. I know it doesn't actually make a difference to the generated bytecode but it makes it difficult to use the import statements to help see the inter class dependencies when looking at the source code. I can use M-x java-check-imports to comment out import statements that import classes that appear not to be referenced in the source code. Obviously it can't check import statements that don't specify the class, i.e. that end in *.
Here's the code for downloading and here it is formatted for easy reading in a browser. The formatting was done using the htmlize package, which does a great job.