Archive for April, 2008

No more source code indentation arguments

April 8, 2008

Guaranteed!1

We’ve all been there. Maybe you’ve joined a new team that uses a one space indent which is driving you crazy. Perhaps your team already compromised on a four space indent and a new hot-shot joins your team and tells you that Linus says that if you don’t use an eight space indent nature will screw you2

What is the solution? Simple – add a hook on check-in to your source repository that automatically reindents the code with the team’s standard. Then, anyone who wants to can configure their editor to reformat the code the way they like it when opening a file. When they check the code back in, their screwy indentation is fixed again. That way, everyone can develop using the style they like.3

You’re probably wondering where the emacs is in this post. Well, I don’t know about you, but I haven’t had much luck with getting indent(1) to format C++ in the way that I want. However, emacs knows how to indent my code just fine.

;; ---------------------------------------------------------------------- ;;

(defconst *my-cc-style*
  '((c-basic-offset . 4)
    (c-comment-only-line-offset . 0)
    (c-hanging-braces-alist . ((brace-list-open)
                               (brace-entry-open)
                               (substatement-open after)
                               (block-close . c-snug-do-while)))
    (c-cleanup-list . (brace-else-brace))
    (c-offsets-alist . ((statement-block-intro . +)
                        (knr-argdecl-intro     . 0)
                        (substatement-open     . 0)
                        (substatement-label    . 0)
                        (innamespace           . 0)
                        (case-label            . +)
                        (statement-cont        . +)))))

;; ---------------------------------------------------------------------- ;;

(provide 'cc-style)

;; ---------------------------------------------------------------------- ;;

A simple shell script wrapper will let you call indent.sh <filename>. Warning: this script has not been tested and may break your code. Use at your own risk.

#!/bin/sh

CC_STYLE=$HOME/emacs-files/cc-style.el

emacs --batch -eval "
  (progn
    (load \"$CC_STYLE\")
    (c-add-style \"PERSONAL\" *my-cc-style*)
    (find-file \"$1\")
    (c++-mode)
    (c-set-style \"PERSONAL\")
    (setq tab-width 4)
    ;; (setq make-backup-files nil)
    (ident-region (point-min) (point-max))
    (untabify (point-min) (point-max))
    (basic-save-buffer))"

1. (or your money back)

2. Yep, horribly misquoted and probably misattributed – sorry

3. They might not like the output from the diff tool though. Tell them about diff -b

Advertisements

Wrapping FTP in emacs – part 2

April 2, 2008

Now that we have a nice wrapper around copying a file that handles connecting to the remote server if we are not already connected, implementing the single key press upload function is trivial.

My website related directories are all under c:/home/juang/website. However, sometimes I like to upload some of my elisp files which live elsewhere. I should probably have an external environment variable which points to my home directory (e.g. %HOME%) but for the moment I have constants within my elisp source.

(defconst *ftp-HOME* "c:/home/juang/")
(defconst *ftp-WEB* (concat *ftp-HOME* "website/"))

In elisp we normally use a list of lists where we might use a hash in Perl. This is not because elisp doesn’t have hashes (it does), but because the syntax for lists is cleaner. A simple mapping might look like this:

(defconst *map*
  '(("c:/www" "/www")
    ("c:/emacs-files" "/www/emacs-files")))

However, quote suppresses variable evaluation so we need to be a little bit more verbose.

(defconst *ftp-location-map*
  (list
   (list (concat *ftp-WEB* "wordpress") "/www/blog")
   (list *ftp-WEB* "/www/")
   (list (concat *ftp-HOME* "emacs-files") "/www/files")))

I like emacs to ask for confirmation before it uploads a file but as others might want to skip that step we add a variable to allow the alternative behaviour.

(defcustom *ftp-upload-skip-prompt* nil
  "Skip the ftp prompt")

A couple of comments about (ftp-upload-file …):

  • (do-list (var list) body) iterates through a list, setting var to each item in turn.
  • (y-or-n-p &prompt) displays the prompt in the mini-buffer and waits for a y or n input.

The rest of the function should be fairly self-explanatory.

(defun ftp-upload-file (from)
  (let ((to from))
    (dolist (tuple *ftp-location-map*)
      (let ((regex (car tuple))
            (replace (cadr tuple)))
        (when (string-match (concat "^" regex) to)
          (setq to (replace-match replace t t to)))))
    (if (or *ftp-upload-skip-prompt*
            (y-or-n-p (format "Query: Upload %s to %s ? " from to)))
        (ftp-copy-file from to)
      (message "Info: upload %s to %s aborted" from to))))

(defun ftp-upload-current-file ()
  (interactive)
  (ftp-upload-file (expand-file-name (buffer-file-name))))

(global-set-key [f9] 'ftp-upload-current-file)

We also provide a convenience function to upload the current file and bind it to a single key. The final result is linked from the original version of this page.

JG