[update] linkd.el 1.31 (alpha release 2)
  Home FAQ Contact Sign in
gnu.emacs.sources only
 
Advanced search
POPULAR GROUPS

more...

 Up
[update] linkd.el 1.31 (alpha release 2)         

Group: gnu.emacs.sources · Group Profile
Author: David O'Toole
Date: Jan 15, 2007 23:46

I am pleased to announce the updated linkd.el. This is a second
alpha release. I have attached the source code and a preliminary
version of the manual in plain-text format.

My thanks to everyone who has submitted bug reports, feature requests,
and feedback.

the linkd homepage is here: http://dto.freeshell.org/notebook/Linkd.html
the icons are here: http://dto.freeshell.org/packages/linkd-icons.tar.gz

Enjoy!

This is a plain text version of the linkd manual for version 1.31 of
linkd.

linkd-mode is a hypertext system for organizing and interlinking all
resources available to GNU Emacs. It can be used to make a "personal
web" out of all your text files---regardless of file format or major
mode.

linkd-mode is not another publishing mode or personal information
management system. Instead it is designed to be used alongside the
modes of your choice. Download links, instructions, and more
screenshots are below.

Features

The user of linkd-mode may:

- Delimit and name regions of text called "blocks" in any type of
text file. (The delimiters are the yellow-on-red stars in the
above screenshot.)
- Insert links to blocks, files, pictures, pdfs, manual pages, web
pages, and more.
- Send blocks to other programs (i.e. bash) for processing
- Identify concepts with "tags" and navigate to other instances of
the concept
- Optionally use icons so that links can be distinguished from each
other by color and shape

Quite soon, linkd-mode will:

- Search and index blocks.
- Analyze and summarize links between blocks.
- Manufacture links related to the current context (a la
remember-annotations.)
- Unify the linking schemes used by different emacs packages (for
example, eev, planner, howm, org)

Acknowledgements

In designing linkd-mode I have drawn on EevMode and HowmMode, among
other things. Special thanks to Eduardo Ochs for writing eev and for
many years of fruitful discussions, to Richard Stallman for feedback
and suggestions regarding golisp-mode, and to Michael Olson for coming
up with the name and contributing ideas.

the icons are here: http://dto.freeshell.org/packages/linkd-icons.tar.gz

Enjoy!

-------------------------

Installation and configuration

NOTE: Linkd requires Emacs 22.

Download linkd.el and put it in your load-path. Then add

(require 'linkd)

to your emacs initialization file, and execute the sexp with C-x C-e.

If you want cool graphical icons, download the icon collection and
unpack it in some suitable folder. Then add the following to your
emacs initialization file:

(setq linkd-use-icons t)
(setq linkd-icons-path "~/.linkd-icons") ;; or wherever you put it

You can set up linkd-mode to turn on automatically whenever you enter
certain major modes. Here is an example that causes linkd-mode to
activate whenever you open an Emacs Lisp, Common Lisp, shell-script,
or text-mode file:

(add-hook 'emacs-lisp-mode-hook 'linkd-mode)
(add-hook 'lisp-mode-hook 'linkd-mode)
(add-hook 'sh-mode-hook 'linkd-mode)
(add-hook 'text-mode-hook 'linkd-mode)

Linkd-mode's keybindings follow the standard conventions for minor
modes: C-c followed by one of a set of reserved punctuation
characters. For speed, I rebind some of them as follows:

(global-set-key [(control \&)] 'linkd-follow-on-this-line)
(global-set-key [(control f3)] 'linkd-process-block)
(global-set-key (kbd "M-[") 'linkd-previous-link)
(global-set-key (kbd "M-]") 'linkd-next-link)
(global-set-key (kbd "M-RET") 'linkd-follow-at-point)

Usage

To use the features of linkd, you must turn on linkd-mode by issuing
the command M-x linkd-mode RET. This will fontify the links and
establish key bindings for linkd-mode's commands.

Basics of links

Links look like this:

(@file :path "/etc/fstab")

or sometimes just

(@> "font locking")

They are typically embedded in the comments of source code files or
configuration files.

Here are a few examples:

(@file :path "/etc/fstab")
(@file :path "~/e/dtox.e" :to "ardour2")
(@file :path "~/e/rlx.el")
(@file :path "~/org/RogueLike.org" :to "* Tasks")
(@file :path "~/e/cl-frame.lisp")
(@man :page "fvwm" :to "ButtonStyle")
(@> "font locking")
(@man :page "conky")
(@file :path "~/org/KarmaPod.org")
(@info :file "elisp" :node "Display Property")

Try typing a few into a scratch buffer where linkd-mode is turned
on. The moment you type the closing parenthesis, the link should
fontify (and display an icon, if you have icons turned on.)

To follow a link, place point on the link and press C-c '. This runs
the command (linkd-follow-at-point). If there is only one link on that
line, you can just place point anywhere in the line and press C-c
{period} to run the command (linkd-follow-on-this-line).

To edit a link, put point at the end of the link, and press backspace
to delete the closing parenthesis---the link will turn back into plain
text.

Of course, you don't actually have to insert and edit links
manually. It's cumbersome and in most cases unnecessary. See the
section "Creating and editing links" below.

Blocks and stars

Certain special links delimit regions of a file. These regions are
called blocks and the delimiters are called stars. A block looks like
this:

(@* "block name")
...
... content of block
...
(@*)

The key C-c {asterisk} executes the command (linkd-process-block),
which triggers an action on the block containing point. The action
taken is controlled by the buffer-local variable
linkd-process-block-function, whose value is the function that
processes the block. An example of this is below in the section
"Example Usages."

Tags

Tags are special links that may appear multiple times in a file. They
look like this:

(@> "topic name")

Following a tag navigates the next instance of the tag with the same
name. Repeatedly hitting C-c ' will cycle through all instances of the
tag in the file, also stopping at any blocks with the same name.

Creating and editing links

There are several commands to insert new links. For most links you can
use C-c {comma} {comma}. This runs the command (linkd-insert-link)

For stars and tags, you can use C-c , s (linkd-insert-star) and C-c ,
t (linkd-insert-tag) respectively.

You can interactively edit a link with C-c , e which runs the command
(linkd-edit-link-at-point).

If you use these functions to create and edit links, you can
effectively ignore the link syntax.

Examples

An interactive self-documenting installer script

My other project DTOX revolves around an interactive
installation/configuration script. It is a bash script split into
named blocks by linkd stars. The blocks contain commentary and active
links to related documentation and configuration files.

(image missing)

The local variables in the script set linkd-process-block-function to
the value linkd-send-block-to-shell, which pops up an emacs shell
window to execute the commands in the block.

So the user can inspect the blocks, edit them if necessary, choose
which blocks are to be executed, and watch the output of the
commands. The user may also follow the links to learn more, or to
tweak a configuration file after it has been installed.

Concept-oriented navigation in lisp source code

When writing a program, I often want to jump to related pieces of
code. But I usually don't just want to jump to a function definition
from one of its callers---more often I want to find pieces of code
that are related in some more abstract sense.

I use linkd-mode's tags and stars to relate pieces of code to one
another and to navigate through related code. First I gather related
functions and variables and put them in their own block, and name the
block after the concept they embody. (Remember that stars are used to
delimit and name blocks.) If I am writing an object-oriented program
with CLOS, I will usually put a class and most of its methods in one
block.

Then I place tags wherever a piece of code is related to the block in
some sense. You can think of following a link as jumping between
related ideas, because it will move point to the next tag or star with
the same concept name (and back to the first when you reach the end of
the file.)

(image missing)

I use linkd-mode to organize my lisp programs, be they 80 lines or
8,000. If you'd like to try out such a file with linkd-mode, you can
download rlx.el (part of the RLX Project) and follow the links in the
file. There is even an index of program concepts that one can use to
get an overview of the program's structure.

I also insert links to pages of relevant documentation---for example
the Emacs Lisp Manual or the ANSI Common Lisp Standard.

Creating a wiki-like personal "hyperweb"

- This section is under construction, as is the functionality
described.

Many people use wikis as personal notebooks. They organize their
knowledge, develop ideas, and keep web bookmarks in their wikis. With
linkd-mode, any text file on your system can be a wiki page, including
system configuration files. Wiki pages can link to any kind of
resource, not just other pages. They can be in any major-mode you
choose. And your wiki pages can contain interactive scripts (see the
section about DTOX above.)

(image missing)

First you should create a directory called ~/linkd-wiki (or set
linkd-wiki-directory to your preferred location.) This is the default
location for new wiki pages.

For fast access, I set a global key that navigates instantly to the
wiki's FrontPage:

(global-set-key [(meta f8)] '(lambda () (interactive)
(linkd-wiki-find-page "FrontPage")))

You can use C-c , w to create a link to a wiki page.

;;; linkd.el --- grand unified hyperlinker for emacs

;; _ _ _ _
;; | (_)_ __ | | ____| |
;; | | | '_ \| |/ / _` |
;; | | | | | | < (_| |
;; |_|_|_| |_|_|\_\__,_|
;;
;; Copyright (C) 2007 David O'Toole

;; Author: David O'Toole gnu.org>
;; Keywords: hypermedia
;; $Id: linkd.el,v 1.31 2007/01/15 22:44:49 dto Exp dto $

;; This file is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.

;; This file is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING. If not, write to
;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.

;; This file is not part of GNU Emacs.

;;; Commentary:

;; Linkd-mode is a hypertext system for organizing and interlinking
;; all resources available to GNU Emacs.
;;
;; This is a preliminary alpha release. Some things are not
;; implemented and there are sure to be bugs. Please send feedback and
;; bug reports to me at dto@gnu.org
;;
;; For now, the documentation is kept on the linkd home page:
;;
;; http://dto.freeshell.org/notebook/Linkd.html
;;
;; You can download the icons from:
;;
;; http://dto.freeshell.org/packages/linkd-icons.tar.gz

;;; Index:

;; (@> "finding links")
;; (@> "following links")
;; (@> "rendering links")
;; (@> "link icons")
;; (@> "navigating links")
;; (@> "inserting links")
;; (@> "specific link types")
;; (@> "tags")
;; (@> "stars")
;; (@> "processing blocks")
;; (@> "wiki")
;; (@> "linkd minor mode")
;; (@> "font locking")
;; (@> "faces")

;;; Code:

(require 'easy-mmode)

;; (@* "finding links")

(defvar linkd-generic-regexp (concat "\(" "@" ".*\)"))

(defun linkd-link-at-point ()
"Get the link around point and return it as a sexp, or nil if
not found."
(if (get-char-property (point) 'linkd)
(read (current-buffer))))

(defun linkd-link-on-this-line ()
"Get the rightmost link from the current line. Return the sexp, or nil if not found."
(end-of-line)
(backward-sexp)
(linkd-link-at-point))

;; (@* "following links")

(defun linkd-follow (sexp)
"Follow the link represented by SEXP."
(let* ((plist (eval sexp))
(follower (plist-get plist :follow)))
(if follower
(funcall follower)
(error "No follow function for link %%S" sexp))))

(defun linkd-follow-on-this-line ()
"Follow the rightmost link on the current line."
(interactive)
(linkd-follow (linkd-link-on-this-line)))

(defun linkd-follow-at-point ()
"Follow the link at point."
(interactive)
(linkd-follow (linkd-link-at-point)))

(defun linkd-follow-mouse (event)
"Follow the clicked link."
(interactive "e")
(when event
(let ((pos (posn-point (car (cdr event)))))
(goto-char pos)
(linkd-follow (linkd-link-at-point)))))

;; (@* "rendering links")

(defvar linkd-default-bullet-string ">>>")

(defun linkd-overlay (beg end display-text &optional display-face bullet-text bullet-face bullet-icon)
(let ((overlay (make-overlay beg end)))
(overlay-put overlay 'display (propertize display-text
'face (or display-face linkd-generic-name-face)))
;;
;; mark the overlay so that we can find it later
;; see also (@> "navigating links")
(overlay-put overlay 'linkd t)
;;
;; make it clickable
(overlay-put overlay 'keymap
(let ((keymap (make-sparse-keymap)))
(define-key keymap [(mouse-2)] 'linkd-follow-mouse)
(define-key keymap (kbd "M-RET") 'linkd-follow-at-point)
keymap))
;;
;; add a bullet, if any
(when bullet-text
(let* ((face (if (and bullet-icon linkd-use-icons)
linkd-icon-face
bullet-face))
(b1 (if face
(propertize bullet-text 'face face)
bullet-text))
(b2 (if (and bullet-icon linkd-use-icons)
(propertize b1 'display `(image :file ,bullet-icon
:type xpm
:ascent center))
b1)))
(overlay-put overlay 'before-string (concat b2 " "))))
;;
(overlay-put overlay 'evaporate t)
;;
;; defontify if the user edits the text
(overlay-put overlay 'modification-hooks
(list (lambda (ov foo bar plic)
(delete-overlay ov)
(remove-text-properties (point-at-bol) (point-at-eol)
(list 'linkd-fontified nil)))))))

;; (@* "link icons")

(defvar linkd-use-icons nil "When non-nil, icons are displayed for links instead of text bullets.")

(defvar linkd-icons-path "~/.linkd-icons" "Directory where linkd's icons are kept.")

(defun linkd-icon (icon-name)
(concat (file-name-as-directory linkd-icons-path) "linkd-" icon-name ".xpm"))

(defun linkd-file-icon (file-name)
"Choose an appropriate icon for FILE-NAME based on the name or extension.
Returns the path to the icon image file."
(let* ((path (file-name-as-directory linkd-icons-path))
(file (concat path "linkd-file-" (file-name-extension file-name) ".xpm")))
(if (file-exists-p file)
file
(concat path "linkd-file-generic.xpm"))))

;; (@* "navigating links")

(defun linkd-next-link ()
(interactive)
(forward-char 1)
(let ((inhibit-point-motion-hooks nil))
;;
;; get out of the current overlay if needed
(when (get-char-property (point) 'linkd)
(while (and (not (eobp))
(get-char-property (point) 'linkd))
(goto-char (min (next-overlay-change (point))
(next-single-char-property-change (point) 'linkd)))))
;;
;; now find the next linkd overlay
(while (and (not (eobp))
(not (get-char-property (point) 'linkd)))
(goto-char (min (next-overlay-change (point))
(next-single-char-property-change (point) 'linkd))))))

(defun linkd-previous-link ()
(interactive)
(forward-char 1)
(let ((inhibit-point-motion-hooks nil))
;;
;; get out of the current overlay if needed
(when (get-char-property (point) 'linkd)
(while (and (not (bobp))
(get-char-property (point) 'linkd))
(goto-char (max (previous-overlay-change (point))
(previous-single-char-property-change (point) 'linkd)))))
;;
;; now find the previous linkd overlay
(while (and (not (bobp))
(not (get-char-property (point) 'linkd)))
(goto-char (max (previous-overlay-change (point))
(previous-single-char-property-change (point) 'linkd))))))

;; (@* "inserting links")

(defun linkd-insert-single-arg-link (type-string argument)
(insert (format (concat "(" "@%%s %%S)") type-string argument)))

(defun linkd-insert-tag (tag-name)
(interactive "sTag name: ")
(linkd-insert-single-arg-link ">" tag-name))

(defun linkd-insert-star (star-name)
(interactive "sStar name: ")
(linkd-insert-single-arg-link "*"star-name))

(defun linkd-insert-wiki (wiki-name)
(interactive "sWiki page: ")
(linkd-insert-single-arg-link "!" wiki-name))

(defun linkd-insert-command (command-name)
(interactive "sCommand name: ")
(linkd-insert-single-arg-link "$" command-name))

(defvar linkd-insertion-schemes '(("file" :path :to :display)
("man" :page :to :display)
("info" :file :node :to :display)
("url" :path :display)))

(defun linkd-insert-link (&optional type current-values)
(interactive)
(let* ((type (or type (completing-read "Link type: " linkd-insertion-schemes)))
(keys (cdr (assoc type linkd-insertion-schemes)))
(key (car keys))
(link-args nil))
(while key
;;
;; read an argument value
(let ((value (read-from-minibuffer (format "%%S " key)
(plist-get current-values key))))
(when (not (string= "" value))
(setq link-args (plist-put link-args key value))))
;;
;; next
(setq keys (cdr keys))
(setq key (car keys)))
;;
;; format and insert the link
(insert (format (concat "(" "@%%s %%s)")
type
(mapconcat (lambda (sexp)
(format "%%S" sexp))
link-args
" ")))))

(defun linkd-edit-link-at-point ()
(interactive)
(let ((link (linkd-link-at-point)))
(when link
(if (keywordp (car (cdr link)))
;;
;; it's a general link
(save-excursion
(linkd-insert-link
;; drop the @ sign
(substring (format "%%S" (car link)) 1)
(cdr link)))
;;
;; it's a single-arg link
(let ((new-value (read-from-minibuffer "New value: " (car (cdr link)))))
(insert (format "%%S" (list (car link) new-value)))))
;;
;; now erase old link
(re-search-backward linkd-generic-regexp)
(delete-region (match-beginning 0) (match-end 0)))))

;; (@* "specific link types")

(defun @file (&rest p)
(let ((path (plist-get p :path))
(to (plist-get p :to))
(display (plist-get p :display)))
`(:follow
(lambda ()
(find-file ,path)
(when ,to
(beginning-of-buffer)
(search-forward ,to)))
:render
(lambda (beg end)
(linkd-overlay beg end ,(or display
(concat path (if to
(concat " : " to)
"")))
nil linkd-default-bullet-string nil ,(linkd-file-icon path))))))

(defun @man (&rest p)
(let ((page (plist-get p :page))
(to (plist-get p :to))
(display (plist-get p :display)))
`(:follow
(lambda ()
(man ,page)
(when ,to
(beginning-of-buffer)
(search-forward ,to)))
:render
(lambda (beg end)
(linkd-overlay beg end ,(or display
(concat page " manual" (if to
(concat " : " to)
"")))
nil linkd-default-bullet-string nil ,(linkd-icon "man"))))))

(defun @info (&rest p)
(let ((file (plist-get p :file))
(node (plist-get p :node))
(to (plist-get p :to))
(display (plist-get p :display)))
`(:follow
(lambda ()
(info (concat "(" ,file ")" ,node))
(when ,to
(beginning-of-buffer)
(search-forward ,to)))
:render
(lambda (beg end)
(linkd-overlay beg end ,(or display
(concat file " manual" (if to
(concat " : " to)
"")))
linkd-generic-name-face linkd-default-bullet-string nil ,(linkd-icon "info"))))))

(defun @url (&rest p)
(let ((path (plist-get p :path))
(display (plist-get p :display)))
`(:follow
(lambda ()
(browse-url ,path))
:render
(lambda (beg end)
(linkd-overlay beg end ,(or display path)
linkd-generic-name-face
linkd-default-bullet-string nil ,(linkd-icon "url"))))))

;; (@* "commands")

(defun @$ (command-name)
`(:follow
(lambda ()
(funcall ,(symbol-function (intern command-name))))
:render
(lambda (beg end)
(linkd-overlay beg end ,(concat "(" command-name ")")
linkd-command-face
"M-x" nil ,(linkd-icon "command")))))

;; (@* "tags")
;; Following a tag link navigates to the next tag (or star) with the same name,
;; cycling to the beginning of the buffer when the end is reached.

(defun @> (tag-name)
`(:follow
(lambda ()
(let ((regexp (concat "\(\@\\(\*\\|>\\) \"" ,tag-name)))
(when (not (re-search-forward regexp nil t))
(goto-char (point-min))
(re-search-forward regexp nil t))
(goto-char (match-beginning 0))))
:render
(lambda (beg end)
(linkd-overlay beg end ,tag-name linkd-tag-name-face
">" linkd-tag-face ,(linkd-icon "tag")))))

;; (@* "stars")
;; Stars delimit (and optionally name) blocks of text.

(defun @* (&optional star-name)
`(:follow
(lambda ()
(let ((regexp (concat "\(\@\\(\*\\|>\\) \"" ,star-name)))
(when (not (re-search-forward regexp nil t))
(goto-char (point-min))
(re-search-forward regexp nil t))
(goto-char (match-beginning 0))))
:render
(lambda (beg end)
(linkd-overlay beg end
,(if star-name
star-name
" ") ;; leave a space so that fontified link doesn't disappear
',(if star-name
linkd-star-name-face
'default)
"*" linkd-star-face ,(linkd-icon "star")))))

;; (@* "wiki")
;;

(defvar linkd-wiki-directory "~/linkd-wiki" "Default directory to look for wiki pages.")

(defun linkd-wiki-find-page (page-name)
(find-file (concat (file-name-as-directory linkd-wiki-directory)
page-name ".linkd")))

(defun @! (page)
`(:follow
(lambda ()
(linkd-wiki-find-page ,page))
:render
(lambda (beg end)
(linkd-overlay beg end ,page linkd-wiki-face)
)))

;; (@* "processing blocks")
;; Sending the text in a block to some lisp function or external
;; program for further processing

(defvar linkd-star-search-string (concat "\(" "\@\*"))

(defun linkd-block-around-point ()
"Return the block around point as a string."
(interactive)
(let ((beg (save-excursion
(search-backward linkd-star-search-string)
(beginning-of-line)
(point)))
(end (save-excursion
(search-forward linkd-star-search-string)
(point))))
(buffer-substring-no-properties beg end)))

(defvar linkd-block-file-name "~/.linkd-block"
"File where temporary block text is stored for processing by
external programs.")

(defun linkd-write-block-to-file (block-text)
"Write the BLOCK-TEXT to the file named by linkd-block-file-name."
(interactive)
(with-temp-buffer
(insert block-text)
(write-file linkd-block-file-name)))

(defvar linkd-process-block-function nil
"This function is called with the contents of the block around
point as a string whenever (linkd-process-block) is called. You
can set this in the Local Variables section of a file.")

(make-variable-buffer-local 'linkd-process-block-function)

(defun linkd-process-block ()
(interactive)
(funcall linkd-process-block-function (linkd-block-around-point)))

;;
;; the following code is adapted from edrx's eepitch.el
;;

(defvar linkd-shell-buffer-name "*linkd shell*")

(defun linkd-send-block-to-shell (block-text)
(interactive)
(when (not (get-buffer-window linkd-shell-buffer-name))
;;
;; create shell if needed, but not in current window
(save-window-excursion (shell linkd-shell-buffer-name))
(display-buffer linkd-shell-buffer-name))
;;
(linkd-write-block-to-file block-text)
(save-selected-window
(select-window (get-buffer-window linkd-shell-buffer-name))
(end-of-buffer)
;;
;; make the shell source the temp file
(insert (concat ". " linkd-block-file-name))
(call-interactively (key-binding "\r"))))

;; (@* "linkd minor mode")

(defvar linkd-map nil)
;;(setq linkd-map nil)
(when (null linkd-map)
(setq linkd-map (make-sparse-keymap))
(define-key linkd-map (kbd "C-c .") 'linkd-follow-on-this-line)
(define-key linkd-map (kbd "C-c *") 'linkd-process-block)
(define-key linkd-map (kbd "C-c [") 'linkd-previous-link)
(define-key linkd-map (kbd "C-c ]") 'linkd-next-link)
(define-key linkd-map (kbd "C-c '") 'linkd-follow-at-point)
(define-key linkd-map (kbd "C-c , ,") 'linkd-insert-link)
(define-key linkd-map (kbd "C-c , t") 'linkd-insert-tag)
(define-key linkd-map (kbd "C-c , s") 'linkd-insert-star)
(define-key linkd-map (kbd "C-c , w") 'linkd-insert-wiki)
(define-key linkd-map (kbd "C-c , c") 'linkd-insert-command)
(define-key linkd-map (kbd "C-c , e") 'linkd-edit-link-at-point))

(define-minor-mode linkd-mode
"Grand unified hyperlinker for emacs."
nil
:lighter " Linkd"
:keymap linkd-map
(if linkd-mode
(linkd-enable)
(linkd-disable)))

(defun linkd-enable ()
(linkd-do-font-lock 'font-lock-add-keywords)
(font-lock-fontify-buffer))

(defun linkd-disable ()
;;
;; remove all linkd's overlays
(mapcar (lambda (overlay)
(when (get-text-property (overlay-start overlay)
'linkd-fontified)
(delete-overlay overlay)))
(overlays-in (point-min) (point-max)))
;;
;; remove font-lock rules and refontify
(linkd-do-font-lock 'font-lock-remove-keywords)
(font-lock-fontify-buffer))

;; (@* "font locking")

(defun linkd-render-link (beg end)
(when (not (get-text-property beg 'linkd-fontified))
(save-excursion
(goto-char beg)
(add-text-properties beg (+ beg 1) (list 'linkd-fontified t))
(let* ((sexp (read (current-buffer)))
(plist (eval sexp))
(renderer (plist-get plist :render)))
(when (null renderer) (error "No renderer for link."))
(funcall renderer beg end)))))

(defun linkd-do-font-lock (add-or-remove)
(funcall add-or-remove nil
`((,linkd-generic-regexp
0
(let ((beg (match-beginning 0))
(end (match-end 0)))
(linkd-render-link beg end)
linkd-generic-face)
prepend))))

;; (@* "faces")

(defface linkd-generic-face '((t (:foreground "yellow")))
"Face for linkd links.")

(defvar linkd-generic-face 'linkd-generic-face)

(defface linkd-generic-name-face '((t (:foreground "yellow")))
"Face for linkd links.")

(defvar linkd-generic-name-face 'linkd-generic-name-face)

(defface linkd-star-face '((t (:foreground "yellow" :background "red" :underline nil)))
"Face for star delimiters.")

(defvar linkd-star-face 'linkd-star-face)

(defface linkd-star-name-face '((t (:foreground "yellow" :background "red" :underline "yellow")))
"Face for star names.")

(defvar linkd-star-name-face 'linkd-star-name-face)

(defface linkd-tag-face '((t (:foreground "yellow" :background "forestgreen")))
"Face for tags.")

(defvar linkd-tag-face 'linkd-tag-face)

(defface linkd-tag-name-face '((t (:foreground "yellow" :background "blue" :underline "yellow")))
"Face for tag names.")

(defvar linkd-tag-name-face 'linkd-tag-name-face)

(defface linkd-icon-face '((t (:underline nil)))
"Face for icons.")

(defvar linkd-icon-face 'linkd-icon-face)

(defface linkd-wiki-face '((t (:foreground "cyan" :underline "yellow")))
"Face for camel case wiki links.")

(defvar linkd-wiki-face 'linkd-wiki-face)

(defface linkd-command-face '((t (:foreground "red" :background "blue")))
"Face for command links.")

(defvar linkd-command-face 'linkd-command-face)

(provide 'linkd)
;;; linkd.el ends here

no comments
diggit! del.icio.us! reddit!