Feeds:
Posts
Comments

Archive for the ‘Emacs’ Category

Remote Killing from Emacs

Previously, I’ve talked about integrating little perl "services" into emacs that read from stdin and write to stdout.

Sometimes the processes that make up your emacs application are running on different servers, picking up various pieces of information and it might be necessary to kill them independently or in groups. By default, comint binds C-c C-c to SIGINT, but if you have a moderately complex application, you don’t want to go through each comint buffer and kill the processes individually.

Each of your perl scripts needs to output the PID and the server it is running on.

use Sys::Hostname;
my $hostname = hostname();

print "Started PID $$ on $hostname\n";

Then the emacs application code needs to use a filter to read that information into a couple of variables.

(defvar proc-pid nil)
(defvar proc-host nil)

(defconst *re-match-pid-host*
  "Started PID \\([0-9]+\\) on \\([a-z0-9]+\\)")

(defun proc-filter (output)
  (when (string-match *re-match-pid-host* output)
    (setq proc-pid (match-string 1 output))
    (setq proc-host (match-string 2 output)))
  output)

(add-hook 'comint-preoutput-filter-functions 'proc-filter)

Running commands remotely is easy with ssh. (Incidentally, how does this work in windows-land?)

(defconst *ssh* "ssh -o StrictHostKeyChecking=no")

(defun kill-proc ()
  (interactive)
  (let ((cmd (format "%s %s kill %s" *ssh* proc-host proc-pid)))
    (message cmd)
    (message (shell-command-to-string cmd))))

I’ve demonstrated the make-comint code previously.

(defun start-proc ()
  (let ((buffer (get-buffer "*stdin-proc*")))
    (when buffer (kill-buffer "*stdin-proc*")))
  (apply 'make-comint "stdin-proc" "perl" nil
         (list "./test-stdin.pl")))

And the result is as expected.

Started PID 5008 on saturn

Process stdin-proc terminated

Read Full Post »

Living Without Emacs

I have integrated emacs into most parts of my daily workflow. In a typical day I might need to:

Some of these I have aliases for in my shell already.

A number of database GUIs have template functionality built-in or I could probably substitute the sql templates with scripts if I was sufficiently creative with the command line args.

And if I kept my TODO/Notes list in a wiki, it might be generally useful to the rest of my team.

Yes, thinking about it, I could live without Emacs. But life would be much less pleasant.

Read Full Post »

Clever Emacs Lisp

Here’s another clever trick. Don’t embed the data together with the logic, as I do with my directory aliases. Save it in a separate file and load it in using (read).

Okay, it’s not that clever – I do it all the time in Perl using YAML and what-not. Slightly concerning, though, is that I didn’t automatically translate the knowledge gained from Perl to lisp. What was I thinking?

The code fragment is beautiful:

(defvar variable-name)

(with-temp-buffer
  (insert-file-contents-literally <filename>)
  (setq variable-name (read (current-buffer))))

Read Full Post »

Finding Useful Posts

One of the weaknesses of many blogs, including my own, is the difficulty of finding old, useful articles. There are a few ways to find articles written previously, such as the Archives, the categories and the tags. But to be honest, a lot of the stuff I write is only relevant (at best) at the time of publishing. And even I have difficulty finding my useful posts again.

There are several possible solutions, e.g. I could tag pages within delicious as curiousprogrammer/useful. For the moment, I’ve decided to keep a blog highlights page, and list the posts which are useful for me. Later on I might try a more comprehensive index.

Read Full Post »

As Emacs is open source, we don’t need to speculate. We can just read the code. I’m lazy though, so I prefer to speculate ;) If you were making a text editor, what would you use for your buffer type?

Array of Chars

The obvious, naive choice is an array of chars. But that means that, while append may be fast (until you need to resize your array at least), insertion is very slow. To insert a character in the middle of an array of chars, you need to move all of the characters following the insertion point up by one character. If we assume that insertions are evenly distributed, inserting a character will take, on average, time proportional to N/2.

Queue up Inserts

We can be a bit smarter than that. With normal text editor usage, for any given insert, we will probably insert a bunch of characters in the same place. We could append those characters into a preallocated array, and after a certain amount of time or number of characters, insert those characters into the array of chars. This brings the amortized speed cost down to N/M/2 where M is the average number of characters inserted in one go.

A Tree of Characters

So much for arrays. How about a tree? Assuming the tree, is balanced, insertion will take log2 N. The downside is that traversal is a lot slower (still order N, but a decent constant factor slower). And we are also likely to lose cache locality.

Side note: I often surprised that the main ‘scripting’ languages don’t provide much more than the equivalent of perl scalars, arrays and hashes. Sure, Python has tuples and Ruby has symbols (okay, I do miss symbols in Perl), but where are the built-in trees? Even Lisp has them. I had a quick look for trees in the CPAN and found Tree::Binary and Tree::RedBlack. I couldn’t find at a quick glance whether Tree::Binary self-balances or not. Hmmm…

A Rope

The final simple option is a rope (PDF). This is a binary tree where the leaves, instead of being characters, are arrays of characters. This improves cache locality (depending on the average size of the array), and traversal speed, although it isn’t as fast as a simple array of characters.

What would you use?

Read Full Post »

When I set up an emacs process filter, I never know how much input the process will deliver me at one time. Therefore I generally buffer the input and process (say) a line at a time.

(defvar buffer "")

(defun process-input (input)
  (setq buffer (concat input buffer))
  (let ((count 0)
        (pos 0))
    (while (string-match "^\\([^\n]+\\)\n" buffer pos)
      (message "Before: [%s] [%d]" (match-string 1 buffer) (match-end 1))
      (process-line (match-string 1 buffer))
      (message "After: [%s] [%d]" (match-string 1 buffer) (match-end 1))

      ;; error handling in case of runaway loops
      (incf count)
      (when (> count 10)
        (error "Looped too many times!"))

      (setq pos (1+ (match-end 1))))
    (when pos (setq buffer (substring buffer pos))))
  (message "REMAINDER: [%s]" buffer))

Given a simple (process-line ...) we can see the results from a simple test.

(defun process-line (line)
  (message "[%s]" line))

(process-input "XXX (line 1)\n13.15 (line 2)\n")
Before: XXX (line 1) 12
[XXX (line 1)]
After: XXX (line 1) 12
Before: 13.15 (line 2) 27
[13.15 (line 2)]
After: 13.15 (line 2) 27
REMAINDER: []

An Infinite Loop

Now say I want to add some special handling for lines with a number in, what do you think happens?

(defun process-line (line)
  (when (string-match "\\([0-9]+\.[0-9]+\\)" line)
    (message "[%s]" line)))
Debugger entered--Lisp error: (error "Looped too many times!")
Before: [XXX (line 1)] [12]
After: [XXX (line 1)] [12]
Before: [13.15 (line 2)] [27]
[13.15 (line 2)]
After: [XXX (] [5]
Before: [13.15 (line 2)] [27]
[13.15 (line 2)]
After: [XXX (] [5]
...

The string match against the number has reset a hidden global variable used by (match-end ...) which has sent us into an infinite loop. Surprising action at a distance indeed!

Perl Regex Globals

Perl also uses global variables such as $1, $2, @- and @+ for its regular expressions, so could this cause a similar problem?

No. Fortunately, these globals are dynamically scoped so the appropriate values are maintained up the call stack.

Read Full Post »

Lisp Macros and Types

(message ...) already accepts a format string, but if it didn’t it would be easy to add that functionality with a macro.

(defmacro message* (str &rest vars)
  `(message (format ,str ,@vars)))

But it only accepts a single format string. Perhaps I would like to concatenate a few strings or variables together. But then if I only pass a string in, I want that to work correctly too. Macros can generate different code if you pass in different types. (I fix the underlying format* so I can use it in message, error, etc.)

(defmacro format* (str &rest vars)
  (if (stringp str)
      `(format ,str ,@vars)
    `(format (concat ,@(mapcar #'identity str)) ,@vars)))

The result looks a little strange. When I see an unquoted list, the first element has to be the name of the function that is being called.

(defvar x "xxx")
(format* (x " " "%s") 1) ;; --> "xxx 1"

Read Full Post »

Older Posts »

Follow

Get every new post delivered to your Inbox.