Feeds:
Posts
Comments

Posts Tagged ‘emacs comint’

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 »

Recently I have been adding more and more functionality into emacs using comint. And as there are more and better perl libraries for hooking into other systems (such as DBI, SOAP, HTTP…), I often start with a script that reads commands from stdin and writes the result to stdout. That is the comint way.

use strict;
use warnings;

use 5.010;

init(); # -- some initialization that takes a while

# ...

while (defined(my $command = <STDIN>)) {
    chomp $command;

    given (lc($command)) {
        when (/^\s*exit\s*$/)    { exit 0; }
        when (/^\s*send\s*$/)    { say 'send' }
        when (/^\s*receive\s*$/) { say 'receive' }
        default { say "Error: unrecognized command $command"; }
    }
}

If initialisation takes a long time, it is a big saving to only do it once for many commands.

As emacs has full control over the process, I can control what gets sent and filter what is received before display. However, I prefer to be a little bit flexible with what the perl process receives for when I am testing it from the command line. That is why I allow surrounding spaces.

when (/^\s*exit\s*$/)    { ... }
when (/^\s*send\s*$/)    { ... }
when (/^\s*receive\s*$/) { ... }

Those matching regexes look pretty similar. I almost feel like I’m violating DRY.

Unfortunately, the subroutine references only take a single parameter.

sub cexit { return $_[0] =~ /^\s*exit\s*$/ }
sub csend { return $_[0] =~ /^\s*send\s*$/ }
sub creceive { return $_[0] =~ /^\s*receive\s*$/ }

# ...

when (\&cexit)    { exit 0; }
when (\&csend)    { say 'send' }
when (\&creceive) { say 'receive' }

But there is a way of storing data along with a subroutine – a closure. However, the following strangely doesn’t work.

sub match_command
{
    my $command = shift;
    return sub {
        my $input = shift;
        say "Input is [$input]";
        return $input =~ /^\s*$command\s*$/;
    }
}

while (defined(my $command = <STDIN>)) {
    chomp $command;

    given (lc($command)) {
        when (match_command('send')) { say 'send' }
        when (match_command('exit')) { exit 0; }
        when (match_command('receive')) { say 'receive' }
        default { say "Error: unrecognized command $command"; }
    }
}

Whereas storing the closures in a variable before smart matching does work. Unfortunately it looks like smart matching isn’t smart enough for my needs.

my $send = match_command('send');
my $exit = match_command('exit');
my $receive = match_command('receive');

# ...

when ($send) { say 'send' }
when ($exit) { exit 0; }
when ($receive) { say 'receive' }

And the result:

c:\home\juang>perl t.pl
send
Input is [send]
send
exit
Input is [exit]

Read Full Post »

I’ve finally published the next installment of my mini-series on Emacs Comint. I’ve decided to publish it as a Squidoo lens. In it, I demonstrate how to create a slick, emacs-based interface to a stock price publisher. Part 1 in the series can be found here.

The first version extracts the ticker and price and displays them nicely in a seperate buffer. The later version highlights the updated line by flashing a magenta background for half a second after any update.

The lens covers the following emacs features: comint, process filters, overlays and timers. Let me know any comments or suggestions you have either here or on the lens itself. Please visit my emacs comint lens.

Read Full Post »

Emacs Comint

I use both vim and emacs regularly1. For me, the most important difference between the two isn’t the modal/modeless thing. Nor is it even that emacs encourages working on multiple buffers within a single instance whereas vim users generally fire up a new instance for each file2.

No, what emacs has that vim does not is its superb handling of asynchronous processes.

The magic of comint

Comint is an emacs lisp library designed to simplify interaction with external interpreters. Here is an example of controlling a DOS prompt.

(require 'comint)

(progn
  (apply 'make-comint "cmd" "cmd" nil '())
  (delete-other-windows)
  (switch-to-buffer-other-window "*cmd*")
  (other-window -1))

(comint-send-string (get-buffer-process "*cmd*") "dir\n")

And lo and behold, scroll up and scroll down work properly!

Sure, for a trivial example like this you would probably use dired, eshell or even my shell wrappers. However, in a similar way to expect, comint makes it easy to interact with anything that provides a stdin/stdout interface. The potential applications are limitless.

In my next comint post, I’ll show you how to interact with a simple subscriber.


1. Is that the sound of 90% of my readers leaving?

2. Yes, I’m aware that vim can work on multiple buffers. In my experience most vimmers (myself included) don’t work like that.

Read Full Post »

Follow

Get every new post delivered to your Inbox.