Feeds:
Posts
Comments

Archive for March, 2009

Gaz asks:

Is there an easy way to select (via buffer-name regex or similar) a subset of terminals in elisp, and then squirt the same command into each one?

Sure Gaz, that sounds fairly straightforward. The shell wrappers already stores the terminal buffers in sw-buffers. We just need to think of how we want to select the buffers. And it probably doesn’t come as any surprise that I’m going to use ido.

Ido provides regex matching by pressing C-t or defining ido-enable-regexp. It also has flex matching with ido-enable-flex-matching and you can continuously refine selections using C-SPC. It really is amazingly cool.

Surprisingly, I couldn’t find a straightforward way of accessing the interim matches. Nor is there a hook that executes at selection time. Instead, we will override RETURN. Dynamic scoping means we can define an effectively global variable, sw-buffer-matches to store the interim matches for later use. Then, we iterate over these matches and send the command using term-simple-send.

We only want RETURN to be overridden for this particular usage of ido, so we set the keymap on the ido-setup-hook.

(defun sw-ido-save-matches ()
  (interactive)
  (setq sw-buffer-matches ido-matches)
  (exit-minibuffer))

(defun sw-ido-my-keys ()
  (define-key ido-completion-map (kbd "RET") 'sw-ido-save-matches))

(defun sw-multi-cmd (&optional cmd)
  (interactive "sCommand: ")
  (let ((ido-setup-hook 'sw-ido-my-keys)
        (sw-buffer-matches ""))
    (ido-completing-read "choose buffers: " sw-buffers)
    (dolist (buffer sw-buffer-matches)
      (term-simple-send (sw-shell-get-process buffer) cmd))))

Read Full Post »

Nik Conwell posted about the Firefox extension mozex which reminds me I need to set that up. I imagine that this will combine nicely with the emacs client trick mentioned in windows and daemons.

I’ve started a mini-series on Emacs comint as I feel it deserves a lot more love than it gets, considering how incredibly useful it is.

Last year, emacs-fu wrote about extending ansi-term. My own recent post on wrapping ansi-term goes even further, configuring the mouse to auto-copy on highlight and using ido to quickly and easily switch between multiple shell buffers.

Still on the ansi-term topic, sadly it doesn’t work for me in windows. And by default, M-x shell starts a DOS prompt. martinez has a fix for this in NTEmacs although the following works a little better for me in EmacsW32 1.58.

(setq explicit-bash-args '("--login" "-i"))
(setq explicit-shell-file-name "c:/packages/cygwin/bin/bash")
(setenv "SHELL" explicit-shell-file-name)

(add-hook 'comint-output-filter-functions 'comint-strip-ctrl-m)

I can’t think of a time I would like to keep trailing whitespace. However, if you need it, flameyes has a solution to selectively remove it or keep it under different directories subtrees. This nicely demonstrates directory local variables (which have been mentioned by previous commenters on this blog).

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 »

Emacs Buttons

Have you ever used emacs buttons? If you’ve used M-x grep then you have used similar functionality. When you click on one of the links it opens the file and goes to the appropriate line. If your mouse is hovering over the link then it highlights it.

How can you make your own button? First of all, you make a function that is called when you click the button.

(defun myfun (button)
  (message (format "Button [%s]" (button-label button))))

Then you make a button template. Buttons are actually made with an overlay that overrides the current keymap. By default, buttons run their action on button 2. I prefer left click so I set follow-link to true. You can make a hierarchy of button types using :supertype.

(define-button-type 'my-button
  'action 'myfun
  'follow-link t
  'help-echo "Click button")

Then insert the button using your template. These automatically highlight when your mouse is hovering over it.

(insert-text-button "xyz" :type 'my-button)

Read Full Post »

I spend a lot of time in the shell and I’ve never found a terminal emulator I really like. Konsole is okay, but the cut and paste is rubbish. PuTTY isn’t too bad either, but it doesn’t have tabs. Wouldn’t it be nice if there was something like PuTTY that gave me something similar to buffers in emacs?

You might be thinking that screen + PuTTY would work but that replaces some of the shortcuts I use all the time (C-a for example). Does eshell give me what I want? What about ansi-term?

Actually, ansi-term is pretty close to what I want. It also reconfigures some of the keys1 but I can easily fix that. So let me make a shortlist of the features I want:

  • fix the key bindings
  • get the mouse cut and paste working properly
  • Have a frame just for the shell
  • Have some kind of alias mapping to shells
  • Have multiple shells in different buffers

Let’s get started.


The #includes.

(require 'cl)
(require 'term)

Connecting to a remote host and a reasonable shell-like font.

(defconst sw-ssh "ssh -q -o StrictHostKeyChecking=no")
(defconst sw-shell-font "-*-courier new-*-r-*-*-18-*-*-*-*-*-iso8859-1")

Somewhere to store the frame for the shell and the buffers.

(defvar sw-frame nil)
(defvar sw-buffers nil)

Configuration variables: does the shell open in a name frame, and if the shell has exited for some reason do we keep it in the list?

(defvar sw-open-in-new-frame nil)
(defvar sw-remove-dead-terms t)

I like to build my software in layers. The lowest layer is the most primitive and provides functions to get processes if they exist and to kill buffers that don’t have a process.

(defun sw-shell-get-process (buffer-name)
  (let ((buffer (get-buffer (concat "*" buffer-name "*"))))
    (and (buffer-live-p buffer) (get-buffer-process buffer))))

(defun sw-get-process-if-live (buffer-name)
  (let ((proc (sw-shell-get-process buffer-name)))
    (and (processp proc)
         (equal (process-status proc) 'run)
         proc)))

(defun sw-kill-buffer-if-no-process (buffer-name)
  (let* ((buffer (get-buffer (concat "*" buffer-name "*")))
         (proc (sw-get-process-if-live buffer-name)))
    (when (and (not proc) (buffer-live-p buffer)) (kill-buffer buffer))))

(defalias 'sw-shell-exists-p 'sw-get-process-if-live)

The next layer provides two functions for wrapping basic buffer selection/creation. The plan is to ensure that there is a shell running in the buffer when the function finishes, but we don’t kill it if it exists already. Then there is another function that allows us to create or select a shell and send commands to it.

(defun sw-basic-shell (buffer-name)
  (sw-kill-buffer-if-no-process buffer-name)
  ;; If there is a process running, leave it, otherwise
  ;; create the new buffer
  (if (sw-shell-exists-p buffer-name)
      (message "Buffer already exists")
    (ansi-term "bash" buffer-name))
  (switch-to-buffer (concat "*" buffer-name "*")))

(defun sw-shell/commands (buffer-name &rest commands)
  (sw-basic-shell buffer-name)
  (let ((proc (sw-shell-get-process buffer-name)))
    (dolist (cmd commands)
      (term-simple-send proc cmd))))

This layer has methods for creating my standard shell locally (sw-standard-shell) and remotely (sw-server-login). I generally need to exec bash -l and su - after I’ve logged in. I could have done this in the .bashrc of course. You can adapt these start-up commands for your own usage.

(defun sw-standard-shell (buffer-name)
  (if (sw-shell-exists-p buffer-name)
      (switch-to-buffer (concat "*" buffer-name "*"))
    (sw-shell/commands buffer-name
                       "exec bash -l"
                       "su -")))

(defun sw-server-login (host &optional buffer-name)
  (setq buffer-name (or buffer-name host))
  (if (sw-shell-exists-p buffer-name)
      (switch-to-buffer (concat "*" buffer-name "*"))
    (sw-shell/commands buffer-name
                       (concat sw-ssh " " host)
                       "exec bash -l"
                       "su -")))

Set up my preferred colors and fonts.

(defun sw-set-display ()
  (interactive)
  (set-background-color "black")
  (set-foreground-color "orange")
  (set-frame-font sw-shell-font))

I need to override a few keys. This should all be fairly obvious.

(defun sw-set-keymap ()
  (term-set-escape-char ?\C-z)
  (define-key term-raw-map "\C-c" 'term-interrupt-subjob)
  (define-key term-raw-map "\C-y" 'yank)
  (define-key term-raw-map (kbd "\M-x") 'execute-extended-command)
  (define-key term-raw-map "\e" 'term-send-raw)
  (define-key term-raw-map (kbd "") 'scroll-down)
  (define-key term-raw-map (kbd "") 'scroll-up))

(sw-set-keymap)

I like highlighted regions to be automatically copied and to paste with the right-hand mouse button. This more-or-less works although it would need some tweaking to get it to send exactly the command you see on the screen.

;; Functions to get the mouse working more-or-less as I like it

(defun sw-mouse-paste-clipboard (click arg)
  (interactive "e\nP")
  (let ((proc (get-buffer-process (current-buffer))))
    (term-send-string proc (current-kill 0)))
  (setq deactivate-mark t))

(defun sw-mouse-copy-region-to-clipboard (click)
  (interactive "e")
  (mouse-set-region click)
  (let ((transient-mark-mode nil))
    (copy-region-as-kill (region-beginning) (region-end))))

(define-key term-raw-map
  [drag-mouse-1]
  'sw-mouse-copy-region-to-clipboard)

(define-key term-raw-map [mouse-3] 'sw-mouse-paste-clipboard)

Functions to read a buffer name from the user and select the shell-specific frame if we’ve set sw-open-in-new-frame.

(defun sw-read-buffer-name ()
  (when sw-remove-dead-terms
    ;; Remove dead terms before offering them to the user
    (setq sw-buffers (delete-if-not 'sw-shell-exists-p sw-buffers)))
  (let ((buffer-name
         (ido-completing-read
          "Choose buffer name: "
          sw-buffers)))

    (if (stringp buffer-name)
        buffer-name
      (error "Invalid buffer name"))))

(defun sw-get-buffer-proc ()
  (sw-get-process-if-live (sw-read-buffer-name)))

(defun sw-select-frame ()
  (if (not sw-open-in-new-frame)
      (sw-set-display)
    (unless (frame-live-p sw-frame)
      (setq sw-frame (make-frame))
      (select-frame-set-input-focus sw-frame)
      (sw-set-display))
    (select-frame-set-input-focus sw-frame)))

This actually selects the buffer, reading the buffer-name from the user if necessary.

(defun sw-choose-buffer (&optional buffer-name)
  (sw-select-frame)

  (unless (stringp buffer-name)
    (setq buffer-name (sw-read-buffer-name)))

  (unless (sw-shell-exists-p buffer-name)
    (sw-kill-buffer-if-no-process buffer-name)
    (setq sw-buffers (delete buffer-name sw-buffers)))

  (let ((already-existed t))
    (if (member buffer-name sw-buffers)
        (switch-to-buffer (concat "*" buffer-name "*"))
      (setq already-existed nil)
      (push buffer-name sw-buffers))

    (list buffer-name already-existed)))

And these are the top level commands – sw-open-shell and sw-open-remote-shell. They build on sw-standard-shell and sw-server-login respectively but read a buffer name and remote server name as appropriate.

(defun sw-open-shell (&optional buffer-name)
  (interactive)
  (multiple-value-bind (buffer-name already-existed)
      (sw-choose-buffer buffer-name)
    (unless already-existed
      (sw-standard-shell buffer-name))))

(defun sw-open-remote-shell (&optional buffer-name server-name)
  (interactive)
  (multiple-value-bind (buffer-name already-existed)
      (sw-choose-buffer buffer-name)
    (unless already-existed
      (unless (stringp server-name)
        (setq server-name (read-string "Server: " buffer-name)))
      (sw-server-login server-name buffer-name))))

(provide 'shell-wrappers)

And there we go. sw-shell/commands is generally useful for firing up shells and executing arbitrary commands. I use this all over the place.

Let me know if you have any tips or suggestions for improvement.

If you liked this post, why not subscribe to my RSS feed.


1.For example changing C-c to C-c C-c is very bad. One day I might need to kill that runaway rm -rf / command very urgently and maybe I’ll have forgotten that C-c is broken.

Read Full Post »

It has been two weeks since the last time I did a round-up of emacs news. That previous post is still 20th on emacs reddit. Is there really so little emacs-related news of note?

Mr Michael Will demonstrates how to fill a paragraph in SGML-mode where presumably there aren’t any convenient line breaks to prevent it from filling the entire buffer. I’d also mention C-x ( ... C-x ) to record a macro if it is something you’re doing a lot, or even add a binding to fill-region-as-paragraph.

beatak has a couple of useful beginner tips on his blog. I’ve added his code for versioned auto-save to my own start-up and he also has some notes an generating a tagfile generating a tagfile.

I have written far too much about emacs muse in the past couple of weeks, and part 1 and part 4 may be useful for beginners to muse.

I presented a way of compiling your start-up code when you run emacs to avoid the problem of emacs preferring compiled code over newer uncompiled code.

Marc Abramowitz wrote about remote compilation back in 2006 but he needs to fix his link to his rcompile patch. (it currently goes to marc-abramowitz.info).

There are posts on searching multiple buffers, emacs version control and emacs buttons within the same blog.

I’ve found another guy compiling emacs news.

Read Full Post »

Deriving Muse Styles

Following some useful comments from Peter Jones, I now have a better way to publish content for this blog – I can derive a style from html and selectively override the strings I want.

The definition of regex-replace is here.

The Constants

(defconst cur-src-prefix
  (concat "<pre style=\"font-size: 130%; border: 1px solid #bbb; "
          "background: #eee; overflow: auto; "
          "margin: 15px 5px; padding: 5px;\">"))

(defconst cur-code-prefix
  (concat "<code style=\"font-size:130%; "
          "background: #eee; padding: 3px;\">"))

(defconst curious-strings
  (list (cons 'begin-literal cur-code-prefix)
        (cons 'end-literal "</code>")))

Deriving The Style

(defun curious-htmlize ()
  (regex-replace "<pre class=\"src\">" cur-src-prefix))

(muse-derive-style "html-curious" "html"
                   :strings 'curious-strings
                   :after 'curious-htmlize)

Much nicer.

Although as far as I am aware, to get nicely font-locked code for Squidoo lenses I still need to add the extra hook as described in my earlier post. Any correction from muse users would be appreciated.

Read Full Post »

Customizing Emacs Muse – Part 3

This is part 3 in the customizing emacs muse series.

In part 1 (Creating Squidoo Lenses With Emacs Muse) we talked about adding hooks to transform the generated HTML to cope with the impoverished tag set that Squidoo provides.

In part 2 (Updating Elements Within Association Lists) we talked about adding a function that can replace values in association lists to manipulate muse-html-markup-strings.

In this post, we will talk about how to select between different output styles if you’re writing for, say, a wordpress.com blog, an article directory and squidoo.

Note: muse already allows for different styles, but I’m not aware of a sub-style mechanism which is what I am really describing here.


Let’s get started. First of all, it is handy to have a mechanism for getting back to the default state. I provide a variable to store a function that will undo the changes that made the current sub-style. Returning to the default is just a matter of calling this function if it is defined.

(defvar muse-style-remove-fn nil)

(defun muse-style-default ()
  (when muse-style-remove-fn (funcall muse-style-remove-fn))
  (setq muse-style-remove-fn nil))

muse-style-select enables me to choose between the various styles using ido. You may recognise this selection mechanism from my directory aliases post.

(defconst muse-style-mapping
  (list (cons "squidoo" 'muse-style-squidoo-enable)
        (cons "curious" 'muse-style-curious-enable)
        (cons "default" 'muse-style-default)))

(defconst muse-style-aliases
  (mapcar (lambda (e) (car e)) muse-style-mapping))

(defun muse-style-select (&optional style)
  (interactive)
  (unless style
    (setq style
          (ido-completing-read "Muse Style: "
                               muse-style-aliases
                               nil t)))
  (if (and (stringp style) (> (length style) 0 ))
      (let ((pair (assoc style muse-style-mapping)))
        (if pair
            (funcall (cdr pair))
          (error "Invalid style %s" style)))
    (error "Invalid style %s" style)))

I have a helper function to replace the keys within muse-html-markup-strings.

(defun muse-strings-replace (mapping)
  (setq muse-html-markup-strings
        (assoc-replace muse-html-markup-strings mapping)))

The disable function resets begin-literal and end-literal to the original state. Possibly I should have copied the entire original associative list instead. It also removes the hooks which have been added.

(defun muse-style-squidoo-disable ()
  (interactive)
  (muse-strings-replace (list (kv 'begin-literal "<code>")
                              (kv 'end-literal "</code>")))
  (remove-hook 'muse-html-after-htmlize-hook 'squidoo-htmlize)
  (remove-hook 'muse-after-publish-hook 'squidoo-fix-para))

The enable function returns us to the default style first, just in case there is already a sub-style selected. It then overwrites begin-literal and end-literal and adds the hooks.

(defun muse-style-squidoo-enable ()
  (interactive)
  (muse-style-default)

  (when muse-style-remove-fn (funcall muse-style-remove-fn))
  (setq muse-style-remove-fn 'muse-style-squidoo-disable)

  (muse-strings-replace (list (kv 'begin-literal sq-code-prefix)
                              (kv 'end-literal "</b>")))
  (add-hook 'muse-html-after-htmlize-hook 'squidoo-htmlize)
  (add-hook 'muse-after-publish-hook 'squidoo-fix-para))

Read Full Post »

Customizing Emacs Muse – Part 2

Here are part 1 (Creating Squidoo Lenses With Emacs Muse) and part 3 (Emacs Muse Aliases).

We all know what an association list is right? If you want to map keys to values, you’re going to use an assocation list.

(defvar v '((GB "Great Britain")
            (UK "United Kingdom")
            (FR "France")))

(assoc 'GB v)

will return the pair (GB "Great Britain").

They are used in a similar way to a hash in various scripting languages. For example, muse-html uses one, muse-html-markup-strings to determine which text to use for each type of markup.

You might remember that previously I advocated the use of regex transforms to correct the html after generation is complete. However, that can cause problems. We therefore need to fix the association list to have the correct mapping.

Assocation lists have a bunch of functions which operate on them, e.g. assoc and rassoc for looking things up and assq-delete-all for removing elements. However, I couldn’t find one which changes elements.

(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))

This will allow me to replace both key and value within the association list at the same time. Normally, I’ll just need to replace the value so I made a helper which saves me from having to repeat the key.

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

And now I can fix the assocation list

(defconst sq-code (concat "<b style=\"font-weight: normal; "
                          "font-size: 115%; background: #eee "
                          "padding: 3px;\">"))

(setq muse-html-markup-strings
      (assoc-replace muse-html-markup-strings
                     (list (kv 'begin-literal sq-code)
                           (kv 'end-literal "</b>"))))

Alternatively, I could delete the existing elements and add the new ones on the start of the list, maybe with something like the following

(cons
 (cons 'begin-literal sq-code)
 (cons '(end-literal "</b>")
       (assq-delete-all 'begin-literal
                        (assq-delete-all 'end-literal
                                         muse-html-markup-strings))))

But I think my way is neater.

Read Full Post »

Customizing Emacs Muse – Part 1

Part 2 (Updating Elements Within Association Lists) and part 3 (Emacs Muse Aliases).

Emacs has some extremely nice extension mechanisms. The most important of these are emacs hooks, but there is also advice and you can even redefine any functions that aren’t behaving exactly as you want.

There comes a time in every emacs user’s life where they are using a great extension, but the hook they want to use isn’t there [where it should be]. The Emacs Muse HTML publisher has a function called muse-html-src-tag that adds syntax highlighting to source code. The hook that I need was missing from the function.

The end of that function looks like this, only now I have added muse-html-after-htmlize-hook. The reason the hook needs to be there, is that after the function returns, the narrowing has gone and we don’t know which area to operate on any more.

(defvar muse-html-after-htmlize-hook nil)

(save-restriction
  (narrow-to-region (point) (point))
  (insert htmltext)
  (goto-char (point-min))
  (re-search-forward "<pre\\([^>]*\\)>\n?" nil t)
  (replace-match "<pre>")
  (goto-char (point-max))
  (run-hooks 'muse-html-after-htmlize-hook)
  (muse-publish-mark-read-only (point-min) (point-max))))))

Okay, so back to Squidoo – why do I need to generate custom HTML? Well, pretty simple – they’ve disabled a bunch of the tags. <pre>, <div> and <span> have gone. Well, <div> and <span> I can pretty much replace with <p> and <b>, kinda, but I’m pretty sad to lose <pre>. More on that shortly.

First, some helper functions. I need to run multiple regex replacements on the region and also it will be useful to have a function that repeats strings. I couldn’t find a built-in function quickly and it took 20 seconds to write (the possibly inefficient) string-repeat.

(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))

The constants: sq-code-prefix is the prefix for all source code regions; regex-multi-space matches multiple spaces at the beginning of a line.

(defconst sq-code-prefix
  (concat "<p style=\"font-family: monospace; font-size: 115%; "
          "border: 1px solid #bbb; "
          "background: #eee; overflow: auto; "
          "margin: 0; padding: 5px;\">"))

(defconst cr (string ?\n))
(defconst regex-multi-space (concat cr "  +"))

The hook function for source code regions needs to do a couple of things – it needs to replace the span and pre tags. It also needs to put non-breaking-spaces at the start of any lines with spaces as that is how a pre would behave. And finally, to reduce the amount of surrounding space it needs to remove the extra lines before and after the region.

(defun squidoo-htmlize ()
  (regex-replace "<span style=\"" "<b style=\"font-weight: normal; ")
  (regex-replace "<pre>" sq-code-prefix)
  (regex-replace "</span>" "</b>")
  (regex-replace "</pre>"  "</p>")

  (goto-char (point-min))
  (while (re-search-forward regex-multi-space nil t)
    (let ((str (buffer-substring-no-properties (match-beginning 0)
                                               (match-end 0))))
      (replace-match (concat cr (string-repeat " "
                                               (1- (length str)))))))

  (goto-char (point-min))
  (beginning-of-line 2)
  (delete-indentation)
  (delete-char 1)

  (goto-char (point-min))
  (insert "XXDELETE")

  (goto-char (point-max))
  (beginning-of-line 1)
  (delete-indentation)
  (delete-char 1))

We also need another hook to clean up duplicate closing tags and style the <code> tag. Actually, I could just redefine one of the variables to fix the code tag, but where would the fun in that be?

(defun squidoo-fix-para ()
  (regex-replace "<p[^>]*>XXDELETE" "")
  (regex-replace "<code>"  "<b style=\"font-weight: normal; font-size:108%; background: #eee; padding: 3px;\">)
  (regex-replace "</code>" "</b>")
  (regex-replace "</p></p>" "</p>"))

Finally, add the two functions to the hooks.

(add-hook 'muse-html-after-htmlize-hook 'squidoo-htmlize)
(add-hook 'muse-after-publish-hook 'squidoo-fix-para)

Whew! After all that the result is: my squidoo lens on how to create a new emacs command. And why do I want to make squidoo lenses? That will have to wait for another post.

Read Full Post »

Older Posts »

Follow

Get every new post delivered to your Inbox.