Feeds:
Posts
Comments

Archive for July, 2009

Does anyone else use tramp? It is a mode that allows emacs to transparently edit remote files. For example, you have a file called afile.txt on a server called someserver.

/ftp:jared@someserver:~/afile.txt

Then behind the scenes emacs will ftp the the file to and from the server every time you save.

What Is Happening In Ange-ftp?

Recently tramp has been letting me down. When I try and access a remote-file it fails to login to the ftp process. Sad.

The ftp login as handled by ange-ftp. I finally tracked the problem down to this section.

(defun ange-ftp-get-process (host user)
      ;; ...
      (let ((pass (ange-ftp-quote-string
                   (ange-ftp-get-passwd host user)))

ange-ftp-get-process calls (ange-ftp-quote-string ...). Normally, this isn’t a problem. For example, on the Windows PC I am using at this precise moment (ange-ftp-quote-string "!\"£$%^&*()") expands to "!\"£$%^&*()". However, at work, I get:

"\\!\"£\\$\\%^\\&\\*()"

Could this be due to the customize EmacsW32 option? I’m not sure. Anyway, absent the underlying cause, my work-around is to (require 'ange-ftp) and then redefine the ange-ftp-get-process without the quoting of the password.

(require 'ange-ftp)

(defun ange-ftp-get-process (host user)
      ;; ...
      (let ((pass (ange-ftp-get-passwd host user))

Read Full Post »

I have a number of utility functions in a file called my-utils.el. The first one I wrote was add-path.

add-path

(defun add-path (&rest path)
  (let ((full-path ""))
    (dolist (var path)
      (setq full-path (concat full-path var)))
    (add-to-list 'load-path (expand-file-name full-path))))

This means that when I add a new third-party library (in its own directory), I can just write:

(add-path *elisp-dir* "muse-3.12")

I have a function called identity that returns whatever was passed to it.

(defun identity (e) e)

This is particularly useful in functions like mapconcat.

(defun add-path (&rest path)
  (add-to-list 'load-path (mapconcat #'identity path "")))

I like this function that I found on the internet a while ago (can’t find it again for attribution unfortunately).

insert-YYMMDD

(defun insert-YYYMMDD ()
  (interactive)
  (insert (format-time-string "%Y%m%d")))

remove-dupes

remove-dupes removes duplicate items that are next to each other in a list in the same way that uniq does. (I mentioned this previously in one of the enabling your users posts.)

(defun remove-dupes (list)
  (let (tmp-list head)
    (while list
      (setq head (pop list))
      (unless (equal head (car list))
        (push head tmp-list)))
    (reverse tmp-list)))

assoc-replace

Sometimes you will have an assocation list where you just want to replace one of the values.

(defun assoc-replace (seq values)
  "Replace an element within an association list where the cars match."
  (mapcar (lambda (elem)
            (let* ((key (car elem))
                   (val (assoc key values)))
              (if val (cadr val) elem))) seq))

And often, only the cdr needs to change so kv is a shortcut for that case. (kv 'a 'b) returns (a (a . b)).

(defsubst kv (k v)
  `(,k (,k . ,v)))

(These functions were mentioned in emacs association lists)

ext-mode-map

ext-mode-map allows me to easily setup a major mode for extensions that are inconsistently capitalized.

(defun file-extensions (l)
  (concat "\\.\\("
          (mapconcat
           (lambda (s)
             (mapconcat (lambda (c)
                          (let ((c (upcase (char-to-string c))))
                            (concat "[" c (downcase c) "]")))
                        (symbol-name s) ""))
           l "\\|")
          "\\)\\'"))

(defun ext-mode-map (extensions mode)
  (cons (file-extensions extensions) mode))

Then, for example, I can setup my perl extensions like this:

(add-to-list 'auto-mode-alist (ext-mode-map '(pl perl pm) 'cperl-mode))

(These functions were previously mentioned in autoloading an emacs major mode)

duplicate-current-line

I’m still using this function to duplicate the current line. It works unless you are on the final line which is very rare and you can work around that by opening a line below.

(defun duplicate-current-line ()
  (interactive)
  (beginning-of-line nil)
  (let ((b (point)))
    (end-of-line nil)
    (copy-region-as-kill b (point)))
  (beginning-of-line 2)
  (open-line 1)
  (yank)
  (back-to-indentation))

(This function was mentioned previously in A Simple Emacs Shortcut – Duplicate Line)

set-longlines-mode

I often copy text into buffer and want to read it nicely wrapped around. I’ll change this function to use visual-line-mode when all my emacs versions are upgraded to 23.

(defun set-longlines-mode ()
  (interactive)
  (text-mode)
  (longlines-mode 1))

count-words

Other people have talked about their own count-words functions in the past. I have my own ideas about what constitutes a word.

(defun count-words ()
  (interactive)
  (let ((words (count-matches "[-A-Za-z0-9][-A-Za-z0-9.]*"
                              (point-min) (point-max))))
    (message (format "There are %d words" words))))

regex-replace and string-repeat

I sometimes have a text transform that I want to apply globally to a buffer. Exercise for the reader, fix regex-replace to only apply to a region if one is selected.

(defun regex-replace (regex string)
  (goto-char (point-min))
  (while (re-search-forward regex nil t)
    (replace-match string)))

(defun string-repeat (str n)
  (let ((retval ""))
    (dotimes (i n)
      (setq retval (concat retval str)))
    retval))

Read Full Post »

I’m (unfortunately) doing more work on Windows on these days so I’ve installed a mini Windows-dev environment at home. It is impressive what the libre software guys have done – between Emacs, Cygwin and Perl (and a rusty MinGW C compiler) I feel almost at home.

On Windows, there is an embarrassment of Perl options. I could choose from ActiveState Perl, Strawberry Perl, CygPerl or try and build it myself. I haven’t tried Strawberry Perl so I thought I’d give it a whirl. The install process was pleasant enough but after that I had some problems. I tried my usual perl -MCPAN -e shell incantation followed by installing (upgrading) the CPAN package but it didn’t work. Woe is me!

I reached for old reliable cygperl but that didn’t work either. Hmmm… fishy, I’m sure I haven’t had problems previously. Thankfully googling the exact error lead to a solution which unfortunately I didn’t make a note of. I do remember that it involved a rebase from the static shell, ash. Following that, Strawberry Perl worked too.

The next thing to do was to install POE. POE allows you to structure your code in an event-driven way. I’ve experienced some strange behaviour with POE on various Windows Perl implementations in the past so I want to test a few things.

My basic POE script.

use strict;
use warnings;

use POE;

POE::Session->create(
    inline_states => {
        _start => sub { $_[KERNEL]->yield('loop'); },
        loop   => sub {
            print "Normal loop\n";
            $_[KERNEL]->delay(loop => 1);
        },
    }
);

$poe_kernel->run();

That was a cumbersome way to implement a while 1 loop eh?

while (1) {
    print "Normal Loop\n";
    sleep 1;
}

The first thing I want to test is timeouts. I actually like the crufty way that timeouts are implemented in perl with eval / sig alarm. I use sleep here to simulate a long running process despite the warning in the documentation.

It is usually a mistake to intermix alarm and sleep calls. (sleep may be internally implemented in your system with alarm)

use strict;
use warnings;

use POE;

sub timeout_test
{
    eval {
        local $SIG{ALRM} = sub { die "alarm\n" }; # NB: \n required
        alarm 3;
        sleep 5; # This simulates a long-running process
        alarm 0;
    };
    if ($@) {
        die unless $@ eq "alarm\n"; # propagate unexpected errors
        print "Timeout\n";
        # timed out
    }
    else {
        # didn't
    }
}

POE sessions are a bit like co-operative threads. If the session doesn’t yield to the kernel, another session can’t run so if the timeout doesn’t work, we won’t see any output after the timeout loop starts.

POE::Session->create(
    inline_states => {
        _start => sub { $_[KERNEL]->yield('loop'); },
        loop   => sub {
            print "Normal Loop\n";
            $_[KERNEL]->delay(loop => 1);
        },
    }
);

POE::Session->create(
    inline_states => {
        _start => sub { $_[KERNEL]->yield('loop'); },
        loop   => sub {
            print "Timeout loop\n";
            timeout_test();
            $_[KERNEL]->delay(loop => 1);
        },
    }
);

$poe_kernel->run();

Fortunately, it works fine.

$ perl basic-poe.pl
Normal Loop
Timeout loop
Timeout
Normal Loop
Timeout loop
Timeout
Normal Loop
Terminating on signal SIGINT(2)

Read Full Post »

Now that I have customised my emacs extensively, the default configuration is quite uncomfortable for me to use. I have a file called my-defaults.el which is the bare minimum I need to make using emacs a pleasant experience. If I have to sit down at your emacs session, I will probably need to cut and paste these into a temp buffer and call M-x eval-region.

I’ve mentioned some of these modifications before.

I always always use ido and uniquify. Ido makes it so nice for finding files and switching buffers, I now find the default behaviour surprising and sometimes even catch myself waiting for the options to appear.

(require 'ido)
(require 'uniquify)

flex-matching is a given of course, and I don’t like being prompted unnecessarily for new buffers – I’m always creating them.

(ido-mode t)
(setq ido-enable-flex-matching t)

(setq ido-create-new-buffer 'always)

The way emacs deals with identically named files by default is poor, but it is great that it is so easy to fix.

(setq uniquify-buffer-name-style 'reverse)
(setq uniquify-separator "|")
(setq uniquify-after-kill-buffer-p t)
(setq uniquify-ignore-buffers-re "^\\*")

Since emacs23, fonts now look great. This is from my windows config. Other folks have written about beautifying emacs for other OSes. I summarised those posts here.

(set-default-font
 "-outline-Consolas-normal-r-normal-normal-14-97-96-96-c-*-iso8859-1")

It should be obvious what most of these do. The most important ones are setting yes-or-no-p to accept y or n rather than forcing me to type yes<RETURN> and removing the toolbar. Actually no, scratch that, these are all important. (Emacs23 has some of these set by default).

(global-font-lock-mode 1)
(setq inhibit-splash-screen t)
(setq font-lock-maximum-decoration 3)
(setq use-file-dialog nil)
(setq use-dialog-box nil)
(fset 'yes-or-no-p 'y-or-n-p)
(tool-bar-mode -1)
(show-paren-mode 1)
(transient-mark-mode t)
(setq case-fold-search t)
(blink-cursor-mode 0)

It took me ages to figure out how to prevent emacs converting a bunch of spaces into tabs. And of course, the scrollbar should always be on the right.

(custom-set-variables
 '(scroll-bar-mode 'right)
 '(indent-tabs-mode nil))

Where am I?

(line-number-mode 1)
(column-number-mode 1)

Keep emacs backup files in one place. This is from my windows config again.

(push '("." . "c:/home/jared/.emacs-backups") backup-directory-alist)

And make it so that when I copy a region, that gets sent to the OS clipboard.

(setq x-select-enable-clipboard t)
(setq interprogram-paste-function 'x-cut-buffer-or-selection-value)

Read Full Post »

Looking back over my old posts, I found I didn’t cover basic muse setup at the time when I did my original muse series.

part 1 part 2 part 3 part 4

As I’ve mentioned previously, I generate the html (including the funky syntax highlighting) for my posts and squidoo lenses using muse with htmlize.

You can download muse here and if you are using emacs23 then you need the patched version of htmlize, available here.


The first thing I do with most third party modules, is I add the require lines and the paths I need.

(load "my-vars.el")

(defconst *elisp-muse* (concat *elisp-3rd* "/muse-3.12/lisp"))

(add-to-list 'load-path *elisp-muse*)

(require 'htmlize)

(require 'muse-mode)
(require 'muse-publish)
(require 'muse-html)

my-var.el contains constants pointing to the top-level directories (I still haven’t got around to putting everything under emacs.d)

(defconst *elisp-dir* (expand-file-name "~/emacs-files"))
(defconst *elisp-3rd* (concat *elisp-dir* "/third-party"))

(provide 'my-vars)

I use a couple of minor modes together with muse which are added to muse-mode-hook. (Note, I’m still using longlines-mode as at work I’m unable to use emacs23 where visual-line-mode was introduced)

(defun muse-minor-modes ()
  (longlines-mode 1)
  (flyspell-mode 1)
  (font-lock-mode 0))

(add-hook 'muse-mode-hook 'muse-minor-modes)

And I like publishing to be activated by a single key.

(defun my-muse-publish ()
  (interactive)
  (muse-publish-file (buffer-file-name) "html"))

(define-key muse-mode-map [f7] 'my-muse-publish)

Read Full Post »

Storing Session History

A little while ago we were talking about writing a little emacs-based application to enable the users to help themselves. The beginning of this tool needs a light-weight dired using emacs buttons to use for navigating around the filesystem. Today we will look at adding functionality to remember which files and directories have been accessed previously.

First of all we need some variables to store the directories and files in.

(defvar file-editor-current-dir nil)

(defvar file-editor-save-dirs nil)
(defvar file-editor-save-dirs '(a b c))
(defvar file-editor-save-files nil)

Then we provide a customizable variable where the history will be saved between emacs sessions.

(defcustom file-editor-history-file "~/.file-editor-history"
  "File in which the file-editor history is saved between invocations.
Variables stored are: `file-editor-save-dirs', `file-editor-save-files'."
  :type 'string
  :group 'file-editor)

We will frequently be adding the same file and directory into the lists and we don’t want to get dupes. I could use a data structure that helps avoid dupes or I could just sort the lists and remove adjacent dupes. Guess which option I chose.

(defun remove-dupes (list)
  (let (tmp-list head)
    (while list
      (setq head (pop list))
      (unless (equal head (car list))
        (push head tmp-list)))
    (reverse tmp-list)))

(defun file-editor-sort-history ()
  (setq file-editor-save-dirs
        (remove-dupes (sort file-editor-save-dirs #'string<)))
  (setq file-editor-save-files
        (remove-dupes (sort file-editor-save-files #'string<))))

ido has code that stores history between sessions. I’ve stolen most of it to save the file editor history. (ido-pp ...) pretty prints the variable contents into the buffer, e.g. something like this.

;; ----- file-editor-save-dirs -----

( "dir1" "dir2" "dir3" )
(require 'ido)

(defun file-editor-save-history ()
  "Save file-editor history between sessions."
  (let ((buf (get-buffer-create " *file-editor data*"))
        (version-control 'never))
    (unwind-protect
        (with-current-buffer buf
          (erase-buffer)
          (file-editor-sort-history)
          (ido-pp 'file-editor-save-dirs)
          (ido-pp 'file-editor-save-files)
          (write-file file-editor-history-file nil))
      (kill-buffer buf))))

When it comes time to load the history back, (read (current-buffer)) loads it back into the variables. You can see the use of unwind-protect and condtion-case in the code below as I talked about in my emacs lisp error handling post.

(defun file-editor-load-history ()
  (let ((file (expand-file-name file-editor-history-file)) buf)
    (when (file-readable-p file)
      (let ((buf (get-buffer-create " *file-editor data*")))
        (unwind-protect
            (with-current-buffer buf
              (erase-buffer)
              (insert-file-contents file)
              (condition-case nil
                  (setq file-editor-save-dirs (read (current-buffer))
                        file-editor-save-files (read (current-buffer)))
                (error nil)))
          (kill-buffer buf))))))

The obvious time to save the history is when we exit emacs.

(defun file-editor-kill-emacs-hook ()
  (file-editor-save-history))

(add-hook 'kill-emacs-hook 'file-editor-kill-emacs-hook)

Modifications To The Original Code

The way we choose which files and directories will be remembered is each time a file is opened, the parent directory and the file including full path are added to the appropriate variable.

(defun file-editor-open-file-editor-file (button)
  (let ((parent (button-get button 'parent))
        (file (button-get button 'file))
        (file-complete (concat parent "/" file)))
    (push parent file-editor-save-dirs)        
    (push file-complete file-editor-save-files)
    (find-file file-complete)
    (file-editor-mode)
    (longlines-mode 1)))

We need to extend the file-editor-default-dirs function to display the previously stored directories and files.

(defun file-editor-default-dirs ()
  (let ((buffer (get-buffer-create "*file-editor-dir-list*")))
    (with-current-buffer buffer
      (let ((inhibit-read-only t))
        (erase-buffer)
        (file-editor-sort-history)

        (insert "*** Default File List ***\n\n")


        (dolist (dir file-editor-default-dirs)
          (file-editor-insert-opendir-button "" dir))

        (when file-editor-save-dirs
          (insert "\n")
          (dolist (dir file-editor-save-dirs)
            (file-editor-insert-opendir-button "" dir)))

        (when file-editor-save-files
          (insert "\n")
          (dolist (file file-editor-save-files)
            (insert-text-button file 'parent "" 'file file
                                :type 'open-file-editor)))))))

And for some future functionality I am thinking about we also store the current directory that is being visited.

(defun file-editor-dir-list (parent)
  (let ((buffer (get-buffer-create "*file-editor-dir-list*")))
    (with-current-buffer buffer
      (let ((inhibit-read-only t) files dirs)
        (setq parent (expand-file-name parent))
        (setq file-editor-current-dir parent)
        (erase-buffer)
        ;; ...
))))

Read Full Post »

Follow

Get every new post delivered to your Inbox.