Posts Tagged ‘squidoo lens’

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)
  (unless style
    (setq style
          (ido-completing-read "Muse Style: "
                               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 ()
  (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 ()

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

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

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-char 1)

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

  (goto-char (point-max))
  (beginning-of-line 1)
  (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 »