Big changes, last one before I wipe it all.

* Added muse

  * Added graphviz-dot-mode

  * Remove all trailing whitespace on save.

This is the last commit I'm going to do before throwing it all away
again.
This commit is contained in:
Tom Willemsen 2011-03-17 11:23:07 +01:00
parent a502df33ce
commit 57366f385a
38 changed files with 15070 additions and 7 deletions

1
emacs.d/.gitignore vendored
View file

@ -1,2 +1,3 @@
tramp tramp
elpa elpa
bookmarks

View file

@ -2,3 +2,4 @@
(add-to-list 'load-path "~/.emacs.d/naquadah-theme") (add-to-list 'load-path "~/.emacs.d/naquadah-theme")
(add-to-list 'load-path "~/.emacs.d/auto-complete-clang") (add-to-list 'load-path "~/.emacs.d/auto-complete-clang")
(add-to-list 'load-path "~/.emacs.d/nxhtml") (add-to-list 'load-path "~/.emacs.d/nxhtml")
(add-to-list 'load-path "~/.emacs.d/elisp/muse")

View file

@ -1,19 +1,25 @@
(require 'minimap) (require 'minimap)
(require 'naquadah-theme) (require 'naquadah-theme)
;(require 'auto-complete-clang) ;(require 'auto-complete-clang)
(require 'muse-mode)
(require 'muse-html)
(require 'muse-project)
(load "autostart.el") (load "autostart.el")
(autoload 'vala-mode (autoload 'vala-mode
"vala-mode" "A Major mode for editing Vala files" t) "vala-mode" "A Major mode for editing Vala files" t)
(autoload 'csharp-mode (autoload 'csharp-mode
"csharp-mode" "A Major mode for editing C# files" t) "csharp-mode" "A Major mode for editing C# files" t)
(autoload 'javascript-mode (autoload 'javascript-mode
"javascript" "A Major mode for editing JavaScript files" t) "javascript" "A Major mode for editing JavaScript files" t)
(autoload 'sqlplus-mode (autoload 'sqlplus-mode
"sqlplus" "A Major mode for communicating with Oracle" t) "sqlplus" "A Major mode for communicating with Oracle" t)
(autoload 'batch-mode (autoload 'batch-mode
"batch-mode" "A Major mode for editing Batch files" t) "batch-mode" "A Major mode for editing Batch files" t)
(autoload 'lua-mode (autoload 'lua-mode
"lua-mode" "A Major mode for editing Lua files" t) "lua-mode" "A Major mode for editing Lua files" t)
(autoload 'php-mode (autoload 'php-mode
"php-mode" "A Major mode for editing PHP files" t) "php-mode" "A Major mode for editing PHP files" t)
(autoload 'graphviz-dot-mode
"graphviz-dot-mode" "A Major mode for editing graphviz dot files" t)

View file

@ -24,6 +24,7 @@
(delete-selection-mode t) ; delete selection upon typing (delete-selection-mode t) ; delete selection upon typing
;; Byte-compile elisp files on save ;; Byte-compile elisp files on save
(add-hook 'before-save-hook 'delete-trailing-whitespace)
(add-hook 'after-save-hook (add-hook 'after-save-hook
(lambda () (lambda ()
(let ((fname (buffer-file-name))) (let ((fname (buffer-file-name)))

View file

@ -13,6 +13,10 @@
(setq org-crypt-key "33E8CC1CC4") (setq org-crypt-key "33E8CC1CC4")
; GPG key used for encryption ; GPG key used for encryption
(setq org-use-fast-todo-selection t) (setq org-use-fast-todo-selection t)
(setq org-default-notes-file (concat org-directory "/notes.org"))
(setq org-refile-targets '((org-agenda-files :maxlevel . 5)
(nil :maxlevel . 5)))
(setq org-outline-path-complete-in-steps t)
(setq org-todo-keyword-faces (setq org-todo-keyword-faces
'(("TODO" :foreground "red" :weight bold) '(("TODO" :foreground "red" :weight bold)

View file

@ -0,0 +1,3 @@
(setq muse-project-alist
'(("lxcoding-docs" ("~/prj/lxcoding-docs" :default "index")
(:base "html" :path "~/devel/mnt/lxcoding/www/docs"))))

View file

@ -0,0 +1,13 @@
(require 'org-publish)
(setq org-publish-project-alist
'(("lxcoding-docs"
:base-directory "~/devel/lxcoding-docs/"
:base-extension "org"
:publishing-directory "~/lxcoding-docs-test/"
:recursive t
:publishing-function org-publish-org-to-html
:headline-levels
:auto-preamble t
:auto-sitemap t
:sitemap-filename "sitemap.org"
:sitemap-title "docs sitemap")))

View file

@ -0,0 +1,946 @@
;;; graphviz-dot-mode.el --- Mode for the dot-language used by graphviz (att).
;; Copyright (C) 2002 - 2011 Pieter Pareit <pieter.pareit@gmail.com>
;; This program 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 of
;; the License, or (at your option) any later version.
;; This program 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 this program; if not, write to the Free
;; Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
;; MA 02111-1307 USA
;; Authors: Pieter Pareit <pieter.pareit@gmail.com>
;; Rubens Ramos <rubensr AT users.sourceforge.net>
;; Eric Anderson http://www.ece.cmu.edu/~andersoe/
;; Maintainer: Pieter Pareit <pieter.pareit@gmail.com>
;; Homepage: http://users.skynet.be/ppareit/projects/graphviz-dot-mode/graphviz-dot-mode.html
;; Created: 28 Oct 2002
;; Last modified: 09 march 2011
;; Version: 0.3.7
;; Keywords: mode dot dot-language dotlanguage graphviz graphs att
;;; Commentary:
;; Use this mode for editing files in the dot-language (www.graphviz.org and
;; http://www.research.att.com/sw/tools/graphviz/).
;;
;; To use graphviz-dot-mode, add
;; (load-file "PATH_TO_FILE/graphviz-dot-mode.el")
;; to your ~/.emacs(.el) or ~/.xemacs/init.el
;;
;; The graphviz-dot-mode will do font locking, indentation, preview of graphs
;; and eases compilation/error location. There is support for both GNU Emacs
;; and XEmacs.
;;
;; Font locking is automatic, indentation uses the same commands as
;; other modes, tab, M-j and C-M-q. Insertion of comments uses the
;; same commands as other modes, M-; . You can compile a file using
;; M-x compile or C-c c, after that M-x next-error will also work.
;; There is support for viewing an generated image with C-c p.
;;; Todo:
;; * cleanup the mess of graphviz-dot-compilation-parse-errors.
;; * electric indentation is fundamentally broken, because
;; {...} are also used for record nodes. You could argue, I suppose, that
;; many diagrams don't need those, but it would be worth having a note (and
;; it makes sense that the default is now for electric indentation to be
;; off).
;; * lines that start with # are comments, lines that start with one or more
;; whitespaces and then a # should give an error.
;;; History:
;; Version 0.3.7 Tim Allen
;; 09/03/2011: * fix spaces in file names when compiling
;; Version 0.3.6 maintenance
;; 19/02/2011: * .gv is the new extension (Pander)
;; * comments can start with # (Pander)
;; * highlight of new keywords (Pander)
;; Version 0.3.5 bug (or at least feature I dislike) fix
;; 11/11/2010: Eric Anderson http://www.ece.cmu.edu/~andersoe/
;; * Preserve indentation across blank (whitespace-only) lines
;; Version 0.3.4 bug fixes
;; 24/02/2005: * fixed a bug in graphviz-dot-preview
;; Version 0.3.3 bug fixes
;; 13/02/2005: Reuben Thomas <rrt AT sc3d.org>
;; * add graphviz-dot-indent-width
;; Version 0.3.2 bug fixes
;; 25/03/2004: Rubens Ramos <rubensr AT users.sourceforge.net>
;; * semi-colons and brackets are added when electric
;; behaviour is disabled.
;; * electric characters do not behave electrically inside
;; comments or strings.
;; * default for electric-braces is disabled now (makes more
;; sense I guess).
;; * using read-from-minibuffer instead of read-shell-command
;; for emacs.
;; * Fixed test for easymenu, so that it works on older
;; versions of XEmacs.
;; * Fixed indentation error when trying to indent last brace
;; of an empty graph.
;; * region-active-p does not exist in emacs (21.2 at least),
;; so removed from code
;; * Added uncomment menu option
;; Version 0.3.1 bug fixes
;; 03/03/2004: * backward-word needs argument for older emacs
;; Version 0.3 added features and fixed bugs
;; 10/01/2004: fixed a bug in graphviz-dot-indent-graph
;; 08/01/2004: Rubens Ramos <rubensr AT users.sourceforge.net>
;; * added customization support
;; * Now it works on XEmacs and Emacs
;; * Added support to use an external Viewer
;; * Now things do not break when dot mode is entered
;; when there is no buffer name, but the side effect is
;; that in this case, the compilation command is not
;; correct.
;; * Preview works on XEmacs and emacs.
;; * Electric indentation on newline
;; * Minor changes to indentation
;; * Added keyword completion (but could be A LOT better)
;; * There are still a couple of ugly hacks. Look for 'RR'.
;; Version 0.2 added features
;; 11/11/2002: added preview support.
;; 10/11/2002: indent a graph or subgraph at once with C-M-q.
;; 08/11/2002: relaxed rules for indentation, the may now be extra chars
;; after beginning of graph (comment's for example).
;; Version 0.1.2 bug fixes and naming issues
;; 06/11/2002: renamed dot-font-lock-defaults to dot-font-lock-keywords.
;; added some documentation to dot-colors.
;; provided a much better way to handle my max-specpdl-size
;; problem.
;; added an extra autoload cookie (hope this helps, as I don't
;; yet use autoload myself)
;; Version 0.1.1 bug fixes
;; 06/11/2002: added an missing attribute, for font-locking to work.
;; fixed the regex generating, so that it only recognizes
;; whole words
;; 05/11/2002: there can now be extra white space chars after an '{'.
;; 04/11/2002: Why I use max-specpdl-size is now documented, and old value
;; gets restored.
;; Version 0.1 initial release
;; 02/11/2002: implemented parser for *compilation* of a .dot file.
;; 01/11/2002: implemented compilation of an .dot file.
;; 31/10/2002: added syntax-table to the mode.
;; 30/10/2002: implemented indentation code.
;; 29/10/2002: implemented all of font-lock.
;; 28/10/2002: derived graphviz-dot-mode from fundamental-mode, started
;; implementing font-lock.
;;; Code:
(defconst graphviz-dot-mode-version "0.3.6"
"Version of `graphviz-dot-mode.el'.")
(defgroup graphviz nil
"Major mode for editing Graphviz Dot files"
:group 'tools)
(defun graphviz-dot-customize ()
"Run \\[customize-group] for the `graphviz' group."
(interactive)
(customize-group 'graphviz))
(defvar graphviz-dot-mode-abbrev-table nil
"Abbrev table in use in Graphviz Dot mode buffers.")
(define-abbrev-table 'graphviz-dot-mode-abbrev-table ())
(defcustom graphviz-dot-dot-program "dot"
"*Location of the dot program. This is used by `compile'."
:type 'string
:group 'graphviz)
(defcustom graphviz-dot-view-command "doted %s"
"*External program to run on the buffer. You can use `%s' in this string,
and it will be substituted by the buffer name."
:type 'string
:group 'graphviz)
(defcustom graphviz-dot-view-edit-command nil
"*Whether to allow the user to edit the command to run an external
viewer."
:type 'boolean
:group 'graphviz)
(defcustom graphviz-dot-save-before-view t
"*If not nil, M-x graphviz-dot-view saves the current buffer before running
the command."
:type 'boolean
:group 'graphviz)
(defcustom graphviz-dot-auto-indent-on-newline t
"*If not nil, `electric-graphviz-dot-terminate-line' is executed in a line is terminated."
:type 'boolean
:group 'graphviz)
(defcustom graphviz-dot-indent-width default-tab-width
"*Indentation width in Graphviz Dot mode buffers."
:type 'integer
:group 'graphviz)
(defcustom graphviz-dot-auto-indent-on-braces nil
"*If not nil, `electric-graphviz-dot-open-brace' and `electric-graphviz-dot-close-brace' are executed when { or } are typed"
:type 'boolean
:group 'graphviz)
(defcustom graphviz-dot-auto-indent-on-semi t
"*If not nil, `electric-graphviz-dot-semi' is executed when semicolon is typed"
:type 'boolean
:group 'graphviz)
(defcustom graphviz-dot-preview-extension "png"
"*The extension to use for the compilation and preview commands. The format
for the compilation command is
`dot -T<extension> file.dot > file.<extension>'."
:type 'string
:group 'graphviz)
(defcustom graphviz-dot-toggle-completions nil
"*Non-nil means that repeated use of \
\\<graphviz-dot-mode-map>\\[graphviz-dot-complete-word] will toggle the possible
completions in the minibuffer. Normally, when there is more than one possible
completion, a buffer will display all completions."
:type 'boolean
:group 'graphviz)
(defcustom graphviz-dot-delete-completions nil
"*Non-nil means that the completion buffer is automatically deleted when a
key is pressed."
:type 'boolean
:group 'graphviz)
(defcustom graphviz-dot-attr-keywords
'("graph" "digraph" "subgraph" "node" "edge" "strict" "rankdir"
"size" "page" "Damping" "Epsilon" "URL" "arrowhead" "arrowsize"
"arrowtail" "bb" "bgcolor" "bottomlabel" "center" "clusterrank"
"color" "comment" "compound" "concentrate" "constraint" "decorate"
"dim" "dir" "distortion" "fillcolor" "fixedsize" "fontcolor"
"fontname" "fontpath" "fontsize" "group" "headURL" "headlabel"
"headport" "height" "label" "labelangle" "labeldistance" "labelfloat"
"labelfontcolor" "labelfontname" "labelfontsize" "labeljust"
"labelloc" "layer" "layers" "len" "lhead" "lp" "ltail" "margin"
"maxiter" "mclimit" "minlen" "model" "nodesep" "normalize" "nslimit"
"nslimit1" "ordering" "orientation" "overlap" "pack" "pagedir"
"pencolor" "peripheries" "pin" "pos" "quantum" "rank" "ranksep"
"ratio" "rects" "regular" "remincross" "rotate" "samehead" "sametail"
"samplepoint" "searchsize" "sep" "shape" "shapefile" "showboxes"
"sides" "skew" "splines" "start" "style" "stylesheet" "tailURL"
"taillabel" "tailport" "toplabel" "vertices" "voro_margin" "weight"
"z" "width" "penwidth" "mindist" "scale" "patch" "root")
"*Keywords for attribute names in a graph. This is used by the auto
completion code. The actual completion tables are built when the mode
is loaded, so changes to this are not immediately visible.
Check http://www.graphviz.org/doc/schema/attributes.xml on new releases."
:type '(repeat (string :tag "Keyword"))
:group 'graphviz)
(defcustom graphviz-dot-value-keywords
'("true" "false" "normal" "inv" "dot" "invdot" "odot" "invodot"
"none" "tee" "empty" "invempty" "diamond" "odiamond" "box" "obox"
"open" "crow" "halfopen" "local" "global" "none" "forward" "back"
"both" "none" "BL" "BR" "TL" "TR" "RB" "RT" "LB" "LT" ":n" ":ne" ":e"
":se" ":s" ":sw" ":w" ":nw" "same" "min" "source" "max" "sink" "LR"
"box" "polygon" "ellipse" "circle" "point" "egg" "triangle"
"plaintext" "diamond" "trapezium" "parallelogram" "house" "hexagon"
"octagon" "doublecircle" "doubleoctagon" "tripleoctagon" "invtriangle"
"invtrapezium" "invhouse" "Mdiamond" "Msquare" "Mcircle" "record"
"Mrecord" "dashed" "dotted" "solid" "invis" "bold" "filled"
"diagonals" "rounded" )
"*Keywords for attribute values. This is used by the auto completion
code. The actual completion tables are built when the mode is loaded,
so changes to this are not immediately visible."
:type '(repeat (string :tag "Keyword"))
:group 'graphviz)
;;; Font-locking:
(defvar graphviz-dot-colors-list
'(aliceblue antiquewhite antiquewhite1 antiquewhite2
antiquewhite3 antiquewhite4 aquamarine aquamarine1
aquamarine2 aquamarine3 aquamarine4 azure azure1
azure2 azure3 azure4 beige bisque bisque1 bisque2
bisque3 bisque4 black blanchedalmond blue blue1
blue2 blue3 blue4 blueviolet brown brown1 brown2
brown3 brown4 burlywood burlywood1 burlywood2
burlywood3 burlywood4 cadetblue cadetblue1
cadetblue2 cadetblue3 cadetblue4 chartreuse
chartreuse1 chartreuse2 chartreuse3 chartreuse4
chocolate chocolate1 chocolate2 chocolate3 chocolate4
coral coral1 coral2 coral3 coral4 cornflowerblue
cornsilk cornsilk1 cornsilk2 cornsilk3 cornsilk4
crimson cyan cyan1 cyan2 cyan3 cyan4 darkgoldenrod
darkgoldenrod1 darkgoldenrod2 darkgoldenrod3
darkgoldenrod4 darkgreen darkkhaki darkolivegreen
darkolivegreen1 darkolivegreen2 darkolivegreen3
darkolivegreen4 darkorange darkorange1 darkorange2
darkorange3 darkorange4 darkorchid darkorchid1
darkorchid2 darkorchid3 darkorchid4 darksalmon
darkseagreen darkseagreen1 darkseagreen2
darkseagreen3 darkseagreen4 darkslateblue
darkslategray darkslategray1 darkslategray2
darkslategray3 darkslategray4 darkslategrey
darkturquoise darkviolet deeppink deeppink1
deeppink2 deeppink3 deeppink4 deepskyblue
deepskyblue1 deepskyblue2 deepskyblue3 deepskyblue4
dimgray dimgrey dodgerblue dodgerblue1 dodgerblue2
dodgerblue3 dodgerblue4 firebrick firebrick1
firebrick2 firebrick3 firebrick4 floralwhite
forestgreen gainsboro ghostwhite gold gold1 gold2
gold3 gold4 goldenrod goldenrod1 goldenrod2
goldenrod3 goldenrod4 gray gray0 gray1 gray10 gray100
gray11 gray12 gray13 gray14 gray15 gray16 gray17
gray18 gray19 gray2 gray20 gray21 gray22 gray23
gray24 gray25 gray26 gray27 gray28 gray29 gray3
gray30 gray31 gray32 gray33 gray34 gray35 gray36
gray37 gray38 gray39 gray4 gray40 gray41 gray42
gray43 gray44 gray45 gray46 gray47 gray48 gray49
gray5 gray50 gray51 gray52 gray53 gray54 gray55
gray56 gray57 gray58 gray59 gray6 gray60 gray61
gray62 gray63 gray64 gray65 gray66 gray67 gray68
gray69 gray7 gray70 gray71 gray72 gray73 gray74
gray75 gray76 gray77 gray78 gray79 gray8 gray80
gray81 gray82 gray83 gray84 gray85 gray86 gray87
gray88 gray89 gray9 gray90 gray91 gray92 gray93
gray94 gray95 gray96 gray97 gray98 gray99 green
green1 green2 green3 green4 greenyellow grey grey0
grey1 grey10 grey100 grey11 grey12 grey13 grey14
grey15 grey16 grey17 grey18 grey19 grey2 grey20
grey21 grey22 grey23 grey24 grey25 grey26 grey27
grey28 grey29 grey3 grey30 grey31 grey32 grey33
grey34 grey35 grey36 grey37 grey38 grey39 grey4
grey40 grey41 grey42 grey43 grey44 grey45 grey46
grey47 grey48 grey49 grey5 grey50 grey51 grey52
grey53 grey54 grey55 grey56 grey57 grey58 grey59
grey6 grey60 grey61 grey62 grey63 grey64 grey65
grey66 grey67 grey68 grey69 grey7 grey70 grey71
grey72 grey73 grey74 grey75 grey76 grey77 grey78
grey79 grey8 grey80 grey81 grey82 grey83 grey84
grey85 grey86 grey87 grey88 grey89 grey9 grey90
grey91 grey92 grey93 grey94 grey95 grey96 grey97
grey98 grey99 honeydew honeydew1 honeydew2 honeydew3
honeydew4 hotpink hotpink1 hotpink2 hotpink3 hotpink4
indianred indianred1 indianred2 indianred3 indianred4
indigo ivory ivory1 ivory2 ivory3 ivory4 khaki khaki1
khaki2 khaki3 khaki4 lavender lavenderblush
lavenderblush1 lavenderblush2 lavenderblush3
lavenderblush4 lawngreen lemonchiffon lemonchiffon1
lemonchiffon2 lemonchiffon3 lemonchiffon4 lightblue
lightblue1 lightblue2 lightblue3 lightblue4
lightcoral lightcyan lightcyan1 lightcyan2 lightcyan3
lightcyan4 lightgoldenrod lightgoldenrod1
lightgoldenrod2 lightgoldenrod3 lightgoldenrod4
lightgoldenrodyellow lightgray lightgrey lightpink
lightpink1 lightpink2 lightpink3 lightpink4
lightsalmon lightsalmon1 lightsalmon2 lightsalmon3
lightsalmon4 lightseagreen lightskyblue lightskyblue1
lightskyblue2 lightskyblue3 lightskyblue4
lightslateblue lightslategray lightslategrey
lightsteelblue lightsteelblue1 lightsteelblue2
lightsteelblue3 lightsteelblue4 lightyellow
lightyellow1 lightyellow2 lightyellow3 lightyellow4
limegreen linen magenta magenta1 magenta2 magenta3
magenta4 maroon maroon1 maroon2 maroon3 maroon4
mediumaquamarine mediumblue mediumorchid
mediumorchid1 mediumorchid2 mediumorchid3
mediumorchid4 mediumpurple mediumpurple1
mediumpurple2 mediumpurple3 mediumpurple4
mediumseagreen mediumslateblue mediumspringgreen
mediumturquoise mediumvioletred midnightblue
mintcream mistyrose mistyrose1 mistyrose2 mistyrose3
mistyrose4 moccasin navajowhite navajowhite1
navajowhite2 navajowhite3 navajowhite4 navy navyblue
oldlace olivedrab olivedrap olivedrab1 olivedrab2
olivedrap3 oragne palegoldenrod palegreen palegreen1
palegreen2 palegreen3 palegreen4 paleturquoise
paleturquoise1 paleturquoise2 paleturquoise3
paleturquoise4 palevioletred palevioletred1
palevioletred2 palevioletred3 palevioletred4
papayawhip peachpuff peachpuff1 peachpuff2
peachpuff3 peachpuff4 peru pink pink1 pink2 pink3
pink4 plum plum1 plum2 plum3 plum4 powderblue
purple purple1 purple2 purple3 purple4 red red1 red2
red3 red4 rosybrown rosybrown1 rosybrown2 rosybrown3
rosybrown4 royalblue royalblue1 royalblue2 royalblue3
royalblue4 saddlebrown salmon salmon1 salmon2 salmon3
salmon4 sandybrown seagreen seagreen1 seagreen2
seagreen3 seagreen4 seashell seashell1 seashell2
seashell3 seashell4 sienna sienna1 sienna2 sienna3
sienna4 skyblue skyblue1 skyblue2 skyblue3 skyblue4
slateblue slateblue1 slateblue2 slateblue3 slateblue4
slategray slategray1 slategray2 slategray3 slategray4
slategrey snow snow1 snow2 snow3 snow4 springgreen
springgreen1 springgreen2 springgreen3 springgreen4
steelblue steelblue1 steelblue2 steelblue3 steelblue4
tan tan1 tan2 tan3 tan4 thistle thistle1 thistle2
thistle3 thistle4 tomato tomato1 tomato2 tomato3
tomato4 transparent turquoise turquoise1 turquoise2
turquoise3 turquoise4 violet violetred violetred1
violetred2 violetred3 violetred4 wheat wheat1 wheat2
wheat3 wheat4 white whitesmoke yellow yellow1 yellow2
yellow3 yellow4 yellowgreen)
"Possible color constants in the dot language.
The list of constant is available at http://www.research.att.com/~erg/graphviz\
/info/colors.html")
(defvar graphviz-dot-color-keywords
(mapcar 'symbol-name graphviz-dot-colors-list))
(defvar graphviz-attr-keywords
(mapcar '(lambda (elm) (cons elm 0)) graphviz-dot-attr-keywords))
(defvar graphviz-value-keywords
(mapcar '(lambda (elm) (cons elm 0)) graphviz-dot-value-keywords))
(defvar graphviz-color-keywords
(mapcar '(lambda (elm) (cons elm 0)) graphviz-dot-color-keywords))
;;; Key map
(defvar graphviz-dot-mode-map ()
"Keymap used in Graphviz Dot mode.")
(if graphviz-dot-mode-map
()
(let ((map (make-sparse-keymap)))
(define-key map "\r" 'electric-graphviz-dot-terminate-line)
(define-key map "{" 'electric-graphviz-dot-open-brace)
(define-key map "}" 'electric-graphviz-dot-close-brace)
(define-key map ";" 'electric-graphviz-dot-semi)
(define-key map "\M-\t" 'graphviz-dot-complete-word)
(define-key map "\C-\M-q" 'graphviz-dot-indent-graph)
(define-key map "\C-cp" 'graphviz-dot-preview)
(define-key map "\C-cc" 'compile)
(define-key map "\C-cv" 'graphviz-dot-view)
(define-key map "\C-c\C-c" 'comment-region)
(define-key map "\C-c\C-u" 'graphviz-dot-uncomment-region)
(setq graphviz-dot-mode-map map)
))
;;; Syntax table
(defvar graphviz-dot-mode-syntax-table nil
"Syntax table for `graphviz-dot-mode'.")
(if graphviz-dot-mode-syntax-table
()
(let ((st (make-syntax-table)))
(modify-syntax-entry ?/ ". 124b" st)
(modify-syntax-entry ?* ". 23" st)
(modify-syntax-entry ?\n "> b" st)
(modify-syntax-entry ?= "." st)
(modify-syntax-entry ?_ "_" st)
(modify-syntax-entry ?- "_" st)
(modify-syntax-entry ?> "." st)
(modify-syntax-entry ?[ "(" st)
(modify-syntax-entry ?] ")" st)
(modify-syntax-entry ?\" "\"" st)
(setq graphviz-dot-mode-syntax-table st)
))
(defvar graphviz-dot-font-lock-keywords
`(("\\(:?di\\|sub\\)?graph \\(\\sw+\\)"
(2 font-lock-function-name-face))
(,(regexp-opt graphviz-dot-value-keywords 'words)
. font-lock-reference-face)
;; to build the font-locking for the colors,
;; we need more room for max-specpdl-size,
;; after that we take the list of symbols,
;; convert them to a list of strings, and make
;; an optimized regexp from them
(,(let ((max-specpdl-size (max max-specpdl-size 1200)))
(regexp-opt graphviz-dot-color-keywords))
. font-lock-string-face)
(,(concat
(regexp-opt graphviz-dot-attr-keywords 'words)
"[ \\t\\n]*=")
;; RR - ugly, really, but I dont know why xemacs does not work
;; if I change the next car to "1"...
(0 font-lock-variable-name-face)))
"Keyword highlighting specification for `graphviz-dot-mode'.")
;;;###autoload
(defun graphviz-dot-mode ()
"Major mode for the dot language. \\<graphviz-dot-mode-map>
TAB indents for graph lines.
\\[graphviz-dot-indent-graph]\t- Indentaion function.
\\[graphviz-dot-preview]\t- Previews graph in a buffer.
\\[graphviz-dot-view]\t- Views graph in an external viewer.
\\[graphviz-dot-indent-line]\t- Indents current line of code.
\\[graphviz-dot-complete-word]\t- Completes the current word.
\\[electric-graphviz-dot-terminate-line]\t- Electric newline.
\\[electric-graphviz-dot-open-brace]\t- Electric open braces.
\\[electric-graphviz-dot-close-brace]\t- Electric close braces.
\\[electric-graphviz-dot-semi]\t- Electric semi colons.
Variables specific to this mode:
graphviz-dot-dot-program (default `dot')
Location of the dot program.
graphviz-dot-view-command (default `doted %s')
Command to run when `graphviz-dot-view' is executed.
graphviz-dot-view-edit-command (default nil)
If the user should be asked to edit the view command.
graphviz-dot-save-before-view (default t)
Automatically save current buffer berore `graphviz-dot-view'.
graphviz-dot-preview-extension (default `png')
File type to use for `graphviz-dot-preview'.
graphviz-dot-auto-indent-on-newline (default t)
Whether to run `electric-graphviz-dot-terminate-line' when
newline is entered.
graphviz-dot-auto-indent-on-braces (default t)
Whether to run `electric-graphviz-dot-open-brace' and
`electric-graphviz-dot-close-brace' when braces are
entered.
graphviz-dot-auto-indent-on-semi (default t)
Whether to run `electric-graphviz-dot-semi' when semi colon
is typed.
graphviz-dot-toggle-completions (default nil)
If completions should be displayed in the buffer instead of a
completion buffer when \\[graphviz-dot-complete-word] is
pressed repeatedly.
This mode can be customized by running \\[graphviz-dot-customize].
Turning on Graphviz Dot mode calls the value of the variable
`graphviz-dot-mode-hook' with no args, if that value is non-nil."
(interactive)
(kill-all-local-variables)
(use-local-map graphviz-dot-mode-map)
(setq major-mode 'graphviz-dot-mode)
(setq mode-name "dot")
(setq local-abbrev-table graphviz-dot-mode-abbrev-table)
(set-syntax-table graphviz-dot-mode-syntax-table)
(set (make-local-variable 'indent-line-function) 'graphviz-dot-indent-line)
(set (make-local-variable 'comment-start) "//")
(set (make-local-variable 'comment-start-skip) "/\\*+ *\\|//+ *")
(modify-syntax-entry ?# "< b" graphviz-dot-mode-syntax-table)
(modify-syntax-entry ?\n "> b" graphviz-dot-mode-syntax-table)
(set (make-local-variable 'font-lock-defaults)
'(graphviz-dot-font-lock-keywords))
;; RR - If user is running this in the scratch buffer, there is no
;; buffer file name...
(if (buffer-file-name)
(set (make-local-variable 'compile-command)
(concat graphviz-dot-dot-program
" -T" graphviz-dot-preview-extension " "
"\"" buffer-file-name "\""
" > \""
(file-name-sans-extension
buffer-file-name)
"." graphviz-dot-preview-extension "\"")))
(set (make-local-variable 'compilation-parse-errors-function)
'graphviz-dot-compilation-parse-errors)
(if dot-menu
(easy-menu-add dot-menu))
(run-hooks 'graphviz-dot-mode-hook)
)
;;;; Menu definitions
(defvar dot-menu nil
"Menu for Graphviz Dot Mode.
This menu will get created automatically if you have the `easymenu'
package. Note that the latest X/Emacs releases contain this package.")
(and (condition-case nil
(require 'easymenu)
(error nil))
(easy-menu-define
dot-menu graphviz-dot-mode-map "Graphviz Mode menu"
'("Graphviz"
["Indent Graph" graphviz-dot-indent-graph t]
["Comment Out Region" comment-region (mark)]
["Uncomment Region" graphviz-dot-uncomment-region (mark)]
"-"
["Compile" compile t]
["Preview" graphviz-dot-preview
(and (buffer-file-name)
(not (buffer-modified-p)))]
["External Viewer" graphviz-dot-view (buffer-file-name)]
"-"
["Customize..." graphviz-dot-customize t]
)))
;;;; Compilation
;; note on graphviz-dot-compilation-parse-errors:
;; It would nicer if we could just use compilation-error-regexp-alist
;; to do that, 3 options:
;; - still write dot-compilation-parse-errors, don't build
;; a return list, but modify the *compilation* buffer
;; in a way compilation-error-regexp-alist recognizes the
;; format.
;; to do that, I should globally change compilation-parse-function
;; to this function, and call the old value of comp..-parse-fun..
;; to provide the return value.
;; two drawbacks are that, every compilation would be run through
;; this function (performance) and that in autoload there would
;; be a chance that this function would not yet be known.
;; - let the compilation run through a filter that would
;; modify the output of dot or neato:
;; dot -Tpng input.dot | filter
;; drawback: ugly, extra work for user, extra decency ...
;; no-option
;; - modify dot and neato !!! (PP:15/02/2005 seems to have happend,
;; so version 0.4.0 should clean this mess up!)
(defun graphviz-dot-compilation-parse-errors (limit-search find-at-least)
"Parse the current buffer for dot errors.
See variable `compilation-parse-errors-functions' for interface."
(interactive)
(save-excursion
(set-buffer "*compilation*")
(goto-char (point-min))
(setq compilation-error-list nil)
(let (buffer-of-error)
(while (not (eobp))
(cond
((looking-at "^dot\\( -[^ ]+\\)* \\(.*\\)")
(setq buffer-of-error (find-file-noselect
(buffer-substring-no-properties
(nth 4 (match-data t))
(nth 5 (match-data t))))))
((looking-at ".*:.*line \\([0-9]+\\)")
(let ((line-of-error
(string-to-number (buffer-substring-no-properties
(nth 2 (match-data t))
(nth 3 (match-data t))))))
(setq compilation-error-list
(cons
(cons
(point-marker)
(save-excursion
(set-buffer buffer-of-error)
(goto-line line-of-error)
(beginning-of-line)
(point-marker)))
compilation-error-list))))
(t t))
(forward-line 1)) )))
;;;;
;;;; Indentation
;;;;
(defun graphviz-dot-uncomment-region (begin end)
"Uncomments a region of code."
(interactive "r")
(comment-region begin end '(4)))
(defun graphviz-dot-indent-line ()
"Indent current line of dot code."
(interactive)
(if (bolp)
(graphviz-dot-real-indent-line)
(save-excursion
(graphviz-dot-real-indent-line))))
(defun graphviz-dot-get-indendation()
"Return current line's indentation"
(interactive)
(message "Current indentation is %d."
(current-indentation))
(current-indentation))
(defun graphviz-dot-real-indent-line ()
"Indent current line of dot code."
(beginning-of-line)
(cond
((bobp)
;; simple case, indent to 0
(indent-line-to 0))
((looking-at "^[ \t]*}[ \t]*$")
;; block closing, deindent relative to previous line
(indent-line-to (save-excursion
(forward-line -1)
(max 0 (- (current-indentation) graphviz-dot-indent-width)))))
;; other cases need to look at previous lines
(t
(indent-line-to (save-excursion
(forward-line -1)
(cond
((looking-at "\\(^.*{[^}]*$\\)")
;; previous line opened a block
;; indent to that line
(+ (current-indentation) graphviz-dot-indent-width))
((and (not (looking-at ".*\\[.*\\].*"))
(looking-at ".*\\[.*")) ; TODO:PP : can be 1 regex
;; previous line started filling
;; attributes, intend to that start
(search-forward "[")
(current-column))
((and (not (looking-at ".*\\[.*\\].*"))
(looking-at ".*\\].*")) ; TODO:PP : "
;; previous line stopped filling
;; attributes, find the line that started
;; filling them and indent to that line
(while (or (looking-at ".*\\[.*\\].*")
(not (looking-at ".*\\[.*"))) ; TODO:PP : "
(forward-line -1))
(current-indentation))
(t
;; default case, indent the
;; same as previous NON-BLANK line
;; (or the first line, if there are no previous non-blank lines)
(while (and (< (point-min) (point))
(looking-at "^\[ \t\]*$"))
(forward-line -1))
(current-indentation)) ))) )))
(defun graphviz-dot-indent-graph ()
"Indent the graph/digraph/subgraph where point is at.
This will first teach the beginning of the graph were point is at, and
then indent this and each subgraph in it."
(interactive)
(save-excursion
;; position point at start of graph
(while (not (or (looking-at "\\(^.*{[^}]*$\\)") (bobp)))
(forward-line -1))
;; bracket { one +; bracket } one -
(let ((bracket-count 0))
(while
(progn
(cond
;; update bracket-count
((looking-at "\\(^.*{[^}]*$\\)")
(setq bracket-count (+ bracket-count 1)))
;; update bracket-count
((looking-at "^[ \t]*}[ \t]*$")
(setq bracket-count (- bracket-count 1))))
;; indent this line and move on
(graphviz-dot-indent-line)
(forward-line 1)
;; as long as we are not completed or at end of buffer
(and (> bracket-count 0) (not (eobp))))))))
;;;;
;;;; Electric indentation
;;;;
(defun graphviz-dot-comment-or-string-p ()
(let ((state (parse-partial-sexp (point-min) (point))))
(or (nth 4 state) (nth 3 state))))
(defun graphviz-dot-newline-and-indent ()
(save-excursion
(beginning-of-line)
(skip-chars-forward " \t")
(graphviz-dot-indent-line))
(delete-horizontal-space)
(newline)
(graphviz-dot-indent-line))
(defun electric-graphviz-dot-terminate-line ()
"Terminate line and indent next line."
(interactive)
(if graphviz-dot-auto-indent-on-newline
(graphviz-dot-newline-and-indent)
(newline)))
(defun electric-graphviz-dot-open-brace ()
"Terminate line and indent next line."
(interactive)
(insert "{")
(if (and graphviz-dot-auto-indent-on-braces
(not (graphviz-dot-comment-or-string-p)))
(graphviz-dot-newline-and-indent)))
(defun electric-graphviz-dot-close-brace ()
"Terminate line and indent next line."
(interactive)
(insert "}")
(if (and graphviz-dot-auto-indent-on-braces
(not (graphviz-dot-comment-or-string-p)))
(progn
(save-excursion
(beginning-of-line)
(skip-chars-forward " \t")
(graphviz-dot-indent-line))
(newline)
(graphviz-dot-indent-line))))
(defun electric-graphviz-dot-semi ()
"Terminate line and indent next line."
(interactive)
(insert ";")
(if (and graphviz-dot-auto-indent-on-semi
(not (graphviz-dot-comment-or-string-p)))
(graphviz-dot-newline-and-indent)))
;;;;
;;;; Preview
;;;;
(defun graphviz-dot-preview ()
"Shows an example of the current dot file in an emacs buffer.
This assumes that we are running GNU Emacs or XEmacs under a windowing system.
See `image-file-name-extensions' for customizing the files that can be
loaded in GNU Emacs, and `image-formats-alist' for XEmacs."
(interactive)
;; unsafe to compile ourself, ask it to the user
(if (buffer-modified-p)
(message "Buffer needs to be compiled.")
(if (string-match "XEmacs" emacs-version)
;; things are easier in XEmacs...
(find-file-other-window (concat (file-name-sans-extension
buffer-file-name)
"." graphviz-dot-preview-extension))
;; run through all the extensions for images
(let ((l image-file-name-extensions))
(while
(let ((f (concat (file-name-sans-extension (buffer-file-name))
"."
(car l))))
;; see if a file matches, might be best also to check
;; if file is up to date TODO:PP
(if (file-exists-p f)
(progn (auto-image-file-mode 1)
;; OK, this is ugly, I would need to
;; know how I can reload a file in an existing buffer
(if (get-buffer "*preview*")
(kill-buffer "*preview*"))
(set-buffer (find-file-noselect f))
(rename-buffer "*preview*")
(display-buffer (get-buffer "*preview*"))
;; stop iterating
'())
;; will stop iterating when l is nil
(setq l (cdr l)))))
;; each extension tested and nothing found, let user know
(when (eq l '())
(message "No image found."))))))
;;;;
;;;; View
;;;;
(defun graphviz-dot-view ()
"Runs an external viewer. This creates an external process every time it
is executed. If `graphviz-dot-save-before-view' is set, the current
buffer is saved before the command is executed."
(interactive)
(let ((cmd (if graphviz-dot-view-edit-command
(if (string-match "XEmacs" emacs-version)
(read-shell-command "View command: "
(format graphviz-dot-view-command
(buffer-file-name)))
(read-from-minibuffer "View command: "
(format graphviz-dot-view-command
(buffer-file-name))))
(format graphviz-dot-view-command (buffer-file-name)))))
(if graphviz-dot-save-before-view
(save-buffer))
(setq novaproc (start-process-shell-command
(downcase mode-name) nil cmd))
(message (format "Executing `%s'..." cmd))))
;;;;
;;;; Completion
;;;;
(defvar graphviz-dot-str nil)
(defvar graphviz-dot-all nil)
(defvar graphviz-dot-pred nil)
(defvar graphviz-dot-buffer-to-use nil)
(defvar graphviz-dot-flag nil)
(defun graphviz-dot-get-state ()
"Returns the syntax state of the current point."
(let ((state (parse-partial-sexp (point-min) (point))))
(cond
((nth 4 state) 'comment)
((nth 3 state) 'string)
((not (nth 1 state)) 'out)
(t (save-excursion
(skip-chars-backward "^[,=\\[]{};")
(backward-char)
(cond
((looking-at "[\\[,]{};") 'attribute)
((looking-at "=") (progn
(backward-word 1)
(if (looking-at "[a-zA-Z]*color")
'color
'value)))
(t 'other)))))))
(defun graphviz-dot-get-keywords ()
"Return possible completions for a word"
(let ((state (graphviz-dot-get-state)))
(cond
((equal state 'comment) ())
((equal state 'string) ())
((equal state 'out) graphviz-attr-keywords)
((equal state 'value) graphviz-value-keywords)
((equal state 'color) graphviz-color-keywords)
((equal state 'attribute) graphviz-attr-keywords)
(t graphviz-attr-keywords))))
(defvar graphviz-dot-last-word-numb 0)
(defvar graphviz-dot-last-word-shown nil)
(defvar graphviz-dot-last-completions nil)
(defun graphviz-dot-complete-word ()
"Complete word at current point."
(interactive)
(let* ((b (save-excursion (skip-chars-backward "a-zA-Z0-9_") (point)))
(e (save-excursion (skip-chars-forward "a-zA-Z0-9_") (point)))
(graphviz-dot-str (buffer-substring b e))
(allcomp (if (and graphviz-dot-toggle-completions
(string= graphviz-dot-last-word-shown
graphviz-dot-str))
graphviz-dot-last-completions
(all-completions graphviz-dot-str
(graphviz-dot-get-keywords))))
(match (if graphviz-dot-toggle-completions
"" (try-completion
graphviz-dot-str (mapcar '(lambda (elm)
(cons elm 0)) allcomp)))))
;; Delete old string
(delete-region b e)
;; Toggle-completions inserts whole labels
(if graphviz-dot-toggle-completions
(progn
;; Update entry number in list
(setq graphviz-dot-last-completions allcomp
graphviz-dot-last-word-numb
(if (>= graphviz-dot-last-word-numb (1- (length allcomp)))
0
(1+ graphviz-dot-last-word-numb)))
(setq graphviz-dot-last-word-shown
(elt allcomp graphviz-dot-last-word-numb))
;; Display next match or same string if no match was found
(if (not (null allcomp))
(insert "" graphviz-dot-last-word-shown)
(insert "" graphviz-dot-str)
(message "(No match)")))
;; The other form of completion does not necessarily do that.
;; Insert match if found, or the original string if no match
(if (or (null match) (equal match 't))
(progn (insert "" graphviz-dot-str)
(message "(No match)"))
(insert "" match))
;; Give message about current status of completion
(cond ((equal match 't)
(if (not (null (cdr allcomp)))
(message "(Complete but not unique)")
(message "(Sole completion)")))
;; Display buffer if the current completion didn't help
;; on completing the label.
((and (not (null (cdr allcomp))) (= (length graphviz-dot-str)
(length match)))
(with-output-to-temp-buffer "*Completions*"
(display-completion-list allcomp))
;; Wait for a keypress. Then delete *Completion* window
(momentary-string-display "" (point))
(if graphviz-dot-delete-completions
(delete-window
(get-buffer-window (get-buffer "*Completions*"))))
)))))
;;;###autoload
(add-to-list 'auto-mode-alist '("\\.dot\\'" . graphviz-dot-mode))
(add-to-list 'auto-mode-alist '("\\.gv\\'" . graphviz-dot-mode))
;;; graphviz-dot-mode.el ends here

View file

@ -0,0 +1,99 @@
.PHONY: all lisp autoloads clean realclean distclean fullclean install test
.PRECIOUS: %.elc
DEFS = $(shell test -f ../Makefile.defs && echo ../Makefile.defs \
|| echo ../Makefile.defs.default)
include $(DEFS)
EL = $(filter-out $(PROJECT)-autoloads.el,$(wildcard *.el))
ELC = $(patsubst %.el,%.elc,$(EL))
all: lisp
lisp: autoloads $(ELC)
$(PROJECT)-build.elc: ../scripts/$(PROJECT)-build.el
@echo $(PROJECT)-build.el is not byte-compiled
autoloads: $(PROJECT)-autoloads.el
$(PROJECT)-autoloads.el: $(EL)
@$(EMACS) -q $(SITEFLAG) -batch -l ../scripts/$(PROJECT)-build.el \
-f $(PROJECT)-generate-autoloads . ../contrib ../experimental
%.elc: %.el
@$(EMACS) -q $(SITEFLAG) -batch -l ../scripts/$(PROJECT)-build.el \
-f batch-byte-compile $<
clean distclean:
-rm -f *.elc *~
realclean fullclean: clean
-rm -f $(PROJECT)-autoloads.el
install: autoloads $(ELC)
install -d $(ELISPDIR)
install -m 0644 $(PROJECT)-autoloads.el $(EL) $(ELC) $(ELISPDIR)
test: $(ELC)
$(EMACS) -q $(SITEFLAG) -batch -l ../scripts/$(PROJECT)-build.el \
-f $(PROJECT)-elint-files $(EL)
# Dependencies
#
# This allows us to recompile Muse safely after an update.
muse-backlink.elc: muse-mode.elc muse-publish.elc muse.elc
muse-blosxom.elc: muse-html.elc muse-project.elc muse-publish.elc
muse-book.elc: muse-latex.elc muse-project.elc muse-publish.elc
muse-book.elc: muse-regexps.elc
muse-colors.elc: muse-mode.elc muse-regexps.elc
muse-context.elc: muse-publish.elc
muse-docbook.elc: muse-publish.elc muse-regexps.elc muse-xml-common.elc
muse-groff.elc: muse-publish.elc
muse-html.elc: muse-publish.elc muse-regexps.elc muse-xml-common.elc
muse-http.elc: muse-html.elc muse-project.elc
muse-ikiwiki.elc: muse-html.elc muse-ipc.elc muse-publish.elc muse.elc
muse-import-docbook.elc: muse-import-xml.elc
muse-import-latex.elc: muse-regexps.elc muse.elc
muse-ipc.elc: muse-publish.elc muse.elc
muse-journal.elc: muse-book.elc muse-html.elc muse-latex.elc
muse-journal.elc: muse-publish.elc
muse-latex.elc: muse-publish.elc
muse-latex2png.elc: muse-publish.elc
muse-mode.elc: muse-regexps.elc muse-project.elc
muse-poem.elc: muse-latex.elc muse-project.elc
muse-project.elc: muse-publish.elc muse.elc
muse-protocols.elc: muse-regexps.elc
muse-publish.elc: muse-regexps.elc muse.elc
muse-texinfo.elc: muse-latex.elc muse-publish.elc
muse-wiki.elc: muse-colors.elc muse-regexps.elc muse-mode.elc
muse-xml-common.elc: muse-publish.elc muse-regexps.elc
muse-xml.elc: muse-publish.elc muse-regexps.elc muse-xml-common.elc
muse.elc: muse-protocols.elc muse-regexps.elc

View file

@ -0,0 +1,303 @@
;;; muse-autoloads.el --- autoloads for Muse
;;
;;; Code:
;;;### (autoloads nil "muse" "muse.el" (19301 54276))
;;; Generated autoloads from muse.el
(add-to-list 'auto-mode-alist '("\\.muse\\'" . muse-mode-choose-mode))
;;;***
;;;### (autoloads (muse-colors-toggle-inline-images) "muse-colors"
;;;;;; "muse-colors.el" (19301 53189))
;;; Generated autoloads from muse-colors.el
(autoload 'muse-colors-toggle-inline-images "muse-colors" "\
Toggle display of inlined images on/off.
\(fn)" t nil)
;;;***
;;;### (autoloads (muse-import-docbook-files muse-import-docbook)
;;;;;; "muse-import-docbook" "muse-import-docbook.el" (19301 53204))
;;; Generated autoloads from muse-import-docbook.el
(autoload 'muse-import-docbook "muse-import-docbook" "\
Convert the Docbook buffer SRC to Muse, writing output in the DEST buffer.
\(fn SRC DEST)" t nil)
(autoload 'muse-import-docbook-files "muse-import-docbook" "\
Convert the Docbook file SRC to Muse, writing output to the DEST file.
\(fn SRC DEST)" t nil)
;;;***
;;;### (autoloads (muse-import-latex) "muse-import-latex" "muse-import-latex.el"
;;;;;; (19301 53192))
;;; Generated autoloads from muse-import-latex.el
(autoload 'muse-import-latex "muse-import-latex" "\
Not documented
\(fn)" t nil)
;;;***
;;;### (autoloads (muse-message-markup) "muse-message" "../experimental/muse-message.el"
;;;;;; (18175 10245))
;;; Generated autoloads from ../experimental/muse-message.el
(autoload 'muse-message-markup "muse-message" "\
Markup a wiki-ish e-mail message as HTML alternative e-mail.
This step is manual by default, to give the author a chance to review
the results and ensure they are appropriate.
If you wish it to be automatic (a risky proposition), just add this
function to `message-send-hook'.
\(fn)" t nil)
;;;***
;;;### (autoloads (muse-list-edit-minor-mode muse-insert-tag muse-index
;;;;;; muse-find-backlinks muse-search muse-search-with-command
;;;;;; muse-what-changed muse-previous-reference muse-next-reference
;;;;;; muse-follow-name-at-point-other-window muse-follow-name-at-point
;;;;;; muse-browse-result muse-edit-link-at-point muse-insert-relative-link-to-file
;;;;;; muse-decrease-list-item-indentation muse-increase-list-item-indentation
;;;;;; muse-insert-list-item muse-mode-choose-mode muse-mode) "muse-mode"
;;;;;; "muse-mode.el" (19301 53218))
;;; Generated autoloads from muse-mode.el
(autoload 'muse-mode "muse-mode" "\
Muse is an Emacs mode for authoring and publishing documents.
\\{muse-mode-map}
\(fn)" t nil)
(autoload 'muse-mode-choose-mode "muse-mode" "\
Turn the proper Emacs Muse related mode on for this file.
\(fn)" nil nil)
(autoload 'muse-insert-list-item "muse-mode" "\
Insert a list item at the current point, taking into account
your current list type and indentation level.
\(fn)" t nil)
(autoload 'muse-increase-list-item-indentation "muse-mode" "\
Increase the indentation of the current list item.
\(fn)" t nil)
(autoload 'muse-decrease-list-item-indentation "muse-mode" "\
Decrease the indentation of the current list item.
\(fn)" t nil)
(autoload 'muse-insert-relative-link-to-file "muse-mode" "\
Insert a relative link to a file, with optional description, at point.
\(fn)" t nil)
(autoload 'muse-edit-link-at-point "muse-mode" "\
Edit the current link.
Do not rename the page originally referred to.
\(fn)" t nil)
(autoload 'muse-browse-result "muse-mode" "\
Visit the current page's published result.
\(fn STYLE &optional OTHER-WINDOW)" t nil)
(autoload 'muse-follow-name-at-point "muse-mode" "\
Visit the link at point.
\(fn &optional OTHER-WINDOW)" t nil)
(autoload 'muse-follow-name-at-point-other-window "muse-mode" "\
Visit the link at point in other window.
\(fn)" t nil)
(autoload 'muse-next-reference "muse-mode" "\
Move forward to next Muse link or URL, cycling if necessary.
\(fn)" t nil)
(autoload 'muse-previous-reference "muse-mode" "\
Move backward to the next Muse link or URL, cycling if necessary.
In case of Emacs x <= 21 and ignoring of intangible properties (see
`muse-mode-intangible-links').
This function is not entirely accurate, but it's close enough.
\(fn)" t nil)
(autoload 'muse-what-changed "muse-mode" "\
Show the unsaved changes that have been made to the current file.
\(fn)" t nil)
(autoload 'muse-search-with-command "muse-mode" "\
Search for the given TEXT string in the project directories
using the specified command.
\(fn TEXT)" t nil)
(autoload 'muse-search "muse-mode" "\
Search for the given TEXT using the default grep command.
\(fn)" t nil)
(autoload 'muse-find-backlinks "muse-mode" "\
Grep for the current pagename in all the project directories.
\(fn)" t nil)
(autoload 'muse-index "muse-mode" "\
Display an index of all known Muse pages.
\(fn)" t nil)
(autoload 'muse-insert-tag "muse-mode" "\
Insert a tag interactively with a blank line after it.
\(fn TAG)" t nil)
(autoload 'muse-list-edit-minor-mode "muse-mode" "\
This is a global minor mode for editing files with lists.
It is meant to be used with other major modes, and not with Muse mode.
Interactively, with no prefix argument, toggle the mode.
With universal prefix ARG turn mode on.
With zero or negative ARG turn mode off.
This minor mode provides the Muse keybindings for editing lists,
and support for filling lists properly.
It recognizes not only Muse-style lists, which use the \"-\"
character or numbers, but also lists that use asterisks or plus
signs. This should make the minor mode generally useful.
Definition lists and footnotes are also recognized.
Note that list items may omit leading spaces, for compatibility
with modes that set `left-margin', such as
`debian-changelog-mode'.
\\{muse-list-edit-minor-mode-map}
\(fn &optional ARG)" t nil)
;;;***
;;;### (autoloads (muse-project-publish muse-project-publish-this-file
;;;;;; muse-project-find-file) "muse-project" "muse-project.el"
;;;;;; (19301 53195))
;;; Generated autoloads from muse-project.el
(autoload 'muse-project-find-file "muse-project" "\
Open the Muse page given by NAME in PROJECT.
If COMMAND is non-nil, it is the function used to visit the file.
If DIRECTORY is non-nil, it is the directory in which the page
will be created if it does not already exist. Otherwise, the
first directory within the project's fileset is used.
\(fn NAME PROJECT &optional COMMAND DIRECTORY)" t nil)
(autoload 'muse-project-publish-this-file "muse-project" "\
Publish the currently-visited file according to `muse-project-alist',
prompting if more than one style applies.
If FORCE is given, publish the file even if it is up-to-date.
If STYLE is given, use that publishing style rather than
prompting for one.
\(fn &optional FORCE STYLE)" t nil)
(autoload 'muse-project-publish "muse-project" "\
Publish the pages of PROJECT that need publishing.
\(fn PROJECT &optional FORCE)" t nil)
;;;***
;;;### (autoloads (muse-browse-url) "muse-protocols" "muse-protocols.el"
;;;;;; (19301 53239))
;;; Generated autoloads from muse-protocols.el
(autoload 'muse-browse-url "muse-protocols" "\
Handle URL with the function specified in `muse-url-protocols'.
If OTHER-WINDOW is non-nil, open in a different window.
\(fn URL &optional OTHER-WINDOW)" t nil)
;;;***
;;;### (autoloads (muse-publish-this-file muse-publish-file muse-publish-region)
;;;;;; "muse-publish" "muse-publish.el" (19301 53119))
;;; Generated autoloads from muse-publish.el
(autoload 'muse-publish-region "muse-publish" "\
Apply the given STYLE's markup rules to the given region.
The result is placed in a new buffer that includes TITLE in its name.
\(fn BEG END &optional TITLE STYLE)" t nil)
(autoload 'muse-publish-file "muse-publish" "\
Publish the given FILE in a particular STYLE to OUTPUT-DIR.
If the argument FORCE is nil, each file is only published if it is
newer than the published version. If the argument FORCE is non-nil,
the file is published no matter what.
\(fn FILE STYLE &optional OUTPUT-DIR FORCE)" t nil)
(autoload 'muse-publish-this-file "muse-publish" "\
Publish the currently-visited file.
Prompt for both the STYLE and OUTPUT-DIR if they are not
supplied.
\(fn STYLE OUTPUT-DIR &optional FORCE)" t nil)
;;;***
;;;### (autoloads nil nil ("../contrib/cgi.el" "../contrib/htmlize-hack.el"
;;;;;; "../contrib/httpd.el" "../experimental/muse-cite.el" "../experimental/muse-mathml.el"
;;;;;; "../experimental/muse-protocol-iw.el" "../experimental/muse-split.el"
;;;;;; "muse-backlink.el" "muse-book.el" "muse-context.el" "muse-docbook.el"
;;;;;; "muse-groff.el" "muse-html.el" "muse-http.el" "muse-ikiwiki.el"
;;;;;; "muse-import-xml.el" "muse-ipc.el" "muse-journal.el" "muse-latex.el"
;;;;;; "muse-latex2png.el" "muse-poem.el" "muse-regexps.el" "muse-texinfo.el"
;;;;;; "muse-wiki.el" "muse-xml-common.el" "muse-xml.el") (19301
;;;;;; 55001 866391))
;;;***
;;;### (autoloads (muse-blosxom-new-entry) "muse-blosxom" "muse-blosxom.el"
;;;;;; (19301 53232))
;;; Generated autoloads from muse-blosxom.el
(autoload 'muse-blosxom-new-entry "muse-blosxom" "\
Start a new blog entry with given CATEGORY.
The filename of the blog entry is derived from TITLE.
The page will be initialized with the current date and TITLE.
\(fn CATEGORY TITLE)" t nil)
;;;***
(provide 'muse-autoloads)
;;; muse-autoloads.el ends here
;;
;; Local Variables:
;; version-control: never
;; no-byte-compile: t
;; no-update-autoloads: t
;; End:

View file

@ -0,0 +1,327 @@
;;; muse-backlink.el --- backlinks for Muse
;; Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010
;; Free Software Foundation, Inc.
;; Author: Jim Ottaway <j.ottaway@lse.ac.uk>
;; Keywords:
;; This file is part of Emacs Muse. It is not part of GNU Emacs.
;; Emacs Muse 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 3, or (at your
;; option) any later version.
;; Emacs Muse 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 Emacs Muse; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.
;;; Commentary:
;; Hierarchical backlink insertion into new muse pages.
;;
;; To add:
;;
;; (require 'muse-backlink)
;; (muse-backlink-install)
;;
;; To control what gets backlinked, modify
;; `muse-backlink-exclude-backlink-regexp' and
;; `muse-backlink-exclude-backlink-parent-regexp'.
;;
;; To stop backlinking temporarily:
;; (setq muse-backlink-create-backlinks nil)
;;
;; To remove the backlink functionality completely:
;;
;; (muse-backlink-remove)
;;; Contributors:
;;; Code:
(require 'muse)
(require 'muse-project)
(eval-when-compile (require 'muse-mode))
(eval-and-compile
(if (< emacs-major-version 22)
(progn
;; Swiped from Emacs 22.0.50.4
(defvar muse-backlink-split-string-default-separators "[ \f\t\n\r\v]+"
"The default value of separators for `split-string'.
A regexp matching strings of whitespace. May be locale-dependent
\(as yet unimplemented). Should not match non-breaking spaces.
Warning: binding this to a different value and using it as default is
likely to have undesired semantics.")
(defun muse-backlink-split-string (string &optional separators omit-nulls)
"Split STRING into substrings bounded by matches for SEPARATORS.
The beginning and end of STRING, and each match for SEPARATORS, are
splitting points. The substrings matching SEPARATORS are removed, and
the substrings between the splitting points are collected as a list,
which is returned.
If SEPARATORS is non-nil, it should be a regular expression matching text
which separates, but is not part of, the substrings. If nil it defaults to
`split-string-default-separators', normally \"[ \\f\\t\\n\\r\\v]+\", and
OMIT-NULLS is forced to t.
If OMIT-NULLS is t, zero-length substrings are omitted from the list \(so
that for the default value of SEPARATORS leading and trailing whitespace
are effectively trimmed). If nil, all zero-length substrings are retained,
which correctly parses CSV format, for example.
Note that the effect of `(split-string STRING)' is the same as
`(split-string STRING split-string-default-separators t)'). In the rare
case that you wish to retain zero-length substrings when splitting on
whitespace, use `(split-string STRING split-string-default-separators)'.
Modifies the match data; use `save-match-data' if necessary."
(let ((keep-nulls (not (if separators omit-nulls t)))
(rexp (or separators muse-backlink-split-string-default-separators))
(start 0)
notfirst
(list nil))
(while (and (string-match rexp string
(if (and notfirst
(= start (match-beginning 0))
(< start (length string)))
(1+ start) start))
(< start (length string)))
(setq notfirst t)
(if (or keep-nulls (< start (match-beginning 0)))
(setq list
(cons (substring string start (match-beginning 0))
list)))
(setq start (match-end 0)))
(if (or keep-nulls (< start (length string)))
(setq list
(cons (substring string start)
list)))
(nreverse list))))
(defalias 'muse-backlink-split-string 'split-string)))
(defgroup muse-backlink nil
"Hierarchical backlinking for Muse."
:group 'muse)
(defcustom muse-backlink-create-backlinks t
"When non-nil, create hierarchical backlinks in new Muse pages.
For control over which pages will receive backlinks, see
`muse-backlink-exclude-backlink-parent-regexp' and
`muse-backlink-exclude-backlink-regexp'."
:type 'boolean
:group 'muse-backlink)
(defcustom muse-backlink-avoid-bad-links t
"When non-nil, avoid bad links when backlinking."
:type 'boolean
:group 'muse-backlink)
;; The default for exclusion stops backlinks from being added to and
;; from planner day pages.
(defcustom muse-backlink-exclude-backlink-parent-regexp
"^[0-9][0-9][0-9][0-9]\\.[0-9][0-9]\\.[0-9][0-9]$"
"Regular expression matching pages whose children should not have backlinks."
:type 'regexp
:group 'muse-backlink)
(defcustom muse-backlink-exclude-backlink-regexp
"^[0-9][0-9][0-9][0-9]\\.[0-9][0-9]\\.[0-9][0-9]$"
"Regular expression matching pages that should not have backlinks."
:type 'regexp
:group 'muse-backlink)
(defcustom muse-backlink-separator "/"
"String that separates backlinks.
Should be something that will not appear as a substring in an explicit
link that has no description."
:type 'string
:group 'muse-backlink)
(defcustom muse-backlink-before-string "backlinks: "
"String to come before the backlink list."
:type 'string
:group 'muse-backlink)
(defcustom muse-backlink-after-string ""
"String to come after the backlink list."
:type 'string
:group 'muse-backlink)
(defcustom muse-backlink-separator "/"
"String that separates backlinks.
Should be something that will not appear as a substring in an explicit
link that has no description."
:type 'string
:group 'muse-backlink)
(defcustom muse-backlink-regexp
(concat "^"
(regexp-quote muse-backlink-before-string)
"\\("
(regexp-quote muse-backlink-separator)
".+\\)"
(regexp-quote muse-backlink-after-string))
;; Really, I want something like this, but I can't make it work:
;; (concat "^\\("
;; (regexp-quote muse-backlink-separator)
;; "\\(?:"
;; muse-explicit-link-regexp
;; "\\)\\)+")
"Regular expression to match backlinks in a buffer.
Match 1 is the list of backlinks without `muse-backlink-before-string'
and `muse-backlink-after-string'."
:type 'regexp
:group 'muse-backlink)
(defun muse-backlink-goto-insertion-point ()
"Find the right place to add backlinks."
(goto-char (point-min))
(when (looking-at "\\(?:^#.+[ \t]*\n\\)+")
(goto-char (match-end 0))))
(defun muse-backlink-get-current ()
"Return a list of backlinks in the current buffer."
(save-excursion
(goto-char (point-min))
(when (re-search-forward muse-backlink-regexp nil t)
(muse-backlink-split-string
(match-string 1)
(regexp-quote muse-backlink-separator) t))))
(defun muse-backlink-format-link-list (links)
"Format the list of LINKS as backlinks."
(concat muse-backlink-separator
(mapconcat #'identity links muse-backlink-separator)))
(defun muse-backlink-insert-links (links)
"Insert backlinks to LINKS into the current page.
LINKS is a list of links ordered by ancestry, with the parent as the
last element."
(muse-backlink-goto-insertion-point)
(insert muse-backlink-before-string
(muse-backlink-format-link-list links)
muse-backlink-after-string
;; Could have this in the after string, but they might get
;; deleted.
"\n\n"))
(defun muse-backlink-unsaved-page-p (page project)
"Return non-nil if PAGE is in PROJECT but has not been saved."
(member
page
(mapcar
#'(lambda (b)
(with-current-buffer b
(and (derived-mode-p 'muse-mode)
(equal muse-current-project project)
(not (muse-project-page-file
(muse-page-name)
muse-current-project))
(muse-page-name))))
(buffer-list))))
(defvar muse-backlink-links nil
"Internal variable.
The links to insert in the forthcomingly visited muse page.")
(defvar muse-backlink-pending nil
"Internal variable.")
(defvar muse-backlink-parent-buffer nil
"Internal variable.
The parent buffer of the forthcomingly visited muse page.")
;;; Attach hook to the derived mode hook, to avoid problems such as
;;; planner-prepare-file thinking that the buffer needs no template.
(defun muse-backlink-get-mode-hook ()
(derived-mode-hook-name major-mode))
(defun muse-backlink-insert-hook-func ()
"Insert backlinks into the current buffer and clean up."
(when (and muse-backlink-links
muse-backlink-pending
(string= (car muse-backlink-links) (muse-page-name)))
(muse-backlink-insert-links (cdr muse-backlink-links))
(when muse-backlink-avoid-bad-links
(save-buffer)
(when muse-backlink-parent-buffer
(with-current-buffer muse-backlink-parent-buffer
(font-lock-fontify-buffer))))
(setq muse-backlink-links nil
muse-backlink-parent-buffer nil
muse-backlink-pending nil)
(remove-hook (muse-backlink-get-mode-hook) #'muse-backlink-insert-hook-func)))
(defun muse-backlink-handle-link (link)
"When appropriate, arrange for backlinks on visiting LINK."
(when (and muse-backlink-create-backlinks
(not muse-backlink-pending)
(memq this-command
'(muse-follow-name-at-point muse-follow-name-at-mouse))
(not muse-publishing-p)
(not (and (boundp 'muse-colors-fontifying-p)
muse-colors-fontifying-p)))
(require 'muse-mode)
(setq
muse-backlink-links
(save-match-data
(let* ((orig-link (or link (match-string 1)))
(link (if (string-match "#" orig-link)
(substring orig-link 0 (match-beginning 0))
orig-link)))
(unless
(or (not muse-current-project)
(string-match muse-url-regexp orig-link)
(string-match muse-image-regexp orig-link)
(and (boundp 'muse-wiki-interwiki-regexp)
(string-match muse-wiki-interwiki-regexp
orig-link))
;; Don't add a backlink if the page already
;; exists, whether it has been saved or not.
(or (muse-project-page-file link muse-current-project)
(muse-backlink-unsaved-page-p link muse-current-project))
(string-match muse-backlink-exclude-backlink-parent-regexp
(muse-page-name))
(string-match muse-backlink-exclude-backlink-regexp link))
;; todo: Hmm. This will only work if the child page is the
;; same mode as the parent page.
(add-hook (muse-backlink-get-mode-hook) #'muse-backlink-insert-hook-func)
(setq muse-backlink-pending t)
(when muse-backlink-avoid-bad-links
(setq muse-backlink-parent-buffer (current-buffer))
(unless (muse-project-page-file
(muse-page-name) muse-current-project)
;; It must be modified...
(save-buffer)))
(cons link
(append (muse-backlink-get-current)
(list (muse-make-link (muse-page-name))))))))))
;; Make sure we always return nil
nil)
(defun muse-backlink-install ()
"Add backlinking functionality to muse-mode."
(add-to-list 'muse-explicit-link-functions #'muse-backlink-handle-link))
(defun muse-backlink-remove ()
"Remove backlinking functionality from muse-mode."
(setq muse-explicit-link-functions
(delq #'muse-backlink-handle-link muse-explicit-link-functions)))
(provide 'muse-backlink)
;;; muse-backlink.el ends here

View file

@ -0,0 +1,306 @@
;;; muse-blosxom.el --- publish a document tree for serving by (py)Blosxom
;; Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010
;; Free Software Foundation, Inc.
;; Author: Michael Olson <mwolson@gnu.org>
;; Date: Wed, 23 March 2005
;; This file is part of Emacs Muse. It is not part of GNU Emacs.
;; Emacs Muse 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 3, or (at your
;; option) any later version.
;; Emacs Muse 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 Emacs Muse; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.
;;; Commentary:
;; The Blosxom publishing style publishes a tree of categorised files
;; to a mirrored tree of stories to be served by blosxom.cgi or
;; pyblosxom.cgi.
;;
;; Serving entries with (py)blosxom
;; --------------------------------
;;
;; Each Blosxom file must include `#date yyyy-mm-dd', or optionally
;; the longer `#date yyyy-mm-dd-hh-mm', a title (using the `#title'
;; directive) plus whatever normal content is desired.
;;
;; The date directive is not used directly by (py)blosxom or this
;; program. You need to find two additional items to make use of this
;; feature.
;;
;; 1. A script to gather date directives from the entire blog tree
;; into a single file. The file must associate a blog entry with
;; a date.
;;
;; 2. A plugin for (py)blosxom that reads this file.
;;
;; These 2 things are provided for pyblosxom in the contrib/pyblosxom
;; subdirectory. `getstamps.py' provides the 1st service, while
;; `hardcodedates.py' provides the second service. Eventually it is
;; hoped that a blosxom plugin and script will be found/written.
;;
;; Alternately, the pyblosxom metadate plugin may be used. On the
;; plus side, there is no need to run a script to gather the date. On
;; the downside, each entry is read twice rather than once when the
;; page is rendered. Set the value of muse-blosxom-use-metadate to
;; non-nil to enable adding a #postdate directive to all published
;; files. You can do this by:
;;
;; M-x customize-variable RET muse-blosxom-use-metadate RET
;;
;; With the metadate plugin installed in pyblosxom, the date set in
;; this directive will be used instead of the file's modification
;; time. The plugin is included with Muse at
;; contrib/pyblosxom/metadate.py.
;;
;; Generating a Muse project entry
;; -------------------------------
;;
;; Muse-blosxom has some helper functions to make specifying
;; muse-blosxom projects a lot easier. An example follows.
;;
;; (setq muse-project-alist
;; `(("blog"
;; (,@(muse-project-alist-dirs "~/path/to/blog-entries")
;; :default "index")
;; ,@(muse-project-alist-styles "~/path/to/blog-entries"
;; "~/public_html/blog"
;; "blosxom-xhtml")
;; )))
;;
;; Note that we need a backtick instead of a single quote on the
;; second line of this example.
;;
;; Creating new blog entries
;; -------------------------
;;
;; There is a function called `muse-blosxom-new-entry' that will
;; automate the process of making a new blog entry. To make use of
;; it, do the following.
;;
;; - Customize `muse-blosxom-base-directory' to the location that
;; your blog entries are stored.
;;
;; - Assign the `muse-blosxom-new-entry' function to a key sequence.
;; I use the following code to assign this function to `C-c p l'.
;;
;; (global-set-key "\C-cpl" 'muse-blosxom-new-entry)
;;
;; - You should create your directory structure ahead of time under
;; your base directory. These directories, which correspond with
;; category names, may be nested.
;;
;; - When you enter this key sequence, you will be prompted for the
;; category of your entry and its title. Upon entering this
;; information, a new file will be created that corresponds with
;; the title, but in lowercase letters and having special
;; characters converted to underscores. The title and date
;; directives will be inserted automatically.
;;
;; Using tags
;; ----------
;;
;; If you wish to keep all of your blog entries in one directory and
;; use tags to classify your entries, set `muse-blosxom-use-tags' to
;; non-nil.
;;
;; For this to work, you will need to be using the PyBlosxom plugin at
;; http://pyblosxom.sourceforge.net/blog/registry/meta/Tags.
;;; Contributors:
;; Gary Vaughan (gary AT gnu DOT org) is the original author of
;; `emacs-wiki-blosxom.el', which is the ancestor of this file.
;; Brad Collins (brad AT chenla DOT org) ported this file to Muse.
;; Björn Lindström (bkhl AT elektrubadur DOT se) made many valuable
;; suggestions.
;; Sasha Kovar (sasha AT arcocene DOT org) fixed
;; muse-blosxom-new-entry when using tags and also implemented support
;; for the #postdate directive.
;;; Code:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; Muse Blosxom Publishing
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(require 'muse-project)
(require 'muse-publish)
(require 'muse-html)
(defgroup muse-blosxom nil
"Options controlling the behavior of Muse Blosxom publishing.
See `muse-blosxom' for more information."
:group 'muse-publish)
(defcustom muse-blosxom-extension ".txt"
"Default file extension for publishing Blosxom files."
:type 'string
:group 'muse-blosxom)
(defcustom muse-blosxom-header
"<lisp>(concat (muse-publishing-directive \"title\") \"\\n\"
(when muse-blosxom-use-metadate
(let ((date (muse-publishing-directive \"date\")))
(when date (concat \"#postdate \"
(muse-blosxom-format-date date) \"\\n\"))))
(when muse-blosxom-use-tags
(let ((tags (muse-publishing-directive \"tags\")))
(when tags (concat \"#tags \" tags \"\\n\")))))</lisp>"
"Header used for publishing Blosxom files. This may be text or a filename."
:type 'string
:group 'muse-blosxom)
(defcustom muse-blosxom-footer ""
"Footer used for publishing Blosxom files. This may be text or a filename."
:type 'string
:group 'muse-blosxom)
(defcustom muse-blosxom-base-directory "~/Blog"
"Base directory of blog entries.
This is the top-level directory where your Muse blog entries may be found."
:type 'directory
:group 'muse-blosxom)
(defcustom muse-blosxom-use-tags nil
"Determine whether or not to enable use of the #tags directive.
If you wish to keep all of your blog entries in one directory and
use tags to classify your entries, set `muse-blosxom-use-tags' to
non-nil.
For this to work, you will need to be using the PyBlosxom plugin
at http://pyblosxom.sourceforge.net/blog/registry/meta/Tags."
:type 'boolean
:group 'muse-blosxom)
(defcustom muse-blosxom-use-metadate nil
"Determine whether or not to use the #postdate directive.
If non-nil, published entries include the original date (as specified
in the muse #date line) which can be read by the metadate PyBlosxom
plugin.
For this to work, you will need to be using the PyBlosxom plugin
at http://pyblosxom.sourceforge.net/blog/registry/date/metadate."
:type 'boolean
:group 'muse-blosxom)
;; Maintain (published-file . date) alist, which will later be written
;; to a timestamps file; not implemented yet.
(defvar muse-blosxom-page-date-alist nil)
(defun muse-blosxom-update-page-date-alist ()
"Add a date entry to `muse-blosxom-page-date-alist' for this page."
(when muse-publishing-current-file
;; Make current file be relative to base directory
(let ((rel-file
(concat
(file-name-as-directory
(or (muse-publishing-directive "category")
(file-relative-name
(file-name-directory
(expand-file-name muse-publishing-current-file))
(file-truename muse-blosxom-base-directory))))
(file-name-nondirectory muse-publishing-current-file))))
;; Strip the file extension
(when muse-ignored-extensions-regexp
(setq rel-file (save-match-data
(and (string-match muse-ignored-extensions-regexp
rel-file)
(replace-match "" t t rel-file)))))
;; Add to page-date alist
(add-to-list
'muse-blosxom-page-date-alist
`(,rel-file . ,(muse-publishing-directive "date"))))))
;; Enter a new blog entry
(defun muse-blosxom-title-to-file (title)
"Derive a file name from the given TITLE.
Feel free to overwrite this if you have a different concept of what
should be allowed in a filename."
(muse-replace-regexp-in-string (concat "[^-." muse-regexp-alnum "]")
"_" (downcase title)))
(defun muse-blosxom-format-date (date)
"Convert a date string to PyBlosxom metadate plugin format."
(apply #'format "%s-%s-%s %s:%s" (split-string date "-")))
;;;###autoload
(defun muse-blosxom-new-entry (category title)
"Start a new blog entry with given CATEGORY.
The filename of the blog entry is derived from TITLE.
The page will be initialized with the current date and TITLE."
(interactive
(list
(if muse-blosxom-use-tags
(let ((tag "foo")
(tags nil))
(while (progn (setq tag (read-string "Tag (RET to continue): "))
(not (string= tag "")))
(add-to-list 'tags tag t))
tags)
(funcall muse-completing-read-function
"Category: "
(mapcar 'list (muse-project-recurse-directory
muse-blosxom-base-directory))))
(read-string "Title: ")))
(let ((file (muse-blosxom-title-to-file title)))
(muse-project-find-file
file "blosxom" nil
(if muse-blosxom-use-tags
(directory-file-name muse-blosxom-base-directory)
(concat (directory-file-name muse-blosxom-base-directory)
"/" category))))
(goto-char (point-min))
(insert "#date " (format-time-string "%Y-%m-%d-%H-%M")
"\n#title " title)
(if muse-blosxom-use-tags
(if (> (length category) 0)
(insert (concat "\n#tags " (mapconcat #'identity category ","))))
(unless (string= category "")
(insert (concat "\n#category " category))))
(insert "\n\n")
(forward-line 2))
;;; Register the Muse Blosxom Publisher
(muse-derive-style "blosxom-html" "html"
:suffix 'muse-blosxom-extension
:link-suffix 'muse-html-extension
:header 'muse-blosxom-header
:footer 'muse-blosxom-footer
:after 'muse-blosxom-update-page-date-alist
:browser 'find-file)
(muse-derive-style "blosxom-xhtml" "xhtml"
:suffix 'muse-blosxom-extension
:link-suffix 'muse-xhtml-extension
:header 'muse-blosxom-header
:footer 'muse-blosxom-footer
:after 'muse-blosxom-update-page-date-alist
:browser 'find-file)
(provide 'muse-blosxom)
;;; muse-blosxom.el ends here

View file

@ -0,0 +1,284 @@
;;; muse-book.el --- publish entries into a compilation
;; Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010
;; Free Software Foundation, Inc.
;; This file is part of Emacs Muse. It is not part of GNU Emacs.
;; Emacs Muse 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 3, or (at your
;; option) any later version.
;; Emacs Muse 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 Emacs Muse; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.
;;; Commentary:
;;; Contributors:
;;; Code:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; Muse Book Publishing
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(require 'muse-publish)
(require 'muse-project)
(require 'muse-latex)
(require 'muse-regexps)
(defgroup muse-book nil
"Module for publishing a series of Muse pages as a complete book.
Each page will become a separate chapter in the book, unless the
style keyword :nochapters is used, in which case they are all run
together as if one giant chapter."
:group 'muse-publish)
(defcustom muse-book-before-publish-hook nil
"A hook run in the book buffer before it is marked up."
:type 'hook
:group 'muse-book)
(defcustom muse-book-after-publish-hook nil
"A hook run in the book buffer after it is marked up."
:type 'hook
:group 'muse-book)
(defcustom muse-book-latex-header
"\\documentclass{book}
\\usepackage[english]{babel}
\\usepackage[latin1]{inputenc}
\\usepackage[T1]{fontenc}
\\begin{document}
\\title{<lisp>(muse-publishing-directive \"title\")</lisp>}
\\author{<lisp>(muse-publishing-directive \"author\")</lisp>}
\\date{<lisp>(muse-publishing-directive \"date\")</lisp>}
\\maketitle
\\tableofcontents\n"
"Header used for publishing books to LaTeX. This may be text or a filename."
:type 'string
:group 'muse-book)
(defcustom muse-book-latex-footer
"<lisp>(muse-latex-bibliography)</lisp>
\\end{document}"
"Footer used for publishing books to LaTeX. This may be text or a filename."
:type 'string
:group 'muse-book)
(defun muse-book-publish-chapter (title entry style &optional nochapters)
"Publish the chapter TITLE for the file ENTRY using STYLE.
TITLE is a string, ENTRY is a cons of the form (PAGE-NAME .
FILE), and STYLE is a Muse style list.
This routine does the same basic work as `muse-publish-markup-buffer',
but treating the page as if it were a single chapter within a book."
(let ((muse-publishing-directives (list (cons "title" title)))
(muse-publishing-current-file (cdr entry))
(beg (point)) end)
(muse-insert-file-contents (cdr entry))
(setq end (copy-marker (point-max) t))
(muse-publish-markup-region beg end (car entry) style)
(goto-char beg)
(unless (or nochapters
(muse-style-element :nochapters style))
(insert "\n")
(muse-insert-markup (muse-markup-text 'chapter))
(insert (let ((chap (muse-publishing-directive "title")))
(if (string= chap title)
(car entry)
chap)))
(muse-insert-markup (muse-markup-text 'chapter-end))
(insert "\n\n"))
(save-restriction
(narrow-to-region beg end)
(muse-publish-markup (or title "")
'((100 "<\\(lisp\\)>" 0
muse-publish-markup-tag)))
(muse-style-run-hooks :after style))
(goto-char end)))
(defun muse-book-publish-p (project target)
"Determine whether the book in PROJECT is out-of-date."
(let ((pats (cadr project)))
(catch 'publish
(while pats
(if (symbolp (car pats))
(if (eq :book-end (car pats))
(throw 'publish nil)
;; skip past symbol-value pair
(setq pats (cddr pats)))
(dolist (entry (muse-project-file-entries (car pats)))
(when (and (not (muse-project-private-p (cdr entry)))
(file-newer-than-file-p (cdr entry) target))
(throw 'publish t)))
(setq pats (cdr pats)))))))
(defun muse-book-get-directives (file)
"Interpret any publishing directives contained in FILE.
This is meant to be called in a temp buffer that will later be
used for publishing."
(save-restriction
(narrow-to-region (point) (point))
(unwind-protect
(progn
(muse-insert-file-contents file)
(muse-publish-markup
"attributes"
`(;; Remove leading and trailing whitespace from the file
(100 "\\(\\`\n+\\|\n+\\'\\)" 0 "")
;; Remove trailing whitespace from all lines
(200 ,(concat "[" muse-regexp-blank "]+$") 0 "")
;; Handle any leading #directives
(300 "\\`#\\([a-zA-Z-]+\\)\\s-+\\(.+\\)\n+"
0 muse-publish-markup-directive))))
(delete-region (point-min) (point-max)))))
(defun muse-book-publish-project
(project book title style &optional output-dir force)
"Publish PROJECT under the name BOOK with the given TITLE and STYLE.
BOOK should be a page name, i.e., letting the style determine the
prefix and/or suffix. The book is published to OUTPUT-DIR. If FORCE
is nil, the book is only published if at least one of its component
pages has changed since it was last published."
(interactive
(let ((project (muse-read-project "Publish project as book: " nil t)))
(append (list project
(read-string "Basename of book (without extension): ")
(read-string "Title of book: "))
(muse-publish-get-info))))
(setq project (muse-project project))
(let ((muse-current-project project))
;; See if any of the project's files need saving first
(muse-project-save-buffers project)
;; Publish the book
(muse-book-publish book style output-dir force title)))
(defun muse-book-publish (file style &optional output-dir force title)
"Publish FILE as a book with the given TITLE and STYLE.
The book is published to OUTPUT-DIR. If FORCE is nil, the book
is only published if at least one of its component pages has
changed since it was last published."
;; Cleanup some of the arguments
(let ((style-name style))
(setq style (muse-style style))
(unless style
(error "There is no style '%s' defined" style-name)))
;; Publish each page in the project as a chapter in one large book
(let* ((output-path (muse-publish-output-file file output-dir style))
(output-suffix (muse-style-element :osuffix style))
(target output-path)
(project muse-current-project)
(published nil))
(when output-suffix
(setq target (concat (muse-path-sans-extension target)
output-suffix)))
;; Unless force is non-nil, determine if the book needs publishing
(if (and (not force)
(not (muse-book-publish-p project target)))
(message "The book \"%s\" is up-to-date." file)
;; Create the book from all its component parts
(muse-with-temp-buffer
(let ((style-final (muse-style-element :final style t))
(style-header (muse-style-element :header style))
(style-footer (muse-style-element :footer style))
(muse-publishing-current-style style)
(muse-publishing-directives
(list (cons "title" (or title (muse-page-name file)))
(cons "date" (format-time-string "%B %e, %Y"))))
(muse-publishing-p t)
(muse-current-project project)
(pats (cadr project))
(nochapters nil))
(run-hooks 'muse-before-book-publish-hook)
(let ((style-final style-final)
(style-header style-header)
(style-footer style-footer))
(unless title
(muse-book-get-directives file)
(setq title (muse-publishing-directive "title")))
(while pats
(if (symbolp (car pats))
(cond
((eq :book-part (car pats))
(insert "\n")
(muse-insert-markup (muse-markup-text 'part))
(insert (cadr pats))
(muse-insert-markup (muse-markup-text 'part-end))
(insert "\n")
(setq pats (cddr pats)))
((eq :book-chapter (car pats))
(insert "\n")
(muse-insert-markup (muse-markup-text 'chapter))
(insert (cadr pats))
(muse-insert-markup (muse-markup-text 'chapter-end))
(insert "\n")
(setq pats (cddr pats)))
((eq :nochapters (car pats))
(setq nochapters t
pats (cddr pats)))
((eq :book-style (car pats))
(setq style (muse-style (cadr pats)))
(setq style-final (muse-style-element :final style t)
style-header (muse-style-element :header style)
style-footer (muse-style-element :footer style)
muse-publishing-current-style style)
(setq pats (cddr pats)))
((eq :book-funcall (car pats))
(funcall (cadr pats))
(setq pats (cddr pats)))
((eq :book-end (car pats))
(setq pats nil))
(t
(setq pats (cddr pats))))
(let ((entries (muse-project-file-entries (car pats))))
(while (and entries (car entries) (caar entries))
(unless (muse-project-private-p (cdar entries))
(muse-book-publish-chapter title (car entries)
style nochapters)
(setq published t))
(setq entries (cdr entries))))
(setq pats (cdr pats)))))
(goto-char (point-min))
(if style-header (muse-insert-file-or-string style-header file))
(goto-char (point-max))
(if style-footer (muse-insert-file-or-string style-footer file))
(run-hooks 'muse-after-book-publish-hook)
(if (muse-write-file output-path)
(if style-final
(funcall style-final file output-path target))
(setq published nil)))))
(if published
(message "The book \"%s\" has been published." file))
published))
;;; Register the Muse BOOK Publishers
(muse-derive-style "book-latex" "latex"
:header 'muse-book-latex-header
:footer 'muse-book-latex-footer
:publish 'muse-book-publish)
(muse-derive-style "book-pdf" "pdf"
:header 'muse-book-latex-header
:footer 'muse-book-latex-footer
:publish 'muse-book-publish)
(provide 'muse-book)
;;; muse-book.el ends here

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,458 @@
;;; muse-context.el --- publish entries in ConTeXt or PDF format
;; Copyright (C) 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
;; Author: Jean Magnan de Bornier (jean@bornier.net)
;; Created: 16-Apr-2007
;; Emacs Muse 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 3, or (at your
;; option) any later version.
;; This file when loaded allows you to publish .muse files as ConTeXt
;; files or as pdf files, using respectively the "context" and
;; "context-pdf" styles. It is far from being perfect, so any feedback
;; will be welcome and any mistake hopefully fixed.
;;; Author:
;; Jean Magnan de Bornier, who based this file on muse-latex.el and
;; made the context, context-pdf, context-slides, and
;; context-slides-pdf Muse publishing styles.
;; 16 Avril 2007
;;; Code:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; Muse ConTeXt Publishing
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(require 'muse-publish)
(defgroup muse-context nil
"Rules for marking up a Muse file as a ConTeXt article."
:group 'muse-publish)
(defcustom muse-context-extension ".tex"
"Default file extension for publishing ConTeXt files."
:type 'string
:group 'muse-context)
(defcustom muse-context-pdf-extension ".pdf"
"Default file extension for publishing ConTeXt files to PDF."
:type 'string
:group 'muse-context)
(defcustom muse-context-pdf-program "texexec --pdf"
"The program that is called to generate PDF content from ConTeXt content."
:type 'string
:group 'muse-context)
(defcustom muse-context-pdf-cruft '(".pgf" ".tmp" ".tui" ".tuo" ".toc" ".log")
"Extensions of files to remove after generating PDF output successfully."
:type 'string
:group 'muse-context)
(defcustom muse-context-header
"\\setupinteraction [state=start]
\\usemodule[tikz]
\\usemodule[bib]\n
<lisp>(muse-context-setup-bibliography)</lisp>
\\setuppublications[]\n
\\setuppublicationlist[]\n\\setupcite[]\n
\\starttext
\\startalignment[center]
\\blank[2*big]
{\\tfd <lisp>(muse-publishing-directive \"title\")</lisp>}
\\blank[3*medium]
{\\tfa <lisp>(muse-publishing-directive \"author\")</lisp>}
\\blank[2*medium]
{\\tfa <lisp>(muse-publishing-directive \"date\")</lisp>}
\\blank[3*medium]
\\stopalignment
<lisp>(and muse-publish-generate-contents
(not muse-context-permit-contents-tag)
\"\\\\placecontent\n\\\\page[yes]\")</lisp>\n\n"
"Header used for publishing ConTeXt files. This may be text or a filename."
:type 'string
:group 'muse-context)
(defcustom muse-context-footer "<lisp>(muse-context-bibliography)</lisp>
\\stoptext\n"
"Footer used for publishing ConTeXt files. This may be text or a filename."
:type 'string
:group 'muse-context)
(defcustom muse-context-markup-regexps
`(;; numeric ranges
(10000 "\\([0-9]+\\)-\\([0-9]+\\)" 0 "\\1--\\2")
;; be careful of closing quote pairs
(10100 "\"'" 0 "\"\\\\-'"))
"List of markup regexps for identifying regions in a Muse page.
For more on the structure of this list, see `muse-publish-markup-regexps'."
:type '(repeat (choice
(list :tag "Markup rule"
integer
(choice regexp symbol)
integer
(choice string function symbol))
function))
:group 'muse-context)
(defcustom muse-context-markup-functions
'((table . muse-context-markup-table))
"An alist of style types to custom functions for that kind of text.
For more on the structure of this list, see
`muse-publish-markup-functions'."
:type '(alist :key-type symbol :value-type function)
:group 'muse-context)
(defcustom muse-context-markup-strings
'((image-with-desc . "\\placefigure[][]{%3%}{\\externalfigure[%1%.%2%]}")
(image . "\\placefigure[][]{}{\\externalfigure[%s.%s]}")
(image-link . "\\useURL[aa][%s][][%1%] \\from[aa]")
(anchor-ref . "\\goto{%2%}{}[%1%]")
(url . "\\useURL[aa][%s][][%s] \\from[aa]")
(url-and-desc . "\\useURL[bb][%s][][%s]\\from[bb]\\footnote{%1%}")
(link . "\\goto{%2%}[program(%1%)]\\footnote{%1%}")
(link-and-anchor . "\\useexternaldocument[%4%][%4%][] \\at{%3%, page}{}[%4%::%2%]\\footnote{%1%}")
(email-addr . "\\useURL[mail][mailto:%s][][%s]\\from[mail]")
(anchor . "\\reference[%s] ")
(emdash . "---")
(comment-begin . "\\doifmode{comment}{")
(comment-end . "}")
(rule . "\\blank[medium]\\hrule\\blank[medium]")
(no-break-space . "~")
(enddots . "\\ldots ")
(dots . "\\dots ")
(part . "\\part{")
(part-end . "}")
(chapter . "\\chapter{")
(chapter-end . "}")
(section . "\\section{")
(section-end . "}")
(subsection . "\\subsection{")
(subsection-end . "}")
(subsubsection . "\\subsubsection{")
(subsubsection-end . "}")
(section-other . "\\subsubsubject{")
(section-other-end . "}")
(footnote . "\\footnote{")
(footnote-end . "}")
(footnotetext . "\\footnotetext[%d]{")
(begin-underline . "\\underbar{")
(end-underline . "}")
(begin-literal . "\\type{")
(end-literal . "}")
(begin-emph . "{\\em ")
(end-emph . "}")
(begin-more-emph . "{\\bf ")
(end-more-emph . "}")
(begin-most-emph . "{\\bf {\\em ")
(end-most-emph . "}}")
(begin-example . "\\starttyping")
(end-example . "\\stoptyping")
(begin-center . "\\startalignment[center]\n")
(end-center . "\n\\stopalignment")
(begin-quote . "\\startquotation\n")
(end-quote . "\n\\stopquotation")
(begin-cite . "\\cite[authoryear][")
(begin-cite-author . "\\cite[author][")
(begin-cite-year . "\\cite[year][")
(end-cite . "]")
(begin-uli . "\\startitemize\n")
(end-uli . "\n\\stopitemize")
(begin-uli-item . "\\item ")
(begin-oli . "\\startitemize[n]\n")
(end-oli . "\n\\stopitemize")
(begin-oli-item . "\\item ")
(begin-dl . "\\startitemize\n")
(end-dl . "\n\\stopitemize")
(begin-ddt . "\\head ")
(end-ddt . "\n")
(begin-verse . "\\blank[big]")
(end-verse-line . "\\par")
(verse-space . "\\fixedspaces ~~")
(end-verse . "\\blank[big]"))
"Strings used for marking up text.
These cover the most basic kinds of markup, the handling of which
differs little between the various styles."
:type '(alist :key-type symbol :value-type string)
:group 'muse-context)
(defcustom muse-context-slides-header
"\\usemodule[<lisp>(if (string-equal (muse-publishing-directive \"module\") nil) \"pre-01\" (muse-publishing-directive \"module\"))</lisp>]
\\usemodule[tikz]
\\usemodule[newmat]
\\setupinteraction [state=start]
\\starttext
\\TitlePage { <lisp>(muse-publishing-directive \"title\")</lisp>
\\blank[3*medium]
\\tfa <lisp>(muse-publishing-directive \"author\")</lisp>
\\blank[2*medium]
\\tfa <lisp>(muse-publishing-directive \"date\")</lisp>}"
"Header for publishing a presentation (slides) using ConTeXt.
Any of the predefined modules, which are available in the
tex/context/base directory, can be used by writing a \"module\"
directive at the top of the muse file; if no such directive is
provided, module pre-01 is used. Alternatively, you can use your
own style (\"mystyle\", in this example) by replacing
\"\\usemodule[]\" with \"\\input mystyle\".
This may be text or a filename."
:type 'string
:group 'muse-context)
(defcustom muse-context-slides-markup-strings
'((section . "\\Topic {")
(subsection . "\\page \n{\\bf ")
(subsubsection . "{\\em "))
"Strings used for marking up text in ConTeXt slides."
:type '(alist :key-type symbol :value-type string)
:group 'muse-context)
(defcustom muse-context-markup-specials-document
'((?\\ . "\\textbackslash{}")
(?\_ . "\\textunderscore{}")
(?\< . "\\switchtobodyfont[small]")
(?\> . "\\switchtobodyfont[big]")
(?^ . "\\^")
(?\~ . "\\~")
(?\@ . "\\@")
(?\$ . "\\$")
(?\% . "\\%")
(?\{ . "\\{")
(?\} . "\\}")
(?\& . "\\&")
(?\# . "\\#"))
"A table of characters which must be represented specially.
These are applied to the entire document, sans already-escaped
regions."
:type '(alist :key-type character :value-type string)
:group 'muse-context)
(defcustom muse-context-markup-specials-example
'()
"A table of characters which must be represented specially.
These are applied to <example> regions.
With the default interpretation of <example> regions, no specials
need to be escaped."
:type '(alist :key-type character :value-type string)
:group 'muse-context)
(defcustom muse-context-markup-specials-literal
'()
"A table of characters which must be represented specially.
This applies to =monospaced text= and <code> regions."
:type '(alist :key-type character :value-type string)
:group 'muse-context)
(defcustom muse-context-markup-specials-url
'((?\\ . "\\textbackslash")
(?\_ . "\\_")
(?\< . "\\<")
(?\> . "\\>")
(?\$ . "\\$")
(?\% . "\\%")
(?\{ . "\\{")
(?\} . "\\}")
(?\& . "\\&")
(?\# . "\\#"))
"A table of characters which must be represented specially.
These are applied to URLs."
:type '(alist :key-type character :value-type string)
:group 'muse-context)
(defcustom muse-context-markup-specials-image
'((?\\ . "\\textbackslash") ; cannot find suitable replacement
(?\< . "\\<")
(?\> . "\\>")
(?\$ . "\\$")
(?\% . "\\%")
(?\{ . "\\{")
(?\} . "\\}")
(?\& . "\\&")
(?\# . "\\#") ; cannot find suitable replacement
)
"A table of characters which must be represented specially.
These are applied to image filenames."
:type '(alist :key-type character :value-type string)
:group 'muse-context)
(defun muse-context-decide-specials (context)
"Determine the specials to escape, depending on the CONTEXT argument."
(cond ((memq context '(underline emphasis document url-desc verbatim
footnote))
muse-context-markup-specials-document)
((eq context 'image)
muse-context-markup-specials-image)
((memq context '(email url))
muse-context-markup-specials-url)
((eq context 'literal)
muse-context-markup-specials-literal)
((eq context 'example)
muse-context-markup-specials-example)
(t (error "Invalid context argument '%s' in muse-context" context))))
(defun muse-context-markup-table ()
(let* ((table-info (muse-publish-table-fields (match-beginning 0)
(match-end 0)))
(row-len (car table-info))
(field-list (cdr table-info)))
(when table-info
(muse-insert-markup "\\starttable[|"
(mapconcat 'symbol-name (make-vector row-len 'l)
"|") "|]\n \\HL\n \\VL ")
(dolist (fields field-list)
(let ((type (car fields)))
(setq fields (cdr fields))
(when (= type 3)
(muse-insert-markup ""))
(insert (car fields))
(setq fields (cdr fields))
(dolist (field fields)
(muse-insert-markup " \\VL ")
(insert field))
(muse-insert-markup "\\VL\\NR\n \\HL\n \\VL ")
(when (= type 2)
(muse-insert-markup " "))))
(muse-insert-markup "\\stoptable\n")
(while (search-backward "VL \\stoptable" nil t)
(replace-match "stoptable" nil t)))))
(defun muse-context-fixup-dquotes ()
"Fixup double quotes."
(goto-char (point-min))
(let ((open t))
(while (search-forward "\"" nil t)
(unless (get-text-property (match-beginning 0) 'read-only)
(when (or (bobp)
(eq (char-before) ?\n))
(setq open t))
(if open
(progn
(replace-match "``")
(setq open nil))
(replace-match "''")
(setq open t))))))
(defcustom muse-context-permit-contents-tag nil
"If nil, ignore <contents> tags. Otherwise, insert table of contents.
Most of the time, it is best to have a table of contents on the
first page, with a new page immediately following. To make this
work with documents published in both HTML and ConTeXt, we need to
ignore the <contents> tag.
If you don't agree with this, then set this option to non-nil,
and it will do what you expect."
:type 'boolean
:group 'muse-context)
(defun muse-context-fixup-citations ()
"Replace semicolons in multi-head citations with colons."
(goto-char (point-min))
(while (re-search-forward "\\\\cite.?\\[" nil t)
(let ((start (point))
(end (re-search-forward "]")))
(save-restriction
(narrow-to-region start end)
(goto-char (point-min))
(while (re-search-forward ";" nil t)
(replace-match ","))))))
(defun muse-context-munge-buffer ()
(muse-context-fixup-dquotes)
(muse-context-fixup-citations)
(when (and muse-context-permit-contents-tag
muse-publish-generate-contents)
(goto-char (car muse-publish-generate-contents))
(muse-insert-markup "\\placecontent")))
(defun muse-context-bibliography ()
(save-excursion
(goto-char (point-min))
(if (re-search-forward "\\\\cite.?\\[" nil t)
"\\completepublications[criterium=all]"
"")))
(defun muse-context-setup-bibliography ()
(save-excursion
(goto-char (point-min))
(if (re-search-forward "\\\\cite.?\\[" nil t)
(concat
"\\usemodule[bibltx]\n\\setupbibtex [database="
(muse-publishing-directive "bibsource") "]")
"")))
(defun muse-context-pdf-browse-file (file)
(shell-command (concat "open " file)))
(defun muse-context-pdf-generate (file output-path final-target)
(apply
#'muse-publish-transform-output
file output-path final-target "PDF"
(function
(lambda (file output-path)
(let* ((fnd (file-name-directory output-path))
(command (format "%s \"%s\""
muse-context-pdf-program
(file-relative-name file fnd)))
(times 0)
(default-directory fnd)
result)
;; XEmacs can sometimes return a non-number result. We'll err
;; on the side of caution by continuing to attempt to generate
;; the PDF if this happens and treat the final result as
;; successful.
(while (and (< times 2)
(or (not (numberp result))
(not (eq result 0))
;; table of contents takes 2 passes
;; (file-readable-p
;; (muse-replace-regexp-in-string
;; "\\.tex\\'" ".toc" file t t))
))
(setq result (shell-command command)
times (1+ times)))
(if (or (not (numberp result))
(eq result 0))
t
nil))))
muse-context-pdf-cruft))
(muse-define-style "context"
:suffix 'muse-context-extension
:regexps 'muse-context-markup-regexps
:functions 'muse-context-markup-functions
:strings 'muse-context-markup-strings
:specials 'muse-context-decide-specials
:after 'muse-context-munge-buffer
:header 'muse-context-header
:footer 'muse-context-footer
:browser 'find-file)
(muse-derive-style "context-pdf" "context"
:final 'muse-context-pdf-generate
:browser 'muse-context-pdf-browse-file
:link-suffix 'muse-context-pdf-extension
:osuffix 'muse-context-pdf-extension)
(muse-derive-style "context-slides" "context"
:header 'muse-context-slides-header
:strings 'muse-context-slides-markup-strings)
(muse-derive-style "context-slides-pdf" "context-pdf"
:header 'muse-context-slides-header
:strings 'muse-context-slides-markup-strings)
(provide 'muse-context)
;;; muse-context.el ends here

View file

@ -0,0 +1,352 @@
;;; muse-docbook.el --- publish DocBook files
;; Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010
;; Free Software Foundation, Inc.
;; This file is part of Emacs Muse. It is not part of GNU Emacs.
;; Emacs Muse 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 3, or (at your
;; option) any later version.
;; Emacs Muse 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 Emacs Muse; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.
;;; Commentary:
;;; Contributors:
;; Dale P. Smith (dpsm AT en DOT com) improved the markup
;; significantly and made many valuable suggestions.
;;; Code:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; Muse DocBook XML Publishing
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(require 'muse-publish)
(require 'muse-regexps)
(require 'muse-xml-common)
(defgroup muse-docbook nil
"Options controlling the behavior of Muse DocBook XML publishing.
See `muse-docbook' for more information."
:group 'muse-publish)
(defcustom muse-docbook-extension ".xml"
"Default file extension for publishing DocBook XML files."
:type 'string
:group 'muse-docbook)
(defcustom muse-docbook-header
"<?xml version=\"1.0\" encoding=\"<lisp>
(muse-docbook-encoding)</lisp>\"?>
<!DOCTYPE article PUBLIC \"-//OASIS//DTD DocBook V4.2//EN\"
\"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd\"<lisp>(muse-docbook-entities)</lisp>>
<article>
<articleinfo>
<title><lisp>(muse-publishing-directive \"title\")</lisp></title>
<author><lisp>(muse-docbook-get-author
(muse-publishing-directive \"author\"))</lisp></author>
<pubdate><lisp>(muse-publishing-directive \"date\")</lisp></pubdate>
</articleinfo>
<!-- Page published by Emacs Muse begins here -->\n"
"Header used for publishing DocBook XML files.
This may be text or a filename."
:type 'string
:group 'muse-docbook)
(defcustom muse-docbook-footer "
<!-- Page published by Emacs Muse ends here -->
<lisp>(muse-docbook-bibliography)</lisp></article>\n"
"Footer used for publishing DocBook XML files.
This may be text or a filename."
:type 'string
:group 'muse-docbook)
(defcustom muse-docbook-markup-regexps
`(;; Beginning of doc, end of doc, or plain paragraph separator
(10000 ,(concat "\\(\\(\n\\(?:[" muse-regexp-blank "]*\n\\)*"
"\\([" muse-regexp-blank "]*\n\\)\\)"
"\\|\\`\\s-*\\|\\s-*\\'\\)")
3 muse-docbook-markup-paragraph))
"List of markup rules for publishing a Muse page to DocBook XML.
For more on the structure of this list, see `muse-publish-markup-regexps'."
:type '(repeat (choice
(list :tag "Markup rule"
integer
(choice regexp symbol)
integer
(choice string function symbol))
function))
:group 'muse-docbook)
(defcustom muse-docbook-markup-functions
'((anchor . muse-xml-markup-anchor)
(table . muse-xml-markup-table))
"An alist of style types to custom functions for that kind of text.
For more on the structure of this list, see
`muse-publish-markup-functions'."
:type '(alist :key-type symbol :value-type function)
:group 'muse-docbook)
(defcustom muse-docbook-markup-strings
'((image-with-desc . "<mediaobject>
<imageobject>
<imagedata fileref=\"%1%.%2%\" format=\"%2%\" />
</imageobject>
<caption><para>%3%</para></caption>
</mediaobject>")
(image . "<inlinemediaobject><imageobject>
<imagedata fileref=\"%1%.%2%\" format=\"%2%\" />
</imageobject></inlinemediaobject>")
(image-link . "<ulink url=\"%1%\"><inlinemediaobject><imageobject>
<imagedata fileref=\"%2%.%3%\" format=\"%3%\" />
</imageobject></inlinemediaobject></ulink>")
(anchor-ref . "<link linkend=\"%s\">%s</link>")
(url . "<ulink url=\"%s\">%s</ulink>")
(link . "<ulink url=\"%s\">%s</ulink>")
(link-and-anchor . "<ulink url=\"%s#%s\">%s</ulink>")
(email-addr . "<email>%s</email>")
(anchor . "<anchor id=\"%s\" />\n")
(emdash . "%s&mdash;%s")
(comment-begin . "<!-- ")
(comment-end . " -->")
(rule . "")
(no-break-space . "&nbsp;")
(enddots . "....")
(dots . "...")
(section . "<section><title>")
(section-end . "</title>")
(subsection . "<section><title>")
(subsection-end . "</title>")
(subsubsection . "<section><title>")
(subsubsection-end . "</title>")
(section-other . "<section><title>")
(section-other-end . "</title>")
(section-close . "</section>")
(footnote . "<footnote><para>")
(footnote-end . "</para></footnote>")
(begin-underline . "")
(end-underline . "")
(begin-literal . "<systemitem>")
(end-literal . "</systemitem>")
(begin-emph . "<emphasis>")
(end-emph . "</emphasis>")
(begin-more-emph . "<emphasis role=\"strong\">")
(end-more-emph . "</emphasis>")
(begin-most-emph . "<emphasis role=\"strong\"><emphasis>")
(end-most-emph . "</emphasis></emphasis>")
(begin-verse . "<literallayout>\n")
(verse-space . " ")
(end-verse . "</literallayout>")
(begin-example . "<programlisting>")
(end-example . "</programlisting>")
(begin-center . "<para role=\"centered\">\n")
(end-center . "\n</para>")
(begin-quote . "<blockquote>\n")
(end-quote . "\n</blockquote>")
(begin-cite . "<citation role=\"%s\">")
(begin-cite-author . "<citation role=\"%s\">A:")
(begin-cite-year . "<citation role=\"%s\">Y:")
(end-cite . "</citation>")
(begin-quote-item . "<para>")
(end-quote-item . "</para>")
(begin-uli . "<itemizedlist mark=\"bullet\">\n")
(end-uli . "\n</itemizedlist>")
(begin-uli-item . "<listitem><para>")
(end-uli-item . "</para></listitem>")
(begin-oli . "<orderedlist>\n")
(end-oli . "\n</orderedlist>")
(begin-oli-item . "<listitem><para>")
(end-oli-item . "</para></listitem>")
(begin-dl . "<variablelist>\n")
(end-dl . "\n</variablelist>")
(begin-dl-item . "<varlistentry>\n")
(end-dl-item . "\n</varlistentry>")
(begin-ddt . "<term>")
(end-ddt . "</term>")
(begin-dde . "<listitem><para>")
(end-dde . "</para></listitem>")
(begin-table . "<informaltable>\n")
(end-table . "</informaltable>")
(begin-table-group . " <tgroup cols='%s'>\n")
(end-table-group . " </tgroup>\n")
(begin-table-row . " <row>\n")
(end-table-row . " </row>\n")
(begin-table-entry . " <entry>")
(end-table-entry . "</entry>\n"))
"Strings used for marking up text.
These cover the most basic kinds of markup, the handling of which
differs little between the various styles."
:type '(alist :key-type symbol :value-type string)
:group 'muse-docbook)
(defcustom muse-docbook-encoding-default 'utf-8
"The default Emacs buffer encoding to use in published files.
This will be used if no special characters are found."
:type 'symbol
:group 'muse-docbook)
(defcustom muse-docbook-charset-default "utf-8"
"The default DocBook XML charset to use if no translation is
found in `muse-docbook-encoding-map'."
:type 'string
:group 'muse-docbook)
(defun muse-docbook-encoding ()
(muse-xml-transform-content-type
(or (and (boundp 'buffer-file-coding-system)
buffer-file-coding-system)
muse-docbook-encoding-default)
muse-docbook-charset-default))
(defun muse-docbook-markup-paragraph ()
(catch 'bail-out
(let ((end (copy-marker (match-end 0) t)))
(goto-char (match-beginning 0))
(when (save-excursion
(save-match-data
(and (not (get-text-property (max (point-min) (1- (point)))
'muse-no-paragraph))
(re-search-backward
"<\\(/?\\)\\(para\\|footnote\\|literallayout\\)[ >]"
nil t)
(cond ((string= (match-string 2) "literallayout")
(and (not (string= (match-string 1) "/"))
(throw 'bail-out t)))
((string= (match-string 2) "para")
(and
(not (string= (match-string 1) "/"))
;; don't mess up nested lists
(not (and (muse-looking-back "<listitem>")
(throw 'bail-out t)))))
((string= (match-string 2) "footnote")
(string= (match-string 1) "/"))
(t nil)))))
(when (get-text-property (1- (point)) 'muse-end-list)
(goto-char (previous-single-property-change (1- (point))
'muse-end-list)))
(muse-insert-markup "</para>"))
(goto-char end))
(cond
((eobp)
(unless (bolp)
(insert "\n")))
((get-text-property (point) 'muse-no-paragraph)
(forward-char 1)
nil)
((eq (char-after) ?\<)
(when (looking-at (concat "<\\(emphasis\\|systemitem\\|inlinemediaobject"
"\\|u?link\\|anchor\\|email\\)[ >]"))
(muse-insert-markup "<para>")))
(t
(muse-insert-markup "<para>")))))
(defun muse-docbook-get-author (&optional author)
"Split the AUTHOR directive into separate fields.
AUTHOR should be of the form: \"Firstname Other Names Lastname\",
and anything after `Firstname' is optional."
(setq author (save-match-data (split-string author)))
(let ((num-el (length author)))
(cond ((eq num-el 1)
(concat "<firstname>" (car author) "</firstname>"))
((eq num-el 2)
(concat "<firstname>" (nth 0 author) "</firstname>"
"<surname>" (nth 1 author) "</surname>"))
((eq num-el 3)
(concat "<firstname>" (nth 0 author) "</firstname>"
"<othername>" (nth 1 author) "</othername>"
"<surname>" (nth 2 author) "</surname>"))
(t
(let (first last)
(setq first (car author))
(setq author (nreverse (cdr author)))
(setq last (car author))
(setq author (nreverse (cdr author)))
(concat "<firstname>" first "</firstname>"
"<othername>"
(mapconcat 'identity author " ")
"</othername>"
"<surname>" last "</surname>"))))))
(defun muse-docbook-fixup-images ()
(goto-char (point-min))
(while (re-search-forward (concat "<imagedata fileref=\"[^\"]+\""
" format=\"\\([^\"]+\\)\" />$")
nil t)
(replace-match (upcase (match-string 1)) t t nil 1)))
(defun muse-docbook-fixup-citations ()
;; remove the role attribute if there is no role
(goto-char (point-min))
(while (re-search-forward "<\\(citation role=\"nil\"\\)>" nil t)
(replace-match "citation" t t nil 1))
;; replace colons in multi-head citations with semicolons
(goto-char (point-min))
(while (re-search-forward "<citation.*>" nil t)
(let ((start (point))
(end (re-search-forward "</citation>")))
(save-restriction
(narrow-to-region start end)
(goto-char (point-min))
(while (re-search-forward "," nil t)
(replace-match ";"))))))
(defun muse-docbook-munge-buffer ()
(muse-docbook-fixup-images)
(muse-docbook-fixup-citations))
(defun muse-docbook-entities ()
(save-excursion
(goto-char (point-min))
(if (re-search-forward "<citation" nil t)
(concat
" [\n<!ENTITY bibliography SYSTEM \""
(if (string-match ".short$" (muse-page-name))
(substring (muse-page-name) 0 -6)
(muse-page-name))
".bib.xml\">\n]")
"")))
(defun muse-docbook-bibliography ()
(save-excursion
(goto-char (point-min))
(if (re-search-forward "<citation" nil t)
"&bibliography;\n"
"")))
(defun muse-docbook-finalize-buffer ()
(when (boundp 'buffer-file-coding-system)
(when (memq buffer-file-coding-system '(no-conversion undecided-unix))
;; make it agree with the default charset
(setq buffer-file-coding-system muse-docbook-encoding-default))))
;;; Register the Muse DocBook XML Publisher
(muse-define-style "docbook"
:suffix 'muse-docbook-extension
:regexps 'muse-docbook-markup-regexps
:functions 'muse-docbook-markup-functions
:strings 'muse-docbook-markup-strings
:specials 'muse-xml-decide-specials
:before-end 'muse-docbook-munge-buffer
:after 'muse-docbook-finalize-buffer
:header 'muse-docbook-header
:footer 'muse-docbook-footer
:browser 'find-file)
(provide 'muse-docbook)
;;; muse-docbook.el ends here

View file

@ -0,0 +1,274 @@
;;; muse-groff.el --- publish groff -mom -mwww files
;; Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010
;; Free Software Foundation, Inc.
;; Author: Andrew J. Korty (ajk AT iu DOT edu)
;; Date: Tue 5-Jul-2005
;; This file is part of Emacs Muse. It is not part of GNU Emacs.
;; Emacs Muse 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 3, or (at your
;; option) any later version.
;; Emacs Muse 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 Emacs Muse; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.
;;; Commentary:
;;; Contributors:
;;; Code:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; Muse Publishing Using groff -mom -mwww
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(require 'muse-publish)
(defgroup muse-groff nil
"Rules for marking up a Muse file with groff -mom -mwww macros."
:group 'muse-publish)
(defcustom muse-groff-extension ".groff"
"Default file extension for publishing groff -mom -mwww files."
:type 'string
:group 'muse-groff)
(defcustom muse-groff-pdf-extension ".pdf"
"Default file extension for publishing groff -mom -mwww files to PDF."
:type 'string
:group 'muse-groff)
(defcustom muse-groff-header
".TITLE \"<lisp>(muse-publishing-directive \"title\")</lisp>\"
.SUBTITLE \"<lisp>(muse-publishing-directive \"date\")</lisp>\"
.AUTHOR \"<lisp>(muse-publishing-directive \"author\")</lisp>\"
.PRINTSTYLE TYPESET
.de list
. LIST \\$1
. SHIFT_LIST \\$2
..
.PARA_INDENT 0
.START
<lisp>(and muse-publish-generate-contents \".TOC\n\")</lisp>\n"
"Header used for publishing groff -mom -mwww files."
:type '(choice string file)
:group 'muse-groff)
(defcustom muse-groff-footer " "
"Footer used for publishing groff -mom -mwww files."
:type '(choice string file)
:group 'muse-groff)
(defcustom muse-groff-markup-regexps
`((10400 ,(concat "\\(\n</\\(blockquote\\|center\\)>\\)?\n"
"\\(["
muse-regexp-blank
"]*\n\\)+\\(<\\(blockquote\\|center\\)>\n\\)?")
0 muse-groff-markup-paragraph))
"List of markup regexps for identifying regions in a Muse page.
For more on the structure of this list, see `muse-publish-markup-regexps'."
:type '(repeat (choice
(list :tag "Markup rule"
integer
(choice regexp symbol)
integer
(choice string function symbol))
function))
:group 'muse-groff)
(defcustom muse-groff-markup-functions
'((table . muse-groff-markup-table))
"An alist of style types to custom functions for that kind of text.
For more on the structure of this list, see
`muse-publish-markup-functions'."
:type '(alist :key-type symbol :value-type function)
:group 'muse-groff)
(defcustom muse-groff-markup-tags
'()
"A list of tag specifications, for specially marking up GROFF."
:type '(repeat (list (string :tag "Markup tag")
(boolean :tag "Expect closing tag" :value t)
(boolean :tag "Parse attributes" :value nil)
(boolean :tag "Nestable" :value nil)
function))
:group 'muse-groff)
(defcustom muse-groff-markup-strings
`((image-with-desc . "\n.MPIMG -R %s.%s\n")
(image . "\n.MPIMG -R %s.%s\n")
(image-link . "\n.\\\" %s\n.MPIMG -R %s.%s")
(url . "\n.URL %s %s\n\\z")
(link . "\n.URL %s %s\n\\z")
(email-addr . "\f[C]%s\f[]")
(emdash . "\\(em")
(rule . "\n.RULE\n")
(no-break-space . "\\h")
(line-break . "\\p")
(enddots . "....")
(dots . "...")
;; (part . "\\part{")
;; (part-end . "}")
;; (chapter . "\\chapter{")
;; (chapter-end . "}")
(section . ".HEAD \"")
(section-end . "\"")
(subsection . ".SUBHEAD \"")
(subsection-end . "\"")
(subsubsection . ".PARAHEAD \"")
(subsubsection-end . "\"")
;; (footnote . "\\c\n.FOOTNOTE\n")
;; (footnote-end . "\n.FOOTNOTE OFF\n")
;; (footnotemark . "\\footnotemark[%d]")
;; (footnotetext . "\\footnotetext[%d]{")
;; (footnotetext-end . "}")
(begin-underline . "\n.UNDERSCORE \"")
(end-underline . "\"\n")
(begin-literal . "\\fC")
(end-literal . "\\fP")
(begin-emph . "\\fI")
(end-emph . "\\fP")
(begin-more-emph . "\\fB")
(end-more-emph . "\\fP")
(begin-most-emph . "\\f(BI")
(end-most-emph . "\\fP")
(begin-verse . ".QUOTE")
(end-verse . ".QUOTE OFF")
(begin-center . "\n.CENTER\n")
(end-center . "\n.QUAD L\n")
(begin-example . ,(concat
".QUOTE_FONT CR\n.QUOTE_INDENT 1\n"".QUOTE_SIZE -2\n"
".UNDERLINE_QUOTES OFF\n.QUOTE"))
(end-example . ".QUOTE OFF")
(begin-quote . ".BLOCKQUOTE")
(end-quote . ".BLOCKQUOTE OFF")
(begin-cite . "")
(begin-cite-author . "")
(begin-cite-year . "")
(end-cite . "")
(begin-uli . ".list BULLET\n.SHIFT_LIST 2m\n.ITEM\n")
(end-uli . "\n.LIST OFF")
(begin-oli . ".list DIGIT\n.SHIFT_LIST 2m\n.ITEM\n")
(end-oli . "\n.LIST OFF")
(begin-ddt . "\\fB")
(begin-dde . "\\fP\n.IR 4P\n")
(end-ddt . ".IRX CLEAR"))
"Strings used for marking up text.
These cover the most basic kinds of markup, the handling of which
differs little between the various styles."
:type '(alist :key-type symbol :value-type string)
:group 'muse-groff)
(defcustom muse-groff-markup-specials
'((?\\ . "\\e"))
"A table of characters which must be represented specially."
:type '(alist :key-type character :value-type string)
:group 'muse-groff)
(defun muse-groff-markup-paragraph ()
(let ((end (copy-marker (match-end 0) t)))
(goto-char (1+ (match-beginning 0)))
(delete-region (point) end)
(unless (looking-at "\.\\(\\(\\(SUB\\|PARA\\)?HEAD \\)\\|RULE$\\)")
(muse-insert-markup ".ALD .5v\n.PP\n.ne 2\n"))))
(defun muse-groff-protect-leading-chars ()
"Protect leading periods and apostrophes from being interpreted as
command characters."
(while (re-search-forward "^[.']" nil t)
(replace-match "\\\\&\\&" t)))
(defun muse-groff-concat-lists ()
"Join like lists."
(let ((type "")
arg begin)
(while (re-search-forward "^\.LIST[ \t]+\\(.*\\)\n" nil t)
(setq arg (match-string 1))
(if (string= arg "OFF")
(setq begin (match-beginning 0))
(if (and begin (string= type arg))
(delete-region begin (match-end 0))
(setq type arg
begin 0))))))
(defun muse-groff-fixup-dquotes ()
"Fixup double quotes."
(let ((open t))
(while (search-forward "\"" nil t)
(unless (get-text-property (match-beginning 0) 'read-only)
(if (and (bolp) (eq (char-before) ?\n))
(setq open t))
(if open
(progn
(replace-match "``")
(setq open nil))
(replace-match "''")
(setq open t))))))
(defun muse-groff-prepare-buffer ()
(goto-char (point-min))
(muse-groff-protect-leading-chars))
(defun muse-groff-munge-buffer ()
(goto-char (point-min))
(muse-groff-concat-lists))
(defun muse-groff-pdf-browse-file (file)
(shell-command (concat "open " file)))
(defun muse-groff-pdf-generate (file output-path final-target)
(muse-publish-transform-output
file output-path final-target "PDF"
(function
(lambda (file output-path)
(let ((command
(format
(concat "file=%s; ext=%s; cd %s && cp $file$ext $file.ref && "
"groff -mom -mwww -t $file$ext > $file.ps && "
"pstopdf $file.ps")
(file-name-sans-extension file)
muse-groff-extension
(file-name-directory output-path))))
(shell-command command))))
".ps"))
;;; Register the Muse GROFF Publisher
(muse-define-style "groff"
:suffix 'muse-groff-extension
:regexps 'muse-groff-markup-regexps
;;; :functions 'muse-groff-markup-functions
:strings 'muse-groff-markup-strings
:tags 'muse-groff-markup-tags
:specials 'muse-groff-markup-specials
:before 'muse-groff-prepare-buffer
:before-end 'muse-groff-munge-buffer
:header 'muse-groff-header
:footer 'muse-groff-footer
:browser 'find-file)
(muse-derive-style "groff-pdf" "groff"
:final 'muse-groff-pdf-generate
:browser 'muse-groff-pdf-browse-file
:osuffix 'muse-groff-pdf-extension)
(provide 'muse-groff)
;;; muse-groff.el ends here
;;
;; Local Variables:
;; indent-tabs-mode: nil
;; End:

View file

@ -0,0 +1,754 @@
;;; muse-html.el --- publish to HTML and XHTML
;; Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010
;; Free Software Foundation, Inc.
;; This file is part of Emacs Muse. It is not part of GNU Emacs.
;; Emacs Muse 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 3, or (at your
;; option) any later version.
;; Emacs Muse 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 Emacs Muse; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.
;;; Commentary:
;;; Contributors:
;; Zhiqiang Ye (yezq AT mail DOT cbi DOT pku DOT edu DOT cn) suggested
;; appending an 'encoding="..."' fragment to the first line of the
;; sample publishing header so that when editing the resulting XHTML
;; file, Emacs would use the proper encoding.
;; Sun Jiyang (sunyijiang AT gmail DOT com) came up with the idea for
;; the <src> tag and provided an implementation for emacs-wiki.
;; Charles Wang (wcy123 AT gmail DOT com) provided an initial
;; implementation of the <src> tag for Muse.
;; Clinton Ebadi (clinton AT unknownlamer DOT org) provided further
;; ideas for the implementation of the <src> tag.
;;; Code:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; Muse HTML Publishing
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(require 'muse-publish)
(require 'muse-regexps)
(require 'muse-xml-common)
(defgroup muse-html nil
"Options controlling the behavior of Muse HTML publishing."
:group 'muse-publish)
(defcustom muse-html-extension ".html"
"Default file extension for publishing HTML files."
:type 'string
:group 'muse-html)
(defcustom muse-xhtml-extension ".html"
"Default file extension for publishing XHTML files."
:type 'string
:group 'muse-html)
(defcustom muse-html-style-sheet
"<style type=\"text/css\">
body {
background: white; color: black;
margin-left: 3%; margin-right: 7%;
}
p { margin-top: 1% }
p.verse { margin-left: 3% }
.example { margin-left: 3% }
h2 {
margin-top: 25px;
margin-bottom: 0px;
}
h3 { margin-bottom: 0px; }
</style>"
"Store your stylesheet definitions here.
This is used in `muse-html-header'.
You can put raw CSS in here or a <link> tag to an external stylesheet.
This text may contain <lisp> markup tags.
An example of using <link> is as follows.
<link rel=\"stylesheet\" type=\"text/css\" charset=\"utf-8\" media=\"all\" href=\"/default.css\">"
:type 'string
:group 'muse-html)
(defcustom muse-xhtml-style-sheet
"<style type=\"text/css\">
body {
background: white; color: black;
margin-left: 3%; margin-right: 7%;
}
p { margin-top: 1% }
p.verse { margin-left: 3% }
.example { margin-left: 3% }
h2 {
margin-top: 25px;
margin-bottom: 0px;
}
h3 { margin-bottom: 0px; }
</style>"
"Store your stylesheet definitions here.
This is used in `muse-xhtml-header'.
You can put raw CSS in here or a <link> tag to an external stylesheet.
This text may contain <lisp> markup tags.
An example of using <link> is as follows.
<link rel=\"stylesheet\" type=\"text/css\" charset=\"utf-8\" media=\"all\" href=\"/default.css\" />"
:type 'string
:group 'muse-html)
(defcustom muse-html-header
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">
<html>
<head>
<title><lisp>
(concat (muse-publishing-directive \"title\")
(let ((author (muse-publishing-directive \"author\")))
(if (not (string= author (user-full-name)))
(concat \" (by \" author \")\"))))</lisp></title>
<meta name=\"generator\" content=\"muse.el\">
<meta http-equiv=\"<lisp>muse-html-meta-http-equiv</lisp>\"
content=\"<lisp>muse-html-meta-content-type</lisp>\">
<lisp>
(let ((maintainer (muse-style-element :maintainer)))
(when maintainer
(concat \"<link rev=\\\"made\\\" href=\\\"\" maintainer \"\\\">\")))
</lisp><lisp>
(muse-style-element :style-sheet muse-publishing-current-style)
</lisp>
</head>
<body>
<h1><lisp>
(concat (muse-publishing-directive \"title\")
(let ((author (muse-publishing-directive \"author\")))
(if (not (string= author (user-full-name)))
(concat \" (by \" author \")\"))))</lisp></h1>
<!-- Page published by Emacs Muse begins here -->\n"
"Header used for publishing HTML files. This may be text or a filename."
:type 'string
:group 'muse-html)
(defcustom muse-html-footer "
<!-- Page published by Emacs Muse ends here -->
</body>
</html>\n"
"Footer used for publishing HTML files. This may be text or a filename."
:type 'string
:group 'muse-html)
(defcustom muse-xhtml-header
"<?xml version=\"1.0\" encoding=\"<lisp>
(muse-html-encoding)</lisp>\"?>
<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"
\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">
<html xmlns=\"http://www.w3.org/1999/xhtml\">
<head>
<title><lisp>
(concat (muse-publishing-directive \"title\")
(let ((author (muse-publishing-directive \"author\")))
(if (not (string= author (user-full-name)))
(concat \" (by \" author \")\"))))</lisp></title>
<meta name=\"generator\" content=\"muse.el\" />
<meta http-equiv=\"<lisp>muse-html-meta-http-equiv</lisp>\"
content=\"<lisp>muse-html-meta-content-type</lisp>\" />
<lisp>
(let ((maintainer (muse-style-element :maintainer)))
(when maintainer
(concat \"<link rev=\\\"made\\\" href=\\\"\" maintainer \"\\\" />\")))
</lisp><lisp>
(muse-style-element :style-sheet muse-publishing-current-style)
</lisp>
</head>
<body>
<h1><lisp>
(concat (muse-publishing-directive \"title\")
(let ((author (muse-publishing-directive \"author\")))
(if (not (string= author (user-full-name)))
(concat \" (by \" author \")\"))))</lisp></h1>
<!-- Page published by Emacs Muse begins here -->\n"
"Header used for publishing XHTML files. This may be text or a filename."
:type 'string
:group 'muse-html)
(defcustom muse-xhtml-footer "
<!-- Page published by Emacs Muse ends here -->
</body>
</html>\n"
"Footer used for publishing XHTML files. This may be text or a filename."
:type 'string
:group 'muse-html)
(defcustom muse-html-anchor-on-word nil
"When true, anchors surround the closest word. This allows you
to select them in a browser (i.e. for pasting), but has the
side-effect of marking up headers in multiple colors if your
header style is different from your link style."
:type 'boolean
:group 'muse-html)
(defcustom muse-html-table-attributes
" class=\"muse-table\" border=\"2\" cellpadding=\"5\""
"The attribute to be used with HTML <table> tags.
Note that Muse supports insertion of raw HTML tags, as long
as you wrap the region in <literal></literal>."
:type 'string
:group 'muse-html)
(defcustom muse-html-markup-regexps
`(;; Beginning of doc, end of doc, or plain paragraph separator
(10000 ,(concat "\\(\\(\n\\(?:[" muse-regexp-blank "]*\n\\)*"
"\\([" muse-regexp-blank "]*\n\\)\\)"
"\\|\\`\\s-*\\|\\s-*\\'\\)")
;; this is somewhat repetitive because we only require the
;; line just before the paragraph beginning to be not
;; read-only
3 muse-html-markup-paragraph))
"List of markup rules for publishing a Muse page to HTML.
For more on the structure of this list, see `muse-publish-markup-regexps'."
:type '(repeat (choice
(list :tag "Markup rule"
integer
(choice regexp symbol)
integer
(choice string function symbol))
function))
:group 'muse-html)
(defcustom muse-html-markup-functions
'((anchor . muse-html-markup-anchor)
(table . muse-html-markup-table)
(footnote . muse-html-markup-footnote))
"An alist of style types to custom functions for that kind of text.
For more on the structure of this list, see
`muse-publish-markup-functions'."
:type '(alist :key-type symbol :value-type function)
:group 'muse-html)
(defcustom muse-html-markup-strings
'((image-with-desc . "<table class=\"image\" width=\"100%%\">
<tr><td align=\"center\"><img src=\"%1%.%2%\" alt=\"%3%\"></td></tr>
<tr><td align=\"center\" class=\"image-caption\">%3%</td></tr>
</table>")
(image . "<img src=\"%s.%s\" alt=\"\">")
(image-link . "<a class=\"image-link\" href=\"%s\">
<img src=\"%s.%s\"></a>")
(anchor-ref . "<a href=\"#%s\">%s</a>")
(url . "<a href=\"%s\">%s</a>")
(link . "<a href=\"%s\">%s</a>")
(link-and-anchor . "<a href=\"%s#%s\">%s</a>")
(email-addr . "<a href=\"mailto:%s\">%s</a>")
(anchor . "<a name=\"%1%\" id=\"%1%\">")
(emdash . "%s&mdash;%s")
(comment-begin . "<!-- ")
(comment-end . " -->")
(rule . "<hr>")
(fn-sep . "<hr>\n")
(no-break-space . "&nbsp;")
(line-break . "<br>")
(enddots . "....")
(dots . "...")
(section . "<h2>")
(section-end . "</h2>")
(subsection . "<h3>")
(subsection-end . "</h3>")
(subsubsection . "<h4>")
(subsubsection-end . "</h4>")
(section-other . "<h5>")
(section-other-end . "</h5>")
(begin-underline . "<u>")
(end-underline . "</u>")
(begin-literal . "<code>")
(end-literal . "</code>")
(begin-cite . "<span class=\"citation\">")
(begin-cite-author . "<span class=\"citation-author\">")
(begin-cite-year . "<span class=\"citation-year\">")
(end-cite . "</span>")
(begin-emph . "<em>")
(end-emph . "</em>")
(begin-more-emph . "<strong>")
(end-more-emph . "</strong>")
(begin-most-emph . "<strong><em>")
(end-most-emph . "</em></strong>")
(begin-verse . "<p class=\"verse\">\n")
(verse-space . "&nbsp;&nbsp;")
(end-verse-line . "<br>")
(end-last-stanza-line . "<br>")
(empty-verse-line . "<br>")
(end-verse . "</p>")
(begin-example . "<pre class=\"example\">")
(end-example . "</pre>")
(begin-center . "<center>\n<p>")
(end-center . "</p>\n</center>")
(begin-quote . "<blockquote>\n")
(end-quote . "\n</blockquote>")
(begin-quote-item . "<p class=\"quoted\">")
(end-quote-item . "</p>")
(begin-uli . "<ul>\n")
(end-uli . "\n</ul>")
(begin-uli-item . "<li>")
(end-uli-item . "</li>")
(begin-oli . "<ol>\n")
(end-oli . "\n</ol>")
(begin-oli-item . "<li>")
(end-oli-item . "</li>")
(begin-dl . "<dl>\n")
(end-dl . "\n</dl>")
(begin-ddt . "<dt><strong>")
(end-ddt . "</strong></dt>")
(begin-dde . "<dd>")
(end-dde . "</dd>")
(begin-table . "<table%s>\n")
(end-table . "</table>")
(begin-table-row . " <tr>\n")
(end-table-row . " </tr>\n")
(begin-table-entry . " <%s>")
(end-table-entry . "</%s>\n"))
"Strings used for marking up text as HTML.
These cover the most basic kinds of markup, the handling of which
differs little between the various styles."
:type '(alist :key-type symbol :value-type string)
:group 'muse-html)
(defcustom muse-xhtml-markup-strings
'((image-with-desc . "<table class=\"image\" width=\"100%%\">
<tr><td align=\"center\"><img src=\"%1%.%2%\" alt=\"%3%\" /></td></tr>
<tr><td align=\"center\" class=\"image-caption\">%3%</td></tr>
</table>")
(image . "<img src=\"%s.%s\" alt=\"\" />")
(image-link . "<a class=\"image-link\" href=\"%s\">
<img src=\"%s.%s\" alt=\"\" /></a>")
(rule . "<hr />")
(fn-sep . "<hr />\n")
(line-break . "<br />")
(begin-underline . "<span style=\"text-decoration: underline;\">")
(end-underline . "</span>")
(begin-center . "<p style=\"text-align: center;\">\n")
(end-center . "\n</p>")
(end-verse-line . "<br />")
(end-last-stanza-line . "<br />")
(empty-verse-line . "<br />"))
"Strings used for marking up text as XHTML.
These cover the most basic kinds of markup, the handling of which
differs little between the various styles.
If a markup rule is not found here, `muse-html-markup-strings' is
searched."
:type '(alist :key-type symbol :value-type string)
:group 'muse-html)
(defcustom muse-xhtml1.1-markup-strings
'((anchor . "<a id=\"%s\">"))
"Strings used for marking up text as XHTML 1.1.
These cover the most basic kinds of markup, the handling of which
differs little between the various styles.
If a markup rule is not found here, `muse-xhtml-markup-strings'
and `muse-html-markup-strings' are searched."
:type '(alist :key-type symbol :value-type string)
:group 'muse-html)
(defcustom muse-html-markup-tags
'(("class" t t t muse-html-class-tag)
("div" t t t muse-html-div-tag)
("src" t t nil muse-html-src-tag))
"A list of tag specifications, for specially marking up HTML."
:type '(repeat (list (string :tag "Markup tag")
(boolean :tag "Expect closing tag" :value t)
(boolean :tag "Parse attributes" :value nil)
(boolean :tag "Nestable" :value nil)
function))
:group 'muse-html)
(defcustom muse-html-meta-http-equiv "Content-Type"
"The http-equiv attribute used for the HTML <meta> tag."
:type 'string
:group 'muse-html)
(defcustom muse-html-meta-content-type "text/html"
"The content type used for the HTML <meta> tag.
If you are striving for XHTML 1.1 compliance, you may want to
change this to \"application/xhtml+xml\"."
:type 'string
:group 'muse-html)
(defcustom muse-html-meta-content-encoding (if (featurep 'mule)
'detect
"iso-8859-1")
"The charset to append to the HTML <meta> tag.
If set to the symbol 'detect, use `muse-html-encoding-map' to try
and determine the HTML charset from emacs's coding. If set to a
string, this string will be used to force a particular charset"
:type '(choice string symbol)
:group 'muse-html)
(defcustom muse-html-encoding-default 'iso-8859-1
"The default Emacs buffer encoding to use in published files.
This will be used if no special characters are found."
:type 'symbol
:group 'muse-html)
(defcustom muse-html-charset-default "iso-8859-1"
"The default HTML meta charset to use if no translation is found in
`muse-html-encoding-map'."
:type 'string
:group 'muse-html)
(defcustom muse-html-src-allowed-modes t
"Modes that we allow the <src> tag to colorize.
If t, permit the <src> tag to colorize any mode.
If a list of mode names, such as '(\"html\" \"latex\"), and the
lang argument to <src> is not in the list, then use fundamental
mode instead."
:type '(choice (const :tag "Any" t)
(repeat (string :tag "Mode")))
:group 'muse-html)
(defun muse-html-insert-anchor (anchor)
"Insert an anchor, either around the word at point, or within a tag."
(skip-chars-forward (concat muse-regexp-blank "\n"))
(if (looking-at (concat "<\\([^" muse-regexp-blank "/>\n]+\\)>"))
(let ((tag (match-string 1)))
(goto-char (match-end 0))
(muse-insert-markup (muse-markup-text 'anchor anchor))
(when muse-html-anchor-on-word
(or (and (search-forward (format "</%s>" tag)
(muse-line-end-position) t)
(goto-char (match-beginning 0)))
(forward-word 1)))
(muse-insert-markup "</a>"))
(muse-insert-markup (muse-markup-text 'anchor anchor))
(when muse-html-anchor-on-word
(forward-word 1))
(muse-insert-markup "</a>\n")))
(defun muse-html-markup-anchor ()
(unless (get-text-property (match-end 1) 'muse-link)
(save-match-data
(muse-html-insert-anchor (match-string 2)))
(match-string 1)))
(defun muse-html-markup-paragraph ()
(let ((end (copy-marker (match-end 0) t)))
(goto-char (match-beginning 0))
(when (save-excursion
(save-match-data
(and (not (get-text-property (max (point-min) (1- (point)))
'muse-no-paragraph))
(re-search-backward "<\\(/?\\)p[ >]" nil t)
(not (string-equal (match-string 1) "/")))))
(when (get-text-property (1- (point)) 'muse-end-list)
(goto-char (previous-single-property-change (1- (point))
'muse-end-list)))
(muse-insert-markup "</p>"))
(goto-char end))
(cond
((eobp)
(unless (bolp)
(insert "\n")))
((get-text-property (point) 'muse-no-paragraph)
(forward-char 1)
nil)
((eq (char-after) ?\<)
(cond
((looking-at "<\\(em\\|strong\\|code\\|span\\)[ >]")
(muse-insert-markup "<p>"))
((looking-at "<a ")
(if (looking-at "<a[^>\n]+><img")
(muse-insert-markup "<p class=\"image-link\">")
(muse-insert-markup "<p>")))
((looking-at "<img[ >]")
(muse-insert-markup "<p class=\"image\">"))
(t
(forward-char 1)
nil)))
((muse-looking-back "\\(</h[1-4]>\\|<hr>\\)\n\n")
(muse-insert-markup "<p class=\"first\">"))
(t
(muse-insert-markup "<p>"))))
(defun muse-html-markup-footnote ()
(cond
((get-text-property (match-beginning 0) 'muse-link)
nil)
((= (muse-line-beginning-position) (match-beginning 0))
(prog1
(let ((text (match-string 1)))
(muse-insert-markup
(concat "<p class=\"footnote\">"
"<a class=\"footnum\" name=\"fn." text
"\" href=\"#fnr." text "\">"
text ".</a>")))
(save-excursion
(save-match-data
(let* ((beg (goto-char (match-end 0)))
(end (and (search-forward "\n\n" nil t)
(prog1
(copy-marker (match-beginning 0))
(goto-char beg)))))
(while (re-search-forward (concat "^["
muse-regexp-blank
"]+\\([^\n]\\)")
end t)
(replace-match "\\1" t)))))
(replace-match "")))
(t (let ((text (match-string 1)))
(muse-insert-markup
(concat "<sup><a class=\"footref\" name=\"fnr." text
"\" href=\"#fn." text "\">"
text "</a></sup>")))
(replace-match ""))))
(defun muse-html-markup-table ()
(muse-xml-markup-table muse-html-table-attributes))
;; Handling of tags for HTML
(defun muse-html-strip-links (string)
"Remove all HTML links from STRING."
(muse-replace-regexp-in-string "\\(<a .*?>\\|</a>\\)" "" string nil t))
(defun muse-html-insert-contents (depth)
"Scan the current document and generate a table of contents at point.
DEPTH indicates how many levels of headings to include. The default is 2."
(let ((max-depth (or depth 2))
(index 1)
base contents l end)
(save-excursion
(goto-char (point-min))
(search-forward "Page published by Emacs Muse begins here" nil t)
(catch 'done
(while (re-search-forward "<h\\([0-9]+\\)>\\(.+?\\)</h\\1>$" nil t)
(unless (and (get-text-property (point) 'read-only)
(not (get-text-property (match-beginning 0)
'muse-contents)))
(remove-text-properties (match-beginning 0) (match-end 0)
'(muse-contents nil))
(setq l (1- (string-to-number (match-string 1))))
(if (null base)
(setq base l)
(if (< l base)
(throw 'done t)))
(when (<= l max-depth)
;; escape specials now before copying the text, so that we
;; can deal sanely with both emphasis in titles and
;; special characters
(goto-char (match-end 2))
(setq end (point-marker))
(muse-publish-escape-specials (match-beginning 2) end
nil 'document)
(muse-publish-mark-read-only (match-beginning 2) end)
(setq contents (cons (cons l (buffer-substring-no-properties
(match-beginning 2) end))
contents))
(set-marker end nil)
(goto-char (match-beginning 2))
(muse-html-insert-anchor (concat "sec" (int-to-string index)))
(setq index (1+ index)))))))
(setq index 1 contents (nreverse contents))
(let ((depth 1) (sub-open 0) (p (point)))
(muse-insert-markup "<div class=\"contents\">\n<dl>\n")
(while contents
(muse-insert-markup "<dt>\n"
"<a href=\"#sec" (int-to-string index) "\">"
(muse-html-strip-links (cdar contents))
"</a>\n"
"</dt>\n")
(setq index (1+ index)
depth (caar contents)
contents (cdr contents))
(when contents
(cond
((< (caar contents) depth)
(let ((idx (caar contents)))
(while (< idx depth)
(muse-insert-markup "</dl>\n</dd>\n")
(setq sub-open (1- sub-open)
idx (1+ idx)))))
((> (caar contents) depth) ; can't jump more than one ahead
(muse-insert-markup "<dd>\n<dl>\n")
(setq sub-open (1+ sub-open))))))
(while (> sub-open 0)
(muse-insert-markup "</dl>\n</dd>\n")
(setq sub-open (1- sub-open)))
(muse-insert-markup "</dl>\n</div>\n")
(muse-publish-mark-read-only p (point)))))
(defun muse-html-denote-headings ()
"Place a text property on any headings in the current buffer.
This allows the headings to be picked up later on if publishing a
table of contents."
(save-excursion
(goto-char (point-min))
(search-forward "Page published by Emacs Muse begins here" nil t)
(while (re-search-forward "<h\\([0-9]+\\)>\\(.+?\\)</h\\1>$" nil t)
(unless (get-text-property (point) 'read-only)
(add-text-properties (match-beginning 0) (match-end 0)
'(muse-contents t))))))
(defun muse-html-class-tag (beg end attrs)
(let ((name (cdr (assoc "name" attrs))))
(when name
(goto-char beg)
(muse-insert-markup "<span class=\"" name "\">")
(save-excursion
(goto-char end)
(muse-insert-markup "</span>")))))
(defun muse-html-div-tag (beg end attrs)
"Publish a <div> tag for HTML."
(let ((id (cdr (assoc "id" attrs)))
(style (cdr (assoc "style" attrs))))
(when (or id style)
(goto-char beg)
(if (null id)
(muse-insert-markup "<div style=\"" style "\">")
(muse-insert-markup "<div id=\"" id "\">"))
(save-excursion
(goto-char end)
(muse-insert-markup "</div>")))))
(defun muse-html-src-tag (beg end attrs)
"Publish the region using htmlize.
The language to use may be specified by the \"lang\" attribute.
Muse will look for a function named LANG-mode, where LANG is the
value of the \"lang\" attribute.
This tag requires htmlize 1.34 or later in order to work."
(if (condition-case nil
(progn
(require 'htmlize)
(if (fboundp 'htmlize-region-for-paste)
nil
(muse-display-warning
(concat "The `htmlize-region-for-paste' function was not"
" found.\nThis is available in htmlize.el 1.34"
" or later."))
t))
(error nil t))
;; if htmlize.el was not found, treat this like an example tag
(muse-publish-example-tag beg end)
(muse-publish-ensure-block beg end)
(let* ((lang (cdr (assoc "lang" attrs)))
(mode (or (and (not (eq muse-html-src-allowed-modes t))
(not (member lang muse-html-src-allowed-modes))
'fundamental-mode)
(intern-soft (concat lang "-mode"))))
(text (muse-delete-and-extract-region beg end))
(htmltext
(with-temp-buffer
(insert text)
(if (functionp mode)
(funcall mode)
(fundamental-mode))
(font-lock-fontify-buffer)
;; silence the byte-compiler
(when (fboundp 'htmlize-region-for-paste)
;; transform the region to HTML
(htmlize-region-for-paste (point-min) (point-max))))))
(save-restriction
(narrow-to-region (point) (point))
(insert htmltext)
(goto-char (point-min))
(re-search-forward "<pre\\([^>]*\\)>\n?" nil t)
(replace-match "<pre class=\"src\">")
(goto-char (point-max))
(muse-publish-mark-read-only (point-min) (point-max))))))
;; Register the Muse HTML Publisher
(defun muse-html-browse-file (file)
(browse-url (concat "file:" file)))
(defun muse-html-encoding ()
(if (stringp muse-html-meta-content-encoding)
muse-html-meta-content-encoding
(muse-xml-transform-content-type
(or (and (boundp 'buffer-file-coding-system)
buffer-file-coding-system)
muse-html-encoding-default)
muse-html-charset-default)))
(defun muse-html-prepare-buffer ()
(make-local-variable 'muse-html-meta-http-equiv)
(set (make-local-variable 'muse-html-meta-content-type)
(if (save-match-data
(string-match "charset=" muse-html-meta-content-type))
muse-html-meta-content-type
(concat muse-html-meta-content-type "; charset="
(muse-html-encoding)))))
(defun muse-html-munge-buffer ()
(if muse-publish-generate-contents
(progn
(goto-char (car muse-publish-generate-contents))
(muse-html-insert-contents (cdr muse-publish-generate-contents))
(setq muse-publish-generate-contents nil))
(muse-html-denote-headings)))
(defun muse-html-finalize-buffer ()
(when (and (boundp 'buffer-file-coding-system)
(memq buffer-file-coding-system '(no-conversion undecided-unix)))
;; make it agree with the default charset
(setq buffer-file-coding-system muse-html-encoding-default)))
;;; Register the Muse HTML and XHTML Publishers
(muse-define-style "html"
:suffix 'muse-html-extension
:regexps 'muse-html-markup-regexps
:functions 'muse-html-markup-functions
:strings 'muse-html-markup-strings
:tags 'muse-html-markup-tags
:specials 'muse-xml-decide-specials
:before 'muse-html-prepare-buffer
:before-end 'muse-html-munge-buffer
:after 'muse-html-finalize-buffer
:header 'muse-html-header
:footer 'muse-html-footer
:style-sheet 'muse-html-style-sheet
:browser 'muse-html-browse-file)
(muse-derive-style "xhtml" "html"
:suffix 'muse-xhtml-extension
:strings 'muse-xhtml-markup-strings
:header 'muse-xhtml-header
:footer 'muse-xhtml-footer
:style-sheet 'muse-xhtml-style-sheet)
;; xhtml1.0 is an alias for xhtml
(muse-derive-style "xhtml1.0" "xhtml")
;; xhtml1.1 has some quirks that need attention from us
(muse-derive-style "xhtml1.1" "xhtml"
:strings 'muse-xhtml1.1-markup-strings)
(provide 'muse-html)
;;; muse-html.el ends here

View file

@ -0,0 +1,239 @@
;;; muse-http.el --- publish HTML files over HTTP
;; Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010
;; Free Software Foundation, Inc.
;; This file is part of Emacs Muse. It is not part of GNU Emacs.
;; Emacs Muse 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 3, or (at your
;; option) any later version.
;; Emacs Muse 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 Emacs Muse; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.
;;; Commentary:
;;; Contributors:
;;; Code:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; Publishing HTML over HTTP (using httpd.el)
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(require 'muse-html)
(require 'muse-project)
(require 'httpd)
(require 'cgi)
(defgroup muse-http nil
"Options controlling the behavior of Emacs Muse over HTTP."
:group 'press)
(defcustom muse-http-maintainer (concat "webmaster@" (system-name))
"The maintainer address to use for the HTTP 'From' field."
:type 'string
:group 'muse-http)
(defcustom muse-http-publishing-style "html"
"The style to use when publishing projects over http."
:type 'string
:group 'muse-http)
(defcustom muse-http-max-cache-size 64
"The number of pages to cache when serving over HTTP.
This only applies if set while running the persisted invocation
server. See main documentation for the `muse-http'
customization group."
:type 'integer
:group 'muse-http)
(defvar muse-buffer-mtime nil)
(make-variable-buffer-local 'muse-buffer-mtime)
(defun muse-sort-buffers (l r)
(let ((l-mtime (with-current-buffer l muse-buffer-mtime))
(r-mtime (with-current-buffer r muse-buffer-mtime)))
(cond
((and (null l-mtime) (null r-mtime)) l)
((null l-mtime) r)
((null r-mtime) l)
(t (muse-time-less-p r-mtime l-mtime)))))
(defun muse-winnow-list (entries &optional predicate)
"Return only those ENTRIES for which PREDICATE returns non-nil."
(let ((flist (list t)))
(let ((entry entries))
(while entry
(if (funcall predicate (car entry))
(nconc flist (list (car entry))))
(setq entry (cdr entry))))
(cdr flist)))
(defun muse-http-prune-cache ()
"If the page cache has become too large, prune it."
(let* ((buflist
(sort (muse-winnow-list (buffer-list)
(function
(lambda (buf)
(with-current-buffer buf
muse-buffer-mtime))))
'muse-sort-buffers))
(len (length buflist)))
(while (> len muse-http-max-cache-size)
(kill-buffer (car buflist))
(setq len (1- len)))))
(defvar muse-http-serving-p nil)
(defun muse-http-send-buffer (&optional modified code msg)
"Markup and send the contents of the current buffer via HTTP."
(httpd-send (or code 200) (or msg "OK")
"Server: muse.el/" muse-version httpd-endl
"Connection: close" httpd-endl
"MIME-Version: 1.0" httpd-endl
"Date: " (format-time-string "%a, %e %b %Y %T %Z")
httpd-endl
"From: " muse-http-maintainer httpd-endl)
(when modified
(httpd-send-data "Last-Modified: "
(format-time-string "%a, %e %b %Y %T %Z" modified)
httpd-endl))
(httpd-send-data "Content-Type: text/html; charset=iso-8859-1" httpd-endl
"Content-Length: " (number-to-string (1- (point-max)))
httpd-endl httpd-endl
(buffer-string))
(httpd-send-eof))
(defun muse-http-reject (title msg &optional annotation)
(muse-with-temp-buffer
(insert msg ".\n")
(if annotation
(insert annotation "\n"))
(muse-publish-markup-buffer title muse-http-publishing-style)
(muse-http-send-buffer nil 404 msg)))
(defun muse-http-prepare-url (target explicit)
(save-match-data
(unless (or (not explicit)
(string-match muse-url-regexp target)
(string-match muse-image-regexp target)
(string-match muse-file-regexp target))
(setq target (concat "page?" target
"&project=" muse-http-serving-p))))
(muse-publish-read-only target))
(defun muse-http-render-page (name)
"Render the Muse page identified by NAME.
When serving from a dedicated Emacs process (see the httpd-serve
script), a maximum of `muse-http-max-cache-size' pages will be
cached in memory to speed up serving time."
(let ((file (muse-project-page-file name muse-http-serving-p))
(muse-publish-url-transforms
(cons 'muse-http-prepare-url muse-publish-url-transforms))
(inhibit-read-only t))
(when file
(with-current-buffer (get-buffer-create file)
(let ((modified-time (nth 5 (file-attributes file)))
(muse-publishing-current-file file)
muse-publishing-current-style)
(when (or (null muse-buffer-mtime)
(muse-time-less-p muse-buffer-mtime modified-time))
(erase-buffer)
(setq muse-buffer-mtime modified-time))
(goto-char (point-max))
(when (bobp)
(muse-insert-file-contents file t)
(let ((styles (cddr (muse-project muse-http-serving-p)))
style)
(while (and styles (null style))
(let ((include-regexp
(muse-style-element :include (car styles)))
(exclude-regexp
(muse-style-element :exclude (car styles))))
(when (and (or (and (null include-regexp)
(null exclude-regexp))
(if include-regexp
(string-match include-regexp file)
(not (string-match exclude-regexp file))))
(not (muse-project-private-p file)))
(setq style (car styles))
(while (muse-style-element :base style)
(setq style
(muse-style (muse-style-element :base style))))
(if (string= (car style) muse-http-publishing-style)
(setq style (car styles))
(setq style nil))))
(setq styles (cdr styles)))
(muse-publish-markup-buffer
name (or style muse-http-publishing-style))))
(set-buffer-modified-p nil)
(muse-http-prune-cache)
(current-buffer))))))
(defun muse-http-transmit-page (name)
"Render the Muse page identified by NAME.
When serving from a dedicated Emacs process (see the httpd-serve
script), a maximum of `muse-http-max-cache-size' pages will be
cached in memory to speed up serving time."
(let ((inhibit-read-only t)
(buffer (muse-http-render-page name)))
(if buffer
(with-current-buffer buffer
(muse-http-send-buffer muse-buffer-mtime)))))
(defvar httpd-vars nil)
(defsubst httpd-var (var)
"Return value of VAR as a URL variable. If VAR doesn't exist, nil."
(cdr (assoc var httpd-vars)))
(defsubst httpd-var-p (var)
"Return non-nil if VAR was passed as a URL variable."
(not (null (assoc var httpd-vars))))
(defun muse-http-serve (page &optional content)
"Serve the given PAGE from this press server."
;; index.html is really a reference to the project home page
(if (and muse-project-alist
(string-match "\\`index.html?\\'" page))
(setq page (concat "page?"
(muse-get-keyword :default
(cadr (car muse-project-alist))))))
;; handle the actual request
(let ((vc-follow-symlinks t)
(muse-publish-report-threshhold nil)
muse-http-serving-p
httpd-vars)
(save-excursion
;; process any CGI variables, if cgi.el is available
(if (string-match "\\`\\([^&]+\\)&" page)
(setq httpd-vars (cgi-decode (substring page (match-end 0)))
page (match-string 1 page)))
(unless (setq muse-http-serving-p (httpd-var "project"))
(let ((project (car muse-project-alist)))
(setq muse-http-serving-p (car project))
(setq httpd-vars (cons (cons "project" (car project))
httpd-vars))))
(if (and muse-http-serving-p
(string-match "\\`page\\?\\(.+\\)" page))
(muse-http-transmit-page (match-string 1 page))))))
(if (featurep 'httpd)
(httpd-add-handler "\\`\\(index\\.html?\\|page\\(\\?\\|\\'\\)\\)"
'muse-http-serve))
(provide 'muse-http)
;;; muse-http.el ends here

View file

@ -0,0 +1,219 @@
;;; muse-ikiwiki.el --- integrate with Ikiwiki
;; Copyright (C) 2008, 2009, 2010 Free Software Foundation, Inc.
;; This file is part of Emacs Muse. It is not part of GNU Emacs.
;; Emacs Muse 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 3, or (at your
;; option) any later version.
;; Emacs Muse 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 Emacs Muse; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.
;;; Commentary:
;;; Contributors:
;;; Code:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; Muse Ikiwiki Integration
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(require 'muse)
(require 'muse-html)
(require 'muse-ipc)
(require 'muse-publish)
(eval-when-compile
(require 'muse-colors))
(defgroup muse-ikiwiki nil
"Options controlling the behavior of Muse integration with Ikiwiki."
:group 'muse-publish)
(defcustom muse-ikiwiki-header ""
"Header used for publishing Ikiwiki output files.
This may be text or a filename."
:type 'string
:group 'muse-ikiwiki)
(defcustom muse-ikiwiki-footer ""
"Footer used for publishing Ikiwiki output files.
This may be text or a filename."
:type 'string
:group 'muse-ikiwiki)
(defcustom muse-ikiwiki-markup-regexps
`(;; Ikiwiki directives
(1350 ,(concat "\\(\\\\?\\)\\[\\[!""\\(?:-\\|\\w\\)+"
"\\([" muse-regexp-blank "\n]+"
"\\(?:\\(?:\\(?:-\\|\\w\\)+=\\)?"
"\\(?:\"\"\".*?\"\"\"\\|\"[^\"]+\""
"\\|[^]" muse-regexp-blank "\n]+\\)"
"[" muse-regexp-blank "\n]*\\)*\\)?\\]\\]")
0 muse-ikiwiki-markup-directive))
"List of markup rules for publishing Ikiwiki markup on Muse pages.
For more on the structure of this list, see `muse-publish-markup-regexps'."
:type '(repeat (choice
(list :tag "Markup rule"
integer
(choice regexp symbol)
integer
(choice string function symbol))
function))
:group 'muse-ikiwiki)
;;; Publishing
(defun muse-ikiwiki-markup-directive ()
"Handle publishing of an Ikiwiki directive."
(unless (get-text-property (match-beginning 0) 'read-only)
(add-text-properties (match-beginning 0) (match-end 0)
'(muse-no-paragraph t))
(muse-publish-mark-read-only (match-beginning 0) (match-end 0))))
(defun muse-ikiwiki-publish-buffer (name title &optional style)
"Publish a buffer for Ikiwki.
The name of the corresponding file is NAME.
The name of the style is given by STYLE. It defaults to \"ikiwiki\"."
(unless style (setq style "ikiwiki"))
(unless title (setq title (muse-page-name name)))
(let ((muse-batch-publishing-p t)
(muse-publishing-current-file name)
(muse-publishing-current-output-path name)
(muse-publishing-current-style style)
(font-lock-verbose nil)
(vc-handled-backends nil)) ; don't activate VC when publishing files
(run-hooks 'muse-before-publish-hook)
(let ((muse-inhibit-before-publish-hook t))
(muse-publish-markup-buffer title style))))
(defun muse-ikiwiki-publish-file (file name &optional style)
"Publish a single file for Ikiwiki.
The name of the real file is NAME, and the name of the temporary
file containing the content is FILE.
The name of the style is given by STYLE. It defaults to \"ikiwiki\"."
(if (not (stringp file))
(message "Error: No file given to publish")
(unless style
(setq style "ikiwiki"))
(let ((output-path file)
(target file)
(vc-handled-backends nil) ; don't activate VC when publishing files
auto-mode-alist
muse-current-output-style)
(setq auto-mode-alist
(delete (cons (concat "\\." muse-file-extension "\\'")
'muse-mode-choose-mode)
auto-mode-alist))
(setq muse-current-output-style (list :base style :path file))
(muse-with-temp-buffer
(muse-insert-file-contents file)
(muse-ikiwiki-publish-buffer name nil style)
(when (muse-write-file output-path t)
(muse-style-run-hooks :final style file output-path target))))))
(defun muse-ikiwiki-start-server (port)
"Start Muse IPC server, initializing with the client on PORT."
(muse-ipc-start "foo" #'muse-ikiwiki-publish-buffer port))
;;; Colors
(defface muse-ikiwiki-directive
'((((class color) (background light))
(:foreground "dark green"))
(((class color) (background dark))
(:foreground "green")))
"Face for Ikiwiki directives."
:group 'muse-ikiwiki)
(defun muse-colors-ikiwiki-directive ()
"Color ikiwiki directives."
(let ((start (match-beginning 0)))
(unless (or (eq (get-text-property start 'invisible) 'muse)
(get-text-property start 'muse-comment)
(get-text-property start 'muse-directive))
;; beginning of line or space or symbol
(save-excursion
(and
(catch 'valid
(while t
(skip-chars-forward "^\"]" muse-colors-region-end)
(cond ((eq (point) (point-max))
(throw 'valid nil))
((> (point) muse-colors-region-end)
(throw 'valid nil))
((eq (char-after) ?\")
(if (and (< (1+ (point)) muse-colors-region-end)
(eq (char-after (1+ (point))) ?\"))
(if (and (< (+ 2 (point)) muse-colors-region-end)
(eq (char-after (+ 2 (point))) ?\"))
;; triple-quote
(progn
(forward-char 3)
(or (and (looking-at "\"\"\"")
(goto-char (match-end 0)))
(re-search-forward
"\"\"\"" muse-colors-region-end t)
(throw 'valid nil)))
;; empty quotes (""), which are invalid
(throw 'valid nil))
;; quote with content
(forward-char 1)
(skip-chars-forward "^\"" muse-colors-region-end)
(when (eq (char-after) ?\")
(forward-char 1))))
((eq (char-after) ?\])
(forward-char 1)
(when (and (< (point) muse-colors-region-end)
(eq (char-after (point)) ?\]))
(forward-char 1)
(throw 'valid t)))
(t (throw 'valid nil)))))
;; found a valid directive
(let ((end (point)))
;; remove flyspell overlays
(when (fboundp 'flyspell-unhighlight-at)
(let ((cur start))
(while (> end cur)
(flyspell-unhighlight-at cur)
(setq cur (1+ cur)))))
(add-text-properties start end
'(face muse-ikiwiki-directive
muse-directive t muse-no-flyspell t))
(when (progn
(goto-char start)
(skip-chars-forward "^\n" end)
(and (eq (char-after) ?\n)
(not (= (point) end))))
(add-text-properties start end
'(font-lock-multiline t)))))))))
(defun muse-ikiwiki-insinuate-colors ()
(add-to-list 'muse-colors-markup
'("\\[\\[!" ?\[ muse-colors-ikiwiki-directive)
nil))
(eval-after-load "muse-colors" '(muse-ikiwiki-insinuate-colors))
;; Styles
(muse-derive-style "ikiwiki" "xhtml"
:header 'muse-ikiwiki-header
:footer 'muse-ikiwiki-footer
:regexps 'muse-ikiwiki-markup-regexps)
(provide 'muse-ikiwiki)
;;; muse-ikiwiki.el ends here

View file

@ -0,0 +1,137 @@
;;; muse-import-docbook.el --- convert Docbook XML into Muse format
;; Copyright (C) 2006, 2007, 2008, 2009, 2010
;; Free Software Foundation, Inc.
;; Author: Elena Pomohaci <e.pomohaci@gmail.com>
;; This file is part of Emacs Muse. It is not part of GNU Emacs.
;; Emacs Muse 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 3, or (at your
;; option) any later version.
;; Emacs Muse 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 Emacs Muse; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.
;;; Commentary:
;; It works only for article type docbook docs and recognize
;; followings elements: article, sect1, sect2, sect3, title,
;;; Contributors:
;;; Code:
(require 'muse-import-xml)
(defvar muse-import-docbook-prefix "muse-import-docbook-"
"The name prefix for tag functions")
(defvar muse-import-docbook-para-indent "\n\n"
"Para elements indentation (0, less than 6 spaces, more than 6 spaces)")
(defun muse-import-docbook-reset-para-indent ()
(setq muse-import-docbook-para-indent "\n\n"))
;;;###autoload
(defun muse-import-docbook (src dest)
"Convert the Docbook buffer SRC to Muse, writing output in the DEST buffer."
(interactive "bDocbook buffer:\nBMuse buffer:")
(setq muse-import-xml-prefix muse-import-docbook-prefix)
(setq muse-import-xml-generic-function-name "muse-import-xml-node")
(muse-import-xml src dest))
;;;###autoload
(defun muse-import-docbook-files (src dest)
"Convert the Docbook file SRC to Muse, writing output to the DEST file."
(interactive "fDocbook file:\nFMuse file:")
(with-temp-file dest
(muse-import-docbook (find-file-noselect src) (current-buffer))))
;;; element specific functions
(defun muse-import-docbook-get-title (node)
(let ((tit (car (xml-get-children node 'title))))
(insert (car (cddr tit)) ?\n ?\n)
(muse-import-xml-parse-tree (xml-node-children (remove tit node)))))
(defun muse-import-docbook-article (node)
"Article conversion function"
(muse-import-xml-node node))
(defun muse-import-docbook-articleinfo (node)
"Article conversion function"
(insert "#title ")
(muse-import-docbook-get-title node)
(insert ?\n))
(defalias 'muse-import-docbook-appendix 'muse-import-docbook-article)
(defalias 'muse-import-docbook-appendixinfo 'muse-import-docbook-articleinfo)
(defun muse-import-docbook-sect1 (node)
"Section 1 conversion function"
(insert ?\n "* ")
(muse-import-docbook-get-title node))
(defun muse-import-docbook-sect2 (node)
"Section 2 conversion function"
(insert ?\n "** ")
(muse-import-docbook-get-title node))
(defun muse-import-docbook-sect3 (node)
"Section 3 conversion function"
(insert ?\n "*** ")
(muse-import-docbook-get-title node))
(defun muse-import-docbook-graphic (node)
"Graphic conversion function. Image format is forced to PNG"
(let ((name (xml-get-attribute node 'fileref)))
(insert "\n[[img/" name ".png][" name "]]")))
(defun muse-import-docbook-para (node)
(insert muse-import-docbook-para-indent)
(muse-import-xml-node node))
(defun muse-import-docbook-emphasis (node)
(insert "*")
(muse-import-xml-node node)
(insert "*"))
(defun muse-import-docbook-quote (node)
(insert "\"")
(muse-import-xml-node node)
(insert "\""))
(defun muse-import-docbook-blockquote (node)
(setq muse-import-docbook-para-indent "\n\n ")
(muse-import-xml-node node)
(muse-import-docbook-reset-para-indent))
(defun muse-import-docbook-member (node)
(insert "\n> ")
(muse-import-xml-node node))
(defun muse-import-docbook-bridgehead (node)
(insert "\n* ")
(muse-import-xml-node node))
(provide 'muse-import-docbook)
;;; muse-import-docbook.el ends here

View file

@ -0,0 +1,149 @@
;;; muse-import-latex.el --- convert a LaTex file into a Muse file
;; Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010
;; Free Software Foundation, Inc.
;; This file is part of Emacs Muse. It is not part of GNU Emacs.
;; Emacs Muse 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 3, or (at your
;; option) any later version.
;; Emacs Muse 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 Emacs Muse; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.
;;; Commentary:
;; Helper commands for converting a LaTeX file into a Muse file.
;;; Contributors:
;;; Code:
(require 'muse)
(require 'muse-regexps)
(defun muse-i-l-write-citation (note author citation pages)
(save-excursion
(goto-char (point-max))
(if (= note 1)
(insert "\nFootnotes:\n\n"))
(let ((beg (point)))
(insert "\n[" (number-to-string note) "] " author)
(if (and citation pages)
(insert ", " citation ", " pages))
(insert "\n")
(goto-char beg)
(while (re-search-forward (concat "p.\\\\[" muse-regexp-blank "\n]+")
nil t)
(replace-match "p."))
(goto-char beg)
(while (re-search-forward "--" nil t)
(replace-match "-")))))
(defun muse-i-l-write-footnote (note text)
(save-excursion
(goto-char (point-max))
(if (= note 1)
(insert "\nFootnotes:\n\n"))
(insert "\n[" (number-to-string note) "] " text ?\n)))
;;;###autoload
(defun muse-import-latex ()
(interactive)
(goto-char (point-min))
(while (not (eobp))
(cond
((or (looking-at "^\\\\documentclass")
(looking-at "^\\\\input")
(looking-at "^\\\\begin{document}")
(looking-at "^\\\\end{document}")
(looking-at "^\\\\author")
(looking-at "^\\\\\\(med\\|big\\|small\\)skip")
(looking-at "^\\\\maketitle"))
(delete-region (point) (muse-line-end-position)))
((looking-at "^\\\\title{\\(.+\\)}")
(delete-region (match-end 1) (muse-line-end-position))
(delete-region (point) (match-beginning 1))
(insert "#title ")))
(forward-line))
(goto-char (point-min))
(while (re-search-forward "\\\\\\(l\\)?dots{}" nil t)
(replace-match (concat (and (string= (match-string 1) "l") ".")
"...")))
(goto-char (point-min))
(while (re-search-forward "\\(``\\|''\\)" nil t)
(replace-match "\""))
(goto-char (point-min))
(while (re-search-forward "---" nil t)
(replace-match " -- "))
(goto-char (point-min))
(while (re-search-forward "\\\\tableofcontents" nil t)
(replace-match "<contents>"))
(goto-char (point-min))
(while (re-search-forward "\\\\\\\\" nil t)
(replace-match ""))
(goto-char (point-min))
(while (re-search-forward "\\\\\\(sub\\)?section{\\([^}]+\\)}" nil t)
(replace-match (concat (if (string= (match-string 1) "sub")
"**" "*")
" " (match-string 2))))
(goto-char (point-min))
(while (re-search-forward "\\\\\\(begin\\|end\\){verse}" nil t)
(replace-match (concat "<" (if (string= (match-string 1) "end") "/")
"verse>")))
(goto-char (point-min))
(while (re-search-forward "\\\\\\(begin\\|end\\){quote}\n" nil t)
(replace-match ""))
(goto-char (point-min))
(while (re-search-forward
"\\\\\\(emph\\|textbf\\){\\([^}]+?\\)\\(\\\\/\\)?}" nil t)
(replace-match
(if (string= (match-string 1) "emph") "*\\2*" "**\\2**")))
(let ((footnote-index 1))
(goto-char (point-min))
(while (re-search-forward
(concat "\\\\\\(q\\)?\\(footnote\\|excerpt\\)\\(np\\)?"
"\\({\\([^}]+\\)}\\)?"
"\\({\\([^}]+\\)}{\\([^}]+\\)}\\)?{\\([^}]+\\)}") nil t)
(let ((beg (match-beginning 0))
(end (match-end 0)))
(unless (string= (match-string 2) "footnote")
(if (null (match-string 1))
(insert " " (match-string 9))
(let ((b (point)) e)
(insert "\"" (match-string 9) "\"")
(setq e (point-marker))
(save-match-data
(save-excursion
(goto-char b)
(while (< (point) e)
(if (looking-at "\\s-+")
(delete-region (match-beginning 0)
(match-end 0)))
(forward-line))))
(set-marker e nil))))
(insert "[" (number-to-string footnote-index) "]")
(if (string= (match-string 2) "footnote")
(muse-i-l-write-footnote footnote-index (match-string 9))
(muse-i-l-write-citation footnote-index (match-string 5)
(match-string 7) (match-string 8)))
(setq footnote-index (1+ footnote-index))
(delete-region beg end))))
(goto-char (point-min))
(while (looking-at "\n") (delete-char 1))
(goto-char (point-min))
(while (re-search-forward "\n\n+" nil t)
(replace-match "\n\n")))
(provide 'muse-import-latex)
;;; muse-import-latex.el ends here

View file

@ -0,0 +1,88 @@
;;; muse-import-xml.el --- common to all from-xml converters
;; Copyright (C) 2006, 2007, 2008, 2009, 2010
;; Free Software Foundation, Inc.
;; Author: Elena Pomohaci <e.pomohaci@gmail.com>
;; This file is part of Emacs Muse. It is not part of GNU Emacs.
;; Emacs Muse 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 3, or (at your
;; option) any later version.
;; Emacs Muse 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 Emacs Muse; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.
;;; Commentary:
;;; Contributors:
;;; Code:
(provide 'muse-import-xml)
(require 'xml)
(require 'muse)
(defvar muse-import-xml-prefix ""
"The name prefix for tag functions")
(defvar muse-import-xml-generic-function-name "muse-import-xml-generic"
"The generic function name")
(defun muse-import-xml-convert-to-list (buf)
"Convert xml BUF in a xml-list"
(with-temp-buffer
(insert-buffer-substring buf)
(goto-char (point-min))
(while (re-search-forward ">[ \n\t]*<" nil t)
(replace-match "><" nil nil)) ; clean all superfluous blank characters
(xml-parse-region (point-min)
(point-max)
(current-buffer))))
(defun muse-import-xml-generic (node)
"The generic function called when there is no node specific function."
(let ((name (xml-node-name node)))
(insert "<" (symbol-name name) ">")
(muse-import-xml-node node)
(insert "</" (symbol-name name) ">")))
(defun muse-import-xml-parse-tree (lst)
"Parse an xml tree list"
(mapc #'muse-import-xml-parse-node lst))
(defun muse-import-xml-parse-node (node)
"Parse a xml tree node"
(if (stringp node)
(insert (muse-replace-regexp-in-string "^[ \t]+" "" node))
(let ((fname (intern-soft (concat muse-import-xml-prefix
(symbol-name (xml-node-name node))))))
(if (functionp fname)
(funcall fname node)
(funcall (intern muse-import-xml-generic-function-name) node)))))
(defun muse-import-xml-node (node)
"Default node function"
(muse-import-xml-parse-tree (xml-node-children node)))
(defun muse-import-xml (src dest)
"Convert the xml SRC buffer in a muse DEST buffer"
(set-buffer (get-buffer-create dest))
(when (fboundp 'muse-mode)
(muse-mode))
(muse-import-xml-parse-tree (muse-import-xml-convert-to-list src)))
;;; muse-import-xml.el ends here

View file

@ -0,0 +1,194 @@
;;; muse-ipc.el --- publish Muse documents from other processes
;; Copyright (C) 2009, 2010 Free Software Foundation, Inc.
;; This file is part of Emacs Muse. It is not part of GNU Emacs.
;; Emacs Muse 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 3, or (at your
;; option) any later version.
;; Emacs Muse 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 Emacs Muse; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.
;;; Commentary:
;; This file is still in alpha state. Not for production use!
;;; Contributors:
;;; Code:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; Muse Inter-Process Communication
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(eval-when-compile (require 'cl))
(require 'muse)
(require 'muse-publish)
(defgroup muse-ipc nil
"Options controlling the behavior of Muse's IPC module."
:group 'muse-publish)
(defcustom muse-ipc-timeout 60
"Maximum time to wait for a client to respond."
:group 'muse-ipc
:type 'number)
(defcustom muse-ipc-ignore-done nil
"If non-nil, ignore any 'done' messages that we get from clients."
:group 'muse-ipc
:type 'boolean)
(defvar muse-ipc-server-port nil
"Port of the Emacs server.")
(defvar muse-ipc-server-process nil
"Process of the Emacs server.")
(defvar muse-ipc-server-registered nil
"Whether we have successfully registered our port with the client.")
(defun muse-ipc-init-filter (proc string)
"Handle data from client while initiating a connection."
(unless muse-ipc-server-registered
(when (string-match "\\`ok$" string)
(setq muse-ipc-server-registered t))))
(defun muse-ipc-delete-client (proc)
"Delete a client."
(let ((buffer (process-get proc :buffer)))
(when (and buffer (buffer-live-p buffer))
(with-current-buffer buffer
(set-buffer-modified-p nil))
(kill-buffer buffer)))
(when (eq (process-status proc) 'open)
(delete-process proc)))
(defun* muse-ipc-server-filter (proc string)
"Handle data from a client after it connects."
;; Authenticate
(unless (process-get proc :authenticated)
(if (and (string-match "\\`begin \\(.+\\)$" string)
(equal (match-string 1 string)
(process-get proc :shared-secret)))
(progn
(setq string (substring string (match-end 0)))
(process-put proc :authenticated t)
(process-send-string proc "ok\n"))
(process-send-string proc "nok\n")
(delete-process proc))
(return-from muse-ipc-server-filter))
;; Handle case where the client is sending data to be published
(when (process-get proc :sending-data)
(with-current-buffer (process-get proc :buffer)
(insert string)
(let ((buf-len (1- (point)))
(expected-len (process-get proc :data-bytes)))
(cond ((= buf-len expected-len)
(process-put proc :sending-data nil))
((> buf-len expected-len)
(process-send-string proc "nok\n")
(muse-ipc-delete-client proc)))))
(return-from muse-ipc-server-filter))
;; Dispatch commands
(cond
((string-match "\\`done$" string)
;; done, close the server
(unless muse-ipc-ignore-done
(muse-ipc-stop-server)))
((string-match "\\`name \\(.+\\)$" string)
;; set name
(process-put proc :file-name (match-string 1 string))
(process-send-string proc "ok\n"))
((string-match "\\`title \\(.+\\)$" string)
;; set title
(process-put proc :title (match-string 1 string))
(process-send-string proc "ok\n"))
(t
;; unrecognized command
(process-send-string proc "nok\n"))))
(defun muse-ipc-stop-server ()
"Stop Muse IPC server and reset connection data."
(stop-process muse-ipc-server-process)
(delete-process muse-ipc-server-process)
(setq muse-ipc-server-port nil)
(setq muse-ipc-server-process nil))
(defun muse-ipc-start (shared-secret publish-fn client-port &optional server-port)
"Start an IPC connection and send a response to CLIENT-PORT.
If SERVER-PORT is provided, start the IPC server on that port, otherwise
choose a random port.
SHARED-SECRET is used as a very minimal security measure to
authenticate the Muse IPC server during initialization, and also
any incoming clients once the server is started.
PUBLISH-FN is the function which should be called in buffer of
the received contents. It should transform the buffer into a
published state. It must take at least two arguments. The first
argument is the full path of the file that the contents
correspond with. The second argument is the title to use when
publishing the file."
(when (stringp client-port)
(setq client-port (string-to-number client-port)))
(when (stringp server-port)
(setq server-port (string-to-number server-port)))
(setq muse-ipc-server-process
(make-network-process
:name "muse-ipc"
:buffer nil
:host 'local :service (or server-port t)
:server t :noquery t :nowait t
:plist (list :authenticated nil :shared-secret shared-secret
:publish-fn publish-fn)
:filter 'muse-ipc-server-filter))
(unless muse-ipc-server-process
(error "Error: Could not start Muse IPC Server process"))
(set-process-coding-system muse-ipc-server-process
'raw-text-unix 'raw-text-unix)
(setq muse-ipc-server-port
(number-to-string
(cadr (process-contact muse-ipc-server-process))))
(let ((client-proc
(make-network-process
:name "muse-ipc-client"
:buffer nil
:host 'local :service client-port
:noquery t
:filter 'muse-ipc-init-filter)))
(setq muse-ipc-server-registered nil)
(process-send-string client-proc
(concat "begin " shared-secret "\n"))
(accept-process-output client-proc muse-ipc-timeout nil t)
(unless muse-ipc-server-registered
(error "Error: Did not register listener"))
(process-send-string client-proc
(concat "port " muse-ipc-server-port "\n"))
(stop-process client-proc)
(delete-process client-proc))
;; Accept process output until the server dies
(while muse-ipc-server-process (accept-process-output nil 1)))
(provide 'muse-ipc)
;;; muse-ipc.el ends here

View file

@ -0,0 +1,774 @@
;;; muse-journal.el --- keep and publish a journal
;; Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010
;; Free Software Foundation, Inc.
;; This file is part of Emacs Muse. It is not part of GNU Emacs.
;; Emacs Muse 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 3, or (at your
;; option) any later version.
;; Emacs Muse 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 Emacs Muse; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.
;;; Commentary:
;; The module facilitates the keeping and publication of a journal.
;; When publishing to HTML, it assumes the form of a web log, or blog.
;;
;; The input format for each entry is as follows:
;;
;; * 20040317: Title of entry
;;
;; Text for the entry.
;;
;; <qotd>
;; "You know who you are. It comes down to a simple gut check: You
;; either love what you do or you don't. Period." -- P. Bronson
;; </qotd>
;;
;; The "qotd", or Quote of the Day, is entirely optional. When
;; generated to HTML, this entry is rendered as:
;;
;; <div class="entry">
;; <div class="entry-qotd">
;; <h3>Quote of the Day:</h3>
;; <p>"You know who you are. It comes down to a simple gut
;; check: You either love what you do or you don't. Period."
;; -- P. Bronson</p>
;; </div>
;; <div class="entry-body">
;; <div class="entry-head">
;; <div class="entry-date">
;; <span class="date">March 17, 2004</span>
;; </div>
;; <div class="entry-title">
;; <h2>Title of entry</h2>
;; </div>
;; </div>
;; <div class="entry-text">
;; <p>Text for the entry.</p>
;; </div>
;; </div>
;; </div>
;;
;; The plurality of "div" tags makes it possible to display the
;; entries in any form you wish, using a CSS style.
;;
;; Also, an .RDF file can be generated from your journal by publishing
;; it with the "rdf" style. It uses the first two sentences of the
;; first paragraph of each entry as its "description", and
;; autogenerates tags for linking to the various entries.
;;; Contributors:
;; René Stadler (mail AT renestadler DOT de) provided a patch that
;; causes dates in RSS feeds to be generated in a format that RSS
;; readers can parse.
;;; Code:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; Muse Journal Publishing
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(require 'muse-publish)
(require 'muse-html)
(require 'muse-latex)
(require 'muse-book)
(defgroup muse-journal nil
"Rules for transforming a journal into its final form."
:group 'muse-publish)
(defcustom muse-journal-heading-regexp
"\\(?:\\([0-9]+\\)\\(?:: \\)?\\)?\\(.+?\\)?"
"A regexp that matches a journal heading.
Paren group 1 is the ISO date, group 2 is the optional category,
and group 3 is the optional heading for the entry."
:type 'regexp
:group 'muse-journal)
(defcustom muse-journal-date-format "%a, %e %b %Y"
"Date format to use for journal entries."
:type 'string
:group 'muse-journal)
(defcustom muse-journal-html-heading-regexp
(concat "^<h2[^>\n]*>" muse-journal-heading-regexp "</h2>$")
"A regexp that matches a journal heading from an HTML document.
Paren group 1 is the ISO date, group 2 is the optional category,
and group 3 is the optional heading for the entry."
:type 'regexp
:group 'muse-journal)
(defcustom muse-journal-rss-heading-regexp
(concat "^\\* " muse-journal-heading-regexp "$")
"A regexp that matches a journal heading from an HTML document.
Paren group 1 is the ISO date, group 2 is the optional category,
and group 3 is the optional heading for the entry."
:type 'regexp
:group 'muse-journal)
(defcustom muse-journal-html-entry-template
"<div class=\"entry\">
<a name=\"%anchor%\" style=\"text-decoration: none\">&nbsp;</a>
<div class=\"entry-body\">
<div class=\"entry-head\">
<div class=\"entry-date\">
<span class=\"date\">%date%</span>
</div>
<div class=\"entry-title\">
<h2>%title%</h2>
</div>
</div>
<div class=\"entry-text\">
<div class=\"entry-qotd\">
<p>%qotd%</p>
</div>
%text%
</div>
</div>
</div>\n\n"
"Template used to publish individual journal entries as HTML.
This may be text or a filename."
:type 'string
:group 'muse-journal)
(defcustom muse-journal-latex-section
"\\section*{%title% \\hfill {\\normalsize %date%}}
\\addcontentsline{toc}{chapter}{%title%}"
"Template used to publish a LaTeX section."
:type 'string
:group 'muse-journal)
(defcustom muse-journal-latex-subsection
"\\subsection*{%title%}
\\addcontentsline{toc}{section}{%title%}"
"Template used to publish a LaTeX subsection."
:type 'string
:group 'muse-journal)
(defcustom muse-journal-markup-tags
'(("qotd" t nil nil muse-journal-qotd-tag))
"A list of tag specifications, for specially marking up Journal entries.
See `muse-publish-markup-tags' for more info.
This is used by journal-latex and its related styles, as well as
the journal-rss-entry style, which both journal-rdf and
journal-rss use."
:type '(repeat (list (string :tag "Markup tag")
(boolean :tag "Expect closing tag" :value t)
(boolean :tag "Parse attributes" :value nil)
(boolean :tag "Nestable" :value nil)
function))
:group 'muse-journal)
;; FIXME: This doesn't appear to be used.
(defun muse-journal-generate-pages ()
(let ((output-dir (muse-style-element :path)))
(goto-char (point-min))
(while (re-search-forward muse-journal-heading-regexp nil t)
(let* ((date (match-string 1))
(category (match-string 1))
(category-file (concat output-dir category "/index.html"))
(heading (match-string 1)))
t))))
(defcustom muse-journal-rdf-extension ".rdf"
"Default file extension for publishing RDF (RSS 1.0) files."
:type 'string
:group 'muse-journal)
(defcustom muse-journal-rdf-base-url ""
"The base URL of the website referenced by the RDF file."
:type 'string
:group 'muse-journal)
(defcustom muse-journal-rdf-header
"<rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\"
xmlns=\"http://purl.org/rss/1.0/\"
xmlns:dc=\"http://purl.org/dc/elements/1.1/\">
<channel rdf:about=\"<lisp>(concat (muse-style-element :base-url)
(muse-publish-link-name))</lisp>\">
<title><lisp>(muse-publishing-directive \"title\")</lisp></title>
<link><lisp>(concat (muse-style-element :base-url)
(concat (muse-page-name)
muse-html-extension))</lisp></link>
<description><lisp>(muse-publishing-directive \"desc\")</lisp></description>
<items>
<rdf:Seq>
<rdf:li resource=\"<lisp>
(concat (muse-style-element :base-url)
(concat (muse-page-name)
muse-html-extension))</lisp>\"/>
</rdf:Seq>
</items>
</channel>\n"
"Header used for publishing RDF (RSS 1.0) files.
This may be text or a filename."
:type 'string
:group 'muse-journal)
(defcustom muse-journal-rdf-footer
"</rdf:RDF>\n"
"Footer used for publishing RDF (RSS 1.0) files.
This may be text or a filename."
:type 'string
:group 'muse-journal)
(defcustom muse-journal-rdf-date-format
"%Y-%m-%dT%H:%M:%S"
"Date format to use for RDF entries."
:type 'string
:group 'muse-journal)
(defcustom muse-journal-rdf-entry-template
"\n <item rdf:about=\"%link%#%anchor%\">
<title>%title%</title>
<description>
%desc%
</description>
<link>%link%#%anchor%</link>
<dc:date>%date%</dc:date>
<dc:creator>%maintainer%</dc:creator>
</item>\n"
"Template used to publish individual journal entries as RDF.
This may be text or a filename."
:type 'string
:group 'muse-journal)
(defcustom muse-journal-rdf-summarize-entries nil
"If non-nil, include only summaries in the RDF file, not the full data.
The default is nil, because this annoys some subscribers."
:type 'boolean
:group 'muse-journal)
(defcustom muse-journal-rss-extension ".xml"
"Default file extension for publishing RSS 2.0 files."
:type 'string
:group 'muse-journal)
(defcustom muse-journal-rss-base-url ""
"The base URL of the website referenced by the RSS file."
:type 'string
:group 'muse-journal)
(defcustom muse-journal-rss-header
"<\?xml version=\"1.0\" encoding=\"<lisp>
(muse-html-encoding)</lisp>\"?>
<rss version=\"2.0\">
<channel>
<title><lisp>(muse-publishing-directive \"title\")</lisp></title>
<link><lisp>(concat (muse-style-element :base-url)
(concat (muse-page-name)
muse-html-extension))</lisp></link>
<description><lisp>(muse-publishing-directive \"desc\")</lisp></description>
<language>en-us</language>
<generator>Emacs Muse</generator>\n\n"
"Header used for publishing RSS 2.0 files. This may be text or a filename."
:type 'string
:group 'muse-journal)
(defcustom muse-journal-rss-footer
"\n\n </channel>
</rss>\n"
"Footer used for publishing RSS 2.0 files. This may be text or a filename."
:type 'string
:group 'muse-journal)
(defcustom muse-journal-rss-date-format
"%a, %d %b %Y %H:%M:%S %Z"
"Date format to use for RSS 2.0 entries."
:type 'string
:group 'muse-journal)
(defcustom muse-journal-rss-entry-template
"\n <item>
<title>%title%</title>
<link>%link%#%anchor%</link>
<description>%desc%</description>
<author><lisp>(muse-publishing-directive \"author\")</lisp></author>
<pubDate>%date%</pubDate>
<guid>%link%#%anchor%</guid>
%enclosure%
</item>\n"
"Template used to publish individual journal entries as RSS 2.0.
This may be text or a filename."
:type 'string
:group 'muse-journal)
(defcustom muse-journal-rss-enclosure-types-alist
'(("mp3" . "audio/mpeg"))
"File types that are accepted as RSS enclosures.
This is an alist that maps file extension to content type.
Useful for podcasting."
:type '(alist :key-type string :value-type string)
:group 'muse-journal)
(defcustom muse-journal-rss-summarize-entries nil
"If non-nil, include only summaries in the RSS file, not the full data.
The default is nil, because this annoys some subscribers."
:type 'boolean
:group 'muse-journal)
(defcustom muse-journal-rss-markup-regexps
'((10000 muse-explicit-link-regexp 0 "\\2"))
"List of markup rules for publishing a Muse journal page to RSS 2.0.
For more information on the structure of this list, see
`muse-publish-markup-regexps'."
:type '(repeat (choice
(list :tag "Markup rule"
integer
(choice regexp symbol)
integer
(choice string function symbol))
function))
:group 'muse-journal)
(defcustom muse-journal-rss-markup-functions
'((email . ignore)
(link . ignore)
(url . ignore))
"An alist of style types to custom functions for that kind of text.
For more on the structure of this list, see
`muse-publish-markup-functions'."
:type '(alist :key-type symbol :value-type function)
:group 'muse-journal)
(defun muse-journal-anchorize-title (title)
"This strips tags from TITLE, truncates TITLE at begin parenthesis,
and escapes any remaining non-alphanumeric characters."
(save-match-data
(if (string-match "(" title)
(setq title (substring title 0 (match-beginning 0))))
(if (string-match "<[^>]+>" title)
(setq title (replace-match "" nil nil title)))
(let (pos code len ch)
(while (setq pos (string-match (concat "[^" muse-regexp-alnum "_]")
title pos))
(setq ch (aref title pos)
code (format "%%%02X" (cond ((fboundp 'char-to-ucs)
(char-to-ucs ch))
((fboundp 'char-to-int)
(char-to-int ch))
(t ch)))
len (length code)
title (concat (substring title 0 pos)
code
(when (< pos (length title))
(substring title (1+ pos) nil)))
pos (+ len pos)))
title)))
(defun muse-journal-sort-entries (&optional direction)
(interactive "P")
(sort-subr
direction
(function
(lambda ()
(if (re-search-forward "^\\* [0-9]+" nil t)
(goto-char (match-beginning 0))
(goto-char (point-max)))))
(function
(lambda ()
(if (re-search-forward "^\\* [0-9]+" nil t)
(goto-char (1- (match-beginning 0)))
(goto-char (point-max)))))
(function
(lambda ()
(forward-char 2)))
(function
(lambda ()
(end-of-line)))))
(defun muse-journal-qotd-tag (beg end)
(muse-publish-ensure-block beg end)
(muse-insert-markup (muse-markup-text 'begin-quote))
(muse-insert-markup (muse-markup-text 'begin-quote-item))
(goto-char end)
(muse-insert-markup (muse-markup-text 'end-quote-item))
(muse-insert-markup (muse-markup-text 'end-quote)))
(defun muse-journal-html-munge-buffer ()
(goto-char (point-min))
(let ((heading-regexp muse-journal-html-heading-regexp)
(inhibit-read-only t))
(while (re-search-forward heading-regexp nil t)
(let* ((date (match-string 1))
(orig-date date)
(title (match-string 2))
(clean-title title)
datestamp qotd text)
(delete-region (match-beginning 0) (match-end 0))
(if clean-title
(save-match-data
(while (string-match "\\(^<[^>]+>\\|<[^>]+>$\\)" clean-title)
(setq clean-title (replace-match "" nil nil clean-title)))))
(save-match-data
(when (and date
(string-match
(concat "\\`\\([1-9][0-9][0-9][0-9]\\)[./]?"
"\\([0-1][0-9]\\)[./]?\\([0-3][0-9]\\)") date))
(setq datestamp
(encode-time
0 0 0
(string-to-number (match-string 3 date))
(string-to-number (match-string 2 date))
(string-to-number (match-string 1 date))
nil)
date (concat (format-time-string
muse-journal-date-format datestamp)
(substring date (match-end 0))))))
(save-restriction
(narrow-to-region
(point) (if (re-search-forward
(concat "\\(^<hr>$\\|"
heading-regexp "\\)") nil t)
(match-beginning 0)
(point-max)))
(goto-char (point-max))
(while (and (not (bobp))
(eq ?\ (char-syntax (char-before))))
(delete-char -1))
(goto-char (point-min))
(while (and (not (eobp))
(eq ?\ (char-syntax (char-after))))
(delete-char 1))
(save-excursion
(when (search-forward "<qotd>" nil t)
(let ((tag-beg (match-beginning 0))
(beg (match-end 0))
end)
(re-search-forward "</qotd>\n*")
(setq end (point-marker))
(save-restriction
(narrow-to-region beg (match-beginning 0))
(muse-publish-escape-specials (point-min) (point-max)
nil 'document)
(setq qotd (buffer-substring-no-properties
(point-min) (point-max))))
(delete-region tag-beg end)
(set-marker end nil))))
(setq text (buffer-string))
(delete-region (point-min) (point-max))
(let ((entry muse-journal-html-entry-template))
(muse-insert-file-or-string entry)
(muse-publish-mark-read-only (point-min) (point-max))
(goto-char (point-min))
(while (search-forward "%date%" nil t)
(remove-text-properties (match-beginning 0) (match-end 0)
'(read-only nil rear-nonsticky nil))
(replace-match (or date "") nil t))
(goto-char (point-min))
(while (search-forward "%title%" nil t)
(remove-text-properties (match-beginning 0) (match-end 0)
'(read-only nil rear-nonsticky nil))
(replace-match (or title "&nbsp;") nil t))
(goto-char (point-min))
(while (search-forward "%anchor%" nil t)
(replace-match (muse-journal-anchorize-title
(or clean-title orig-date))
nil t))
(goto-char (point-min))
(while (search-forward "%qotd%" nil t)
(save-restriction
(narrow-to-region (match-beginning 0) (match-end 0))
(delete-region (point-min) (point-max))
(when qotd (muse-insert-markup qotd))))
(goto-char (point-min))
(while (search-forward "%text%" nil t)
(remove-text-properties (match-beginning 0) (match-end 0)
'(read-only nil rear-nonsticky nil))
(replace-match text nil t))
(when (null qotd)
(goto-char (point-min))
(when (search-forward "<div class=\"entry-qotd\">" nil t)
(let ((beg (match-beginning 0)))
(re-search-forward "</div>\n*" nil t)
(delete-region beg (point))))))))))
;; indicate that we are to continue the :before-end processing
nil)
(defun muse-journal-latex-munge-buffer ()
(goto-char (point-min))
(let ((heading-regexp
(concat "^" (regexp-quote (muse-markup-text 'section))
muse-journal-heading-regexp
(regexp-quote (muse-markup-text 'section-end)) "$"))
(inhibit-read-only t))
(when (re-search-forward heading-regexp nil t)
(goto-char (match-beginning 0))
(sort-subr nil
(function
(lambda ()
(if (re-search-forward heading-regexp nil t)
(goto-char (match-beginning 0))
(goto-char (point-max)))))
(function
(lambda ()
(if (re-search-forward heading-regexp nil t)
(goto-char (1- (match-beginning 0)))
(goto-char (point-max)))))
(function
(lambda ()
(forward-char 2)))
(function
(lambda ()
(end-of-line)))))
(while (re-search-forward heading-regexp nil t)
(let ((date (match-string 1))
(title (match-string 2))
;; FIXME: Nothing is done with qotd
qotd section)
(save-match-data
(when (and date
(string-match
(concat "\\([1-9][0-9][0-9][0-9]\\)[./]?"
"\\([0-1][0-9]\\)[./]?\\([0-3][0-9]\\)") date))
(setq date (encode-time
0 0 0
(string-to-number (match-string 3 date))
(string-to-number (match-string 2 date))
(string-to-number (match-string 1 date))
nil)
date (format-time-string
muse-journal-date-format date))))
(save-restriction
(narrow-to-region (match-beginning 0) (match-end 0))
(delete-region (point-min) (point-max))
(muse-insert-markup muse-journal-latex-section)
(goto-char (point-min))
(while (search-forward "%title%" nil t)
(replace-match (or title "Untitled") nil t))
(goto-char (point-min))
(while (search-forward "%date%" nil t)
(replace-match (or date "") nil t))))))
(goto-char (point-min))
(let ((subheading-regexp
(concat "^" (regexp-quote (muse-markup-text 'subsection))
"\\([^\n}]+\\)"
(regexp-quote (muse-markup-text 'subsection-end)) "$"))
(inhibit-read-only t))
(while (re-search-forward subheading-regexp nil t)
(let ((title (match-string 1)))
(save-restriction
(narrow-to-region (match-beginning 0) (match-end 0))
(delete-region (point-min) (point-max))
(muse-insert-markup muse-journal-latex-subsection)
(goto-char (point-min))
(while (search-forward "%title%" nil t)
(replace-match title nil t))))))
;; indicate that we are to continue the :before-end processing
nil)
(defun muse-journal-rss-munge-buffer ()
(goto-char (point-min))
(let ((heading-regexp muse-journal-rss-heading-regexp)
(inhibit-read-only t))
(while (re-search-forward heading-regexp nil t)
(let* ((date (match-string 1))
(orig-date date)
(title (match-string 2))
;; FIXME: Nothing is done with qotd
enclosure qotd desc)
(if title
(save-match-data
(if (string-match muse-explicit-link-regexp title)
(setq enclosure (muse-get-link title)
title (muse-get-link-desc title)))))
(save-match-data
(when (and date
(string-match
(concat "\\([1-9][0-9][0-9][0-9]\\)[./]?"
"\\([0-1][0-9]\\)[./]?\\([0-3][0-9]\\)") date))
(setq date (encode-time 0 0 0
(string-to-number (match-string 3 date))
(string-to-number (match-string 2 date))
(string-to-number (match-string 1 date))
nil)
;; make sure that date is in a format that RSS
;; readers can handle
date (let ((system-time-locale "C"))
(format-time-string
(muse-style-element :date-format) date)))))
(save-restriction
(narrow-to-region
(match-beginning 0)
(if (re-search-forward heading-regexp nil t)
(match-beginning 0)
(if (re-search-forward "^Footnotes:" nil t)
(match-beginning 0)
(point-max))))
(goto-char (point-min))
(delete-region (point) (muse-line-end-position))
(re-search-forward "</qotd>\n+" nil t)
(while (and (char-after)
(eq ?\ (char-syntax (char-after))))
(delete-char 1))
(let ((beg (point)))
(if (muse-style-element :summarize)
(progn
(forward-sentence 2)
(setq desc (concat (buffer-substring beg (point)) "...")))
(save-restriction
(muse-publish-markup-buffer "rss-entry" "journal-rss-entry")
(goto-char (point-min))
(if (re-search-forward "Page published by Emacs Muse" nil t)
(goto-char (muse-line-end-position))
(muse-display-warning
(concat
"Cannot find 'Page published by Emacs Muse begins here'.\n"
"You will probably need this text in your header."))
(goto-char (point-min)))
(setq beg (point))
(if (re-search-forward "Page published by Emacs Muse" nil t)
(goto-char (muse-line-beginning-position))
(muse-display-warning
(concat
"Cannot find 'Page published by Emacs Muse ends here'.\n"
"You will probably need this text in your footer."))
(goto-char (point-max)))
(setq desc (buffer-substring beg (point))))))
(unless (string= desc "")
(setq desc (concat "<![CDATA[" desc "]]>")))
(delete-region (point-min) (point-max))
(let ((entry (muse-style-element :entry-template)))
(muse-insert-file-or-string entry)
(goto-char (point-min))
(while (search-forward "%date%" nil t)
(replace-match (or date "") nil t))
(goto-char (point-min))
(while (search-forward "%title%" nil t)
(replace-match "")
(save-restriction
(narrow-to-region (point) (point))
(insert (or title "Untitled"))
(remove-text-properties (match-beginning 0) (match-end 0)
'(read-only nil rear-nonsticky nil))
(let ((muse-publishing-current-style (muse-style "html")))
(muse-publish-escape-specials (point-min) (point-max)
nil 'document))))
(goto-char (point-min))
(while (search-forward "%desc%" nil t)
(replace-match desc nil t))
(goto-char (point-min))
(while (search-forward "%enclosure%" nil t)
(replace-match
(if (null enclosure)
""
(save-match-data
(format
"<enclosure url=\"%s\" %stype=\"%s\"/>"
(if (string-match "//" enclosure)
enclosure
(concat (muse-style-element :base-url)
enclosure))
(let ((file
(expand-file-name enclosure
(muse-style-element :path))))
(if (file-readable-p file)
(format "length=\"%d\" "
(nth 7 (file-attributes file)))
""))
(if (string-match "\\.\\([^.]+\\)$" enclosure)
(let* ((ext (match-string 1 enclosure))
(type
(assoc
ext muse-journal-rss-enclosure-types-alist)))
(if type
(cdr type)
"application/octet-stream"))))))
nil t))
(goto-char (point-min))
(while (search-forward "%link%" nil t)
(replace-match
(concat (muse-style-element :base-url)
(concat (muse-page-name)
muse-html-extension))
nil t))
(goto-char (point-min))
(while (search-forward "%anchor%" nil t)
(replace-match
(muse-journal-anchorize-title (or title orig-date))
nil t))
(goto-char (point-min))
(while (search-forward "%maintainer%" nil t)
(replace-match
(or (muse-style-element :maintainer)
(concat "webmaster@" (system-name)))
nil t)))))))
;; indicate that we are to continue the :before-end processing
nil)
;;; Register the Muse Journal Publishers
(muse-derive-style "journal-html" "html"
:before-end 'muse-journal-html-munge-buffer)
(muse-derive-style "journal-xhtml" "xhtml"
:before-end 'muse-journal-html-munge-buffer)
(muse-derive-style "journal-latex" "latex"
:tags 'muse-journal-markup-tags
:before-end 'muse-journal-latex-munge-buffer)
(muse-derive-style "journal-pdf" "pdf"
:tags 'muse-journal-markup-tags
:before-end 'muse-journal-latex-munge-buffer)
(muse-derive-style "journal-book-latex" "book-latex"
;;:nochapters
:tags 'muse-journal-markup-tags
:before-end 'muse-journal-latex-munge-buffer)
(muse-derive-style "journal-book-pdf" "book-pdf"
;;:nochapters
:tags 'muse-journal-markup-tags
:before-end 'muse-journal-latex-munge-buffer)
(muse-define-style "journal-rdf"
:suffix 'muse-journal-rdf-extension
:regexps 'muse-journal-rss-markup-regexps
:functions 'muse-journal-rss-markup-functions
:before 'muse-journal-rss-munge-buffer
:header 'muse-journal-rdf-header
:footer 'muse-journal-rdf-footer
:date-format 'muse-journal-rdf-date-format
:entry-template 'muse-journal-rdf-entry-template
:base-url 'muse-journal-rdf-base-url
:summarize 'muse-journal-rdf-summarize-entries)
(muse-define-style "journal-rss"
:suffix 'muse-journal-rss-extension
:regexps 'muse-journal-rss-markup-regexps
:functions 'muse-journal-rss-markup-functions
:before 'muse-journal-rss-munge-buffer
:header 'muse-journal-rss-header
:footer 'muse-journal-rss-footer
:date-format 'muse-journal-rss-date-format
:entry-template 'muse-journal-rss-entry-template
:base-url 'muse-journal-rss-base-url
:summarize 'muse-journal-rss-summarize-entries)
;; Used by `muse-journal-rss-munge-buffer' to mark up individual entries
(muse-derive-style "journal-rss-entry" "html"
:tags 'muse-journal-markup-tags)
(provide 'muse-journal)
;;; muse-journal.el ends here

View file

@ -0,0 +1,669 @@
;;; muse-latex.el --- publish entries in LaTex or PDF format
;; Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010
;; Free Software Foundation, Inc.
;; This file is part of Emacs Muse. It is not part of GNU Emacs.
;; Emacs Muse 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 3, or (at your
;; option) any later version.
;; Emacs Muse 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 Emacs Muse; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.
;;; Commentary:
;;; Contributors:
;; Li Daobing (lidaobing AT gmail DOT com) provided CJK support.
;; Trent Buck (trentbuck AT gmail DOT com) gave valuable advice for
;; how to treat LaTeX specials and the like.
;; Matthias Kegelmann (mathias DOT kegelmann AT sdm DOT de) provided a
;; scenario where we would need to respect the <contents> tag.
;; Jean Magnan de Bornier (jean AT bornier DOT net) provided the
;; markup string for link-and-anchor.
;; Jim Ottaway (j DOT ottaway AT lse DOT ac DOT uk) implemented slides
;; and lecture notes.
;; Karl Berry (karl AT freefriends DOT org) suggested how to escape
;; additional special characters in image filenames.
;;; Code:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; Muse LaTeX Publishing
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(require 'muse-publish)
(defgroup muse-latex nil
"Rules for marking up a Muse file as a LaTeX article."
:group 'muse-publish)
(defcustom muse-latex-extension ".tex"
"Default file extension for publishing LaTeX files."
:type 'string
:group 'muse-latex)
(defcustom muse-latex-pdf-extension ".pdf"
"Default file extension for publishing LaTeX files to PDF."
:type 'string
:group 'muse-latex)
(defcustom muse-latex-pdf-browser "open %s"
"The program to use when browsing a published PDF file.
This should be a format string."
:type 'string
:group 'muse-latex)
(defcustom muse-latex-pdf-program "pdflatex"
"The program that is called to generate PDF content from LaTeX content."
:type 'string
:group 'muse-latex)
(defcustom muse-latex-pdf-cruft
'(".aux" ".log" ".nav" ".out" ".snm" ".toc" ".vrb")
"Extensions of files to remove after generating PDF output successfully."
:type 'string
:group 'muse-latex)
(defcustom muse-latex-header
"\\documentclass{article}
\\usepackage[english]{babel}
\\usepackage{ucs}
\\usepackage[utf8x]{inputenc}
\\usepackage[T1]{fontenc}
\\usepackage{hyperref}
\\usepackage[pdftex]{graphicx}
\\def\\museincludegraphics{%
\\begingroup
\\catcode`\\|=0
\\catcode`\\\\=12
\\catcode`\\#=12
\\includegraphics[width=0.75\\textwidth]
}
\\begin{document}
\\title{<lisp>(muse-publish-escape-specials-in-string
(muse-publishing-directive \"title\") 'document)</lisp>}
\\author{<lisp>(muse-publishing-directive \"author\")</lisp>}
\\date{<lisp>(muse-publishing-directive \"date\")</lisp>}
\\maketitle
<lisp>(and muse-publish-generate-contents
(not muse-latex-permit-contents-tag)
\"\\\\tableofcontents\n\\\\newpage\")</lisp>\n\n"
"Header used for publishing LaTeX files. This may be text or a filename."
:type 'string
:group 'muse-latex)
(defcustom muse-latex-footer "<lisp>(muse-latex-bibliography)</lisp>
\\end{document}\n"
"Footer used for publishing LaTeX files. This may be text or a filename."
:type 'string
:group 'muse-latex)
(defcustom muse-latexcjk-header
"\\documentclass{article}
\\usepackage{CJK}
\\usepackage{indentfirst}
\\usepackage[CJKbookmarks=true]{hyperref}
\\usepackage[pdftex]{graphicx}
\\begin{document}
\\begin{CJK*}<lisp>(muse-latexcjk-encoding)</lisp>
\\title{<lisp>(muse-publish-escape-specials-in-string
(muse-publishing-directive \"title\") 'document)</lisp>}
\\author{<lisp>(muse-publishing-directive \"author\")</lisp>}
\\date{<lisp>(muse-publishing-directive \"date\")</lisp>}
\\maketitle
<lisp>(and muse-publish-generate-contents
(not muse-latex-permit-contents-tag)
\"\\\\tableofcontents\n\\\\newpage\")</lisp>\n\n"
"Header used for publishing LaTeX files (CJK). This may be text or a
filename."
:type 'string
:group 'muse-latex)
(defcustom muse-latexcjk-footer
"\n\\end{CJK*}
\\end{document}\n"
"Footer used for publishing LaTeX files (CJK). This may be text or a
filename."
:type 'string
:group 'muse-latex)
(defcustom muse-latex-slides-header
"\\documentclass[ignorenonframetext]{beamer}
\\usepackage[english]{babel}
\\usepackage{ucs}
\\usepackage[utf8x]{inputenc}
\\usepackage[T1]{fontenc}
\\usepackage{hyperref}
\\def\\museincludegraphics{%
\\begingroup
\\catcode`\\|=0
\\catcode`\\\\=12
\\catcode`\\#=12
\\includegraphics[width=0.50\\textwidth]
}
\\title{<lisp>(muse-publish-escape-specials-in-string
(muse-publishing-directive \"title\") 'document)</lisp>}
\\author{<lisp>(muse-publishing-directive \"author\")</lisp>}
\\date{<lisp>(muse-publishing-directive \"date\")</lisp>}
\\begin{document}
\\frame{\\titlepage}
<lisp>(and muse-publish-generate-contents
\"\\\\frame{\\\\tableofcontents}\")</lisp>\n\n"
"Header for publishing of slides using LaTeX.
This may be text or a filename.
You must have the Beamer extension for LaTeX installed for this to work."
:type 'string
:group 'muse-latex)
(defcustom muse-latex-lecture-notes-header
"\\documentclass{article}
\\usepackage{beamerarticle}
\\usepackage[english]{babel}
\\usepackage{ucs}
\\usepackage[utf8x]{inputenc}
\\usepackage[T1]{fontenc}
\\usepackage{hyperref}
\\usepackage[pdftex]{graphicx}
\\def\\museincludegraphics{%
\\begingroup
\\catcode`\\|=0
\\catcode`\\\\=12
\\catcode`\\#=12
\\includegraphics[width=0.50\\textwidth]
}
\\title{<lisp>(muse-publish-escape-specials-in-string
(muse-publishing-directive \"title\") 'document)</lisp>}
\\author{<lisp>(muse-publishing-directive \"author\")</lisp>}
\\date{<lisp>(muse-publishing-directive \"date\")</lisp>}
\\begin{document}
\\frame{\\titlepage}
<lisp>(and muse-publish-generate-contents
\"\\\\frame{\\\\tableofcontents}\")</lisp>\n\n"
"Header for publishing of lecture notes using LaTeX.
This may be text or a filename.
You must have the Beamer extension for LaTeX installed for this to work."
:type 'string
:group 'muse-latex)
(defcustom muse-latex-markup-regexps
`(;; numeric ranges
(10000 "\\([0-9]+\\)-\\([0-9]+\\)" 0 "\\1--\\2")
;; be careful of closing quote pairs
(10100 "\"'" 0 "\"\\\\-'"))
"List of markup regexps for identifying regions in a Muse page.
For more on the structure of this list, see `muse-publish-markup-regexps'."
:type '(repeat (choice
(list :tag "Markup rule"
integer
(choice regexp symbol)
integer
(choice string function symbol))
function))
:group 'muse-latex)
(defcustom muse-latex-markup-functions
'((table . muse-latex-markup-table))
"An alist of style types to custom functions for that kind of text.
For more on the structure of this list, see
`muse-publish-markup-functions'."
:type '(alist :key-type symbol :value-type function)
:group 'muse-latex)
(defcustom muse-latex-markup-strings
'((image-with-desc . "\\begin{figure}[h]
\\centering\\museincludegraphics{%s.%s}|endgroup
\\caption{%s}
\\end{figure}")
(image . "\\begin{figure}[h]
\\centering\\museincludegraphics{%s.%s}|endgroup
\\end{figure}")
(image-link . "%% %s
\\museincludegraphics{%s.%s}|endgroup")
(anchor-ref . "\\ref{%s}")
(url . "\\url{%s}")
(url-and-desc . "\\href{%s}{%s}\\footnote{%1%}")
(link . "\\href{%s}{%s}\\footnote{%1%}")
(link-and-anchor . "\\href{%1%}{%3%}\\footnote{%1%}")
(email-addr . "\\verb|%s|")
(anchor . "\\label{%s}")
(emdash . "---")
(comment-begin . "% ")
(rule . "\\vspace{.5cm}\\hrule\\vspace{.5cm}")
(no-break-space . "~")
(line-break . "\\\\")
(enddots . "\\ldots{}")
(dots . "\\dots{}")
(part . "\\part{")
(part-end . "}")
(chapter . "\\chapter{")
(chapter-end . "}")
(section . "\\section{")
(section-end . "}")
(subsection . "\\subsection{")
(subsection-end . "}")
(subsubsection . "\\subsubsection{")
(subsubsection-end . "}")
(section-other . "\\paragraph{")
(section-other-end . "}")
(footnote . "\\footnote{")
(footnote-end . "}")
(footnotetext . "\\footnotetext[%d]{")
(begin-underline . "\\underline{")
(end-underline . "}")
(begin-literal . "\\texttt{")
(end-literal . "}")
(begin-emph . "\\emph{")
(end-emph . "}")
(begin-more-emph . "\\textbf{")
(end-more-emph . "}")
(begin-most-emph . "\\textbf{\\emph{")
(end-most-emph . "}}")
(begin-verse . "\\begin{verse}\n")
(end-verse-line . " \\\\")
(verse-space . "~~~~")
(end-verse . "\n\\end{verse}")
(begin-example . "\\begin{quote}\n\\begin{verbatim}")
(end-example . "\\end{verbatim}\n\\end{quote}")
(begin-center . "\\begin{center}\n")
(end-center . "\n\\end{center}")
(begin-quote . "\\begin{quote}\n")
(end-quote . "\n\\end{quote}")
(begin-cite . "\\cite{")
(begin-cite-author . "\\citet{")
(begin-cite-year . "\\citet{")
(end-cite . "}")
(begin-uli . "\\begin{itemize}\n")
(end-uli . "\n\\end{itemize}")
(begin-uli-item . "\\item ")
(begin-oli . "\\begin{enumerate}\n")
(end-oli . "\n\\end{enumerate}")
(begin-oli-item . "\\item ")
(begin-dl . "\\begin{description}\n")
(end-dl . "\n\\end{description}")
(begin-ddt . "\\item[")
(end-ddt . "] \\mbox{}\n"))
"Strings used for marking up text.
These cover the most basic kinds of markup, the handling of which
differs little between the various styles."
:type '(alist :key-type symbol :value-type string)
:group 'muse-latex)
(defcustom muse-latex-slides-markup-tags
'(("slide" t t nil muse-latex-slide-tag))
"A list of tag specifications, for specially marking up LaTeX slides."
:type '(repeat (list (string :tag "Markup tag")
(boolean :tag "Expect closing tag" :value t)
(boolean :tag "Parse attributes" :value nil)
(boolean :tag "Nestable" :value nil)
function))
:group 'muse-latex)
(defcustom muse-latexcjk-encoding-map
'((utf-8 . "{UTF8}{song}")
(japanese-iso-8bit . "[dnp]{JIS}{min}")
(chinese-big5 . "{Bg5}{bsmi}")
(mule-utf-8 . "{UTF8}{song}")
(chinese-iso-8bit . "{GB}{song}")
(chinese-gbk . "{GBK}{song}"))
"An alist mapping emacs coding systems to appropriate CJK codings.
Use the base name of the coding system (ie, without the -unix)."
:type '(alist :key-type coding-system :value-type string)
:group 'muse-latex)
(defcustom muse-latexcjk-encoding-default "{GB}{song}"
"The default Emacs buffer encoding to use in published files.
This will be used if no special characters are found."
:type 'string
:group 'muse-latex)
(defun muse-latexcjk-encoding ()
(when (boundp 'buffer-file-coding-system)
(muse-latexcjk-transform-content-type buffer-file-coding-system)))
(defun muse-latexcjk-transform-content-type (content-type)
"Using `muse-cjklatex-encoding-map', try and resolve an emacs coding
system to an associated CJK coding system."
(let ((match (and (fboundp 'coding-system-base)
(assoc (coding-system-base content-type)
muse-latexcjk-encoding-map))))
(if match
(cdr match)
muse-latexcjk-encoding-default)))
(defcustom muse-latex-markup-specials-document
'((?\\ . "\\textbackslash{}")
(?\_ . "\\textunderscore{}")
(?\< . "\\textless{}")
(?\> . "\\textgreater{}")
(?^ . "\\^{}")
(?\~ . "\\~{}")
(?\@ . "\\@")
(?\$ . "\\$")
(?\% . "\\%")
(?\{ . "\\{")
(?\} . "\\}")
(?\& . "\\&")
(?\# . "\\#"))
"A table of characters which must be represented specially.
These are applied to the entire document, sans already-escaped
regions."
:type '(alist :key-type character :value-type string)
:group 'muse-latex)
(defcustom muse-latex-markup-specials-example
'()
"A table of characters which must be represented specially.
These are applied to <example> regions.
With the default interpretation of <example> regions, no specials
need to be escaped."
:type '(alist :key-type character :value-type string)
:group 'muse-latex)
(defcustom muse-latex-markup-specials-literal
'((?\n . "\\\n")
(?\\ . "\\textbackslash{}")
(?_ . "\\textunderscore{}")
(?\< . "\\textless{}")
(?\> . "\\textgreater{}")
(?^ . "\\^{}")
(?\~ . "\\~{}")
(?\$ . "\\$")
(?\% . "\\%")
(?\{ . "\\{")
(?\} . "\\}")
(?\& . "\\&")
(?\# . "\\#"))
"A table of characters which must be represented specially.
This applies to =monospaced text= and <code> regions."
:type '(alist :key-type character :value-type string)
:group 'muse-latex)
(defcustom muse-latex-markup-specials-url
'((?\\ . "\\textbackslash{}")
(?\_ . "\\_")
(?\< . "\\<")
(?\> . "\\>")
(?\$ . "\\$")
(?\% . "\\%")
(?\{ . "\\{")
(?\} . "\\}")
(?\& . "\\&")
(?\# . "\\#"))
"A table of characters which must be represented specially.
These are applied to URLs."
:type '(alist :key-type character :value-type string)
:group 'muse-latex)
(defcustom muse-latex-markup-specials-image
'((?\\ . "\\\\")
(?\< . "\\<")
(?\> . "\\>")
(?\$ . "\\$")
(?\% . "\\%")
(?\{ . "\\{")
(?\} . "\\}")
(?\& . "\\&")
(?\# . "\\#")
(?\| . "\\|"))
"A table of characters which must be represented specially.
These are applied to image filenames."
:type '(alist :key-type character :value-type string)
:group 'muse-latex)
(defun muse-latex-decide-specials (context)
"Determine the specials to escape, depending on CONTEXT."
(cond ((memq context '(underline emphasis document url-desc verbatim
footnote))
muse-latex-markup-specials-document)
((eq context 'image)
muse-latex-markup-specials-image)
((memq context '(email url))
muse-latex-markup-specials-url)
((eq context 'literal)
muse-latex-markup-specials-literal)
((eq context 'example)
muse-latex-markup-specials-example)
(t (error "Invalid context '%s' in muse-latex" context))))
(defcustom muse-latex-permit-contents-tag nil
"If nil, ignore <contents> tags. Otherwise, insert table of contents.
Most of the time, it is best to have a table of contents on the
first page, with a new page immediately following. To make this
work with documents published in both HTML and LaTeX, we need to
ignore the <contents> tag.
If you don't agree with this, then set this option to non-nil,
and it will do what you expect."
:type 'boolean
:group 'muse-latex)
(defun muse-latex-markup-table ()
(let* ((table-info (muse-publish-table-fields (match-beginning 0)
(match-end 0)))
(row-len (car table-info))
(field-list (cdr table-info)))
(when table-info
(muse-insert-markup "\\begin{tabular}{" (make-string row-len ?l) "}\n")
(dolist (fields field-list)
(let ((type (car fields)))
(setq fields (cdr fields))
(if (eq type 'hline)
(muse-insert-markup "\\hline\n")
(when (= type 3)
(muse-insert-markup "\\hline\n"))
(insert (car fields))
(setq fields (cdr fields))
(dolist (field fields)
(muse-insert-markup " & ")
(insert field))
(muse-insert-markup " \\\\\n")
(when (= type 2)
(muse-insert-markup "\\hline\n")))))
(muse-insert-markup "\\end{tabular}"))))
;;; Tags for LaTeX
(defun muse-latex-slide-tag (beg end attrs)
"Publish the <slide> tag in LaTeX.
This is used by the slides and lecture-notes publishing styles."
(let ((title (cdr (assoc "title" attrs))))
(goto-char beg)
(muse-insert-markup "\\begin{frame}[fragile]\n")
(when title
(muse-insert-markup "\\frametitle{")
(insert title)
(muse-insert-markup "}\n"))
(save-excursion
(goto-char end)
(muse-insert-markup "\n\\end{frame}"))))
;;; Post-publishing functions
(defun muse-latex-fixup-dquotes ()
"Fixup double quotes."
(goto-char (point-min))
(let ((open t))
(while (search-forward "\"" nil t)
(unless (get-text-property (match-beginning 0) 'read-only)
(when (or (bobp)
(eq (char-before) ?\n))
(setq open t))
(if open
(progn
(replace-match "``")
(setq open nil))
(replace-match "''")
(setq open t))))))
(defun muse-latex-fixup-citations ()
"Replace semicolons in multi-head citations with colons."
(goto-char (point-min))
(while (re-search-forward "\\\\cite.?{" nil t)
(let ((start (point))
(end (re-search-forward "}")))
(save-restriction
(narrow-to-region start end)
(goto-char (point-min))
(while (re-search-forward ";" nil t)
(replace-match ","))))))
(defun muse-latex-fixup-headings ()
"Remove footnotes in headings, since LaTeX does not permit them to exist.
This can happen if there is a link in a heading, because by
default Muse will add a footnote for each link."
(goto-char (point-min))
(while (re-search-forward "^\\\\section.?{" nil t)
(save-restriction
(narrow-to-region (match-beginning 0) (muse-line-end-position))
(goto-char (point-min))
(while (re-search-forward "\\\\footnote{[^}\n]+}" nil t)
(replace-match ""))
(forward-line 1))))
(defun muse-latex-munge-buffer ()
(muse-latex-fixup-dquotes)
(muse-latex-fixup-citations)
(muse-latex-fixup-headings)
(when (and muse-latex-permit-contents-tag
muse-publish-generate-contents)
(goto-char (car muse-publish-generate-contents))
(muse-insert-markup "\\tableofcontents")))
(defun muse-latex-bibliography ()
(save-excursion
(goto-char (point-min))
(if (re-search-forward "\\\\cite.?{" nil t)
(concat
"\\bibliography{"
(muse-publishing-directive "bibsource")
"}\n")
"")))
(defun muse-latex-pdf-browse-file (file)
(shell-command (format muse-latex-pdf-browser file)))
(defun muse-latex-pdf-generate (file output-path final-target)
(apply
#'muse-publish-transform-output
file output-path final-target "PDF"
(function
(lambda (file output-path)
(let* ((fnd (file-name-directory output-path))
(command (format "%s \"%s\""
muse-latex-pdf-program
(file-relative-name file fnd)))
(times 0)
(default-directory fnd)
result)
;; XEmacs can sometimes return a non-number result. We'll err
;; on the side of caution by continuing to attempt to generate
;; the PDF if this happens and treat the final result as
;; successful.
(while (and (< times 2)
(or (not (numberp result))
(not (eq result 0))
;; table of contents takes 2 passes
(file-readable-p
(muse-replace-regexp-in-string
"\\.tex\\'" ".toc" file t t))))
(setq result (shell-command command)
times (1+ times)))
(if (or (not (numberp result))
(eq result 0))
t
nil))))
muse-latex-pdf-cruft))
;;; Register the Muse LATEX Publishers
(muse-define-style "latex"
:suffix 'muse-latex-extension
:regexps 'muse-latex-markup-regexps
:functions 'muse-latex-markup-functions
:strings 'muse-latex-markup-strings
:specials 'muse-latex-decide-specials
:before-end 'muse-latex-munge-buffer
:header 'muse-latex-header
:footer 'muse-latex-footer
:browser 'find-file)
(muse-derive-style "pdf" "latex"
:final 'muse-latex-pdf-generate
:browser 'muse-latex-pdf-browse-file
:link-suffix 'muse-latex-pdf-extension
:osuffix 'muse-latex-pdf-extension)
(muse-derive-style "latexcjk" "latex"
:header 'muse-latexcjk-header
:footer 'muse-latexcjk-footer)
(muse-derive-style "pdfcjk" "latexcjk"
:final 'muse-latex-pdf-generate
:browser 'muse-latex-pdf-browse-file
:link-suffix 'muse-latex-pdf-extension
:osuffix 'muse-latex-pdf-extension)
(muse-derive-style "slides" "latex"
:header 'muse-latex-slides-header
:tags 'muse-latex-slides-markup-tags)
(muse-derive-style "slides-pdf" "pdf"
:header 'muse-latex-slides-header
:tags 'muse-latex-slides-markup-tags)
(muse-derive-style "lecture-notes" "slides"
:header 'muse-latex-lecture-notes-header)
(muse-derive-style "lecture-notes-pdf" "slides-pdf"
:header 'muse-latex-lecture-notes-header)
(provide 'muse-latex)
;;; muse-latex.el ends here

View file

@ -0,0 +1,277 @@
;; muse-latex2png.el --- generate PNG images from inline LaTeX code
;; Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010
;; Free Software Foundation, Inc.
;; Author: Michael Olson <mwolson@gnu.org>
;; Created: 12-Oct-2005
;; This file is part of Emacs Muse. It is not part of GNU Emacs.
;; Emacs Muse 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 3, or (at your
;; option) any later version.
;; Emacs Muse 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 Emacs Muse; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.
;;; Commentary:
;; This was taken from latex2png.el, by Ganesh Swami <ganesh AT
;; iamganesh DOT com>, which was made for emacs-wiki. It has since
;; been extensively rewritten for Muse.
;;; To do
;; Remove stale image files. This could be done by making a function
;; for `muse-before-publish-hook' that deletes according to
;; (muse-page-name).
;;; Code
(require 'muse-publish)
(defgroup muse-latex2png nil
"Publishing LaTeX formulas as PNG files."
:group 'muse-publish)
(defcustom muse-latex2png-img-dest "./latex"
"The folder where the generated images will be placed.
This is relative to the current publishing directory."
:type 'string
:group 'muse-latex2png)
(defcustom muse-latex2png-scale-factor 2.5
"The scale factor to be used for sizing the resulting LaTeX output."
:type 'number
:group 'muse-latex2png)
(defcustom muse-latex2png-fg "Black"
"The foreground color."
:type 'string
:group 'muse-latex2png)
(defcustom muse-latex2png-bg "Transparent"
"The background color."
:type 'string
:group 'muse-latex2png)
(defcustom muse-latex2png-template
"\\documentclass{article}
\\usepackage{fullpage}
\\usepackage{amssymb}
\\usepackage[usenames]{color}
\\usepackage{amsmath}
\\usepackage{latexsym}
\\usepackage[mathscr]{eucal}
%preamble%
\\pagestyle{empty}
\\begin{document}
{%code%}
\\end{document}\n"
"The LaTeX template to use."
:type 'string
:group 'muse-latex2png)
(defun muse-latex2png-move2pubdir (file prefix pubdir)
"Move FILE to the PUBDIR folder.
This is done so that the resulting images do not clutter your
main publishing directory.
Old files with PREFIX in the name are deleted."
(when file
(if (file-exists-p file)
(progn
(unless (file-directory-p pubdir)
(message "Creating latex directory %s" pubdir)
(make-directory pubdir))
(copy-file file (expand-file-name (file-name-nondirectory file)
pubdir)
t)
(delete-file file)
(concat muse-latex2png-img-dest "/" (file-name-nondirectory file)))
(message "Cannot find %s!" file))))
(defun muse-latex2png (code prefix preamble)
"Convert the LaTeX CODE into a png file beginning with PREFIX.
PREAMBLE indicates extra packages and definitions to include."
(unless preamble
(setq preamble ""))
(unless prefix
(setq prefix "muse-latex2png"))
(let* ((tmpdir (cond ((boundp 'temporary-file-directory)
temporary-file-directory)
((fboundp 'temp-directory)
(temp-directory))
(t "/tmp")))
(texfile (expand-file-name
(concat prefix "__" (format "%d" (abs (sxhash code))))
tmpdir))
(defalt-directory default-directory))
(with-temp-file (concat texfile ".tex")
(insert muse-latex2png-template)
(goto-char (point-min))
(while (search-forward "%preamble%" nil t)
(replace-match preamble nil t))
(goto-char (point-min))
(while (search-forward "%code%" nil t)
(replace-match code nil t)))
(setq default-directory tmpdir)
(call-process "latex" nil nil nil texfile)
(if (file-exists-p (concat texfile ".dvi"))
(progn
(call-process
"dvipng" nil nil nil
"-E"
"-fg" muse-latex2png-fg
"-bg" muse-latex2png-bg
"-T" "tight"
"-x" (format "%s" (* muse-latex2png-scale-factor 1000))
"-y" (format "%s" (* muse-latex2png-scale-factor 1000))
"-o" (concat texfile ".png")
(concat texfile ".dvi"))
(if (file-exists-p (concat texfile ".png"))
(progn
(delete-file (concat texfile ".dvi"))
(delete-file (concat texfile ".tex"))
(delete-file (concat texfile ".aux"))
(delete-file (concat texfile ".log"))
(concat texfile ".png"))
(message "Failed to create png file")
nil))
(message (concat "Failed to create dvi file " texfile))
nil)))
(defun muse-latex2png-region (beg end attrs)
"Generate an image for the Latex code between BEG and END.
If a Muse page is currently being published, replace the given
region with the appropriate markup that displays the image.
Otherwise, just return the path of the generated image.
Valid keys for the ATTRS alist are as follows.
prefix: The prefix given to the image file.
preamble: Extra text to add to the Latex preamble.
inline: Display image as inline, instead of a block."
(let ((end-marker (set-marker (make-marker) (1+ end)))
(pubdir (expand-file-name
muse-latex2png-img-dest
(file-name-directory muse-publishing-current-output-path))))
(save-restriction
(narrow-to-region beg end)
(let* ((text (buffer-substring-no-properties beg end))
;; the prefix given to the image file.
(prefix (cdr (assoc "prefix" attrs)))
;; preamble (for extra options)
(preamble (cdr (assoc "preamble" attrs)))
;; display inline or as a block
(display (car (assoc "inline" attrs))))
(when muse-publishing-p
(delete-region beg end)
(goto-char (point-min)))
(unless (file-directory-p pubdir)
(make-directory pubdir))
(let ((path (muse-latex2png-move2pubdir
(muse-latex2png text prefix preamble)
prefix pubdir)))
(when path
(when muse-publishing-p
(muse-insert-markup
(if (muse-style-derived-p "html")
(concat "<img src=\"" path
"\" alt=\"latex2png equation\" "
(if display (concat "class=\"latex-inline\"")
(concat "class=\"latex-display\""))
(if (muse-style-derived-p "xhtml")
" />"
">")
(muse-insert-markup "<!-- " text "-->"))
(let ((ext (or (file-name-extension path) ""))
(path (muse-path-sans-extension path)))
(muse-markup-text 'image path ext))))
(goto-char (point-max)))
path))))))
(defun muse-publish-latex-tag (beg end attrs)
"If the current style is not Latex-based, generate an image for the
given Latex code. Otherwise, don't do anything to the region.
See `muse-latex2png-region' for valid keys for ATTRS."
(unless (assoc "prefix" attrs)
(setq attrs (cons (cons "prefix"
(concat "latex2png-" (muse-page-name)))
attrs)))
(if (or (muse-style-derived-p "latex") (muse-style-derived-p "context"))
(muse-publish-mark-read-only beg end)
(muse-latex2png-region beg end attrs)))
(put 'muse-publish-latex-tag 'muse-dangerous-tag t)
(defun muse-publish-math-tag (beg end)
"Surround the given region with \"$\" characters. Then, if the
current style is not Latex-based, generate an image for the given
Latex math code.
If 6 or more spaces come before the tag, and the end of the tag
is at the end of a line, then surround the region with the
equivalent of \"$$\" instead. This causes the region to be
centered in the published output, among other things."
(let* ((centered (and (re-search-backward
(concat "^[" muse-regexp-blank "]\\{6,\\}\\=")
nil t)
(save-excursion
(save-match-data
(goto-char end)
(looking-at (concat "[" muse-regexp-blank "]*$"))))
(prog1 t
(replace-match "")
(when (and (or (muse-style-derived-p "latex")
(muse-style-derived-p "context"))
(not (bobp)))
(backward-char 1)
(if (bolp)
(delete-char 1)
(forward-char 1)))
(setq beg (point)))))
(tag-beg (if centered
(if (muse-style-derived-p "context")
"\\startformula " "\\[ ")
"$"))
(tag-end (if centered
(if (muse-style-derived-p "context")
" \\stopformula" " \\]")
"$"))
(attrs (nconc (list (cons "prefix"
(concat "latex2png-" (muse-page-name))))
(if centered nil
'(("inline" . t))))))
(goto-char beg)
(muse-insert-markup tag-beg)
(goto-char end)
(muse-insert-markup tag-end)
(if (or (muse-style-derived-p "latex") (muse-style-derived-p "context"))
(muse-publish-mark-read-only beg (point))
(muse-latex2png-region beg (point) attrs))))
(put 'muse-publish-math-tag 'muse-dangerous-tag t)
;;; Insinuate with muse-publish
(add-to-list 'muse-publish-markup-tags
'("latex" t t nil muse-publish-latex-tag)
t)
(add-to-list 'muse-publish-markup-tags
'("math" t nil nil muse-publish-math-tag)
t)
(provide 'muse-latex2png)
;;; muse-latex2png.el ends here

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,263 @@
;;; muse-poem.el --- publish a poem to LaTex or PDF
;; Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010
;; Free Software Foundation, Inc.
;; This file is part of Emacs Muse. It is not part of GNU Emacs.
;; Emacs Muse 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 3, or (at your
;; option) any later version.
;; Emacs Muse 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 Emacs Muse; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.
;;; Commentary:
;; This file specifies a form for recording poetry. It is as follows.
;;
;; Title
;;
;;
;; Body of poem
;;
;;
;; Annotations, history, notes, etc.
;;
;; The `muse-poem' module makes it easy to attractively publish and
;; reference poems in this format, using the "memoir" module for LaTeX
;; publishing. It will also markup poems for every other output
;; style, though none are nearly as pretty.
;;
;; Once a poem is written in this format, just publish it to PDF using
;; the "poem-pdf" style. To make an inlined reference to a poem that
;; you've written -- for example, from a blog page -- there is a
;; "poem" tag defined by this module:
;;
;; <poem title="name.of.poem.page">
;;
;; Let's assume the template above was called "name.of.poem.page";
;; then the above tag would result in this inclusion:
;;
;; ** Title
;;
;; > Body of poem
;;
;; I use this module for publishing all of the poems on my website,
;; which are at: http://www.newartisans.com/johnw/poems.html.
;;; Contributors:
;;; Code:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; Muse Poem Publishing
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(require 'muse-latex)
(require 'muse-project)
(defgroup muse-poem nil
"Rules for marking up a Muse file as a LaTeX article."
:group 'muse-latex)
(defcustom muse-poem-latex-header
"\\documentclass[14pt,oneside]{memoir}
\\usepackage[english]{babel}
\\usepackage[latin1]{inputenc}
\\usepackage[T1]{fontenc}
\\setlength{\\beforepoemtitleskip}{-5.0ex}
\\begin{document}
\\pagestyle{empty}
\\renewcommand{\\poemtoc}{section}
\\settocdepth{section}
\\mbox{}
\\vfill
\\poemtitle{<lisp>(muse-publishing-directive \"title\")</lisp>}
\\settowidth{\\versewidth}{<lisp>muse-poem-longest-line</lisp>}\n\n"
"Header used for publishing LaTeX poems. This may be text or a filename."
:type 'string
:group 'muse-poem)
(defcustom muse-poem-latex-footer "\n\\vfill
\\mbox{}
\\end{document}"
"Footer used for publishing LaTeX files. This may be text or a filename."
:type 'string
:group 'muse-poem)
(defcustom muse-poem-markup-strings
'((begin-verse . "\\begin{verse}[\\versewidth]\n")
(verse-space . "\\vin "))
"Strings used for marking up poems.
These cover the most basic kinds of markup, the handling of which
differs little between the various styles."
:type '(alist :key-type symbol :value-type string)
:group 'muse-poem)
(defcustom muse-chapbook-latex-header
"\\documentclass{book}
\\usepackage[english]{babel}
\\usepackage[latin1]{inputenc}
\\usepackage[T1]{fontenc}
\\setlength{\\beforepoemtitleskip}{-5.0ex}
\\begin{document}
\\title{<lisp>(muse-publishing-directive \"title\")</lisp>}
\\author{<lisp>(muse-publishing-directive \"author\")</lisp>}
\\date{<lisp>(muse-publishing-directive \"date\")</lisp>}
\\maketitle
\\tableofcontents
\\renewcommand{\\poemtoc}{section}
\\settocdepth{section}\n"
"Header used for publishing a book of poems in LaTeX form.
This may be text or a filename."
:type 'string
:group 'muse-poem)
(defcustom muse-chapbook-latex-footer "\n\\end{document}"
"Footer used for publishing a book of poems in LaTeX form.
This may be text or a filename."
:type 'string
:group 'muse-poem)
(defvar muse-poem-longest-line "")
(defcustom muse-poem-chapbook-strings
'((begin-verse . "\\newpage
\\mbox{}
\\vfill
\\poemtitle{<lisp>(muse-publishing-directive \"title\")</lisp>}
\\settowidth{\\versewidth}{<lisp>muse-poem-longest-line</lisp>}
\\begin{verse}[\\versewidth]\n")
(end-verse . "\n\\end{verse}\n\\vfill\n\\mbox{}")
(verse-space . "\\vin "))
"Strings used for marking up books of poems.
These cover the most basic kinds of markup, the handling of which
differs little between the various styles."
:type '(alist :key-type symbol :value-type string)
:group 'muse-poem)
(defun muse-poem-prepare-buffer ()
(goto-char (point-min))
(insert "#title ")
(forward-line 1)
(delete-region (point) (1+ (muse-line-end-position)))
(insert "\n<verse>")
(let ((beg (point)) end line)
(if (search-forward "\n\n\n" nil t)
(progn
(setq end (copy-marker (match-beginning 0) t))
(replace-match "\n</verse>\n")
(delete-region (point) (point-max)))
(goto-char (point-max))
(setq end (point))
(insert "</verse>\n"))
(goto-char (1+ beg))
(set (make-local-variable 'muse-poem-longest-line) "")
(while (< (point) end)
(setq line (buffer-substring-no-properties (point)
(muse-line-end-position)))
(if (> (length line) (length muse-poem-longest-line))
(setq muse-poem-longest-line line))
(forward-line 1))
nil))
(defvar muse-poem-tag '("poem" nil t nil muse-poem-markup-tag))
(defun muse-poem-markup-tag (beg end attrs)
"This markup tag allows a poem to be included from another project page.
The form of usage is:
<poem title=\"page.name\">"
(let ((page (cdr (assoc (cdr (assoc "title" attrs))
(muse-project-file-alist))))
beg end)
(if (null page)
(insert " *Reference to\n unknown poem \""
(cdr (assoc "title" attrs)) "\".*\n")
(setq beg (point))
(insert
(muse-with-temp-buffer
(muse-insert-file-contents page)
(goto-char (point-min))
(if (assoc "nohead" attrs)
(progn
(forward-line 3)
(delete-region (point-min) (point)))
(insert "** ")
(search-forward "\n\n\n")
(replace-match "\n\n"))
(if (search-forward "\n\n\n" nil t)
(setq end (match-beginning 0))
(setq end (point-max)))
(buffer-substring-no-properties (point-min) end)))
(setq end (point-marker))
(goto-char beg)
(unless (assoc "nohead" attrs)
(forward-line 2))
(while (< (point) end)
(insert "> ")
(forward-line 1))
(set-marker end nil))))
(put 'muse-poem-markup-tag 'muse-dangerous-tag t)
(add-to-list 'muse-publish-markup-tags muse-poem-tag)
;;; Register the Muse POEM Publishers
(muse-derive-style "poem-latex" "latex"
:before 'muse-poem-prepare-buffer
:strings 'muse-poem-markup-strings
:header 'muse-poem-latex-header
:footer 'muse-poem-latex-footer)
(muse-derive-style "poem-pdf" "pdf"
:before 'muse-poem-prepare-buffer
:strings 'muse-poem-markup-strings
:header 'muse-poem-latex-header
:footer 'muse-poem-latex-footer)
(muse-derive-style "chapbook-latex" "latex"
:before 'muse-poem-prepare-buffer
:strings 'muse-poem-chapbook-strings
:header 'muse-chapbook-latex-header
:footer 'muse-chapbook-latex-footer)
(muse-derive-style "chapbook-pdf" "pdf"
:before 'muse-poem-prepare-buffer
:strings 'muse-poem-chapbook-strings
:header 'muse-chapbook-latex-header
:footer 'muse-chapbook-latex-footer)
(provide 'muse-poem)
;;; muse-poem.el ends here

View file

@ -0,0 +1,973 @@
;;; muse-project.el --- handle Muse projects
;; Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010
;; Free Software Foundation, Inc.
;; This file is part of Emacs Muse. It is not part of GNU Emacs.
;; Emacs Muse 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 3, or (at your
;; option) any later version.
;; Emacs Muse 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 Emacs Muse; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.
;;; Commentary:
;;; Contributors:
;;; Code:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; Muse Project Maintainance
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(provide 'muse-project)
(require 'muse)
(require 'muse-publish)
(require 'cus-edit)
(defgroup muse-project nil
"Options controlling the behavior of Muse project handling."
:group 'muse)
(defcustom muse-before-project-publish-hook nil
"A hook run before a project is published.
Each function is passed the project object, a cons with the format
(PROJNAME . SETTINGS)"
:type 'hook
:group 'muse-project)
(defcustom muse-after-project-publish-hook nil
"A hook run after a project is published.
Each function is passed the project object, a cons with the format
(PROJNAME . SETTINGS)"
:type 'hook
:group 'muse-project)
(defvar muse-project-alist-using-customize nil
"Used internally by Muse to indicate whether `muse-project-alist'
has been modified via the customize interface.")
(make-variable-buffer-local 'muse-project-alist-using-customize)
(defmacro with-muse-project (project &rest body)
`(progn
(unless (muse-project ,project)
(error "Can't find project %s" ,project))
(with-temp-buffer
(muse-mode)
(setq muse-current-project (muse-project ,project))
(muse-project-set-variables)
,@body)))
(put 'with-muse-project 'lisp-indent-function 0)
(put 'with-muse-project 'edebug-form-spec '(sexp body))
(defun muse-project-alist-get (sym)
"Turn `muse-project-alist' into something we can customize easily."
(when (boundp sym)
(setq muse-project-alist-using-customize t)
(let* ((val (copy-alist (symbol-value sym)))
(head val))
(while val
(let ((head (car (cdar val)))
res)
;; Turn settings of first part into cons cells, symbol->string
(while head
(cond ((stringp (car head))
(add-to-list 'res (car head) t)
(setq head (cdr head)))
((symbolp (car head))
(add-to-list 'res (list (symbol-name (car head))
(cadr head)) t)
(setq head (cddr head)))
(t
(setq head (cdr head)))))
(setcdr (car val) (cons res (cdr (cdar val)))))
(let ((styles (cdar val)))
;; Symbol->string in every style
(while (cdr styles)
(let ((head (cadr styles))
res)
(while (consp head)
(setq res (plist-put res (symbol-name (car head))
(cadr head)))
(setq head (cddr head)))
(setcdr styles (cons res (cddr styles))))
(setq styles (cdr styles))))
(setq val (cdr val)))
head)))
(defun muse-project-alist-set (sym val)
"Turn customized version of `muse-project-alist' into something
Muse can make use of."
(set sym val)
(when muse-project-alist-using-customize
;; Make sure the unescaped version is written to .emacs
(put sym 'saved-value (list (custom-quote val)))
;; Perform unescaping
(while val
(let ((head (car (cdar val)))
res)
;; Turn cons cells into flat list, string->symbol
(while head
(cond ((stringp (car head))
(add-to-list 'res (car head) t))
((consp (car head))
(add-to-list 'res (intern (caar head)) t)
(add-to-list 'res (car (cdar head)) t)))
(setq head (cdr head)))
(setcdr (car val) (cons res (cdr (cdar val)))))
(let ((styles (cdar val)))
;; String->symbol in every style
(while (cdr styles)
(let ((head (cadr styles))
res)
(while (consp head)
(setq res (plist-put res (intern (car head))
(cadr head)))
(setq head (cddr head)))
(setcdr styles (cons res (cddr styles))))
(setq styles (cdr styles))))
(setq val (cdr val)))))
(define-widget 'muse-project 'default
"A widget that defines a Muse project."
:format "\n%v"
:value-create 'muse-widget-type-value-create
:value-get 'muse-widget-child-value-get
:value-delete 'ignore
:match 'muse-widget-type-match
:type '(cons :format " %v"
(repeat :tag "Settings" :format "%{%t%}:\n%v%i\n\n"
(choice
(string :tag "Directory")
(list :tag "Book function"
(const :tag ":book-funcall" ":book-funcall")
(choice (function)
(sexp :tag "Unknown")))
(list :tag "Book part"
(const :tag ":book-part" ":book-part")
(string :tag "Name"))
(list :tag "Book style"
(const :tag ":book-style" ":book-style")
(string :tag "Style"))
(list :tag "Default file"
(const :tag ":default" ":default")
(string :tag "File"))
(list :tag "End of book"
(const :tag ":book-end" ":book-end")
(const t))
(list :tag "Force publishing"
(const :tag ":force-publish" ":force-publish")
(repeat (string :tag "File")))
(list :tag "Major mode"
(const :tag ":major-mode" ":major-mode")
(choice (function :tag "Mode")
(sexp :tag "Unknown")))
(list :tag "New chapter"
(const :tag ":book-chapter" ":book-chapter")
(string :tag "Name"))
(list :tag "No chapters"
(const :tag ":nochapters" ":nochapters")
(const t))
(list :tag "Project-level publishing function"
(const :tag ":publish-project"
":publish-project")
(choice (function :tag "Function")
(sexp :tag "Unknown")))
(list :tag "Set variables"
(const :tag ":set" ":set")
(repeat (list :inline t
(symbol :tag "Variable")
(sexp :tag "Setting"))))
(list :tag "Visit links using"
(const :tag ":visit-link" ":visit-link")
(choice (function)
(sexp :tag "Unknown")))))
(repeat :tag "Output styles" :format "%{%t%}:\n%v%i\n\n"
(set :tag "Style"
(list :inline t
:tag "Publishing style"
(const :tag ":base" ":base")
(string :tag "Style"))
(list :inline t
:tag "Base URL"
(const :tag ":base-url" ":base-url")
(string :tag "URL"))
(list :inline t
:tag "Exclude matching"
(const :tag ":exclude" ":exclude")
(regexp))
(list :inline t
:tag "Include matching"
(const :tag ":include" ":include")
(regexp))
(list :inline t
:tag "Timestamps file"
(const :tag ":timestamps" ":timestamps")
(file))
(list :inline t
:tag "Path"
(const :tag ":path" ":path")
(string :tag "Path"))))))
(defcustom muse-project-alist nil
"An alist of Muse projects.
A project defines a fileset, and a list of custom attributes for use
when publishing files in that project."
:type '(choice (const :tag "No projects defined." nil)
(repeat (cons :format "%{%t%}:\n\n%v"
:tag "Project" :indent 4
(string :tag "Project name")
muse-project))
(sexp :tag "Cannot parse expression"))
:get 'muse-project-alist-get
:set 'muse-project-alist-set
:group 'muse-project)
;; Make it easier to specify a muse-project-alist entry
(defcustom muse-project-ignore-regexp
(concat "\\`\\(#.*#\\|.*,v\\|.*~\\|\\.\\.?\\|\\.#.*\\|,.*\\)\\'\\|"
"/\\(CVS\\|RCS\\|\\.arch-ids\\|{arch}\\|,.*\\|\\.svn\\|"
"\\.hg\\|\\.git\\|\\.bzr\\|_darcs\\)\\(/\\|\\'\\)")
"A regexp matching files to be ignored in Muse directories.
You should set `case-fold-search' to nil before using this regexp
in code."
:type 'regexp
:group 'muse-regexp)
(defcustom muse-project-publish-private-files t
"If this is non-nil, files will be published even if their permissions
are set so that no one else on the filesystem can read them.
Set this to nil if you would like to indicate that some files
should not be published by manually doing \"chmod o-rwx\" on
them.
This setting has no effect under Windows (that is, all files are
published regardless of permissions) because Windows lacks the
needed filesystem attributes."
:type 'boolean
:group 'muse-project)
(defun muse-project-recurse-directory (base)
"Recusively retrieve all of the directories underneath BASE.
A list of these directories is returned.
Directories starting with \".\" will be ignored, as well as those
which match `muse-project-ignore-regexp'."
(let ((case-fold-search nil)
list dir)
(when (and (file-directory-p base)
(not (string-match muse-project-ignore-regexp base)))
(dolist (file (directory-files base t "^[^.]"))
(when (and (file-directory-p file)
(not (string-match muse-project-ignore-regexp file)))
(setq dir (file-name-nondirectory file))
(push dir list)
(nconc list (mapcar #'(lambda (item)
(concat dir "/" item))
(muse-project-recurse-directory file)))))
list)))
(defun muse-project-alist-styles (entry-dir output-dir style &rest other)
"Return a list of styles to use in a `muse-project-alist' entry.
ENTRY-DIR is the top-level directory of the project.
OUTPUT-DIR is where Muse files are published, keeping directory structure.
STYLE is the publishing style to use.
OTHER contains other definitions to add to each style. It is optional.
For an example of the use of this function, see
`examples/mwolson/muse-init.el' from the Muse distribution."
(let ((fnd (file-name-nondirectory entry-dir)))
(when (string= fnd "")
;; deal with cases like "foo/" that have a trailing slash
(setq fnd (file-name-nondirectory (substring entry-dir 0 -1))))
(cons `(:base ,style :path ,(if (muse-file-remote-p output-dir)
output-dir
(expand-file-name output-dir))
:include ,(concat "/" fnd "/[^/]+$")
,@other)
(mapcar (lambda (dir)
`(:base ,style
:path ,(expand-file-name dir output-dir)
:include ,(concat "/" dir "/[^/]+$")
,@other))
(muse-project-recurse-directory entry-dir)))))
(defun muse-project-alist-dirs (entry-dir)
"Return a list of directories to use in a `muse-project-alist' entry.
ENTRY-DIR is the top-level directory of the project.
For an example of the use of this function, see
`examples/mwolson/muse-init.el' from the Muse distribution."
(cons (expand-file-name entry-dir)
(mapcar (lambda (dir) (expand-file-name dir entry-dir))
(muse-project-recurse-directory entry-dir))))
;; Constructing the file-alist
(defvar muse-project-file-alist nil
"This variable is automagically constructed as needed.")
(defvar muse-project-file-alist-hook nil
"Functions that are to be exectuted immediately after updating
`muse-project-file-alist'.")
(defvar muse-current-project nil
"Project we are currently visiting.")
(make-variable-buffer-local 'muse-current-project)
(defvar muse-current-project-global nil
"Project we are currently visiting. This is used to propagate the value
of `muse-current-project' into a new buffer during publishing.")
(defvar muse-current-output-style nil
"The output style that we are currently using for publishing files.")
(defsubst muse-project (&optional project)
"Resolve the given PROJECT into a full Muse project, if it is a string."
(if (null project)
(or muse-current-project
(muse-project-of-file))
(if (stringp project)
(assoc project muse-project-alist)
(muse-assert (consp project))
project)))
(defun muse-project-page-file (page project &optional no-check-p)
"Return a filename if PAGE exists within the given Muse PROJECT."
(setq project (muse-project project))
(if (null page)
;; if not given a page, return the first directory instead
(let ((pats (cadr project)))
(catch 'done
(while pats
(if (symbolp (car pats))
(setq pats (cddr pats))
(throw 'done (file-name-as-directory (car pats)))))))
(let ((dir (file-name-directory page))
(expanded-path nil))
(when dir
(setq expanded-path (concat (expand-file-name
page
(file-name-directory (muse-current-file)))
(when muse-file-extension
(concat "." muse-file-extension))))
(setq page (file-name-nondirectory page)))
(let ((files (muse-collect-alist
(muse-project-file-alist project no-check-p)
page))
(matches nil))
(if dir
(catch 'done
(save-match-data
(dolist (file files)
(if (and expanded-path
(string= expanded-path (cdr file)))
(throw 'done (cdr file))
(let ((pos (string-match (concat (regexp-quote dir) "\\'")
(file-name-directory
(cdr file)))))
(when pos
(setq matches (cons (cons pos (cdr file))
matches)))))))
;; if we haven't found an exact match, pick a candidate
(car (muse-sort-by-rating matches)))
(dolist (file files)
(setq matches (cons (cons (length (cdr file)) (cdr file))
matches)))
(car (muse-sort-by-rating matches '<)))))))
(defun muse-project-private-p (file)
"Return non-nil if NAME is a private page with PROJECT."
(unless (or muse-under-windows-p
muse-project-publish-private-files)
(setq file (file-truename file))
(if (file-attributes file) ; don't publish if no attributes exist
(or (when (eq ?- (aref (nth 8 (file-attributes
(file-name-directory file))) 7))
(message (concat
"The " (file-name-directory file)
" directory must be readable by others"
" in order for its contents to be published.")))
(eq ?- (aref (nth 8 (file-attributes file)) 7)))
t)))
(defun muse-project-file-entries (path)
(let* ((names (list t))
(lnames names)
(case-fold-search nil))
(cond
((file-directory-p path)
(dolist (file (directory-files
path t (when (and muse-file-extension
(not (string= muse-file-extension "")))
(concat "." muse-file-extension "\\'"))))
(unless (or (string-match muse-project-ignore-regexp file)
(string-match muse-project-ignore-regexp
(file-name-nondirectory file))
(file-directory-p file))
(setcdr lnames
(cons (cons (muse-page-name file) file) nil))
(setq lnames (cdr lnames)))))
((file-readable-p path)
(setcdr lnames
(cons (cons (muse-page-name path) path) nil))
(setq lnames (cdr lnames)))
(t ; regexp
(muse-assert (file-name-directory path))
(dolist (file (directory-files
(file-name-directory path) t
(file-name-nondirectory path)))
(unless (or (string-match muse-project-ignore-regexp file)
(string-match muse-project-ignore-regexp
(file-name-nondirectory file)))
(setcdr lnames
(cons (cons (muse-page-name file) file) nil))
(setq lnames (cdr lnames))))))
(cdr names)))
(defvar muse-updating-file-alist-p nil
"Make sure that recursive calls to `muse-project-file-alist' are bounded.")
(defun muse-project-determine-last-mod (project &optional no-check-p)
"Return the most recent last-modified timestamp of dirs in PROJECT."
(let ((last-mod nil))
(unless (or muse-under-windows-p no-check-p)
(let ((pats (cadr project)))
(while pats
(if (symbolp (car pats))
(setq pats (cddr pats))
(let* ((fnd (file-name-directory (car pats)))
(dir (cond ((file-directory-p (car pats))
(car pats))
((and (not (file-readable-p (car pats)))
fnd
(file-directory-p fnd))
fnd))))
(when dir
(let ((mod-time (nth 5 (file-attributes dir))))
(when (or (null last-mod)
(and mod-time
(muse-time-less-p last-mod mod-time)))
(setq last-mod mod-time)))))
(setq pats (cdr pats))))))
last-mod))
(defun muse-project-file-alist (&optional project no-check-p)
"Return member filenames for the given Muse PROJECT.
Also, update the `muse-project-file-alist' variable.
On UNIX, this alist is only updated if one of the directories'
contents have changed. On Windows, it is always reread from
disk.
If NO-CHECK-P is non-nil, do not update the alist, just return
the current one."
(setq project (muse-project project))
(when (and project muse-project-alist)
(let* ((file-alist (assoc (car project) muse-project-file-alist))
(last-mod (muse-project-determine-last-mod project no-check-p)))
;; Either return the currently known list, or read it again from
;; disk
(if (or (and no-check-p (cadr file-alist))
muse-updating-file-alist-p
(not (or muse-under-windows-p
(null (cddr file-alist))
(null last-mod)
(muse-time-less-p (cddr file-alist) last-mod))))
(cadr file-alist)
(if file-alist
(setcdr (cdr file-alist) last-mod)
(setq file-alist (cons (car project) (cons nil last-mod))
muse-project-file-alist
(cons file-alist muse-project-file-alist)))
;; Read in all of the file entries
(let ((muse-updating-file-alist-p t))
(prog1
(save-match-data
(setcar
(cdr file-alist)
(let* ((names (list t))
(pats (cadr project)))
(while pats
(if (symbolp (car pats))
(setq pats (cddr pats))
(nconc names (muse-project-file-entries (car pats)))
(setq pats (cdr pats))))
(cdr names))))
(run-hooks 'muse-project-file-alist-hook)))))))
(defun muse-project-add-to-alist (file &optional project)
"Make sure FILE is added to `muse-project-file-alist'.
It works by either calling the `muse-project-file-alist' function
if a directory has been modified since we last checked, or
manually forcing the file entry to exist in the alist. This
works around an issue where if several files being saved at the
same time, only the first one will make it into the alist. It is
meant to be called by `muse-project-after-save-hook'.
The project of the file is determined by either the PROJECT
argument, or `muse-project-of-file' if PROJECT is not specified."
(setq project (or (muse-project project) (muse-project-of-file file)))
(when (and project muse-project-alist)
(let* ((file-alist (assoc (car project) muse-project-file-alist))
(last-mod (muse-project-determine-last-mod project)))
;; Determine whether we need to call this
(if (or (null (cddr file-alist))
(null last-mod)
(muse-time-less-p (cddr file-alist) last-mod))
;; The directory will show up as modified, so go ahead and
;; call `muse-project-file-alist'
(muse-project-file-alist project)
;; It is not showing as modified, so forcefully add the
;; current file to the project file-alist
(let ((muse-updating-file-alist-p t))
(prog1
(save-match-data
(setcar (cdr file-alist)
(nconc (muse-project-file-entries file)
(cadr file-alist))))
(run-hooks 'muse-project-file-alist-hook)))))))
(defun muse-project-of-file (&optional pathname)
"Determine which project the given PATHNAME relates to.
If PATHNAME is nil, the current buffer's filename is used."
(if (and (null pathname) muse-current-project)
muse-current-project
(unless pathname (setq pathname (muse-current-file)))
(save-match-data
(when (and (stringp pathname)
muse-project-alist
(not (string= pathname ""))
(not (let ((case-fold-search nil))
(or (string-match muse-project-ignore-regexp
pathname)
(string-match muse-project-ignore-regexp
(file-name-nondirectory
pathname))))))
(let* ((file (file-truename pathname))
(dir (file-name-directory file))
found rating matches)
(catch 'found
(dolist (project-entry muse-project-alist)
(let ((pats (cadr project-entry)))
(while pats
(if (symbolp (car pats))
(setq pats (cddr pats))
(let ((tname (file-truename (car pats))))
(cond ((or (string= tname file)
(string= (file-name-as-directory tname) dir))
(throw 'found project-entry))
((string-match (concat "\\`" (regexp-quote tname))
file)
(setq matches (cons (cons (match-end 0)
project-entry)
matches)))))
(setq pats (cdr pats))))))
;; if we haven't found an exact match, pick a candidate
(car (muse-sort-by-rating matches))))))))
(defun muse-project-after-save-hook ()
"Update Muse's file-alist if we are saving a Muse file."
(let ((project (muse-project-of-file)))
(when project
(muse-project-add-to-alist (buffer-file-name) project))))
(add-hook 'after-save-hook 'muse-project-after-save-hook)
(defun muse-read-project (prompt &optional no-check-p no-assume)
"Read a project name from the minibuffer, if it can't be figured
out."
(if (null muse-project-alist)
(error "There are no Muse projects defined; see `muse-project-alist'")
(or (unless no-check-p
(muse-project-of-file))
(if (and (not no-assume)
(= 1 (length muse-project-alist)))
(car muse-project-alist)
(assoc (funcall muse-completing-read-function
prompt muse-project-alist)
muse-project-alist)))))
(defvar muse-project-page-history nil)
(defun muse-read-project-file (project prompt &optional default)
(let* ((file-list (muse-delete-dups
(mapcar #'(lambda (a) (list (car a)))
(muse-project-file-alist project))))
(name (funcall muse-completing-read-function
prompt file-list nil nil nil
'muse-project-page-history default)))
(cons name (muse-project-page-file name project))))
;;;###autoload
(defun muse-project-find-file (name project &optional command directory)
"Open the Muse page given by NAME in PROJECT.
If COMMAND is non-nil, it is the function used to visit the file.
If DIRECTORY is non-nil, it is the directory in which the page
will be created if it does not already exist. Otherwise, the
first directory within the project's fileset is used."
(interactive
(let* ((project (muse-read-project "Find in project: "
current-prefix-arg))
(default (muse-get-keyword :default (cadr project)))
(entry (muse-read-project-file
project (if default
(format "Find page: (default: %s) "
default)
"Find page: ")
default)))
(list entry project)))
(setq project (muse-project project))
(let ((project-name (car project)))
(unless (interactive-p)
(setq project (muse-project project)
name (cons name (muse-project-page-file name project))))
;; If we're given a relative or absolute filename, open it as-is
(if (and (car name)
(save-match-data
(or (string-match "\\`\\.+/" (car name))
(string-match muse-file-regexp (car name))
(string-match muse-image-regexp (car name)))))
(setcdr name (car name))
;; At this point, name is (PAGE . FILE).
(unless (cdr name)
(let ((pats (cadr project)))
(while (and pats (null directory))
(if (symbolp (car pats))
(setq pats (cddr pats))
(if (file-directory-p (car pats))
(setq directory (car pats) pats nil)
(setq pats (cdr pats))))))
(when directory
(let ((filename (expand-file-name (car name) directory)))
(when (and muse-file-extension
(not (string= muse-file-extension ""))
(not (file-exists-p (car name))))
(setq filename (concat filename "." muse-file-extension)))
(unless (file-exists-p directory)
(make-directory directory t))
(setcdr name filename)))))
;; Open the file
(if (cdr name)
(funcall (or command 'find-file) (cdr name))
(error "There is no page %s in project %s"
(car name) project-name))))
(defun muse-project-choose-style (closure test styles)
"Run TEST on STYLES and return first style where TEST yields non-nil.
TEST should take two arguments. The first is CLOSURE, which is
passed verbatim. The second if the current style to consider.
If no style passes TEST, return the first style."
(or (catch 'winner
(dolist (style styles)
(when (funcall test closure style)
(throw 'winner style))))
(car styles)))
(defun muse-project-choose-style-by-link-suffix (given-suffix style)
"If the given STYLE has a link-suffix that equals GIVEN-SUFFIX,
return non-nil."
(let ((link-suffix (or (muse-style-element :link-suffix style)
(muse-style-element :suffix style))))
(and (stringp link-suffix)
(string= given-suffix link-suffix))))
(defun muse-project-applicable-styles (file styles)
"Given STYLES, return a list of the ones that are considered for FILE.
The name of a project may be used for STYLES."
(when (stringp styles)
(setq styles (cddr (muse-project styles))))
(when (and file styles)
(let ((used-styles nil))
(dolist (style styles)
(let ((include-regexp (muse-style-element :include style))
(exclude-regexp (muse-style-element :exclude style))
(rating nil))
(when (and (or (and (null include-regexp)
(null exclude-regexp))
(if include-regexp
(setq rating (string-match include-regexp file))
(not (string-match exclude-regexp file))))
(file-exists-p file)
(not (muse-project-private-p file)))
(setq used-styles (cons (cons rating style) used-styles)))))
(muse-sort-by-rating (nreverse used-styles)))))
(defun muse-project-get-applicable-style (file styles)
"Choose a style from the STYLES that FILE can publish to.
The user is prompted if several styles are found."
(muse-publish-get-style
(mapcar (lambda (style)
(cons (muse-get-keyword :base style) style))
(muse-project-applicable-styles file styles))))
(defun muse-project-resolve-directory (page local-style remote-style)
"Figure out the directory part of the path that provides a link to PAGE.
LOCAL-STYLE is the style of the current Muse file, and
REMOTE-STYLE is the style associated with PAGE.
If REMOTE-STYLE has a :base-url element, concatenate it and PAGE.
Otherwise, return a relative link."
(let ((prefix (muse-style-element :base-url remote-style)))
(if prefix
(concat prefix page)
(file-relative-name (expand-file-name
(file-name-nondirectory page)
(muse-style-element :path remote-style))
(expand-file-name
(muse-style-element :path local-style))))))
(defun muse-project-resolve-link (page local-style remote-styles)
"Return a published link from the output path of one file to another file.
The best match for PAGE is determined by comparing the link
suffix of the given local style and that of the remote styles.
The remote styles are usually populated by
`muse-project-applicable-styles'.
If no remote style is found, return PAGE verbatim
If PAGE has a :base-url associated with it, return the
concatenation of the :base-url value and PAGE.
Otherwise, return a relative path from the directory of
LOCAL-STYLE to the best directory among REMOTE-STYLES."
(let ((link-suffix (or (muse-style-element :link-suffix local-style)
(muse-style-element :suffix local-style)))
remote-style)
(if (not (stringp link-suffix))
(setq remote-style (car remote-styles))
(setq remote-style (muse-project-choose-style
link-suffix
#'muse-project-choose-style-by-link-suffix
remote-styles)))
(if (null remote-style)
page
(setq page (muse-project-resolve-directory
page local-style remote-style))
(concat (file-name-directory page)
(muse-publish-link-name page remote-style)))))
(defun muse-project-current-output-style (&optional file project)
(or muse-current-output-style
(progn
(unless file (setq file (muse-current-file)))
(unless project (setq project (muse-project-of-file file)))
(car (muse-project-applicable-styles file (cddr project))))))
(defun muse-project-link-page (page)
(let ((project (muse-project-of-file)))
(muse-project-resolve-link page
(muse-project-current-output-style)
(muse-project-applicable-styles
(muse-project-page-file page project)
(cddr project)))))
(defun muse-project-publish-file-default (file style output-dir force)
;; ensure the publishing location is available
(unless (file-exists-p output-dir)
(message "Creating publishing directory %s" output-dir)
(make-directory output-dir t))
;; publish the member file!
(muse-publish-file file style output-dir force))
(defun muse-project-publish-file (file styles &optional force)
(setq styles (muse-project-applicable-styles file styles))
(let (published)
(dolist (style styles)
(if (or (not (listp style))
(not (cdr style)))
(muse-display-warning
(concat "Skipping malformed muse-project-alist style."
"\nPlease double-check your configuration,"))
(let ((output-dir (muse-style-element :path style))
(muse-current-output-style style)
(fun (or (muse-style-element :publish style t)
'muse-project-publish-file-default)))
(when (funcall fun file style output-dir force)
(setq published t)))))
published))
;;;###autoload
(defun muse-project-publish-this-file (&optional force style)
"Publish the currently-visited file according to `muse-project-alist',
prompting if more than one style applies.
If FORCE is given, publish the file even if it is up-to-date.
If STYLE is given, use that publishing style rather than
prompting for one."
(interactive (list current-prefix-arg))
(let ((muse-current-project (muse-project-of-file)))
(if (not muse-current-project)
;; file is not part of a project, so fall back to muse-publish
(if (interactive-p) (call-interactively 'muse-publish-this-file)
(muse-publish-this-file style nil force))
(unless style
(setq style (muse-project-get-applicable-style
buffer-file-name (cddr muse-current-project))))
(let* ((output-dir (muse-style-element :path style))
(muse-current-project-global muse-current-project)
(muse-current-output-style (list :base (car style)
:path output-dir))
(fun (or (muse-style-element :publish style t)
'muse-project-publish-file-default)))
(unless (funcall fun buffer-file-name style output-dir force)
(message (concat "The published version is up-to-date; use"
" C-u C-c C-t to force an update.")))))))
(defun muse-project-save-buffers (&optional project)
(setq project (muse-project project))
(when project
(save-excursion
(map-y-or-n-p
(function
(lambda (buffer)
(and (buffer-modified-p buffer)
(not (buffer-base-buffer buffer))
(or (buffer-file-name buffer)
(progn
(set-buffer buffer)
(and buffer-offer-save
(> (buffer-size) 0))))
(with-current-buffer buffer
(let ((proj (muse-project-of-file)))
(and proj (string= (car proj)
(car project)))))
(if (buffer-file-name buffer)
(format "Save file %s? "
(buffer-file-name buffer))
(format "Save buffer %s? "
(buffer-name buffer))))))
(function
(lambda (buffer)
(set-buffer buffer)
(save-buffer)))
(buffer-list)
'("buffer" "buffers" "save")
(if (boundp 'save-some-buffers-action-alist)
save-some-buffers-action-alist)))))
(defun muse-project-publish-default (project styles &optional force)
"Publish the pages of PROJECT that need publishing."
(setq project (muse-project project))
(let ((published nil))
;; publish all files in the project, for each style; the actual
;; publishing will only happen if the files are newer than the
;; last published output, or if the file is listed in
;; :force-publish. Files in :force-publish will not trigger the
;; "All pages need to be published" message.
(let ((forced-files (muse-get-keyword :force-publish (cadr project)))
(file-alist (muse-project-file-alist project)))
(dolist (pair file-alist)
(when (muse-project-publish-file (cdr pair) styles force)
(setq forced-files (delete (car pair) forced-files))
(setq published t)))
(dolist (file forced-files)
(muse-project-publish-file (cdr (assoc file file-alist)) styles t)))
;; run hook after publishing ends
(run-hook-with-args 'muse-after-project-publish-hook project)
;; notify the user that everything is now done
(if published
(message "All pages in %s have been published." (car project))
(message "No pages in %s need publishing at this time."
(car project)))))
;;;###autoload
(defun muse-project-publish (project &optional force)
"Publish the pages of PROJECT that need publishing."
(interactive (list (muse-read-project "Publish project: " nil t)
current-prefix-arg))
(setq project (muse-project project))
(let ((styles (cddr project))
(muse-current-project project)
(muse-current-project-global project))
;; determine the style from the project, or else ask
(unless styles
(setq styles (list (muse-publish-get-style))))
(unless project
(error "Cannot find a project to publish"))
;; prompt to save any buffers related to this project
(muse-project-save-buffers project)
;; run hook before publishing begins
(run-hook-with-args 'muse-before-project-publish-hook project)
;; run the project-level publisher
(let ((fun (or (muse-get-keyword :publish-project (cadr project) t)
'muse-project-publish-default)))
(funcall fun project styles force))))
(defun muse-project-batch-publish ()
"Publish Muse files in batch mode."
(let ((muse-batch-publishing-p t)
force)
(if (string= "--force" (or (car command-line-args-left) ""))
(setq force t
command-line-args-left (cdr command-line-args-left)))
(if command-line-args-left
(dolist (project command-line-args-left)
(message "Publishing project %s ..." project)
(muse-project-publish project force))
(message "No projects specified."))))
(eval-when-compile
(put 'make-local-hook 'byte-compile nil))
(defun muse-project-set-variables ()
"Load project-specific variables."
(when (and muse-current-project-global (null muse-current-project))
(setq muse-current-project muse-current-project-global))
(let ((vars (muse-get-keyword :set (cadr muse-current-project)))
sym custom-set var)
(while vars
(setq sym (car vars))
(setq custom-set (or (get sym 'custom-set) 'set))
(setq var (if (eq (get sym 'custom-type) 'hook)
(make-local-hook sym)
(make-local-variable sym)))
(funcall custom-set var (car (cdr vars)))
(setq vars (cdr (cdr vars))))))
(custom-add-option 'muse-before-publish-hook 'muse-project-set-variables)
(add-to-list 'muse-before-publish-hook 'muse-project-set-variables)
(defun muse-project-delete-output-files (project)
(interactive
(list (muse-read-project "Remove all output files for project: " nil t)))
(setq project (muse-project project))
(let ((file-alist (muse-project-file-alist project))
(styles (cddr project))
output-file path)
(dolist (entry file-alist)
(dolist (style styles)
(setq output-file
(and (setq path (muse-style-element :path style))
(expand-file-name
(concat (muse-style-element :prefix style)
(car entry)
(or (muse-style-element :osuffix style)
(muse-style-element :suffix style)))
path)))
(if output-file
(muse-delete-file-if-exists output-file))))))
;;; muse-project.el ends here

View file

@ -0,0 +1,251 @@
;;; muse-protocols.el --- URL protocols that Muse recognizes
;; Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010
;; Free Software Foundation, Inc.
;; Author: Brad Collins (brad AT chenla DOT org)
;; This file is part of Emacs Muse. It is not part of GNU Emacs.
;; Emacs Muse 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 3, or (at your
;; option) any later version.
;; Emacs Muse 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 Emacs Muse; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.
;;; Commentary:
;; Here's an example for adding a protocol for the site yubnub, a Web
;; Command line service.
;;
;; (add-to-list 'muse-url-protocols '("yubnub://" muse-browse-url-yubnub
;; muse-resolve-url-yubnub))
;;
;; (defun muse-resolve-url-yubnub (url)
;; "Resolve a yubnub URL."
;; ;; Remove the yubnub://
;; (when (string-match "\\`yubnub://\\(.+\\)" url)
;; (match-string 1)))
;;
;; (defun muse-browse-url-yubnub (url)
;; "If this is a yubnub URL-command, jump to it."
;; (setq url (muse-resolve-url-yubnub url))
;; (browse-url (concat "http://yubnub.org/parser/parse?command="
;; url)))
;;; Contributors:
;; Phillip Lord (Phillip.Lord AT newcastle DOT ac DOT uk) provided a
;; handler for DOI URLs.
;; Stefan Schlee fixed a bug with handling of colons at the end of
;; URLs.
;; Valery V. Vorotyntsev contribued the woman:// protocol handler and
;; simplified `muse-browse-url-man'.
;;; Code:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; Muse URL Protocols
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(require 'info)
(require 'muse-regexps)
(defvar muse-url-regexp nil
"A regexp used to match URLs within a Muse page.
This is autogenerated from `muse-url-protocols'.")
(defun muse-update-url-regexp (sym value)
(setq muse-url-regexp
(concat "\\<\\(" (mapconcat 'car value "\\|") "\\)"
"[^][" muse-regexp-blank "\"'()<>^`{}\n]*"
"[^][" muse-regexp-blank "\"'()<>^`{}.,;:\n]+"))
(set sym value))
(defcustom muse-url-protocols
'(("[uU][rR][lL]:" muse-browse-url-url identity)
("info://" muse-browse-url-info nil)
("man://" muse-browse-url-man nil)
("woman://" muse-browse-url-woman nil)
("google://" muse-browse-url-google muse-resolve-url-google)
("http:/?/?" browse-url identity)
("https:/?/?" browse-url identity)
("ftp:/?/?" browse-url identity)
("gopher://" browse-url identity)
("telnet://" browse-url identity)
("wais://" browse-url identity)
("file://?" browse-url identity)
("dict:" muse-browse-url-dict muse-resolve-url-dict)
("doi:" muse-browse-url-doi muse-resolve-url-doi)
("news:" browse-url identity)
("snews:" browse-url identity)
("mailto:" browse-url identity))
"A list of (PROTOCOL BROWSE-FUN RESOLVE-FUN) used to match URL protocols.
PROTOCOL describes the first part of the URL, including the
\"://\" part. This may be a regexp.
BROWSE-FUN should accept URL as an argument and open the URL in
the current window.
RESOLVE-FUN should accept URL as an argument and return the final
URL, or nil if no URL should be included."
:type '(repeat (list :tag "Protocol"
(string :tag "Regexp")
(function :tag "Browse")
(choice (function :tag "Resolve")
(const :tag "Don't resolve" nil))))
:set 'muse-update-url-regexp
:group 'muse)
(add-hook 'muse-update-values-hook
(lambda ()
(muse-update-url-regexp 'muse-url-protocols muse-url-protocols)))
(defcustom muse-wikipedia-country "en"
"Indicate the 2-digit country code that we use for Wikipedia
queries."
:type 'string
:options '("de" "en" "es" "fr" "it" "pl" "pt" "ja" "nl" "sv")
:group 'muse)
(defun muse-protocol-find (proto list)
"Return the first element of LIST whose car matches the regexp PROTO."
(catch 'found
(dolist (item list)
(when (string-match (concat "\\`" (car item)) proto)
(throw 'found item)))))
;;;###autoload
(defun muse-browse-url (url &optional other-window)
"Handle URL with the function specified in `muse-url-protocols'.
If OTHER-WINDOW is non-nil, open in a different window."
(interactive (list (read-string "URL: ")
current-prefix-arg))
;; Strip text properties
(when (fboundp 'set-text-properties)
(set-text-properties 0 (length url) nil url))
(when other-window
(switch-to-buffer-other-window (current-buffer)))
(when (string-match muse-url-regexp url)
(let* ((proto (match-string 1 url))
(entry (muse-protocol-find proto muse-url-protocols)))
(when entry
(funcall (cadr entry) url)))))
(defun muse-resolve-url (url &rest ignored)
"Resolve URL with the function specified in `muse-url-protocols'."
(when (string-match muse-url-regexp url)
(let* ((proto (match-string 1 url))
(entry (muse-protocol-find proto muse-url-protocols)))
(when entry
(let ((func (car (cddr entry))))
(if func
(setq url (funcall func url))
(setq url nil))))))
url)
(defun muse-protocol-add (protocol browse-function resolve-function)
"Add PROTOCOL to `muse-url-protocols'. PROTOCOL may be a regexp.
BROWSE-FUNCTION should be a function that visits a URL in the
current buffer.
RESOLVE-FUNCTION should be a function that transforms a URL for
publishing or returns nil if not linked."
(add-to-list 'muse-url-protocols
(list protocol browse-function resolve-function))
(muse-update-url-regexp 'muse-url-protocols
muse-url-protocols))
(defun muse-browse-url-url (url)
"Call `muse-protocol-browse-url' to browse URL.
This is used when we are given something like
\"URL:http://example.org/\".
If you're looking for a good example for how to make a custom URL
handler, look at `muse-browse-url-dict' instead."
(when (string-match "\\`[uU][rR][lL]:\\(.+\\)" url)
(muse-browse-url (match-string 1 url))))
(defun muse-resolve-url-dict (url)
"Return the Wikipedia link corresponding with the given URL."
(when (string-match "\\`dict:\\(.+\\)" url)
(concat "http://" muse-wikipedia-country ".wikipedia.org/"
"wiki/Special:Search?search=" (match-string 1 url))))
(defun muse-browse-url-dict (url)
"If this is a Wikipedia URL, browse it."
(let ((dict-url (muse-resolve-url-dict url)))
(when dict-url
(browse-url dict-url))))
(defun muse-resolve-url-doi (url)
"Return the URL through DOI proxy server."
(when (string-match "\\`doi:\\(.+\\)" url)
(concat "http://dx.doi.org/"
(match-string 1 url))))
(defun muse-browse-url-doi (url)
"If this is a DOI URL, browse it.
DOI's (digitial object identifiers) are a standard identifier
used in the publishing industry."
(let ((doi-url (muse-resolve-url-doi url)))
(when doi-url
(browse-url doi-url))))
(defun muse-resolve-url-google (url)
"Return the correct Google search string."
(when (string-match "\\`google:/?/?\\(.+\\)" url)
(concat "http://www.google.com/search?q="
(match-string 1 url))))
(defun muse-browse-url-google (url)
"If this is a Google URL, jump to it."
(let ((google-url (muse-resolve-url-google url)))
(when google-url
(browse-url google-url))))
(defun muse-browse-url-info (url)
"If this in an Info URL, jump to it."
(require 'info)
(cond
((string-match "\\`info://\\([^#\n]+\\)#\\(.+\\)" url)
(Info-find-node (match-string 1 url)
(match-string 2 url)))
((string-match "\\`info://\\([^#\n]+\\)" url)
(Info-find-node (match-string 1 url)
"Top"))
((string-match "\\`info://(\\([^)\n]+\\))\\(.+\\)" url)
(Info-find-node (match-string 1 url) (match-string 2 url)))
((string-match "\\`info://\\(.+\\)" url)
(Info-find-node (match-string 1 url) "Top"))))
(defun muse-browse-url-man (url)
"If this in a manpage URL, jump to it."
(require 'man)
(when (string-match "\\`man://\\([^(]+\\(([^)]+)\\)?\\)" url)
(man (match-string 1 url))))
(defun muse-browse-url-woman (url)
"If this is a WoMan URL, jump to it."
(require 'woman)
(when (string-match "\\`woman://\\(.+\\)" url)
(woman (match-string 1 url))))
(provide 'muse-protocols)
;;; muse-protocols.el ends here

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,270 @@
;;; muse-regexps.el --- define regexps used by Muse
;; Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010
;; Free Software Foundation, Inc.
;; This file is part of Emacs Muse. It is not part of GNU Emacs.
;; Emacs Muse 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 3, or (at your
;; option) any later version.
;; Emacs Muse 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 Emacs Muse; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.
;;; Commentary:
;; This file is the part of the Muse project that describes regexps
;; that are used throughout the project.
;;; Contributors:
;;; Code:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; Muse Regular Expressions
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defgroup muse-regexp nil
"Regular expressions used in publishing and syntax highlighting."
:group 'muse)
;;; Deal with the lack of character classes for regexps in Emacs21 and
;;; XEmacs
(defcustom muse-regexp-use-character-classes 'undecided
"Indicate whether to use extended character classes like [:space:].
If 'undecided, Muse will use them if your emacs is known to support them.
Emacs 22 and Emacs 21.3.50 are known to support them. XEmacs
does not support them.
Emacs 21.2 or higher support them, but with enough annoying edge
cases that the sanest default is to leave them disabled."
:type '(choice (const :tag "Yes" t)
(const :tag "No" nil)
(const :tag "Let Muse decide" undecided))
:group 'muse-regexp)
(defvar muse-regexp-emacs-revision
(save-match-data
(and (string-match "^[0-9]+\\.[0-9]+\\.\\([0-9]+\\)"
emacs-version)
(match-string 1 emacs-version)
(string-to-number (match-string 1 emacs-version))))
"The revision number of this version of Emacs.")
(defun muse-extreg-usable-p ()
"Return non-nil if extended character classes can be used,
nil otherwise.
This is used when deciding the initial values of the muse-regexp
options."
(cond
((eq muse-regexp-use-character-classes t)
t)
((eq muse-regexp-use-character-classes nil)
nil)
((featurep 'xemacs) nil) ; unusable on XEmacs
((> emacs-major-version 21) t) ; usable if > 21
((< emacs-major-version 21) nil)
((< emacs-minor-version 3) nil)
;; don't use if version is of format 21.x
((null muse-regexp-emacs-revision) nil)
;; only trust 21.3.50 or higher
((>= muse-regexp-emacs-revision 50) t)
(t nil)))
(defcustom muse-regexp-blank
(if (muse-extreg-usable-p)
"[:blank:]"
" \t")
"Regexp to use in place of \"[:blank:]\".
This should be something that matches spaces and tabs.
It is like a regexp, but should be embeddable inside brackets.
Muse will detect the appropriate value correctly most of
the time."
:type 'string
:options '("[:blank:]" " \t")
:group 'muse-regexp)
(defcustom muse-regexp-alnum
(if (muse-extreg-usable-p)
"[:alnum:]"
"A-Za-z0-9")
"Regexp to use in place of \"[:alnum:]\".
This should be something that matches all letters and numbers.
It is like a regexp, but should be embeddable inside brackets.
muse will detect the appropriate value correctly most of
the time."
:type 'string
:options '("[:alnum:]" "A-Za-z0-9")
:group 'muse-regexp)
(defcustom muse-regexp-lower
(if (muse-extreg-usable-p)
"[:lower:]"
"a-z")
"Regexp to use in place of \"[:lower:]\".
This should match all lowercase characters.
It is like a regexp, but should be embeddable inside brackets.
muse will detect the appropriate value correctly most of
the time."
:type 'string
:options '("[:lower:]" "a-z")
:group 'muse-regexp)
(defcustom muse-regexp-upper
(if (muse-extreg-usable-p)
"[:upper:]"
"A-Z")
"Regexp to use in place of \"[:upper:]\".
This should match all uppercase characters.
It is like a regexp, but should be embeddable inside brackets.
muse will detect the appropriate value correctly most of
the time."
:type 'string
:options '("[:upper:]" "A-Z")
:group 'muse-regexp)
;;; Regexps used to define Muse publishing syntax
(defcustom muse-list-item-regexp
(concat "^%s\\(\\([^\n" muse-regexp-blank "].*?\\)?::"
"\\(?:[" muse-regexp-blank "]+\\|$\\)"
"\\|[" muse-regexp-blank "]-[" muse-regexp-blank "]*"
"\\|[" muse-regexp-blank "][0-9]+\\.[" muse-regexp-blank "]*\\)")
"Regexp used to match the beginning of a list item.
The '%s' will be replaced with a whitespace regexp when publishing."
:type 'regexp
:group 'muse-regexp)
(defcustom muse-ol-item-regexp (concat "\\`[" muse-regexp-blank "]+[0-9]+\\.")
"Regexp used to match an ordered list item."
:type 'regexp
:group 'muse-regexp)
(defcustom muse-ul-item-regexp (concat "\\`[" muse-regexp-blank "]+-")
"Regexp used to match an unordered list item."
:type 'regexp
:group 'muse-regexp)
(defcustom muse-dl-term-regexp
(concat "[" muse-regexp-blank "]*\\(.+?\\)["
muse-regexp-blank "]+::\\(?:[" muse-regexp-blank "]+\\|$\\)")
"Regexp used to match a definition list term.
The first match string must contain the term."
:type 'regexp
:group 'muse-regexp)
(defcustom muse-dl-entry-regexp (concat "\\`[" muse-regexp-blank "]*::")
"Regexp used to match a definition list entry."
:type 'regexp
:group 'muse-regexp)
(defcustom muse-table-field-regexp
(concat "[" muse-regexp-blank "]+\\(|+\\)\\(?:["
muse-regexp-blank "]\\|$\\)")
"Regexp used to match table separators when publishing."
:type 'regexp
:group 'muse-regexp)
(defcustom muse-table-line-regexp (concat ".*" muse-table-field-regexp ".*")
"Regexp used to match a table line when publishing."
:type 'regexp
:group 'muse-regexp)
(defcustom muse-table-hline-regexp (concat "[" muse-regexp-blank
"]*|[-+]+|[" muse-regexp-blank
"]*")
"Regexp used to match a horizontal separator line in a table."
:type 'regexp
:group 'muse-regexp)
(defcustom muse-table-el-border-regexp (concat "[" muse-regexp-blank "]*"
"\\+\\(-*\\+\\)+"
"[" muse-regexp-blank "]*")
"Regexp used to match the beginning and end of a table.el-style table."
:type 'regexp
:group 'muse-regexp)
(defcustom muse-table-el-line-regexp (concat "[" muse-regexp-blank "]*"
"|\\(.*|\\)*"
"[" muse-regexp-blank "]*")
"Regexp used to match a table line of a table.el-style table."
:type 'regexp
:group 'muse-regexp)
(defcustom muse-tag-regexp
(concat "<\\([^/" muse-regexp-blank "\n][^" muse-regexp-blank
"</>\n]*\\)\\(\\s-+[^<>]+[^</>\n]\\)?\\(/\\)?>")
"A regexp used to find XML-style tags within a buffer when publishing.
Group 1 should be the tag name, group 2 the properties, and group
3 the optional immediate ending slash."
:type 'regexp
:group 'muse-regexp)
(defcustom muse-explicit-link-regexp
"\\[\\[\\([^][\n]+\\)\\]\\(?:\\[\\([^][\n]+\\)\\]\\)?\\]"
"Regexp used to match [[target][description]] links.
Paren group 1 must match the URL, and paren group 2 the description."
:type 'regexp
:group 'muse-regexp)
(defcustom muse-implicit-link-regexp
(concat "\\([^" muse-regexp-blank "\n]+\\)")
"Regexp used to match an implicit link.
An implicit link is the largest block of text to be checked for
URLs and bare WikiNames by the `muse-link-at-point' function.
Paren group 1 is the text to be checked.
URLs are checked by default. To get WikiNames, load
muse-wiki.el.
This is only used when you are using muse-mode.el, but not
muse-colors.el.
If the above applies, and you want to match things with spaces in
them, you will have to modify this."
:type 'regexp
:group 'muse-regexp)
;;; Regexps used to determine file types
(defcustom muse-file-regexp
(concat "\\`[~/]\\|\\?\\|/\\'\\|\\."
"\\(html?\\|pdf\\|mp3\\|el\\|zip\\|txt\\|tar\\)"
"\\(\\.\\(gz\\|bz2\\)\\)?\\'")
"A link matching this regexp will be regarded as a link to a file."
:type 'regexp
:group 'muse-regexp)
(defcustom muse-image-regexp
"\\.\\(eps\\|gif\\|jp\\(e?g\\)\\|p\\(bm\\|ng\\)\\|tiff\\|x\\([bp]m\\)\\)\\'"
"A link matching this regexp will be published inline as an image.
For example:
[[./wife.jpg][A picture of my wife]]
If you omit the description, the alt tag of the resulting HTML
buffer will be the name of the file."
:type 'regexp
:group 'muse-regexp)
(provide 'muse-regexps)
;;; muse-regexps.el ends here

View file

@ -0,0 +1,346 @@
;;; muse-texinfo.el --- publish entries to Texinfo format or PDF
;; Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010
;; Free Software Foundation, Inc.
;; This file is part of Emacs Muse. It is not part of GNU Emacs.
;; Emacs Muse 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 3, or (at your
;; option) any later version.
;; Emacs Muse 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 Emacs Muse; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.
;;; Commentary:
;;; Contributors:
;;; Code:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; Muse Texinfo Publishing
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(require 'muse-publish)
(require 'muse-latex)
(require 'texnfo-upd)
(defgroup muse-texinfo nil
"Rules for marking up a Muse file as a Texinfo article."
:group 'muse-publish)
(defcustom muse-texinfo-process-natively nil
"If non-nil, use the Emacs `texinfmt' module to make Info files."
:type 'boolean
:require 'texinfmt
:group 'muse-texinfo)
(defcustom muse-texinfo-extension ".texi"
"Default file extension for publishing Texinfo files."
:type 'string
:group 'muse-texinfo)
(defcustom muse-texinfo-info-extension ".info"
"Default file extension for publishing Info files."
:type 'string
:group 'muse-texinfo)
(defcustom muse-texinfo-pdf-extension ".pdf"
"Default file extension for publishing PDF files."
:type 'string
:group 'muse-texinfo)
(defcustom muse-texinfo-header
"\\input texinfo @c -*-texinfo-*-
@setfilename <lisp>(concat (muse-page-name) \".info\")</lisp>
@settitle <lisp>(muse-publishing-directive \"title\")</lisp>
@documentencoding iso-8859-1
@iftex
@finalout
@end iftex
@titlepage
@title <lisp>(muse-publishing-directive \"title\")</lisp>
@author <lisp>(muse-publishing-directive \"author\")</lisp>
@end titlepage
<lisp>(and muse-publish-generate-contents \"@contents\")</lisp>
@node Top, Overview, , (dir)
@top Overview
@c Page published by Emacs Muse begins here\n\n"
"Text to prepend to a Muse page being published as Texinfo.
This may be text or a filename.
It may contain <lisp> markup tags."
:type 'string
:group 'muse-texinfo)
(defcustom muse-texinfo-footer
"\n@c Page published by Emacs Muse ends here
@bye\n"
"Text to append to a Muse page being published as Texinfo.
This may be text or a filename.
It may contain <lisp> markup tags."
:type 'string
:group 'muse-texinfo)
(defcustom muse-texinfo-markup-regexps nil
"List of markup rules for publishing a Muse page to Texinfo.
For more on the structure of this list, see `muse-publish-markup-regexps'."
:type '(repeat (choice
(list :tag "Markup rule"
integer
(choice regexp symbol)
integer
(choice string function symbol))
function))
:group 'muse-texinfo)
(defcustom muse-texinfo-markup-functions
'((table . muse-texinfo-markup-table)
(heading . muse-texinfo-markup-heading))
"An alist of style types to custom functions for that kind of text.
For more on the structure of this list, see
`muse-publish-markup-functions'."
:type '(alist :key-type symbol :value-type function)
:group 'muse-texinfo)
(defcustom muse-texinfo-markup-strings
'((image-with-desc . "@center @image{%1%, , , %3%, %2%}@*\n@center %3%")
(image . "@noindent @image{%s, , , , %s}")
(image-link . "@uref{%s, %s.%s}")
(anchor-ref . "@ref{%s, %s}")
(url . "@uref{%s, %s}")
(link . "@ref{Top, %2%, , %1%, }")
(link-and-anchor . "@ref{%3%, %2%, , %1%, %3%}")
(email-addr . "@email{%s}")
(anchor . "@anchor{%s} ")
(emdash . "---")
(comment-begin . "@ignore\n")
(comment-end . "\n@end ignore\n")
(rule . "@sp 1")
(no-break-space . "@w{ }")
(line-break . "@*")
(enddots . "@enddots{}")
(dots . "@dots{}")
(section . "@chapter ")
(subsection . "@section ")
(subsubsection . "@subsection ")
(section-other . "@subsubheading ")
(footnote . "@footnote{")
(footnote-end . "}")
(begin-underline . "_")
(end-underline . "_")
(begin-literal . "@samp{")
(end-literal . "}")
(begin-emph . "@emph{")
(end-emph . "}")
(begin-more-emph . "@strong{")
(end-more-emph . "}")
(begin-most-emph . "@strong{@emph{")
(end-most-emph . "}}")
(begin-verse . "@display\n")
(end-verse-line . "")
(verse-space . "@ @ ")
(end-verse . "\n@end display")
(begin-example . "@example\n")
(end-example . "\n@end example")
(begin-center . "@quotation\n")
(end-center . "\n@end quotation")
(begin-quote . "@quotation\n")
(end-quote . "\n@end quotation")
(begin-cite . "")
(begin-cite-author . "")
(begin-cite-year . "")
(end-cite . "")
(begin-uli . "@itemize @bullet\n")
(end-uli . "\n@end itemize")
(begin-uli-item . "@item\n")
(begin-oli . "@enumerate\n")
(end-oli . "\n@end enumerate")
(begin-oli-item . "@item\n")
(begin-dl . "@table @strong\n")
(end-dl . "\n@end table")
(begin-ddt . "@item "))
"Strings used for marking up text.
These cover the most basic kinds of markup, the handling of which
differs little between the various styles."
:type '(alist :key-type symbol :value-type string)
:group 'muse-texinfo)
(defcustom muse-texinfo-markup-specials
'((?@ . "@@")
(?{ . "@{")
(?} . "@}"))
"A table of characters which must be represented specially."
:type '(alist :key-type character :value-type string)
:group 'muse-texinfo)
(defcustom muse-texinfo-markup-specials-url
'((?@ . "@@")
(?{ . "@{")
(?} . "@}")
(?, . "@comma{}"))
"A table of characters which must be represented specially.
These are applied to URLs."
:type '(alist :key-type character :value-type string)
:group 'muse-texinfo)
(defun muse-texinfo-decide-specials (context)
"Determine the specials to escape, depending on CONTEXT."
(cond ((memq context '(underline literal emphasis email url url-desc image
footnote))
muse-texinfo-markup-specials-url)
(t muse-texinfo-markup-specials)))
(defun muse-texinfo-markup-table ()
(let* ((table-info (muse-publish-table-fields (match-beginning 0)
(match-end 0)))
(row-len (car table-info))
(field-list (cdr table-info)))
(when table-info
(muse-insert-markup "@multitable @columnfractions")
(dotimes (field row-len)
(muse-insert-markup " " (number-to-string (/ 1.0 row-len))))
(dolist (fields field-list)
(let ((type (car fields)))
(unless (eq type 'hline)
(setq fields (cdr fields))
(if (= type 2)
(muse-insert-markup "\n@headitem ")
(muse-insert-markup "\n@item "))
(insert (car fields))
(setq fields (cdr fields))
(dolist (field fields)
(muse-insert-markup " @tab ")
(insert field)))))
(muse-insert-markup "\n@end multitable")
(insert ?\n))))
(defun muse-texinfo-remove-links (string)
"Remove explicit links from STRING, replacing them with the link
description.
If no description exists for the link, use the link itself."
(let ((start nil))
(while (setq start (string-match muse-explicit-link-regexp string
start))
(setq string
(replace-match (or (match-string 2 string)
(match-string 1 string))
t t string)))
string))
(defun muse-texinfo-protect-wikiwords (start end)
"Protect all wikiwords from START to END from further processing."
(and (boundp 'muse-wiki-wikiword-regexp)
(featurep 'muse-wiki)
(save-excursion
(goto-char start)
(while (re-search-forward muse-wiki-wikiword-regexp end t)
(muse-publish-mark-read-only (match-beginning 0)
(match-end 0))))))
(defun muse-texinfo-markup-heading ()
(save-excursion
(muse-publish-markup-heading))
(let* ((eol (muse-line-end-position))
(orig-heading (buffer-substring (point) eol))
(beg (point)))
(delete-region (point) eol)
;; don't allow links to be published in headings
(insert (muse-texinfo-remove-links orig-heading))
(muse-texinfo-protect-wikiwords beg (point))))
(defun muse-texinfo-munge-buffer ()
(muse-latex-fixup-dquotes)
(texinfo-insert-node-lines (point-min) (point-max) t)
(texinfo-all-menus-update t))
(defun muse-texinfo-pdf-browse-file (file)
(shell-command (concat "open " file)))
(defun muse-texinfo-info-generate (file output-path final-target)
;; The version of `texinfmt.el' that comes with Emacs 21 doesn't
;; support @documentencoding, so hack it in.
(when (and (not (featurep 'xemacs))
(eq emacs-major-version 21))
(put 'documentencoding 'texinfo-format
'texinfo-discard-line-with-args))
;; Most versions of `texinfmt.el' do not support @headitem, so hack
;; it in.
(unless (get 'headitem 'texinfo-format)
(put 'headitem 'texinfo-format 'texinfo-multitable-item))
(muse-publish-transform-output
file output-path final-target "Info"
(function
(lambda (file output-path)
(if muse-texinfo-process-natively
(save-window-excursion
(save-excursion
(find-file file)
(let ((inhibit-read-only t))
(texinfo-format-buffer))
(save-buffer)
(kill-buffer (current-buffer))
(let ((buf (get-file-buffer file)))
(with-current-buffer buf
(set-buffer-modified-p nil)
(kill-buffer (current-buffer))))
t))
(let ((result (shell-command
(concat "makeinfo --enable-encoding --output="
output-path " " file))))
(if (or (not (numberp result))
(eq result 0))
t
nil)))))))
(defun muse-texinfo-pdf-generate (file output-path final-target)
(let ((muse-latex-pdf-program "pdftex")
(muse-latex-pdf-cruft '(".aux" ".cp" ".fn" ".ky" ".log" ".pg" ".toc"
".tp" ".vr")))
(muse-latex-pdf-generate file output-path final-target)))
;;; Register the Muse TEXINFO Publishers
(muse-define-style "texi"
:suffix 'muse-texinfo-extension
:regexps 'muse-texinfo-markup-regexps
:functions 'muse-texinfo-markup-functions
:strings 'muse-texinfo-markup-strings
:specials 'muse-texinfo-decide-specials
:after 'muse-texinfo-munge-buffer
:header 'muse-texinfo-header
:footer 'muse-texinfo-footer
:browser 'find-file)
(muse-derive-style "info" "texi"
:final 'muse-texinfo-info-generate
:link-suffix 'muse-texinfo-info-extension
:osuffix 'muse-texinfo-info-extension
:browser 'info)
(muse-derive-style "info-pdf" "texi"
:final 'muse-texinfo-pdf-generate
:link-suffix 'muse-texinfo-pdf-extension
:osuffix 'muse-texinfo-pdf-extension
:browser 'muse-texinfo-pdf-browse-file)
(provide 'muse-texinfo)
;;; muse-texinfo.el ends here

View file

@ -0,0 +1,498 @@
;;; muse-wiki.el --- wiki features for Muse
;; Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010
;; Free Software Foundation, Inc.
;; Author: Yann Hodique <Yann.Hodique@lifl.fr>
;; Keywords:
;; This file is part of Emacs Muse. It is not part of GNU Emacs.
;; Emacs Muse 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 3, or (at your
;; option) any later version.
;; Emacs Muse 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 Emacs Muse; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.
;;; Commentary:
;;; Contributors:
;; Per B. Sederberg (per AT med DOT upenn DOT edu) made it so that all
;; files in a Muse project can become implicit links.
;;; Code:
(require 'muse-regexps)
(require 'muse-mode)
(eval-when-compile
(require 'muse-colors))
(defgroup muse-wiki nil
"Options controlling the behavior of Emacs Muse Wiki features."
:group 'muse-mode)
(defcustom muse-wiki-use-wikiword t
"Whether to use color and publish bare WikiNames."
:type 'boolean
:group 'muse-wiki)
(defcustom muse-wiki-allow-nonexistent-wikiword nil
"Whether to color bare WikiNames that don't have an existing file."
:type 'boolean
:group 'muse-wiki)
(defcustom muse-wiki-match-all-project-files nil
"If non-nil, Muse will color and publish implicit links to any
file in your project, regardless of whether its name is a WikiWord."
:type 'boolean
:group 'muse-wiki)
(defcustom muse-wiki-ignore-implicit-links-to-current-page nil
"If non-nil, Muse will not recognize implicit links to the current
page, both when formatting and publishing."
:type 'boolean
:group 'muse-wiki)
(defvar muse-wiki-project-file-regexp nil
"Regexp used to match the files in the current project.
This is set by `muse-wiki-update-project-file-regexp' automatically
when `muse-wiki-match-all-project-files' is non-nil.")
(make-variable-buffer-local 'muse-wiki-project-file-regexp)
(defun muse-wiki-update-project-file-regexp ()
"Update a local copy of `muse-wiki-project-file-regexp' to include
all the files in the project."
;; see if the user wants to match project files
(when muse-wiki-match-all-project-files
(let ((files (mapcar #'car (muse-project-file-alist (muse-project)))))
(setq muse-wiki-project-file-regexp
(when files
(concat "\\("
;; include all files from the project
(regexp-opt files 'words)
"\\)"))))
;; update coloring setup
(when (featurep 'muse-colors)
(muse-colors-define-highlighting 'muse-mode muse-colors-markup))))
(add-hook 'muse-update-values-hook
'muse-wiki-update-project-file-regexp)
(add-hook 'muse-project-file-alist-hook
'muse-wiki-update-project-file-regexp)
(defcustom muse-wiki-wikiword-regexp
(concat "\\<\\(\\(?:[" muse-regexp-upper
"]+[" muse-regexp-lower "]+\\)\\(?:["
muse-regexp-upper "]+[" muse-regexp-lower "]+\\)+\\)")
"Regexp used to match WikiWords."
:set (function
(lambda (sym value)
(set sym value)
(when (featurep 'muse-colors)
(muse-colors-define-highlighting 'muse-mode muse-colors-markup))))
:type 'regexp
:group 'muse-wiki)
(defcustom muse-wiki-ignore-bare-project-names nil
"Determine whether project names without a page specifer are links.
If non-nil, project names without a page specifier will not be
considered links.
When nil, project names without a specifier are highlighted and
they link to the default page of the project that they name."
:type 'boolean
:group 'muse-wiki)
(defvar muse-wiki-interwiki-regexp nil
"Regexp that matches all interwiki links.
This is automatically generated by setting `muse-wiki-interwiki-alist'.
It can also be set by calling `muse-wiki-update-interwiki-regexp'.")
(defcustom muse-wiki-interwiki-delimiter "#\\|::"
"Delimiter regexp used for InterWiki links.
If you use groups, use only shy groups."
:type 'regexp
:group 'muse-wiki)
(defcustom muse-wiki-interwiki-replacement ": "
"Regexp used for replacing `muse-wiki-interwiki-delimiter' in
InterWiki link descriptions.
If you want this replacement to happen, you must add
`muse-wiki-publish-pretty-interwiki' to
`muse-publish-desc-transforms'."
:type 'regexp
:group 'muse-wiki)
(eval-when-compile
(defvar muse-wiki-interwiki-alist))
(defun muse-wiki-project-files-with-spaces (&optional project)
"Return a list of files in PROJECT that have spaces."
(setq project (muse-project project))
(let ((flist nil))
(save-match-data
(dolist (entry (muse-project-file-alist project))
(when (string-match " " (car entry))
(setq flist (cons (car entry) flist)))))
flist))
(defun muse-wiki-update-interwiki-regexp ()
"Update the value of `muse-wiki-interwiki-regexp' based on
`muse-wiki-interwiki-alist' and `muse-project-alist'."
(if (null muse-project-alist)
(setq muse-wiki-interwiki-regexp nil)
(let ((old-value muse-wiki-interwiki-regexp))
(setq muse-wiki-interwiki-regexp
(concat "\\<\\(" (regexp-opt (mapcar #'car muse-project-alist))
(when muse-wiki-interwiki-alist
(let ((interwiki-rules
(mapcar #'car muse-wiki-interwiki-alist)))
(when interwiki-rules
(concat "\\|" (regexp-opt interwiki-rules)))))
"\\)\\(?:\\(" muse-wiki-interwiki-delimiter
"\\)\\("
(when muse-wiki-match-all-project-files
;; append the files from the project
(let ((files nil))
(dolist (proj muse-project-alist)
(setq files
(nconc (muse-wiki-project-files-with-spaces
(car proj))
files)))
(when files
(concat (regexp-opt files) "\\|"))))
"\\sw+\\)\\(#\\S-+\\)?\\)?\\>"))
(when (and (featurep 'muse-colors)
(not (string= old-value muse-wiki-interwiki-regexp)))
(muse-colors-define-highlighting 'muse-mode muse-colors-markup)))))
(defcustom muse-wiki-interwiki-alist
'(("EmacsWiki" . "http://www.emacswiki.org/cgi-bin/wiki/"))
"A table of WikiNames that refer to external entities.
The format of this table is an alist, or series of cons cells.
Each cons cell must be of the form:
(WIKINAME . STRING-OR-FUNCTION)
The second part of the cons cell may either be a STRING, which in most
cases should be a URL, or a FUNCTION. If a function, it will be
called with one argument: the tag applied to the Interwiki name, or
nil if no tag was used. If the cdr was a STRING and a tag is used,
the tag is simply appended.
Here are some examples:
(\"JohnWiki\" . \"http://alice.dynodns.net/wiki?\")
Referring to [[JohnWiki::EmacsModules]] then really means:
http://alice.dynodns.net/wiki?EmacsModules
If a function is used for the replacement text, you can get creative
depending on what the tag is. Tags may contain any alphabetic
character, any number, % or _. If you need other special characters,
use % to specify the hex code, as in %2E. All browsers should support
this."
:type '(repeat (cons (string :tag "WikiName")
(choice (string :tag "URL") function)))
:set (function
(lambda (sym value)
(set sym value)
(muse-wiki-update-interwiki-regexp)))
:group 'muse-wiki)
(add-hook 'muse-update-values-hook
'muse-wiki-update-interwiki-regexp)
(defun muse-wiki-resolve-project-page (&optional project page)
"Return the published path from the current page to PAGE of PROJECT.
If PAGE is not specified, use the value of :default in PROJECT.
If PROJECT is not specified, default to the current project. If
no project is current, use the first project of
`muse-projects-alist'.
Note that PAGE can have several output directories. If this is
the case, we will use the first one that matches our current
style and has the same link suffix, ignoring the others. If no
style has the same link suffix as the current publishing style,
use the first style we find."
(setq project (or (and project
(muse-project project))
(muse-project)
(car muse-project-alist))
page (or page (muse-get-keyword :default (cadr project))))
(let* ((page-path (and muse-project-alist
(muse-project-page-file page project)))
(remote-styles (and page-path (muse-project-applicable-styles
page-path (cddr project))))
(local-style (muse-project-current-output-style)))
(cond ((and remote-styles local-style muse-publishing-p)
(muse-project-resolve-link page local-style remote-styles))
((not muse-publishing-p)
(if page-path
page-path
(when muse-wiki-allow-nonexistent-wikiword
;; make a path to a nonexistent file in project
(setq page-path (expand-file-name
page (car (cadr project))))
(if (and muse-file-extension
(not (string= muse-file-extension "")))
(concat page-path "." muse-file-extension)
page-path)))))))
(defun muse-wiki-handle-implicit-interwiki (&optional string)
"If STRING or point has an interwiki link, resolve it to a filename.
Match string 0 is set to the link."
(when (and muse-wiki-interwiki-regexp
(if string (string-match muse-wiki-interwiki-regexp string)
(looking-at muse-wiki-interwiki-regexp)))
(let* ((project (match-string 1 string))
(subst (cdr (assoc project muse-wiki-interwiki-alist)))
(word (match-string 3 string))
(anchor (if (match-beginning 4)
(match-string 4 string)
"")))
(if subst
(if (functionp subst)
(and (setq word (funcall subst word))
(concat word anchor))
(concat subst word anchor))
(and (assoc project muse-project-alist)
(or word (not muse-wiki-ignore-bare-project-names))
(setq word (muse-wiki-resolve-project-page project word))
(concat word anchor))))))
(defun muse-wiki-handle-explicit-interwiki (&optional string)
"If STRING or point has an interwiki link, resolve it to a filename."
(let ((right-pos (if string (length string) (match-end 1))))
(when (and muse-wiki-interwiki-regexp
(if string (string-match muse-wiki-interwiki-regexp string)
(save-restriction
(narrow-to-region (point) right-pos)
(looking-at muse-wiki-interwiki-regexp))))
(let* ((project (match-string 1 string))
(subst (cdr (assoc project muse-wiki-interwiki-alist)))
(anchor (and (match-beginning 4)
(match-string 4 string)))
(word (when (match-end 2)
(cond (anchor (match-string 3 string))
(string (substring string (match-end 2)))
(right-pos (buffer-substring (match-end 2)
right-pos))
(t nil)))))
(if (and (null word)
right-pos
(not (= right-pos (match-end 1))))
;; if only a project name was found, it must take up the
;; entire string or link
nil
(unless anchor
(if (or (null word)
(not (string-match "#[^#]+\\'" word)))
(setq anchor "")
(setq anchor (match-string 0 word))
(setq word (substring word 0 (match-beginning 0)))))
(if subst
(if (functionp subst)
(and (setq word (funcall subst word))
(concat word anchor))
(concat subst word anchor))
(and (assoc project muse-project-alist)
(or word (not muse-wiki-ignore-bare-project-names))
(setq word (muse-wiki-resolve-project-page project word))
(concat word anchor))))))))
(defun muse-wiki-handle-wikiword (&optional string)
"If STRING or point has a WikiWord, return it.
Match 1 is set to the WikiWord."
(when (and (or (and muse-wiki-match-all-project-files
muse-wiki-project-file-regexp
(if string
(string-match muse-wiki-project-file-regexp string)
(looking-at muse-wiki-project-file-regexp)))
(and muse-wiki-use-wikiword
(if string
(string-match muse-wiki-wikiword-regexp string)
(looking-at muse-wiki-wikiword-regexp))))
(cond
(muse-wiki-allow-nonexistent-wikiword
t)
((and muse-wiki-ignore-implicit-links-to-current-page
(string= (match-string 1 string) (muse-page-name)))
nil)
((and (muse-project-of-file)
(muse-project-page-file
(match-string 1 string) muse-current-project t))
t)
((file-exists-p (match-string 1 string))
t)
(t nil)))
(match-string 1 string)))
;;; Prettifications
(defcustom muse-wiki-publish-small-title-words
'("the" "and" "at" "on" "of" "for" "in" "an" "a")
"Strings that should be downcased in a page title.
This is used by `muse-wiki-publish-pretty-title', which must be
called manually."
:type '(repeat string)
:group 'muse-wiki)
(defcustom muse-wiki-hide-nop-tag t
"If non-nil, hide <nop> tags when coloring a Muse buffer."
:type 'boolean
:group 'muse-wiki)
(defun muse-wiki-publish-pretty-title (&optional title explicit)
"Return a pretty version of the given TITLE.
If EXPLICIT is non-nil, TITLE will be returned unmodified."
(unless title (setq title (or (muse-publishing-directive "title") "")))
(if (or explicit
(save-match-data (string-match muse-url-regexp title)))
title
(save-match-data
(let ((case-fold-search nil))
(while (string-match (concat "\\([" muse-regexp-lower
"]\\)\\([" muse-regexp-upper
"0-9]\\)")
title)
(setq title (replace-match "\\1 \\2" t nil title)))
(let* ((words (split-string title))
(w (cdr words)))
(while w
(if (member (downcase (car w))
muse-wiki-publish-small-title-words)
(setcar w (downcase (car w))))
(setq w (cdr w)))
(mapconcat 'identity words " "))))))
(defun muse-wiki-publish-pretty-interwiki (desc &optional explicit)
"Replace instances of `muse-wiki-interwiki-delimiter' with
`muse-wiki-interwiki-replacement'."
(if (or explicit
(save-match-data (string-match muse-url-regexp desc)))
desc
(muse-replace-regexp-in-string muse-wiki-interwiki-delimiter
muse-wiki-interwiki-replacement
desc)))
;;; Coloring setup
(defun muse-wiki-colors-nop-tag (beg end)
"Inhibit the colorization of inhibit links just after the tag.
Example: <nop>WikiWord"
(when muse-wiki-hide-nop-tag
(add-text-properties beg (+ beg 5)
'(invisible muse intangible t)))
(unless (> (+ beg 6) (point-max))
(add-text-properties (+ beg 5) (+ beg 6)
'(muse-no-implicit-link t))))
(defun muse-colors-wikiword-separate ()
(add-text-properties (match-beginning 0) (match-end 0)
'(invisible muse intangible t)))
(defun muse-wiki-insinuate-colors ()
(add-to-list 'muse-colors-tags
'("nop" nil nil nil muse-wiki-colors-nop-tag)
t)
(add-to-list 'muse-colors-markup
'(muse-wiki-interwiki-regexp t muse-colors-implicit-link)
t)
(add-to-list 'muse-colors-markup
'(muse-wiki-wikiword-regexp t muse-colors-implicit-link)
t)
(add-to-list 'muse-colors-markup
'(muse-wiki-project-file-regexp t muse-colors-implicit-link)
t)
(add-to-list 'muse-colors-markup
'("''''" ?\' muse-colors-wikiword-separate)
nil)
(muse-colors-define-highlighting 'muse-mode muse-colors-markup))
(eval-after-load "muse-colors" '(muse-wiki-insinuate-colors))
;;; Publishing setup
(defun muse-wiki-publish-nop-tag (beg end)
"Inhibit the colorization of inhibit links just after the tag.
Example: <nop>WikiWord"
(unless (= (point) (point-max))
(muse-publish-mark-read-only (point) (+ (point) 1))))
(defun muse-wiki-insinuate-publish ()
(add-to-list 'muse-publish-markup-tags
'("nop" nil nil nil muse-wiki-publish-nop-tag)
t)
(add-to-list 'muse-publish-markup-regexps
'(3100 muse-wiki-interwiki-regexp 0 link)
t)
(add-to-list 'muse-publish-markup-regexps
'(3200 muse-wiki-wikiword-regexp 0 link)
t)
(add-to-list 'muse-publish-markup-regexps
'(3250 muse-wiki-project-file-regexp 0 link)
t)
(add-to-list 'muse-publish-markup-regexps
'(3300 "''''" 0 "")
t)
(custom-add-option 'muse-publish-desc-transforms
'muse-wiki-publish-pretty-interwiki)
(custom-add-option 'muse-publish-desc-transforms
'muse-wiki-publish-pretty-title))
(eval-after-load "muse-publish" '(muse-wiki-insinuate-publish))
;;; Insinuate link handling
(custom-add-option 'muse-implicit-link-functions
'muse-wiki-handle-implicit-interwiki)
(custom-add-option 'muse-implicit-link-functions
'muse-wiki-handle-wikiword)
(custom-add-option 'muse-explicit-link-functions
'muse-wiki-handle-explicit-interwiki)
(add-to-list 'muse-implicit-link-functions
'muse-wiki-handle-implicit-interwiki t)
(add-to-list 'muse-implicit-link-functions
'muse-wiki-handle-wikiword t)
(add-to-list 'muse-explicit-link-functions
'muse-wiki-handle-explicit-interwiki t)
;;; Obsolete functions
(defun muse-wiki-update-custom-values ()
(muse-display-warning
(concat "Please remove `muse-wiki-update-custom-values' from"
" `muse-mode-hook'. Its use is now deprecated.")))
(provide 'muse-wiki)
;;; muse-wiki.el ends here

View file

@ -0,0 +1,201 @@
;;; muse-xml-common.el --- common routines for XML-like publishing styles
;; Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010
;; Free Software Foundation, Inc.
;; This file is part of Emacs Muse. It is not part of GNU Emacs.
;; Emacs Muse 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 3, or (at your
;; option) any later version.
;; Emacs Muse 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 Emacs Muse; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.
;;; Commentary:
;;; Contributors:
;;; Code:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; Muse XML Publishing - Common Elements
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(require 'muse-publish)
(require 'muse-regexps)
(defcustom muse-xml-encoding-map
'((iso-8859-1 . "iso-8859-1")
(iso-2022-jp . "iso-2022-jp")
(utf-8 . "utf-8")
(japanese-iso-8bit . "euc-jp")
(chinese-big5 . "big5")
(mule-utf-8 . "utf-8")
(chinese-iso-8bit . "gb2312")
(chinese-gbk . "gbk"))
"An alist mapping Emacs coding systems to appropriate XML charsets.
Use the base name of the coding system (i.e. without the -unix)."
:type '(alist :key-type coding-system :value-type string)
:group 'muse-xml)
(defun muse-xml-transform-content-type (content-type default)
"Using `muse-xml-encoding-map', try and resolve an Emacs coding
system to an associated XML coding system.
If no match is found, the DEFAULT charset is used instead."
(let ((match (and (fboundp 'coding-system-base)
(assoc (coding-system-base content-type)
muse-xml-encoding-map))))
(if match
(cdr match)
default)))
(defcustom muse-xml-markup-specials
'((?\" . "&quot;")
(?\< . "&lt;")
(?\> . "&gt;")
(?\& . "&amp;"))
"A table of characters which must be represented specially."
:type '(alist :key-type character :value-type string)
:group 'muse-xml)
(defcustom muse-xml-markup-specials-url-extra
'((?\" . "&quot;")
(?\< . "&lt;")
(?\> . "&gt;")
(?\& . "&amp;")
(?\ . "%20")
(?\n . "%0D%0A"))
"A table of characters which must be represented specially.
These are extra characters that are escaped within URLs."
:type '(alist :key-type character :value-type string)
:group 'muse-xml)
(defun muse-xml-decide-specials (context)
"Determine the specials to escape, depending on CONTEXT."
(cond ((memq context '(email url image))
'muse-xml-escape-url)
((eq context 'url-extra)
muse-xml-markup-specials-url-extra)
(t muse-xml-markup-specials)))
(defun muse-xml-escape-url (str)
"Convert to character entities any non-alphanumeric characters
outside a few punctuation symbols, that risk being misinterpreted
if not escaped."
(when str
(setq str (muse-publish-escape-specials-in-string str 'url-extra))
(let (pos code len ch)
(save-match-data
(while (setq pos (string-match (concat "[^-"
muse-regexp-alnum
"/:._=@\\?~#%\"\\+<>()&;]")
str pos))
(setq ch (aref str pos)
code (concat "&#" (int-to-string
(cond ((fboundp 'char-to-ucs)
(char-to-ucs ch))
((fboundp 'char-to-int)
(char-to-int ch))
(t ch)))
";")
len (length code)
str (concat (substring str 0 pos)
code
(when (< pos (length str))
(substring str (1+ pos) nil)))
pos (+ len pos)))
str))))
(defun muse-xml-markup-anchor ()
(unless (get-text-property (match-end 1) 'muse-link)
(let ((text (muse-markup-text 'anchor (match-string 2))))
(save-match-data
(skip-chars-forward (concat muse-regexp-blank "\n"))
(when (looking-at (concat "<\\([^" muse-regexp-blank "/>\n]+\\)>"))
(goto-char (match-end 0)))
(muse-insert-markup text)))
(match-string 1)))
(defun muse-xml-sort-table (table)
"Sort the given table structure so that it validates properly."
;; Note that the decision matrix must have a nil diagonal, or else
;; elements with the same type will be reversed with respect to each
;; other.
(let ((decisions '((nil nil nil) ; body < header, body < footer
(t nil t) ; header stays where it is
(t nil nil)))) ; footer < header
(sort table #'(lambda (l r)
(and (integerp (car l)) (integerp (car r))
(nth (1- (car r))
(nth (1- (car l)) decisions)))))))
(defun muse-xml-markup-table (&optional attributes)
"Publish the matched region into a table.
If a string ATTRIBUTES is given, pass it to the markup string begin-table."
(let* ((table-info (muse-publish-table-fields (match-beginning 0)
(match-end 0)))
(row-len (car table-info))
(supports-group (not (string= (muse-markup-text 'begin-table-group
row-len)
"")))
(field-list (muse-xml-sort-table (cdr table-info)))
last-part)
(when table-info
(let ((beg (point)))
(muse-publish-ensure-block beg))
(muse-insert-markup (muse-markup-text 'begin-table (or attributes "")))
(muse-insert-markup (muse-markup-text 'begin-table-group row-len))
(dolist (fields field-list)
(let* ((type (car fields))
(part (cond ((eq type 'hline) nil)
((= type 1) "tbody")
((= type 2) "thead")
((= type 3) "tfoot")))
(col (cond ((eq type 'hline) nil)
((= type 1) "td")
((= type 2) "th")
((= type 3) "td"))))
(setq fields (cdr fields))
(unless (and part last-part (string= part last-part))
(when last-part
(muse-insert-markup " </" last-part ">\n")
(when (eq type 'hline)
;; horizontal separators are represented by closing
;; the current table group and opening a new one
(muse-insert-markup (muse-markup-text 'end-table-group))
(muse-insert-markup (muse-markup-text 'begin-table-group
row-len))))
(when part
(muse-insert-markup " <" part ">\n"))
(setq last-part part))
(unless (eq type 'hline)
(muse-insert-markup (muse-markup-text 'begin-table-row))
(dolist (field fields)
(muse-insert-markup (muse-markup-text 'begin-table-entry col))
(insert field)
(muse-insert-markup (muse-markup-text 'end-table-entry col)))
(muse-insert-markup (muse-markup-text 'end-table-row)))))
(when last-part
(muse-insert-markup " </" last-part ">\n"))
(muse-insert-markup (muse-markup-text 'end-table-group))
(muse-insert-markup (muse-markup-text 'end-table))
(insert ?\n))))
(defun muse-xml-prepare-buffer ()
(set (make-local-variable 'muse-publish-url-transforms)
(cons 'muse-xml-escape-string muse-publish-url-transforms)))
(provide 'muse-xml-common)
;;; muse-xml-common.el ends here

View file

@ -0,0 +1,274 @@
;;; muse-xml.el --- publish XML files
;; Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010
;; Free Software Foundation, Inc.
;; Author: Michael Olson <mwolson@gnu.org>
;; Date: Sat 23-Jul-2005
;; This file is part of Emacs Muse. It is not part of GNU Emacs.
;; Emacs Muse 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 3, or (at your
;; option) any later version.
;; Emacs Muse 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 Emacs Muse; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.
;;; Commentary:
;; James Clarke's nxml-mode can be used for editing and validating
;; Muse-generated XML files. If you are in nxml-mode use the command
;; C-c C-s C-f to point to the schema in `contrib/muse.rnc', which
;; comes with Muse. Say yes if you are asked if you want to copy the
;; file to your location. C-c C-s C-a can then be used to reload the
;; schema if you make changes to the file.
;;; Contributors:
;; Peter K. Lee (saint AT corenova DOT com) made the initial
;; implementation of planner-publish.el, which was heavily borrowed
;; from.
;; Brad Collins (brad AT chenla DOT org) provided a Compact RelaxNG
;; schema.
;;; Code:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; Muse XML Publishing
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(require 'muse-publish)
(require 'muse-regexps)
(require 'muse-xml-common)
(defgroup muse-xml nil
"Options controlling the behavior of Muse XML publishing.
See `muse-xml' for more information."
:group 'muse-publish)
(defcustom muse-xml-extension ".xml"
"Default file extension for publishing XML files."
:type 'string
:group 'muse-xml)
(defcustom muse-xml-header
"<?xml version=\"1.0\" encoding=\"<lisp>
(muse-xml-encoding)</lisp>\"?>
<MUSE>
<pageinfo>
<title><lisp>(muse-publishing-directive \"title\")</lisp></title>
<author><lisp>(muse-publishing-directive \"author\")</lisp></author>
<maintainer><lisp>(muse-style-element :maintainer)</lisp></maintainer>
<pubdate><lisp>(muse-publishing-directive \"date\")</lisp></pubdate>
</pageinfo>
<!-- Page published by Emacs Muse begins here -->\n"
"Header used for publishing XML files.
This may be text or a filename."
:type 'string
:group 'muse-xml)
(defcustom muse-xml-footer "
<!-- Page published by Emacs Muse ends here -->
</MUSE>\n"
"Footer used for publishing XML files.
This may be text or a filename."
:type 'string
:group 'muse-xml)
(defcustom muse-xml-markup-regexps
`(;; Beginning of doc, end of doc, or plain paragraph separator
(10000 ,(concat "\\(\\(\n\\(?:[" muse-regexp-blank "]*\n\\)*"
"\\([" muse-regexp-blank "]*\n\\)\\)"
"\\|\\`\\s-*\\|\\s-*\\'\\)")
;; this is somewhat repetitive because we only require the
;; line just before the paragraph beginning to be not
;; read-only
3 muse-xml-markup-paragraph))
"List of markup rules for publishing a Muse page to XML.
For more on the structure of this list, see `muse-publish-markup-regexps'."
:type '(repeat (choice
(list :tag "Markup rule"
integer
(choice regexp symbol)
integer
(choice string function symbol))
function))
:group 'muse-xml)
(defcustom muse-xml-markup-functions
'((anchor . muse-xml-markup-anchor)
(table . muse-xml-markup-table))
"An alist of style types to custom functions for that kind of text.
For more on the structure of this list, see
`muse-publish-markup-functions'."
:type '(alist :key-type symbol :value-type function)
:group 'muse-xml)
(defcustom muse-xml-markup-strings
'((image-with-desc . "<image href=\"%s.%s\">%s</image>")
(image . "<image href=\"%s.%s\"></image>")
(image-link . "<link type=\"image\" href=\"%s\">%s.%s</link>")
(anchor-ref . "<link type=\"url\" href=\"#%s\">%s</link>")
(url . "<link type=\"url\" href=\"%s\">%s</link>")
(link . "<link type=\"url\" href=\"%s\">%s</link>")
(link-and-anchor . "<link type=\"url\" href=\"%s#%s\">%s</link>")
(email-addr . "<link type=\"email\" href=\"%s\">%s</link>")
(anchor . "<anchor id=\"%s\" />\n")
(emdash . "%s--%s")
(comment-begin . "<!-- ")
(comment-end . " -->")
(rule . "<hr />")
(fn-sep . "<hr />\n")
(no-break-space . "&nbsp;")
(line-break . "<br>")
(enddots . "....")
(dots . "...")
(section . "<section level=\"1\"><title>")
(section-end . "</title>")
(subsection . "<section level=\"2\"><title>")
(subsection-end . "</title>")
(subsubsection . "<section level=\"3\"><title>")
(subsubsection-end . "</title>")
(section-other . "<section level=\"%s\"><title>")
(section-other-end . "</title>")
(section-close . "</section>")
(footnote . "<footnote>")
(footnote-end . "</footnote>")
(begin-underline . "<format type=\"underline\">")
(end-underline . "</format>")
(begin-literal . "<code>")
(end-literal . "</code>")
(begin-emph . "<format type=\"emphasis\" level=\"1\">")
(end-emph . "</format>")
(begin-more-emph . "<format type=\"emphasis\" level=\"2\">")
(end-more-emph . "</format>")
(begin-most-emph . "<format type=\"emphasis\" level=\"3\">")
(end-most-emph . "</format>")
(begin-verse . "<verse>\n")
(begin-verse-line . "<line>")
(end-verse-line . "</line>")
(empty-verse-line . "<line />")
(begin-last-stanza-line . "<line>")
(end-last-stanza-line . "</line>")
(end-verse . "</verse>")
(begin-example . "<example>")
(end-example . "</example>")
(begin-center . "<p><format type=\"center\">\n")
(end-center . "\n</format></p>")
(begin-quote . "<blockquote>\n")
(end-quote . "\n</blockquote>")
(begin-cite . "<cite>")
(begin-cite-author . "<cite type=\"author\">")
(begin-cite-year . "<cite type=\"year\">")
(end-cite . "</cite>")
(begin-quote-item . "<p>")
(end-quote-item . "</p>")
(begin-uli . "<list type=\"unordered\">\n")
(end-uli . "\n</list>")
(begin-uli-item . "<item>")
(end-uli-item . "</item>")
(begin-oli . "<list type=\"ordered\">\n")
(end-oli . "\n</list>")
(begin-oli-item . "<item>")
(end-oli-item . "</item>")
(begin-dl . "<list type=\"definition\">\n")
(end-dl . "\n</list>")
(begin-dl-item . "<item>\n")
(end-dl-item . "\n</item>")
(begin-ddt . "<term>")
(end-ddt . "</term>")
(begin-dde . "<definition>")
(end-dde . "</definition>")
(begin-table . "<table%s>\n")
(end-table . "</table>")
(begin-table-row . " <tr>\n")
(end-table-row . " </tr>\n")
(begin-table-entry . " <%s>")
(end-table-entry . "</%s>\n"))
"Strings used for marking up text.
These cover the most basic kinds of markup, the handling of which
differs little between the various styles."
:type '(alist :key-type symbol :value-type string)
:group 'muse-xml)
(defcustom muse-xml-encoding-default 'utf-8
"The default Emacs buffer encoding to use in published files.
This will be used if no special characters are found."
:type 'symbol
:group 'muse-xml)
(defcustom muse-xml-charset-default "utf-8"
"The default XML charset to use if no translation is
found in `muse-xml-encoding-map'."
:type 'string
:group 'muse-xml)
(defun muse-xml-encoding ()
(muse-xml-transform-content-type
(or (and (boundp 'buffer-file-coding-system)
buffer-file-coding-system)
muse-xml-encoding-default)
muse-xml-charset-default))
(defun muse-xml-markup-paragraph ()
(let ((end (copy-marker (match-end 0) t)))
(goto-char (match-beginning 0))
(when (save-excursion
(save-match-data
(and (not (get-text-property (max (point-min) (1- (point)))
'muse-no-paragraph))
(re-search-backward "<\\(/?\\)p[ >]" nil t)
(not (string-equal (match-string 1) "/")))))
(when (get-text-property (1- (point)) 'muse-end-list)
(goto-char (previous-single-property-change (1- (point))
'muse-end-list)))
(muse-insert-markup "</p>"))
(goto-char end))
(cond
((eobp)
(unless (bolp)
(insert "\n")))
((get-text-property (point) 'muse-no-paragraph)
(forward-char 1)
nil)
((eq (char-after) ?\<)
(when (looking-at (concat "<\\(format\\|code\\|link\\|image"
"\\|anchor\\|footnote\\)[ >]"))
(muse-insert-markup "<p>")))
(t
(muse-insert-markup "<p>"))))
(defun muse-xml-finalize-buffer ()
(when (boundp 'buffer-file-coding-system)
(when (memq buffer-file-coding-system '(no-conversion undecided-unix))
;; make it agree with the default charset
(setq buffer-file-coding-system muse-xml-encoding-default))))
;;; Register the Muse XML Publisher
(muse-define-style "xml"
:suffix 'muse-xml-extension
:regexps 'muse-xml-markup-regexps
:functions 'muse-xml-markup-functions
:strings 'muse-xml-markup-strings
:specials 'muse-xml-decide-specials
:after 'muse-xml-finalize-buffer
:header 'muse-xml-header
:footer 'muse-xml-footer
:browser 'find-file)
(provide 'muse-xml)
;;; muse-xml.el ends here

881
emacs.d/elisp/muse/muse.el Normal file
View file

@ -0,0 +1,881 @@
;;; muse.el --- an authoring and publishing tool for Emacs
;; Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010
;; Free Software Foundation, Inc.
;; Emacs Lisp Archive Entry
;; Filename: muse.el
;; Version: 3.20
;; Date: Sun 31 Jan-2010
;; Keywords: hypermedia
;; Author: John Wiegley <johnw@gnu.org>
;; Maintainer: Michael Olson <mwolson@gnu.org>
;; Description: An authoring and publishing tool for Emacs
;; URL: http://mwolson.org/projects/EmacsMuse.html
;; Compatibility: Emacs21 XEmacs21 Emacs22
;; This file is part of Emacs Muse. It is not part of GNU Emacs.
;; Emacs Muse 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 3, or (at your
;; option) any later version.
;; Emacs Muse 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 Emacs Muse; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.
;;; Commentary:
;; Muse is a tool for easily authoring and publishing documents. It
;; allows for rapid prototyping of hyperlinked text, which may then be
;; exported to multiple output formats -- such as HTML, LaTeX,
;; Texinfo, etc.
;; The markup rules used by Muse are intended to be very friendly to
;; people familiar with Emacs. See the included manual for more
;; information.
;;; Contributors:
;;; Code:
;; Indicate that this version of Muse supports nested tags
(provide 'muse-nested-tags)
(defvar muse-version "3.20"
"The version of Muse currently loaded")
(defun muse-version (&optional insert)
"Display the version of Muse that is currently loaded.
If INSERT is non-nil, insert the text instead of displaying it."
(interactive "P")
(if insert
(insert muse-version)
(message muse-version)))
(defgroup muse nil
"Options controlling the behavior of Muse.
The markup used by Muse is intended to be very friendly to people
familiar with Emacs."
:group 'hypermedia)
(defvar muse-under-windows-p (memq system-type '(ms-dos windows-nt)))
(provide 'muse)
(condition-case nil
(require 'derived)
(error nil))
(require 'wid-edit)
(require 'muse-regexps)
(defvar muse-update-values-hook nil
"Hook for values that are automatically generated.
This is to be used by add-on modules for Muse.
It is run just before colorizing or publishing a buffer.")
(defun muse-update-values ()
"Update various values that are automatically generated.
Call this after changing `muse-project-alist'."
(interactive)
(run-hooks 'muse-update-values-hook)
(dolist (buffer (buffer-list))
(when (buffer-live-p buffer)
(with-current-buffer buffer
(when (derived-mode-p 'muse-mode)
(and (boundp 'muse-current-project)
(fboundp 'muse-project-of-file)
(setq muse-current-project nil)
(setq muse-current-project (muse-project-of-file))))))))
;; Default file extension
;; By default, use the .muse file extension.
;;;###autoload (add-to-list 'auto-mode-alist '("\\.muse\\'" . muse-mode-choose-mode))
;; We need to have this at top-level, as well, so that any Muse or
;; Planner documents opened during init will just work.
(add-to-list 'auto-mode-alist '("\\.muse\\'" . muse-mode-choose-mode))
(eval-when-compile
(defvar muse-ignored-extensions))
(defvar muse-ignored-extensions-regexp nil
"A regexp of extensions to omit from the ending of a Muse page name.
This is autogenerated from `muse-ignored-extensions'.")
(defun muse-update-file-extension (sym val)
"Update the value of `muse-file-extension'."
(let ((old (and (boundp sym) (symbol-value sym))))
(set sym val)
(when (and (featurep 'muse-mode)
(or (not (stringp val))
(not (stringp old))
(not (string= old val))))
;; remove old auto-mode-alist association
(when (and (boundp sym) (stringp old))
(setq auto-mode-alist
(delete (cons (concat "\\." old "\\'")
'muse-mode-choose-mode)
auto-mode-alist)))
;; associate the new file extension with muse-mode
(when (stringp val)
(add-to-list 'auto-mode-alist
(cons (concat "\\." val "\\'")
'muse-mode-choose-mode)))
;; update the ignored extensions regexp
(when (fboundp 'muse-update-ignored-extensions-regexp)
(muse-update-ignored-extensions-regexp
'muse-ignored-extensions muse-ignored-extensions)))))
(defcustom muse-file-extension "muse"
"File extension of Muse files. Omit the period at the beginning.
If you don't want Muse files to have an extension, set this to nil."
:type '(choice
(const :tag "None" nil)
(string))
:set 'muse-update-file-extension
:group 'muse)
(defcustom muse-completing-read-function 'completing-read
"Function to call when prompting user to choose between a list of options.
This should take the same arguments as `completing-read'."
:type 'function
:group 'muse)
(defun muse-update-ignored-extensions-regexp (sym val)
"Update the value of `muse-ignored-extensions-regexp'."
(set sym val)
(if val
(setq muse-ignored-extensions-regexp
(concat "\\.\\("
(regexp-quote (or muse-file-extension "")) "\\|"
(mapconcat 'identity val "\\|")
"\\)\\'"))
(setq muse-ignored-extensions-regexp
(if muse-file-extension
(concat "\\.\\(" muse-file-extension "\\)\\'")
nil))))
(add-hook 'muse-update-values-hook
(lambda ()
(muse-update-ignored-extensions-regexp
'muse-ignored-extensions muse-ignored-extensions)))
(defcustom muse-ignored-extensions '("bz2" "gz" "[Zz]")
"A list of extensions to omit from the ending of a Muse page name.
These are regexps.
Don't put a period at the beginning of each extension unless you
understand that it is part of a regexp."
:type '(repeat (regexp :tag "Extension"))
:set 'muse-update-ignored-extensions-regexp
:group 'muse)
(defun muse-update-file-extension-after-init ()
;; This is short, but it has to be a function, otherwise Emacs21
;; does not load it properly when running after-init-hook
(unless (string= muse-file-extension "muse")
(let ((val muse-file-extension)
(muse-file-extension "muse"))
(muse-update-file-extension 'muse-file-extension val))))
;; Once the user's init file has been processed, determine whether
;; they want a file extension
(add-hook 'after-init-hook 'muse-update-file-extension-after-init)
;; URL protocols
(require 'muse-protocols)
;; Helper functions
(defsubst muse-delete-file-if-exists (file)
(when (file-exists-p file)
(delete-file file)
(message "Removed %s" file)))
(defsubst muse-time-less-p (t1 t2)
"Say whether time T1 is less than time T2."
(or (< (car t1) (car t2))
(and (= (car t1) (car t2))
(< (nth 1 t1) (nth 1 t2)))))
(eval-when-compile
(defvar muse-publishing-current-file nil))
(defun muse-current-file ()
"Return the name of the currently visited or published file."
(or (and (boundp 'muse-publishing-current-file)
muse-publishing-current-file)
(buffer-file-name)
(concat default-directory (buffer-name))))
(defun muse-page-name (&optional name)
"Return the canonical form of a Muse page name.
What this means is that the directory part of NAME is removed,
and the file extensions in `muse-ignored-extensions' are also
removed from NAME."
(save-match-data
(unless (and name (not (string= name "")))
(setq name (muse-current-file)))
(if name
(let ((page (file-name-nondirectory name)))
(if (and muse-ignored-extensions-regexp
(string-match muse-ignored-extensions-regexp page))
(replace-match "" t t page)
page)))))
(defun muse-display-warning (message)
"Display the given MESSAGE as a warning."
(if (fboundp 'display-warning)
(display-warning 'muse message
(if (featurep 'xemacs)
'warning
:warning))
(let ((buf (get-buffer-create "*Muse warnings*")))
(with-current-buffer buf
(goto-char (point-max))
(insert "Warning (muse): " message)
(unless (bolp)
(newline)))
(display-buffer buf)
(sit-for 0))))
(defun muse-eval-lisp (form)
"Evaluate the given form and return the result as a string."
(require 'pp)
(save-match-data
(condition-case err
(let ((object (eval (read form))))
(cond
((stringp object) object)
((and (listp object)
(not (eq object nil)))
(let ((string (pp-to-string object)))
(substring string 0 (1- (length string)))))
((numberp object)
(number-to-string object))
((eq object nil) "")
(t
(pp-to-string object))))
(error
(muse-display-warning (format "%s: Error evaluating %s: %s"
(muse-page-name) form err))
"; INVALID LISP CODE"))))
(defmacro muse-with-temp-buffer (&rest body)
"Create a temporary buffer, and evaluate BODY there like `progn'.
See also `with-temp-file' and `with-output-to-string'.
Unlike `with-temp-buffer', this will never attempt to save the
temp buffer. It is meant to be used along with
`insert-file-contents' or `muse-insert-file-contents'.
The undo feature will be disabled in the new buffer.
If `debug-on-error' is set to t, keep the buffer around for
debugging purposes rather than removing it."
(let ((temp-buffer (make-symbol "temp-buffer")))
`(let ((,temp-buffer (generate-new-buffer " *muse-temp*")))
(buffer-disable-undo ,temp-buffer)
(unwind-protect
(if debug-on-error
(with-current-buffer ,temp-buffer
,@body)
(condition-case err
(with-current-buffer ,temp-buffer
,@body)
(error
(if (and (boundp 'muse-batch-publishing-p)
muse-batch-publishing-p)
(progn
(message "%s: Error occured: %s"
(muse-page-name) err)
(backtrace))
(muse-display-warning
(format (concat "An error occurred while publishing"
" %s:\n %s\n\nSet debug-on-error to"
" `t' if you would like a backtrace.")
(muse-page-name) err))))))
(when (buffer-live-p ,temp-buffer)
(with-current-buffer ,temp-buffer
(set-buffer-modified-p nil))
(unless debug-on-error (kill-buffer ,temp-buffer)))))))
(put 'muse-with-temp-buffer 'lisp-indent-function 0)
(put 'muse-with-temp-buffer 'edebug-form-spec '(body))
(defun muse-insert-file-contents (filename &optional visit)
"Insert the contents of file FILENAME after point.
Do character code conversion and end-of-line conversion, but none
of the other unnecessary things like format decoding or
`find-file-hook'.
If VISIT is non-nil, the buffer's visited filename
and last save file modtime are set, and it is marked unmodified.
If visiting and the file does not exist, visiting is completed
before the error is signaled."
(let ((format-alist nil)
(after-insert-file-functions nil)
(inhibit-file-name-handlers
(append '(jka-compr-handler image-file-handler epa-file-handler)
inhibit-file-name-handlers))
(inhibit-file-name-operation 'insert-file-contents))
(insert-file-contents filename visit)))
(defun muse-write-file (filename &optional nomessage)
"Write current buffer into file FILENAME.
Unlike `write-file', this does not visit the file, try to back it
up, or interact with vc.el in any way.
If the file was not written successfully, return nil. Otherwise,
return non-nil.
If the NOMESSAGE argument is non-nil, suppress the \"Wrote file\"
message."
(when nomessage (setq nomessage 'nomessage))
(let ((backup-inhibited t)
(buffer-file-name filename)
(buffer-file-truename (file-truename filename)))
(save-current-buffer
(save-restriction
(widen)
(if (not (file-writable-p buffer-file-name))
(prog1 nil
(muse-display-warning
(format "Cannot write file %s:\n %s" buffer-file-name
(let ((dir (file-name-directory buffer-file-name)))
(if (not (file-directory-p dir))
(if (file-exists-p dir)
(format "%s is not a directory" dir)
(format "No directory named %s exists" dir))
(if (not (file-exists-p buffer-file-name))
(format "Directory %s write-protected" dir)
"File is write-protected"))))))
(let ((coding-system-for-write
(or (and (boundp 'save-buffer-coding-system)
save-buffer-coding-system)
coding-system-for-write)))
(write-region (point-min) (point-max) buffer-file-name
nil nomessage))
(when (boundp 'last-file-coding-system-used)
(when (boundp 'buffer-file-coding-system-explicit)
(setq buffer-file-coding-system-explicit
last-coding-system-used))
(if save-buffer-coding-system
(setq save-buffer-coding-system last-coding-system-used)
(setq buffer-file-coding-system last-coding-system-used)))
t)))))
(defun muse-collect-alist (list element &optional test)
"Collect items from LIST whose car is equal to ELEMENT.
If TEST is specified, use it to compare ELEMENT."
(unless test (setq test 'equal))
(let ((items nil))
(dolist (item list)
(when (funcall test element (car item))
(setq items (cons item items))))
items))
(defmacro muse-sort-with-closure (list predicate closure)
"Sort LIST, stably, comparing elements using PREDICATE.
Returns the sorted list. LIST is modified by side effects.
PREDICATE is called with two elements of list and CLOSURE.
PREDICATE should return non-nil if the first element should sort
before the second."
`(sort ,list (lambda (a b) (funcall ,predicate a b ,closure))))
(put 'muse-sort-with-closure 'lisp-indent-function 0)
(put 'muse-sort-with-closure 'edebug-form-spec '(form function-form form))
(defun muse-sort-by-rating (rated-list &optional test)
"Sort RATED-LIST according to the rating of each element.
The rating is stripped out in the returned list.
Default sorting is highest-first.
If TEST if specified, use it to sort the list. The default test is '>."
(unless test (setq test '>))
(mapcar (function cdr)
(muse-sort-with-closure
rated-list
(lambda (a b closure)
(let ((na (numberp (car a)))
(nb (numberp (car b))))
(cond ((and na nb) (funcall closure (car a) (car b)))
(na (not nb))
(t nil))))
test)))
(defun muse-escape-specials-in-string (specials string &optional reverse)
"Apply the transformations in SPECIALS to STRING.
The transforms should form a fully reversible and non-ambiguous
syntax when STRING is parsed from left to right.
If REVERSE is specified, reverse an already-escaped string."
(let ((rules (mapcar (lambda (rule)
(cons (regexp-quote (if reverse
(cdr rule)
(car rule)))
(if reverse (car rule) (cdr rule))))
specials)))
(save-match-data
(with-temp-buffer
(insert string)
(goto-char (point-min))
(while (not (eobp))
(unless (catch 'found
(dolist (rule rules)
(when (looking-at (car rule))
(replace-match (cdr rule) t t)
(throw 'found t))))
(forward-char)))
(buffer-string)))))
(defun muse-trim-whitespace (string)
"Return a version of STRING with no initial nor trailing whitespace."
(muse-replace-regexp-in-string
(concat "\\`[" muse-regexp-blank "]+\\|[" muse-regexp-blank "]+\\'")
"" string))
(defun muse-path-sans-extension (path)
"Return PATH sans final \"extension\".
The extension, in a file name, is the part that follows the last `.',
except that a leading `.', if any, doesn't count.
This differs from `file-name-sans-extension' in that it will
never modify the directory part of the path."
(concat (file-name-directory path)
(file-name-nondirectory (file-name-sans-extension path))))
;; The following code was extracted from cl
(defun muse-const-expr-p (x)
(cond ((consp x)
(or (eq (car x) 'quote)
(and (memq (car x) '(function function*))
(or (symbolp (nth 1 x))
(and (eq (and (consp (nth 1 x))
(car (nth 1 x))) 'lambda) 'func)))))
((symbolp x) (and (memq x '(nil t)) t))
(t t)))
(put 'muse-assertion-failed 'error-conditions '(error))
(put 'muse-assertion-failed 'error-message "Assertion failed")
(defun muse-list* (arg &rest rest)
"Return a new list with specified args as elements, cons'd to last arg.
Thus, `(list* A B C D)' is equivalent to `(nconc (list A B C) D)', or to
`(cons A (cons B (cons C D)))'."
(cond ((not rest) arg)
((not (cdr rest)) (cons arg (car rest)))
(t (let* ((n (length rest))
(copy (copy-sequence rest))
(last (nthcdr (- n 2) copy)))
(setcdr last (car (cdr last)))
(cons arg copy)))))
(defmacro muse-assert (form &optional show-args string &rest args)
"Verify that FORM returns non-nil; signal an error if not.
Second arg SHOW-ARGS means to include arguments of FORM in message.
Other args STRING and ARGS... are arguments to be passed to `error'.
They are not evaluated unless the assertion fails. If STRING is
omitted, a default message listing FORM itself is used."
(let ((sargs
(and show-args
(delq nil (mapcar
(function
(lambda (x)
(and (not (muse-const-expr-p x)) x)))
(cdr form))))))
(list 'progn
(list 'or form
(if string
(muse-list* 'error string (append sargs args))
(list 'signal '(quote muse-assertion-failed)
(muse-list* 'list (list 'quote form) sargs))))
nil)))
;; Compatibility functions
(if (fboundp 'looking-back)
(defalias 'muse-looking-back 'looking-back)
(defun muse-looking-back (regexp &optional limit &rest ignored)
(save-excursion
(re-search-backward (concat "\\(?:" regexp "\\)\\=") limit t))))
(eval-and-compile
(if (fboundp 'line-end-position)
(defalias 'muse-line-end-position 'line-end-position)
(defun muse-line-end-position (&optional n)
(save-excursion (end-of-line n) (point))))
(if (fboundp 'line-beginning-position)
(defalias 'muse-line-beginning-position 'line-beginning-position)
(defun muse-line-beginning-position (&optional n)
(save-excursion (beginning-of-line n) (point))))
(if (fboundp 'match-string-no-properties)
(defalias 'muse-match-string-no-properties 'match-string-no-properties)
(defun muse-match-string-no-properties (num &optional string)
(match-string num string))))
(defun muse-replace-regexp-in-string (regexp replacement text &optional fixedcase literal)
"Replace REGEXP with REPLACEMENT in TEXT.
Return a new string containing the replacements.
If fourth arg FIXEDCASE is non-nil, do not alter case of replacement text.
If fifth arg LITERAL is non-nil, insert REPLACEMENT literally."
(cond
((and (featurep 'xemacs) (fboundp 'replace-in-string))
(and (fboundp 'replace-in-string) ; stupid byte-compiler warning
(replace-in-string text regexp replacement literal)))
((fboundp 'replace-regexp-in-string)
(replace-regexp-in-string regexp replacement text fixedcase literal))
(t (error (concat "Neither `replace-in-string' nor "
"`replace-regexp-in-string' was found")))))
(if (fboundp 'add-to-invisibility-spec)
(defalias 'muse-add-to-invisibility-spec 'add-to-invisibility-spec)
(defun muse-add-to-invisibility-spec (element)
"Add ELEMENT to `buffer-invisibility-spec'.
See documentation for `buffer-invisibility-spec' for the kind of elements
that can be added."
(if (eq buffer-invisibility-spec t)
(setq buffer-invisibility-spec (list t)))
(setq buffer-invisibility-spec
(cons element buffer-invisibility-spec))))
(if (fboundp 'read-directory-name)
(defalias 'muse-read-directory-name 'read-directory-name)
(defun muse-read-directory-name (prompt &optional dir default-dirname mustmatch initial)
"Read directory name - see `read-file-name' for details."
(unless dir
(setq dir default-directory))
(read-file-name prompt dir (or default-dirname
(if initial (expand-file-name initial dir)
dir))
mustmatch initial)))
(defun muse-file-remote-p (file)
"Test whether FILE specifies a location on a remote system.
Return non-nil if the location is indeed remote.
For example, the filename \"/user@host:/foo\" specifies a location
on the system \"/user@host:\"."
(cond ((fboundp 'file-remote-p)
(file-remote-p file))
((fboundp 'tramp-handle-file-remote-p)
(tramp-handle-file-remote-p file))
((and (boundp 'ange-ftp-name-format)
(string-match (car ange-ftp-name-format) file))
t)
(t nil)))
(if (fboundp 'delete-and-extract-region)
(defalias 'muse-delete-and-extract-region 'delete-and-extract-region)
(defun muse-delete-and-extract-region (start end)
"Delete the text between START and END and return it."
(prog1 (buffer-substring start end)
(delete-region start end))))
(if (fboundp 'delete-dups)
(defalias 'muse-delete-dups 'delete-dups)
(defun muse-delete-dups (list)
"Destructively remove `equal' duplicates from LIST.
Store the result in LIST and return it. LIST must be a proper list.
Of several `equal' occurrences of an element in LIST, the first
one is kept."
(let ((tail list))
(while tail
(setcdr tail (delete (car tail) (cdr tail)))
(setq tail (cdr tail))))
list))
;; Set face globally in a predictable fashion
(defun muse-copy-face (old new)
"Copy face OLD to NEW."
(if (featurep 'xemacs)
(copy-face old new 'all)
(copy-face old new)))
;; Widget compatibility functions
(defun muse-widget-type-value-create (widget)
"Convert and instantiate the value of the :type attribute of WIDGET.
Store the newly created widget in the :children attribute.
The value of the :type attribute should be an unconverted widget type."
(let ((value (widget-get widget :value))
(type (widget-get widget :type)))
(widget-put widget :children
(list (widget-create-child-value widget
(widget-convert type)
value)))))
(defun muse-widget-child-value-get (widget)
"Get the value of the first member of :children in WIDGET."
(widget-value (car (widget-get widget :children))))
(defun muse-widget-type-match (widget value)
"Non-nil if the :type value of WIDGET matches VALUE.
The value of the :type attribute should be an unconverted widget type."
(widget-apply (widget-convert (widget-get widget :type)) :match value))
;; Link-handling functions and variables
(defun muse-get-link (&optional target)
"Based on the match data, retrieve the link.
Use TARGET to get the string, if it is specified."
(muse-match-string-no-properties 1 target))
(defun muse-get-link-desc (&optional target)
"Based on the match data, retrieve the link description.
Use TARGET to get the string, if it is specified."
(muse-match-string-no-properties 2 target))
(defvar muse-link-specials
'(("[" . "%5B")
("]" . "%5D")
("%" . "%%"))
"Syntax used for escaping and unescaping links.
This allows brackets to occur in explicit links as long as you
use the standard Muse functions to create them.")
(defun muse-link-escape (text)
"Escape characters in TEXT that conflict with the explicit link
regexp."
(when (stringp text)
(muse-escape-specials-in-string muse-link-specials text)))
(defun muse-link-unescape (text)
"Un-escape characters in TEXT that conflict with the explicit
link regexp."
(when (stringp text)
(muse-escape-specials-in-string muse-link-specials text t)))
(defun muse-handle-url (&optional string)
"If STRING or point has a URL, match and return it."
(if (if string (string-match muse-url-regexp string)
(looking-at muse-url-regexp))
(match-string 0 string)))
(defcustom muse-implicit-link-functions '(muse-handle-url)
"A list of functions to handle an implicit link.
An implicit link is one that is not surrounded by brackets.
By default, Muse handles URLs only.
If you want to handle WikiWords, load muse-wiki.el."
:type 'hook
:options '(muse-handle-url)
:group 'muse)
(defun muse-handle-implicit-link (&optional link)
"Handle implicit links. If LINK is not specified, look at point.
An implicit link is one that is not surrounded by brackets.
By default, Muse handles URLs only.
If you want to handle WikiWords, load muse-wiki.el.
This function modifies the match data so that match 0 is the
link.
The match data is restored after each unsuccessful handler
function call. If LINK is specified, only restore at very end.
This behavior is needed because the part of the buffer that
`muse-implicit-link-regexp' matches must be narrowed to the part
that is an accepted link."
(let ((funcs muse-implicit-link-functions)
(res nil)
(data (match-data t)))
(while funcs
(setq res (funcall (car funcs) link))
(if res
(setq funcs nil)
(unless link (set-match-data data))
(setq funcs (cdr funcs))))
(when link (set-match-data data))
res))
(defcustom muse-explicit-link-functions nil
"A list of functions to handle an explicit link.
An explicit link is one [[like][this]] or [[this]]."
:type 'hook
:group 'muse)
(defun muse-handle-explicit-link (&optional link)
"Handle explicit links. If LINK is not specified, look at point.
An explicit link is one that looks [[like][this]] or [[this]].
The match data is preserved. If no handlers are able to process
LINK, return LINK (if specified) or the 1st match string. If
LINK is not specified, it is assumed that Muse has matched
against `muse-explicit-link-regexp' before calling this
function."
(let ((funcs muse-explicit-link-functions)
(res nil))
(save-match-data
(while funcs
(setq res (funcall (car funcs) link))
(if res
(setq funcs nil)
(setq funcs (cdr funcs)))))
(muse-link-unescape
(if res
res
(or link (muse-get-link))))))
;; Movement functions
(defun muse-list-item-type (str)
"Determine the type of list given STR.
Returns either 'ul, 'ol, 'dl-term, 'dl-entry, or nil."
(save-match-data
(cond ((or (string= str "")
(< (length str) 2))
nil)
((string-match muse-dl-entry-regexp str)
'dl-entry)
((string-match muse-dl-term-regexp str)
'dl-term)
((string-match muse-ol-item-regexp str)
'ol)
((string-match muse-ul-item-regexp str)
'ul)
(t nil))))
(defun muse-list-item-critical-point (&optional offset)
"Figure out where the important markup character for the
currently-matched list item is.
If OFFSET is specified, it is the number of groupings outside of
the contents of `muse-list-item-regexp'."
(unless offset (setq offset 0))
(if (match-end (+ offset 2))
;; at a definition list
(match-end (+ offset 2))
;; at a different kind of list
(match-beginning (+ offset 1))))
(defun muse-forward-paragraph (&optional pattern)
"Move forward safely by one paragraph, or according to PATTERN."
(when (get-text-property (point) 'muse-end-list)
(goto-char (next-single-property-change (point) 'muse-end-list)))
(setq pattern (if pattern
(concat "^\\(?:" pattern "\\|\n\\|\\'\\)")
"^\\s-*\\(\n\\|\\'\\)"))
(let ((next-list-end (or (next-single-property-change (point) 'muse-end-list)
(point-max))))
(forward-line 1)
(if (re-search-forward pattern nil t)
(goto-char (match-beginning 0))
(goto-char (point-max)))
(when (> (point) next-list-end)
(goto-char next-list-end))))
(defun muse-forward-list-item-1 (type empty-line indented-line)
"Determine whether a nested list item is after point."
(if (match-beginning 1)
;; if we are given a dl entry, skip past everything on the same
;; level, except for other dl entries
(and (eq type 'dl-entry)
(not (eq (char-after (match-beginning 2)) ?\:)))
;; blank line encountered with no list item on the same
;; level after it
(let ((beg (point)))
(forward-line 1)
(if (save-match-data
(and (looking-at indented-line)
(not (looking-at empty-line))))
;; found that this blank line is followed by some
;; indentation, plus other text, so we'll keep
;; going
t
(goto-char beg)
nil))))
(defun muse-forward-list-item (type indent &optional no-skip-nested)
"Move forward to the next item of TYPE.
Return non-nil if successful, nil otherwise.
The beginning indentation is given by INDENT.
If NO-SKIP-NESTED is non-nil, do not skip past nested items.
Note that if you desire this behavior, you will also need to
provide a very liberal INDENT value, such as
\(concat \"[\" muse-regexp-blank \"]*\")."
(let* ((list-item (format muse-list-item-regexp indent))
(empty-line (concat "^[" muse-regexp-blank "]*\n"))
(indented-line (concat "^" indent "[" muse-regexp-blank "]"))
(list-pattern (concat "\\(?:" empty-line "\\)?"
"\\(" list-item "\\)")))
(while (progn
(muse-forward-paragraph list-pattern)
;; make sure we don't go past boundary
(and (not (or (get-text-property (point) 'muse-end-list)
(>= (point) (point-max))))
;; move past markup that is part of another construct
(or (and (match-beginning 1)
(or (get-text-property
(muse-list-item-critical-point 1) 'muse-link)
(and (derived-mode-p 'muse-mode)
(get-text-property
(muse-list-item-critical-point 1)
'face))))
;; skip nested items
(and (not no-skip-nested)
(muse-forward-list-item-1 type empty-line
indented-line))))))
(cond ((or (get-text-property (point) 'muse-end-list)
(>= (point) (point-max)))
;; at a list boundary, so stop
nil)
((let ((str (when (match-beginning 2)
;; get the entire line
(save-excursion
(goto-char (match-beginning 2))
(buffer-substring (muse-line-beginning-position)
(muse-line-end-position))))))
(and str (eq type (muse-list-item-type str))))
;; same type, so indicate that there are more items to be
;; parsed
(goto-char (match-beginning 1)))
(t
(when (match-beginning 1)
(goto-char (match-beginning 1)))
;; move to just before foreign list item markup
nil))))
(defun muse-goto-tag-end (tag nested)
"Move forward past the end of TAG.
If NESTED is non-nil, look for other instances of this tag that
may be nested inside of this tag, and skip past them."
(if (not nested)
(search-forward (concat "</" tag ">") nil t)
(let ((nesting 1)
(tag-regexp (concat "\\(<\\(/?\\)" tag "\\([ >]\\)\\)"))
(match-found nil))
(while (and (> nesting 0)
(setq match-found (re-search-forward tag-regexp nil t)))
;; for the sake of font-locking code, skip matches in comments
(unless (get-text-property (match-beginning 0) 'muse-comment)
(if (string-equal (match-string 2) "/")
(and (string-equal (match-string 3) ">")
(setq nesting (1- nesting)))
(setq nesting (1+ nesting)))))
match-found)))
;;; muse.el ends here