Feeds:
Posts
Comments

Archive for March, 2009

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 »

This post started as a quick note (mainly to myself) about a handy sub-feature within emacs find. Since reading through the documentation, I need to mention a few other sub-features too.

As I’ve probably mentioned before, I use both vim and emacs. One great feature of vim is that if you press # or * it searches for the word that your cursor is over. # or * choose a backwards or forwards search respectively.

There is a similar convenience when using emacs search in that if you enter search using C-s then C-w chooses the word you are looking at. I always forget about this feature and my google-fu is never good enough to find it again. Fortunately I recently came across it again when I was looking at the documentation for C-s (isearch-forward) using C-h k C-s.

It turns out that isearch is implemented by a minor mode (logically enough when you think about it) that adds a whole bunch of useful keys, including C-w we mentioned earlier. Other notable features are:

  • C-M-w delete last character in search string
  • M-r toggle regular-expression mode
  • M-e edit search string

And to replace search strings there are:

  • M-% query-replace and
  • C-M-% query-replace-regexp

Read Full Post »

Does anyone else have this problem? You’ve made some changes to one of your start-up emacs files. Then you fire up a new instance of emacs to check if your new feature works. However, none of your changes seem to have made a difference. So you spend some time looking into what you did wrong. And finally you realise – Emacs loads compiled emacs-lisp code in preference to uncompiled code, even when the uncompiled code is newer.

True story – this has happened to me more than a couple of times. The fifth second time I decided to fix it. I’ve come up with a solution that I’m fairly happy with: Emacs is started from a script called run_emacs.sh that runs another script called compile.sh (I’ve done it this way so I can run compile.sh independently).

#!/bin/sh

$HOME/emacs-files/compile.sh
emacs $*

compile.sh calls batch-byte-compile-if-not-done.

#!/bin/sh

cd $HOME/emacs-files

emacs --batch \
      --eval "(add-to-list 'load-path (expand-file-name \"~/emacs-files\"))" \
      --eval "(add-to-list 'load-path (expand-file-name \"~/emacs-files/org-xxx\"))" \
      --eval "(batch-byte-compile-if-not-done)" *.el

And last but not least I alias emacs to run_emacs.sh.

$ alias emacs='$HOME/emacs-files/run_emacs.sh'

Read Full Post »

Bill has some useful tips on how to get back to where you were with exchange-point-and-mark (C-x C-x) and pop-global-mark (C-x C-SPC).

Nicolas Lara wrote about converting fontlocked emacs buffers to html using htmlize. As htmlize uses css classes rather than inline styles it uses a snippet from Ruslan’s blog. htmlize-region-for-paste can also convert the css classes to inline styles although it isn’t an interactive function. Doesn’t everyone write their blog posts using muse (with htmlize) which already does this and more?

Amongst other great posts (!), I should also point out my own posts on solving mathematical problems using calc mode and on mapping aliases to dired directories.

emacs-fu wrote a post on querying emacs itself to find out how it does stuff. Steve Yegge mentioned the help functions in his effective emacs post – Item 8: Learn the most important help functions.

Ryan McGuire wrote about using autocomplete.el with python. dabbrev-expand (M-/) is built into emacs and provides dumb but very useful autocompletion. However, autocomplete.el looks a lot prettier.

Joseph Miklojcik wrote a macro to define functions which switch to certain buffers and back again.

Read Full Post »

« Newer Posts

Follow

Get every new post delivered to your Inbox.