Monday, June 25, 2007

Programmable IDEs

Ok, I'll admit it. I still use Emacs as my "IDE". It's what I started programming with when I was learning Unix/C in college, and it's always served my needs well. Nowadays, I pretend to be a Java developer, and Emacs still works fine for me. I've been meaning to try Eclipse for some time now, but have ended up having issues getting it to work on my 64-bit RHEL workstation, and I just haven't had time to make it work yet.

However, what Emacs and Eclipse have in common is that they are both programmable IDEs. Emacs is pretty explicitly programmable with Emacs Lisp (in fact, you pretty much couldn't even do any amount of preference setting or customization without learning Elisp, for quite a long while), and Eclipse lets you code up extensions to customize its behavior. So what's the big deal about your IDE being programmable?

Well, it means that you can start writing programs to save yourself work. I remember sitting with a colleague who was using Eclipse while I was getting back up to speed on Java development, and got a twinge of Eclipse envy when he right-clicked on an instance variable and selected "create setters and getters". I had just been typing those out by hand in Emacs, but a couple of hours later, I had hacked it up in Emacs, and use it all the time:

(defun java-insert-getter (bean-name bean-type)
 "Insert a simple getter method."
 (interactive "sInsert getter for bean: \nsInsert getter for %s of type[]: ")
 (let ((upcase-bean-name (concat (capitalize (substring bean-name 0 1))
                 (substring bean-name 1))))
   (let ((bean-setter (concat "set" upcase-bean-name))
     (bean-getter (concat "get" upcase-bean-name))
     (bean-use-type (if (string= "" bean-type)
                upcase-bean-name
              bean-type)))
     (insert (concat "public " bean-use-type " " bean-getter "() {"))
     (c-indent-command)
     (newline)
     (insert (concat "return this." bean-name ";"))
     (c-indent-command)
     (newline)
     (insert "}")
     (c-indent-command)
     (newline)
     (c-indent-command))))

(defun java-insert-setter (bean-name bean-type)
 "Insert a simple setter method."
 (interactive "sInsert setter for bean: \nsInsert setter for %s of type[]: ")
 (let ((upcase-bean-name (concat (capitalize (substring bean-name 0 1))
                 (substring bean-name 1))))
   (let ((bean-setter (concat "set" upcase-bean-name))
     (bean-getter (concat "get" upcase-bean-name))
     (bean-use-type (if (string= "" bean-type)
                upcase-bean-name
              bean-type)))
     (insert (concat "public void " bean-setter
             "(" bean-use-type " " bean-name ") {"))
     (c-indent-command)
     (newline)
     (insert (concat "this." bean-name " = " bean-name ";"))
     (c-indent-command)
     (newline)
     (insert "}")
     (c-indent-command)
     (newline)
     (c-indent-command))))

(defun java-insert-setter-and-getter (bean-name bean-type)
 "Insert both setter and getter methods."
 (interactive "sInsert setter/getter for bean: \nsInsert setter/getter for %s of type[]: ")
 (java-insert-setter bean-name bean-type)
 (java-insert-getter bean-name bean-type))

(setq java-mode-hook '(lambda ()
             (interactive)
             (local-set-key "\C-c\C-i" 'java-insert-setter-and-getter)
             (local-set-key "\C-c\C-j" 'java-insert-setter)
             (local-set-key "\C-c\C-m" 'comment-region)
             (local-set-key "\M-d" 'java-kill-word)
             (if (and window-system (x-display-color-p))
             (font-lock-mode))
                     (setq indent-tabs-mode t)
             (setq tab-width 4)
             (auto-fill-mode 1)))

So what's the point? Isn't this just reinventing the Eclipse wheel? Perhaps, but now I can control exactly what it does and why. In some later posts, we'll talk about test-driven development (TDD) and why I might want to inject a protected getter where I might normally have just put a setter method for dependency injection. Later, I'll probably modify this to add in some basic Javadocs.

But here's the main point: get into the habit of automating your best practices. As we learn and share what those best practices are, we should be building tools / macros / scripts that codify those best practices, to free us up for the algorithm, object model, and application design that's the meat of what we do.

0 comments: