Keyword Optimization - Extending Emacs for SEO

January 2, 2009 by John Jenkins · 5 Comments
Filed under: Emacs 

I read an interesting post about a tool for writing keyword optimized articles on my new favourite blog. Now, potpiegirl has a lot of great information, and every so often she talks about a handy tool and throws a few affiliate links in there. Nice marketing strategy! Anyway, the tool’s features that she mentioned do sound handy.

  • Check keyword density
  • Check spelling
  • Show word count
  • Save content as either text or HTML
  • Highlight keywords

I like to use a pretty geeky tool called emacs for almost all my content generation. The tool was created in the seventies before any such thing as keyword optimization. However, it fits me like an old glove - I feel really productive when using it. And it does allow a dedicated user to add almost any functionality they might want.

There are already third party extensions to add on the fly spellcheck and easy HTML generation which just leaves word count keyword density and highlighting.

The basic plan is to have a small frame at the bottom of the editor which constantly updates with the number of words and keyword density. Highlighting will be controlled by a key combination and disabled when any other key is pressed.

We need to explain to emacs what a word is (so it can count them) and we also need a name for the buffer at the bottom of the window.

(defconst *ac-re-word* "\\(\\b[-A-Za-z0-9_]+\\b\\)")
(defconst *ac-info-buffer-name* "*article-info*")

We will need a timer as we won’t want to update the information after every keystroke. Providing a hook for the variable means that other people will be able to add their own functionality when the timer fires.

We also need somewhere to store the keywords and a variable to indicate whether the keywords are highlighted or not.

(defvar ac-timer nil)
(defvar ac-timer-hooks nil)

(defvar ac-info-buffer nil)
(defvar ac-keywords nil)
(defvar ac-keywords-regex nil)
(defvar ac-highlighting-on 0)

We’ll want a way to add and remove keywords that are relevant to the article.

(defun ac-set-keyword-regex ()
  (let ((re (mapconcat (lambda (e) e) ac-keywords "\\|")))
    (setq ac-keywords-regex
          (if (> (length re) 0) (concat "\\(" re "\\)") ""))))

(defun ac-add-keyword (keyword)
  (interactive "sKeyword: ")
  (if (member keyword ac-keywords)
      (error (format "Error: keyword %s is already present" keyword))
    (push keyword ac-keywords)
    (ac-set-keyword-regex)))

(defun ac-remove-keyword (&optional keyword)
  (interactive)
  (when (not ac-keywords)
    (error "Error: no keywords set"))
  (unless (stringp keyword)
    (setq keyword (ido-completing-read "Keyword: "
                                       ac-keywords nil t nil)))
  (setq ac-keywords (remove keyword ac-keywords))
  (ac-set-keyword-regex))

Highlighting the keywords takes three functions - one to add the highlight, one to remove it and one to toggle it.

(defun ac-highlight-keywords ()
  (when (not ac-keywords)
    (error "Error: no keywords set"))
  (save-excursion
    (goto-char (point-min))
    (while (re-search-forward ac-keywords-regex nil t)
      (add-text-properties (match-beginning 1)
                           (match-end 1)
                           '(face highlight)))
    (setq ac-highlighting-on 2)))

(defun ac-remove-highlight ()
  (remove-text-properties (point-min) (point-max) '(face highlight))
  (setq ac-highlighting-on 0))

(defun ac-set-highlight (&optional value)
  (interactive)
  (when (not value)
    (setq value (if (> ac-highlighting-on 0) nil t)))
  (if value (ac-highlight-keywords) (ac-remove-highlight)))

Populating the information buffer is fairly simple - we clear any text that was there previously and redraw it all. We only need to report keyword density if there are actually some keywords defined.

(defun ac-update-info ()
  (save-excursion
    (let ((words (count-matches *ac-re-word* (point-min) (point-max)))
          (keywords (if (= (length ac-keywords-regex) 0) 0
                      (count-matches ac-keywords-regex
                                     (point-min)
                                     (point-max)))))
      (set-buffer ac-info-buffer)
      (erase-buffer)
      (insert (format "# Words      : %s\n" words))

      (when (and (> words 0) (> (length ac-keywords-regex) 0))
        (insert (format "# Keywords   : %d\n" keywords))
        (insert (format "Density      : %.2f%%\n"
                        (* (/ keywords (float words)) 100.0))))

      (insert (format "Keywords     : %s\n" ac-keywords)))))

ac-add-info-window takes care of adding the appropriately sized frame at the bottom of the window.

(defun ac-add-info-window ()
  (interactive)
  (delete-other-windows)
  (split-window-vertically (- (window-height) 6))
  (other-window 1)
  (setq ac-info-buffer *ac-info-buffer-name*)
  (switch-to-buffer ac-info-buffer)
  (ac-update-info)
  (other-window -1))

We set a timer to fire whenever emacs has been idle for half a second. The timer runs all the hooks in the hook variable to allow additional user customisation.

(defun ac-run-timer-hooks ()
  (run-hooks 'ac-timer-hooks))

(defun ac-add-timer ()
  (setq ac-timer (run-with-idle-timer 0.5 t 'ac-run-timer-hooks)))

This is fairly standard boiler plate for any derived mode I write for emacs.

(defvar article-mode-hook nil
  "Hook run when entering article mode.")

(defvar article-mode-map nil
  "Keymap for article major mode.")

(define-derived-mode article-mode text-mode "Article"
  "Major mode for editing articles
Special commands:
\\{article-mode-map}")

Set up the special keys which the mode uses - one for highlighting keywords and one each for adding and removing them. CTRL-C + <key> is an emacs convention for user commands (so perhaps I shouldn’t be using it - oops! Force of habit)

(if article-mode-map
    nil
  (progn
    (setq article-mode-map (make-sparse-keymap))
    (define-key article-mode-map (kbd "C-c a") 'ac-add-keyword)
    (define-key article-mode-map (kbd "C-c d") 'ac-remove-keyword)
    (define-key article-mode-map (kbd "C-c h") 'ac-set-highlight)))

Figuring out how to disable the highlight after any key was pressed took the most time. There is an emacs hook that allows you to insert a function each time a key is pressed. However, the obvious thing to do - disabling the highlight, doesn’t work as adding the highlight is a command. It would add the highlight and then the hook would run which would remove it immediately.

I worked around this by setting the highlight flag to 2 when the highlight is set. Then, each time the post command hook fires, this value is decremented. This is more for micro-efficiency so that when the flag is set to 0 we don’t have to remove the highlight on every keypress.

(defun ac-post-command-hook ()
  (cond ((>= ac-highlighting-on 2) (setq ac-highlighting-on 1))
        ((and (= ac-highlighting-on 1)
              (equal major-mode 'article-mode))
         (ac-remove-highlight))))

Enabling article mode sets a bunch of things up such as adding the information frame, enabling the on the fly spellcheck and enabling longlines mode. Longlines mode causes the words to wrap around, just as a normal word processor which emacs normally doesn’t do.

(defun ac-article-mode-entry ()
  (add-hook 'post-command-hook 'ac-post-command-hook t)
  (ac-add-info-window)
  (flyspell-mode 1)
  (longlines-mode 1))

(add-hook 'ac-timer-hooks 'ac-update-info)

(add-hook 'article-mode-hook 'ac-add-timer)
(add-hook 'article-mode-hook 'ac-article-mode-entry)

(provide 'article-mode)

And there you go - all done. The majority of the extension was finished in an hour. Getting the highlight to disappear after any keypress (which was an entirely unnecessary feature, but something I wanted) was a bit fiddly and took another hour. But now it’s complete and emacs becomes the perfect tool for writing keyword optimized articles. Yay!

Download article-mode-major.el.

Note: this has now been superseded by a minor mode version. The updated code can be downloaded here.

Comments

5 Responses to “Keyword Optimization - Extending Emacs for SEO”
  1. Kim says:

    Check the keyword density of your website using this free online tool - http://tools.khrido.com/webtoo.....ecker.aspx

  2. John Jenkins says:

    Hi Kim, interesting tool thanks. It doesn’t quite fulfill the same role as article mode as it shows the percentage of every word present in the article. I might add this as a feature to my article mode. Cheers

  3. Ted says:

    that’s a great tutorial on creating a mode in emacs. That makes it looks so easy! Great job as this is definitely useful to learning the details of emacs

  4. John Jenkins says:

    Hi Ted, thanks for the comment and I’m glad you found it useful.

Trackbacks

Check out what others are saying about this post...
  1. fsdaily.com says:

    Story added…

    This story has been submitted to fsdaily.com! If you think this story should be read by the free software community, come vote it up and discuss it here:

    http://www.fsdaily.com/Busines....._for_SEO...



Speak Your Mind

Tell us what you're thinking...
and oh, if you want a pic to show with your comment, go get a gravatar!