From 82b8ca280905ea284730f228ae082c42c348e818 Mon Sep 17 00:00:00 2001 From: Tom Willemsen Date: Thu, 17 Mar 2011 11:36:56 +0100 Subject: Big emacs cleanup, must be lighter --- emacs.d/elisp/muse/muse-publish.el | 2193 ------------------------------------ 1 file changed, 2193 deletions(-) delete mode 100644 emacs.d/elisp/muse/muse-publish.el (limited to 'emacs.d/elisp/muse/muse-publish.el') diff --git a/emacs.d/elisp/muse/muse-publish.el b/emacs.d/elisp/muse/muse-publish.el deleted file mode 100644 index ec6e176..0000000 --- a/emacs.d/elisp/muse/muse-publish.el +++ /dev/null @@ -1,2193 +0,0 @@ -;;; muse-publish.el --- base publishing implementation - -;; 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: - -;; Yann Hodique (yann DOT hodique AT gmail DOT com) fixed an -;; unnecessary URL description transform in `muse-publish-url'. - -;; Peter K. Lee (saint AT corenova DOT com) provided the -;; `muse-style-elements-list' function. - -;; Jim Ottaway (j DOT ottaway AT lse DOT ac DOT uk) provided a -;; reference implementation for nested lists, as well as some code for -;; the "style" element of the tag. - -;; Deus Max (deusmax AT gmail DOT com) provided the tag. - -;;; Code: - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; -;; Muse Publishing -;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(provide 'muse-publish) - -(require 'muse) -(require 'muse-regexps) - -(defgroup muse-publish nil - "Options controlling the general behavior of Muse publishing." - :group 'muse) - -(defcustom muse-before-publish-hook nil - "A hook run in the buffer to be published, before it is done." - :type 'hook - :group 'muse-publish) - -(defcustom muse-after-publish-hook nil - "A hook run in the buffer to be published, after it is done." - :type 'hook - :group 'muse-publish) - -(defcustom muse-publish-url-transforms - '(muse-resolve-url) - "A list of functions used to prepare URLs for publication. -Each is passed the URL. The transformed URL should be returned." - :type 'hook - :options '(muse-resolve-url) - :group 'muse-publish) - -(defcustom muse-publish-desc-transforms - '(muse-publish-strip-URL) - "A list of functions used to prepare URL desciptions for publication. -Each is passed the description. The modified description should -be returned." - :type 'hook - :options '(muse-publish-strip-URL) - :group 'muse-publish) - -(defcustom muse-publish-date-format "%B %e, %Y" - "Format string for the date, used by `muse-publish-markup-buffer'. -See `format-time-string' for details on the format options." - :type 'string - :group 'muse-publish) - -(defcustom muse-publish-comments-p nil - "If nil, remove comments before publishing. -If non-nil, publish comments using the markup of the current style." - :type 'boolean - :group 'muse-publish) - -(defcustom muse-publish-report-threshhold 100000 - "If a file is this size or larger, report publishing progress." - :type 'integer - :group 'muse-publish) - -(defcustom muse-publish-markup-regexps - `(;; Remove leading and trailing whitespace from the file - (1000 "\\(\\`\n+\\|\n+\\'\\)" 0 "") - - ;; Remove trailing whitespace from all lines - (1100 ,(concat "[" muse-regexp-blank "]+$") 0 "") - - ;; Handle any leading #directives - (1200 "\\`#\\([a-zA-Z-]+\\)\\s-+\\(.+\\)\n+" 0 directive) - - ;; commented lines - (1250 ,(concat "^;\\(?:[" muse-regexp-blank "]+\\(.+\\)\\|$\\|'\\)") - 0 comment) - - ;; markup tags - (1300 muse-tag-regexp 0 tag) - - ;; prevent emphasis characters in explicit links from being marked - (1400 muse-explicit-link-regexp 0 muse-publish-mark-link) - - ;; emphasized or literal text - (1600 ,(concat "\\(^\\|[-[" muse-regexp-blank - "<('`\"\n]\\)\\(=[^=" muse-regexp-blank - "\n]\\|_[^_" muse-regexp-blank - "\n]\\|\\*+[^*" muse-regexp-blank - "\n]\\)") - 2 word) - - ;; headings, outline-mode style - (1700 "^\\(\\*+\\)\\s-+" 0 heading) - - ;; ellipses - (1800 "\\.\\.\\.\\." 0 enddots) - (1850 "\\.\\.\\." 0 dots) - - ;; horizontal rule, or section separator - (1900 "^----+" 0 rule) - - ;; non-breaking space - (1950 "~~" 0 no-break-space) - - ;; beginning of footnotes section - (2000 "^Footnotes:?\\s-*" 0 fn-sep) - ;; footnote definition/reference (def if at beginning of line) - (2100 "\\[\\([1-9][0-9]*\\)\\]" 0 footnote) - - ;; unnumbered List items begin with a -. numbered list items - ;; begin with number and a period. definition lists have a - ;; leading term separated from the body with ::. centered - ;; paragraphs begin with at least six columns of whitespace; any - ;; other whitespace at the beginning indicates a blockquote. The - ;; reason all of these rules are handled here, is so that - ;; blockquote detection doesn't interfere with indented list - ;; members. - (2200 ,(format muse-list-item-regexp (concat "[" muse-regexp-blank "]*")) - 0 list) - - ;; support table.el style tables - (2300 ,(concat "^" muse-table-el-border-regexp "\n" - "\\(\\(" muse-table-el-line-regexp "\n\\)+" - "\\(" muse-table-el-border-regexp "\\)" - "\\(\n\\|\\'\\)\\)+") - 0 table-el) - - ;; simple table markup is supported, nothing fancy. use | to - ;; separate cells, || to separate header cells, and ||| for footer - ;; cells - (2350 ,(concat "\\(\\([" muse-regexp-blank "]*\n\\)?" - "\\(\\(?:" muse-table-line-regexp "\\|" - muse-table-hline-regexp "\\)\\(?:\n\\|\\'\\)\\)\\)+") - 0 table) - - ;; blockquote and centered text - (2400 ,(concat "^\\([" muse-regexp-blank "]+\\).+") 0 quote) - - ;; the emdash ("--" or "---") - (2500 ,(concat "\\(^\\|[" muse-regexp-blank "]*\\)---?\\($\\|[" - muse-regexp-blank "]*\\)") - 0 emdash) - - ;; "verse" text is indicated the same way as a quoted e-mail - ;; response: "> text", where text may contain initial whitespace - ;; (see below). - (2600 ,(concat "^[" muse-regexp-blank "]*> ") 0 verse) - - ;; define anchor points - (2700 "^\\(\\W*\\)#\\(\\S-+\\)\\s-*" 0 anchor) - - ;; replace links in the buffer (links to other pages) - (2900 muse-explicit-link-regexp 0 link) - - ;; bare URLs - (3000 muse-url-regexp 0 url) - - ;; bare email addresses - (3500 - "\\([^[]\\)[-a-zA-Z0-9._]+@\\([-a-zA-z0-9_]+\\.\\)+[a-zA-Z0-9]+" 0 email) - ) - "List of markup rules for publishing a page with Muse. -The rules given in this variable are invoked first, followed by -whatever rules are specified by the current style. - -Each member of the list is either a function, or a list of the form: - - (REGEXP/SYMBOL TEXT-BEGIN-GROUP REPLACEMENT-TEXT/FUNCTION/SYMBOL) - -REGEXP is a regular expression, or symbol whose value is a regular -expression, which is searched for using `re-search-forward'. -TEXT-BEGIN-GROUP is the matching group within that regexp which -denotes the beginning of the actual text to be marked up. -REPLACEMENT-TEXT is a string that will be passed to `replace-match'. -If it is not a string, but a function, it will be called to determine -what the replacement text should be (it must return a string). If it -is a symbol, the value of that symbol should be a string. - -The replacements are done in order, one rule at a time. Writing -the regular expressions can be a tricky business. Note that case -is never ignored. `case-fold-search' is always bound to nil -while processing the markup rules." - :type '(repeat (choice - (list :tag "Markup rule" - integer - (choice regexp symbol) - integer - (choice string function symbol)) - function)) - :group 'muse-publish) - -(defcustom muse-publish-markup-functions - '((directive . muse-publish-markup-directive) - (comment . muse-publish-markup-comment) - (anchor . muse-publish-markup-anchor) - (tag . muse-publish-markup-tag) - (word . muse-publish-markup-word) - (emdash . muse-publish-markup-emdash) - (enddots . muse-publish-markup-enddots) - (dots . muse-publish-markup-dots) - (rule . muse-publish-markup-rule) - (no-break-space . muse-publish-markup-no-break-space) - (heading . muse-publish-markup-heading) - (footnote . muse-publish-markup-footnote) - (fn-sep . muse-publish-markup-fn-sep) - (list . muse-publish-markup-list) - (quote . muse-publish-markup-quote) - (verse . muse-publish-markup-verse) - (table . muse-publish-markup-table) - (table-el . muse-publish-markup-table-el) - (email . muse-publish-markup-email) - (link . muse-publish-markup-link) - (url . muse-publish-markup-url)) - "An alist of style types to custom functions for that kind of text. - -Each member of the list is of the form: - - (SYMBOL FUNCTION) - -SYMBOL describes the type of text to associate with this rule. -`muse-publish-markup-regexps' maps regexps to these symbols. - -FUNCTION is the function to use to mark up this kind of rule if -no suitable function is found through the :functions tag of the -current style." - :type '(alist :key-type symbol :value-type function) - :group 'muse-publish) - -(defcustom muse-publish-markup-tags - '(("contents" nil t nil muse-publish-contents-tag) - ("verse" t nil nil muse-publish-verse-tag) - ("example" t nil nil muse-publish-example-tag) - ("src" t t nil muse-publish-src-tag) - ("code" t nil nil muse-publish-code-tag) - ("quote" t nil t muse-publish-quote-tag) - ("literal" t t nil muse-publish-literal-tag) - ("verbatim" t nil nil muse-publish-verbatim-tag) - ("br" nil nil nil muse-publish-br-tag) - ("lisp" t t nil muse-publish-lisp-tag) - ("class" t t nil muse-publish-class-tag) - ("div" t t nil muse-publish-div-tag) - ("command" t t nil muse-publish-command-tag) - ("perl" t t nil muse-publish-perl-tag) - ("php" t t nil muse-publish-php-tag) - ("python" t t nil muse-publish-python-tag) - ("ruby" t t nil muse-publish-ruby-tag) - ("comment" t nil nil muse-publish-comment-tag) - ("include" nil t nil muse-publish-include-tag) - ("markup" t t nil muse-publish-mark-up-tag) - ("cite" t t nil muse-publish-cite-tag)) - "A list of tag specifications, for specially marking up text. -XML-style tags are the best way to add custom markup to Muse. -This is easily accomplished by customizing this list of markup tags. - -For each entry, the name of the tag is given, whether it expects -a closing tag, whether it takes an optional set of attributes, -whether it is nestable, and a function that performs whatever -action is desired within the delimited region. - -The tags themselves are deleted during publishing, before the -function is called. The function is called with three arguments, -the beginning and end of the region surrounded by the tags. If -properties are allowed, they are passed as a third argument in -the form of an alist. The `end' argument to the function is -always a marker. - -Point is always at the beginning of the region within the tags, when -the function is called. Wherever point is when the function finishes -is where tag markup will resume. - -These tag rules are processed once at the beginning of markup, and -once at the end, to catch any tags which may have been inserted -in-between." - :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-publish) - -(defcustom muse-publish-markup-header-footer-tags - '(("lisp" t t nil muse-publish-lisp-tag) - ("markup" t t nil muse-publish-mark-up-tag)) - "Tags used when publishing headers and footers. -See `muse-publish-markup-tags' for details." - :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-publish) - -(defcustom muse-publish-markup-specials nil - "A table of characters which must be represented specially." - :type '(alist :key-type character :value-type string) - :group 'muse-publish) - -(defcustom muse-publish-enable-local-variables nil - "If non-nil, interpret local variables in a file when publishing." - :type 'boolean - :group 'muse-publish) - -(defcustom muse-publish-enable-dangerous-tags t - "If non-nil, publish tags like and that can -call external programs or expose sensitive information. -Otherwise, ignore tags like this. - -This is useful to set to nil when the file to publish is coming -from an untrusted source." - :type 'boolean - :group 'muse-publish) - -(defvar muse-publishing-p nil - "This is set to t while a page is being published.") -(defvar muse-batch-publishing-p nil - "This is set to t while a page is being batch published.") -(defvar muse-inhibit-before-publish-hook nil - "This is set to t when publishing a file rather than just a buffer. -It is used by `muse-publish-markup-buffer'.") -(defvar muse-publishing-styles nil - "The publishing styles that Muse recognizes. -This is automatically generated when loading publishing styles.") -(defvar muse-publishing-current-file nil - "The file that is currently being published.") -(defvar muse-publishing-current-output-path nil - "The path where the current file will be published to.") -(defvar muse-publishing-current-style nil - "The style of the file that is currently being published.") -(defvar muse-publishing-directives nil - "An alist of publishing directives from the top of a file.") -(defvar muse-publish-generate-contents nil - "Non-nil if a table of contents should be generated. -If non-nil, it is a cons cell specifying (MARKER . DEPTH), to -tell where the was seen, and to what depth the -contents were requested.") -(defvar muse-publishing-last-position nil - "Last position of the point when publishing. -This is used to make sure that publishing doesn't get stalled.") - -(defvar muse-publish-inhibit-style-hooks nil - "If non-nil, do not call the :before or :before-end hooks when publishing.") - -(defvar muse-publish-use-header-footer-tags nil - "If non-nil, use `muse-publish-markup-header-footer-tags' for looking up -tags. Otherwise, use `muse-publish-markup-tags'.") - -(defvar muse-inhibit-style-tags nil - "If non-nil, do not search for style-specific tags. -This is used when publishing headers and footers.") - -;; Functions for handling style information - -(defsubst muse-style (&optional style) - "Resolve the given STYLE into a Muse style, if it is a string." - (if (null style) - muse-publishing-current-style - (if (stringp style) - (assoc style muse-publishing-styles) - (muse-assert (consp style)) - style))) - -(defun muse-define-style (name &rest elements) - (let ((entry (assoc name muse-publishing-styles))) - (if entry - (setcdr entry elements) - (setq muse-publishing-styles - (cons (append (list name) elements) - muse-publishing-styles))))) - -(defun muse-derive-style (new-name base-name &rest elements) - (apply 'muse-define-style new-name - (append elements (list :base base-name)))) - -(defsubst muse-get-keyword (keyword list &optional direct) - (let ((value (cadr (memq keyword list)))) - (if (and (not direct) (symbolp value)) - (symbol-value value) - value))) - -(defun muse-style-elements-list (elem &optional style) - "Return a list all references to ELEM in STYLE, including base styles. -If STYLE is not specified, use current style." - (let (base elements) - (while style - (setq style (muse-style style)) - (setq elements (append elements - (muse-get-keyword elem style))) - (setq style (muse-get-keyword :base style))) - elements)) - -(defun muse-style-element (elem &optional style direct) - "Search for ELEM in STYLE, including base styles. -If STYLE is not specified, use current style." - (setq style (muse-style style)) - (let ((value (muse-get-keyword elem style direct))) - (if value - value - (let ((base (muse-get-keyword :base style))) - (if base - (muse-style-element elem base direct)))))) - -(defun muse-style-derived-p-1 (base style) - "Internal function used by `muse-style-derived-p'." - (if (and (stringp style) - (string= style base)) - t - (setq style (muse-style style)) - (let ((value (muse-get-keyword :base style))) - (when value - (muse-style-derived-p base value))))) - -(defun muse-style-derived-p (base &optional style) - "Return non-nil if STYLE is equal to or derived from BASE, -non-nil otherwise. - -BASE should be a string." - (unless style - (setq style (muse-style))) - (when (and (consp style) - (stringp (car style))) - (setq style (car style))) - (muse-style-derived-p-1 base style)) - -(defun muse-find-markup-element (keyword ident style) - (let ((def (assq ident (muse-style-element keyword style)))) - (if def - (cdr def) - (let ((base (muse-style-element :base style))) - (if base - (muse-find-markup-element keyword ident base)))))) - -(defun muse-markup-text (ident &rest args) - "Insert ARGS into the text markup associated with IDENT. -If the markup text has sections like %N%, this will be replaced -with the N-1th argument in ARGS. After that, `format' is applied -to the text with ARGS as parameters." - (let ((text (muse-find-markup-element :strings ident (muse-style)))) - (if (and text args) - (progn - (let (start repl-text) - (while (setq start (string-match "%\\([1-9][0-9]*\\)%" text start)) - ;; escape '%' in the argument text, since we will be - ;; using format on it - (setq repl-text (muse-replace-regexp-in-string - "%" "%%" - (nth (1- (string-to-number - (match-string 1 text))) args) - t t) - start (+ start (length repl-text)) - text (replace-match repl-text t t text)))) - (apply 'format text args)) - (or text "")))) - -(defun muse-insert-markup (&rest args) - (let ((beg (point))) - (apply 'insert args) - (muse-publish-mark-read-only beg (point)))) - -(defun muse-find-markup-tag (keyword tagname style) - (let ((def (assoc tagname (muse-style-element keyword style)))) - (or def - (let ((base (muse-style-element :base style))) - (if base - (muse-find-markup-tag keyword tagname base)))))) - -(defun muse-markup-tag-info (tagname &rest args) - (let ((tag-info (and (not muse-inhibit-style-tags) - (muse-find-markup-tag :tags tagname (muse-style))))) - (or tag-info - (assoc tagname - (if muse-publish-use-header-footer-tags - muse-publish-markup-header-footer-tags - muse-publish-markup-tags))))) - -(defsubst muse-markup-function (category) - (let ((func (muse-find-markup-element :functions category (muse-style)))) - (or func - (cdr (assq category muse-publish-markup-functions))))) - -;; Publishing routines - -(defun muse-publish-markup (name rules) - (let* ((case-fold-search nil) - (inhibit-read-only t) - (limit (* (length rules) (point-max))) - (verbose (and muse-publish-report-threshhold - (> (point-max) muse-publish-report-threshhold))) - (base 0)) - (while rules - (goto-char (point-min)) - (let ((regexp (nth 1 (car rules))) - (group (nth 2 (car rules))) - (repl (nth 3 (car rules))) - pos) - (setq muse-publishing-last-position nil) - (if (symbolp regexp) - (setq regexp (symbol-value regexp))) - (if (and verbose (not muse-batch-publishing-p)) - (message "Publishing %s...%d%%" name - (* (/ (float (+ (point) base)) limit) 100))) - (while (and regexp (progn - (when (and (get-text-property (point) 'read-only) - (> (point) (point-min))) - (goto-char (or (next-single-property-change - (point) 'read-only) - (point-max)))) - (setq pos (re-search-forward regexp nil t)))) - (if (and verbose (not muse-batch-publishing-p)) - (message "Publishing %s...%d%%" name - (* (/ (float (+ (point) base)) limit) 100))) - (unless (and (> (- (match-end 0) (match-beginning 0)) 0) - (match-beginning group) - (get-text-property (match-beginning group) 'read-only)) - (let* (func - (text (cond - ((and (symbolp repl) - (setq func (muse-markup-function repl))) - (funcall func)) - ((functionp repl) - (funcall repl)) - ((symbolp repl) - (symbol-value repl)) - (t repl)))) - (if (stringp text) - (replace-match text t)))) - (if (and muse-publishing-last-position - (= pos muse-publishing-last-position)) - (if (eobp) - (setq regexp nil) - (forward-char 1))) - (setq muse-publishing-last-position pos))) - (setq rules (cdr rules) - base (+ base (point-max)))) - (if (and verbose (not muse-batch-publishing-p)) - (message "Publishing %s...done" name)))) - -(defun muse-insert-file-or-string (file-or-string &optional title) - (let ((beg (point)) end) - (if (and (not (string-equal file-or-string "")) - (not (string-match "\n" file-or-string)) - (file-readable-p file-or-string)) - (setq end (+ beg - (cadr (muse-insert-file-contents file-or-string)))) - (insert file-or-string) - (setq end (point))) - (save-restriction - (narrow-to-region beg end) - (remove-text-properties (point-min) (point-max) - '(read-only nil rear-nonsticky nil)) - (goto-char (point-min)) - (let ((muse-inhibit-style-tags t) - (muse-publish-use-header-footer-tags t)) - (muse-publish-markup (or title "") - '((100 muse-tag-regexp 0 - muse-publish-markup-tag))))))) - -(defun muse-style-run-hooks (keyword style &rest args) - (catch 'handled - (let ((cache nil)) - (while (and style - (setq style (muse-style style))) - (let ((func (muse-style-element keyword style t))) - (when (and func - (not (member func cache))) - (setq cache (cons func cache)) - (when (apply func args) - (throw 'handled t)))) - (setq style (muse-style-element :base style)))))) - -(defun muse-publish-markup-region (beg end &optional title style) - "Apply the given STYLE's markup rules to the given region. -TITLE is used when indicating the publishing progress; it may be nil. - -The point is guaranteed to be at END if the routine terminates -normally." - (unless title (setq title "")) - (unless style - (or (setq style muse-publishing-current-style) - (error "Cannot find any publishing styles to use"))) - (save-restriction - (narrow-to-region beg end) - (let ((muse-publish-generate-contents nil)) - (unless muse-publish-inhibit-style-hooks - (muse-style-run-hooks :before style)) - (muse-publish-markup - title - (sort (copy-alist (append muse-publish-markup-regexps - (muse-style-elements-list :regexps style))) - (function - (lambda (l r) - (< (car l) (car r)))))) - (unless muse-publish-inhibit-style-hooks - (muse-style-run-hooks :before-end style)) - (muse-publish-escape-specials (point-min) (point-max) nil 'document)) - (goto-char (point-max)))) - -(defun muse-publish-markup-buffer (title style) - "Apply the given STYLE's markup rules to the current buffer." - (setq style (muse-style style)) - (let ((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" title) - (cons "author" (user-full-name)) - (cons "date" (format-time-string - muse-publish-date-format - (if muse-publishing-current-file - (nth 5 (file-attributes - muse-publishing-current-file)) - (current-time)))))) - (muse-publishing-p t) - (inhibit-read-only t)) - (run-hooks 'muse-update-values-hook) - (unless muse-inhibit-before-publish-hook - (run-hooks 'muse-before-publish-hook)) - (muse-publish-markup-region (point-min) (point-max) title style) - (goto-char (point-min)) - (when style-header - (muse-insert-file-or-string style-header title)) - (goto-char (point-max)) - (when style-footer - (muse-insert-file-or-string style-footer title)) - (muse-style-run-hooks :after style) - (run-hooks 'muse-after-publish-hook))) - -(defun muse-publish-markup-string (string &optional style) - "Markup STRING using the given STYLE's markup rules." - (setq style (muse-style style)) - (muse-with-temp-buffer - (insert string) - (let ((muse-publishing-current-style style) - (muse-publishing-p t)) - (muse-publish-markup "*string*" (muse-style-element :rules style))) - (buffer-string))) - -;; Commands for publishing files - -(defun muse-publish-get-style (&optional styles) - (unless styles (setq styles muse-publishing-styles)) - (if (= 1 (length styles)) - (car styles) - (when (catch 'different - (let ((first (car (car styles)))) - (dolist (style (cdr styles)) - (unless (equal first (car style)) - (throw 'different t))))) - (setq styles (muse-collect-alist - styles - (funcall muse-completing-read-function - "Publish with style: " styles nil t)))) - (if (or (= 1 (length styles)) - (not (muse-get-keyword :path (car styles)))) - (car styles) - (setq styles (mapcar (lambda (style) - (cons (muse-get-keyword :path style) - style)) - styles)) - (cdr (assoc (funcall muse-completing-read-function - "Publish to directory: " styles nil t) - styles))))) - -(defsubst muse-publish-get-output-dir (style) - (let ((default-directory (or (muse-style-element :path style) - default-directory))) - (muse-read-directory-name "Publish to directory: " nil default-directory))) - -(defsubst muse-publish-get-info () - (let ((style (muse-publish-get-style))) - (list style (muse-publish-get-output-dir style) - current-prefix-arg))) - -(defsubst muse-publish-output-name (&optional file style) - (setq style (muse-style style)) - (concat (muse-style-element :prefix style) - (muse-page-name file) - (muse-style-element :suffix style))) - -(defsubst muse-publish-output-file (file &optional output-dir style) - (setq style (muse-style style)) - (if output-dir - (expand-file-name (muse-publish-output-name file style) output-dir) - (concat (file-name-directory file) - (muse-publish-output-name file style)))) - -(defsubst muse-publish-link-name (&optional file style) - "Take FILE and add :prefix and either :link-suffix or :suffix from STYLE. -We assume that FILE is a Muse file. - -We call `muse-page-name' on FILE to remove the directory part of -FILE and any extensions that are in `muse-ignored-extensions'." - (setq style (muse-style style)) - (concat (muse-style-element :prefix style) - (muse-page-name file) - (or (muse-style-element :link-suffix style) - (muse-style-element :suffix style)))) - -(defsubst muse-publish-link-file (file &optional style) - "Turn FILE into a URL. - -If FILE exists on the system as-is, return it without -modification. In the case of wanting to link to Muse files when -`muse-file-extension' is nil, you should load muse-project.el. - -Otherwise, assume that it is a Muse file and call -`muse-publish-link-name' to add :prefix, :link-suffix, :suffix, -and removing ignored file extensions, but preserving the -directory part of FILE." - (setq style (muse-style style)) - (if (file-exists-p file) - file - (concat (file-name-directory file) - (muse-publish-link-name file style)))) - -(defsubst muse-publish-link-page (page) - "Turn PAGE into a URL. - -This is called by `muse-publish-classify-url' to figure out what -a link to another file or Muse page should look like. - -If muse-project.el is loaded, call `muse-project-link-page' for this. -Otherwise, call `muse-publish-link-file'." - (if (fboundp 'muse-project-link-page) - (muse-project-link-page page) - (muse-publish-link-file page))) - -(defmacro muse-publish-ensure-block (beg &optional end) - "Ensure that block-level markup at BEG is published with at least one -preceding blank line. BEG must be an unquoted symbol that contains a -position or marker. BEG is modified to be the new position. -The point is left at the new value of BEG. - -Additionally, make sure that BEG is placed on a blank line. - -If END is given, make sure that it is placed on a blank line. In -order to achieve this, END must be an unquoted symbol that -contains a marker. This is the case with Muse tag functions." - `(progn - (goto-char ,beg) - (cond ((not (bolp)) (insert "\n\n")) - ((eq (point) (point-min)) nil) - ((prog2 (backward-char) (bolp) (forward-char)) nil) - (t (insert "\n"))) - (unless (and (bolp) (eolp)) - (insert "\n") - (backward-char)) - (setq ,beg (point)) - (when (markerp ,end) - (goto-char ,end) - (unless (and (bolp) (eolp)) - (insert-before-markers "\n"))) - (goto-char ,beg))) - -;;;###autoload -(defun muse-publish-region (beg end &optional title style) - "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." - (interactive "r") - (when (interactive-p) - (unless title (setq title (read-string "Title: "))) - (unless style (setq style (muse-publish-get-style)))) - (let ((text (buffer-substring beg end)) - (buf (generate-new-buffer (concat "*Muse: " title "*")))) - (with-current-buffer buf - (insert text) - (muse-publish-markup-buffer title style) - (goto-char (point-min)) - (let ((inhibit-read-only t)) - (remove-text-properties (point-min) (point-max) - '(rear-nonsticky nil read-only nil)))) - (pop-to-buffer buf))) - -;;;###autoload -(defun muse-publish-file (file style &optional output-dir force) - "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." - (interactive (cons (read-file-name "Publish file: ") - (muse-publish-get-info))) - (let ((style-name style)) - (setq style (muse-style style)) - (unless style - (error "There is no style '%s' defined" style-name))) - (let* ((output-path (muse-publish-output-file file output-dir style)) - (output-suffix (muse-style-element :osuffix style)) - (muse-publishing-current-file file) - (muse-publishing-current-output-path output-path) - (target (if output-suffix - (concat (muse-path-sans-extension output-path) - output-suffix) - output-path)) - (threshhold (nth 7 (file-attributes file)))) - (if (not threshhold) - (message "Please save %s before publishing" file) - (when (or force (file-newer-than-file-p file target)) - (if (and muse-publish-report-threshhold - (> threshhold - muse-publish-report-threshhold)) - (message "Publishing %s ..." file)) - (muse-with-temp-buffer - (muse-insert-file-contents file) - (run-hooks 'muse-before-publish-hook) - (when muse-publish-enable-local-variables - (hack-local-variables)) - (let ((muse-inhibit-before-publish-hook t)) - (muse-publish-markup-buffer (muse-page-name file) style)) - (when (muse-write-file output-path) - (muse-style-run-hooks :final style file output-path target))) - t)))) - -;;;###autoload -(defun muse-publish-this-file (style output-dir &optional force) - "Publish the currently-visited file. -Prompt for both the STYLE and OUTPUT-DIR if they are not -supplied." - (interactive (muse-publish-get-info)) - (setq style (muse-style style)) - (if buffer-file-name - (let ((muse-current-output-style (list :base (car style) - :path output-dir))) - (unless (muse-publish-file 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.")))) - (message "This buffer is not associated with any file"))) - -(defun muse-batch-publish-files () - "Publish Muse files in batch mode." - (let ((muse-batch-publishing-p t) - (font-lock-verbose nil) - muse-current-output-style - style output-dir) - ;; don't activate VC when publishing files - (setq vc-handled-backends nil) - (setq style (car command-line-args-left) - command-line-args-left (cdr command-line-args-left) - output-dir (car command-line-args-left) - output-dir - (if (string-match "\\`--output-dir=" output-dir) - (prog1 - (substring output-dir (match-end 0)) - (setq command-line-args-left (cdr command-line-args-left)))) - muse-current-output-style (list :base style :path output-dir)) - (setq auto-mode-alist - (delete (cons (concat "\\." muse-file-extension "\\'") - 'muse-mode-choose-mode) - auto-mode-alist)) - (dolist (file command-line-args-left) - (muse-publish-file file style output-dir t)))) - -;; Default publishing rules - -(defun muse-publish-section-close (depth) - "Seach forward for the closing tag of given DEPTH." - (let (not-end) - (save-excursion - (while (and (setq not-end (re-search-forward - (concat "^\\*\\{1," (number-to-string depth) - "\\}\\s-+") - nil t)) - (get-text-property (match-beginning 0) 'read-only))) - (if not-end - (forward-line 0) - (goto-char (point-max))) - (cond ((not (eq (char-before) ?\n)) - (insert "\n\n")) - ((not (eq (char-before (1- (point))) ?\n)) - (insert "\n"))) - (muse-insert-markup (muse-markup-text 'section-close depth)) - (insert "\n")))) - -(defun muse-publish-markup-directive (&optional name value) - (unless name (setq name (match-string 1))) - (unless value (setq value (match-string 2))) - (let ((elem (assoc name muse-publishing-directives))) - (if elem - (setcdr elem value) - (setq muse-publishing-directives - (cons (cons name value) - muse-publishing-directives)))) - ;; Make sure we don't ever try to move the point forward (past the - ;; beginning of buffer) while we're still searching for directives. - (setq muse-publishing-last-position nil) - (delete-region (match-beginning 0) (match-end 0))) - -(defsubst muse-publishing-directive (name) - (cdr (assoc name muse-publishing-directives))) - -(defmacro muse-publish-get-and-delete-attr (attr attrs) - "Delete attribute ATTR from ATTRS only once, destructively. - -This function returns the matching attribute value, if found." - (let ((last (make-symbol "last")) - (found (make-symbol "found")) - (vals (make-symbol "vals"))) - `(let ((,vals ,attrs)) - (if (string= (caar ,vals) ,attr) - (prog1 (cdar ,vals) - (setq ,attrs (cdr ,vals))) - (let ((,last ,vals) - (,found nil)) - (while ,vals - (setq ,vals (cdr ,vals)) - (when (string= (caar ,vals) ,attr) - (setq ,found (cdar ,vals)) - (setcdr ,last (cdr ,vals)) - (setq ,vals nil)) - (setq ,last ,vals)) - ,found))))) - -(defun muse-publish-markup-anchor () - (unless (get-text-property (match-end 1) 'muse-link) - (let ((text (muse-markup-text 'anchor (match-string 2)))) - (unless (string= text "") - (save-match-data - (skip-chars-forward (concat muse-regexp-blank "\n")) - (muse-insert-markup text))) - (match-string 1)))) - -(defun muse-publish-markup-comment () - (if (null muse-publish-comments-p) - "" - (goto-char (match-end 0)) - (muse-insert-markup (muse-markup-text 'comment-end)) - (if (match-beginning 1) - (progn - (muse-publish-mark-read-only (match-beginning 1) (match-end 1)) - (delete-region (match-beginning 0) (match-beginning 1))) - (delete-region (match-beginning 0) (match-end 0))) - (goto-char (match-beginning 0)) - (muse-insert-markup (muse-markup-text 'comment-begin)))) - -(defun muse-publish-markup-tag () - (let ((tag-info (muse-markup-tag-info (match-string 1)))) - (when (and tag-info - (not (get-text-property (match-beginning 0) 'read-only)) - (nth 4 tag-info) - (or muse-publish-enable-dangerous-tags - (not (get (nth 4 tag-info) 'muse-dangerous-tag)))) - (let ((closed-tag (match-string 3)) - (start (match-beginning 0)) - (beg (point)) - end attrs) - (when (nth 2 tag-info) - (let ((attrstr (match-string 2))) - (while (and attrstr - (string-match (concat "\\([^" - muse-regexp-blank - "=\n]+\\)\\(=\"\\" - "([^\"]+\\)\"\\)?") - attrstr)) - (let ((attr (cons (downcase - (muse-match-string-no-properties 1 attrstr)) - (muse-match-string-no-properties 3 attrstr)))) - (setq attrstr (replace-match "" t t attrstr)) - (if attrs - (nconc attrs (list attr)) - (setq attrs (list attr))))))) - (if (and (cadr tag-info) (not closed-tag)) - (if (muse-goto-tag-end (car tag-info) (nth 3 tag-info)) - (delete-region (match-beginning 0) (point)) - (setq tag-info nil))) - (when tag-info - (setq end (point-marker)) - (delete-region start beg) - (goto-char start) - (let ((args (list start end))) - (if (nth 2 tag-info) - (nconc args (list attrs))) - (let ((muse-inhibit-style-tags nil)) - ;; remove the inhibition - (apply (nth 4 tag-info) args))) - (set-marker end nil))))) - nil) - -(defun muse-publish-escape-specials (beg end &optional ignore-read-only context) - "Escape specials from BEG to END using style-specific :specials. -If IGNORE-READ-ONLY is non-nil, ignore the read-only property. -CONTEXT is used to figure out what kind of specials to escape. - -The following contexts exist in Muse. -'underline _underlined text_ -'literal =monospaced text= or region (monospaced, escaped) -'emphasis *emphasized text* -'email email@example.com -'url http://example.com -'url-desc [[...][description of an explicit link]] -'image [[image.png]] -'example region (monospaced, block context, escaped) -'verbatim region (escaped) -'footnote footnote text -'document normal text" - (let ((specials (muse-style-element :specials nil t))) - (cond ((functionp specials) - (setq specials (funcall specials context))) - ((symbolp specials) - (setq specials (symbol-value specials)))) - (if (functionp specials) - (funcall specials beg end ignore-read-only) - (save-excursion - (save-restriction - (narrow-to-region beg end) - (goto-char (point-min)) - (while (< (point) (point-max)) - (if (and (not ignore-read-only) - (get-text-property (point) 'read-only)) - (goto-char (or (next-single-property-change (point) 'read-only) - (point-max))) - (let ((repl (or (assoc (char-after) specials) - (assoc (char-after) - muse-publish-markup-specials)))) - (if (null repl) - (forward-char 1) - (delete-char 1) - (insert-before-markers (cdr repl))))))))))) - -(defun muse-publish-markup-word () - (let* ((beg (match-beginning 2)) - (end (1- (match-end 2))) - (leader (buffer-substring-no-properties beg end)) - open-tag close-tag mark-read-only loc context) - (cond - ((string= leader "_") - (setq context 'underline - open-tag (muse-markup-text 'begin-underline) - close-tag (muse-markup-text 'end-underline))) - ((string= leader "=") - (setq context 'literal - open-tag (muse-markup-text 'begin-literal) - close-tag (muse-markup-text 'end-literal)) - (setq mark-read-only t)) - (t - (let ((l (length leader))) - (setq context 'emphasis) - (cond - ((= l 1) (setq open-tag (muse-markup-text 'begin-emph) - close-tag (muse-markup-text 'end-emph))) - ((= l 2) (setq open-tag (muse-markup-text 'begin-more-emph) - close-tag (muse-markup-text 'end-more-emph))) - ((= l 3) (setq open-tag (muse-markup-text 'begin-most-emph) - close-tag (muse-markup-text 'end-most-emph))) - (t (setq context nil)))))) - (if (and context - (not (get-text-property beg 'muse-link)) - (setq loc (search-forward leader nil t)) - (or (eobp) (not (eq (char-syntax (char-after loc)) ?w))) - (not (eq (char-syntax (char-before (point))) ?\ )) - (not (get-text-property (point) 'muse-link))) - (progn - (replace-match "") - (delete-region beg end) - (setq end (point-marker)) - (muse-insert-markup close-tag) - (goto-char beg) - (muse-insert-markup open-tag) - (setq beg (point)) - (when mark-read-only - (muse-publish-escape-specials beg end t context) - (muse-publish-mark-read-only beg end)) - (set-marker end nil)) - (backward-char)) - nil)) - -(defun muse-publish-markup-emdash () - (unless (get-text-property (match-beginning 0) 'muse-link) - (let ((prespace (match-string 1)) - (postspace (match-string 2))) - (delete-region (match-beginning 0) (match-end 0)) - (muse-insert-markup (muse-markup-text 'emdash prespace postspace)) - (when (eq (char-after) ?\<) - (insert ?\n))))) - -(defun muse-publish-markup-enddots () - (unless (get-text-property (match-beginning 0) 'muse-link) - (delete-region (match-beginning 0) (match-end 0)) - (muse-insert-markup (muse-markup-text 'enddots)))) - -(defun muse-publish-markup-dots () - (unless (get-text-property (match-beginning 0) 'muse-link) - (delete-region (match-beginning 0) (match-end 0)) - (muse-insert-markup (muse-markup-text 'dots)))) - -(defun muse-publish-markup-rule () - (unless (get-text-property (match-beginning 0) 'muse-link) - (delete-region (match-beginning 0) (match-end 0)) - (muse-insert-markup (muse-markup-text 'rule)))) - -(defun muse-publish-markup-no-break-space () - (unless (get-text-property (match-beginning 0) 'muse-link) - (delete-region (match-beginning 0) (match-end 0)) - (muse-insert-markup (muse-markup-text 'no-break-space)))) - -(defun muse-publish-markup-heading () - (let* ((len (length (match-string 1))) - (start (muse-markup-text - (cond ((= len 1) 'section) - ((= len 2) 'subsection) - ((= len 3) 'subsubsection) - (t 'section-other)) - len)) - (end (muse-markup-text - (cond ((= len 1) 'section-end) - ((= len 2) 'subsection-end) - ((= len 3) 'subsubsection-end) - (t 'section-other-end)) - len))) - (delete-region (match-beginning 0) (match-end 0)) - (muse-insert-markup start) - (end-of-line) - (when end - (muse-insert-markup end)) - (forward-line 1) - (unless (eq (char-after) ?\n) - (insert "\n")) - (muse-publish-section-close len))) - -(defvar muse-publish-footnotes nil) - -(defun muse-publish-markup-footnote () - "Scan ahead and snarf up the footnote body." - (cond - ((get-text-property (match-beginning 0) 'muse-link) - nil) - ((= (muse-line-beginning-position) (match-beginning 0)) - "") - (t - (let ((footnote (save-match-data - (string-to-number (match-string 1)))) - (oldtext (match-string 0)) - footnotemark) - (delete-region (match-beginning 0) (match-end 0)) - (save-excursion - (when (re-search-forward (format "^\\[%d\\]\\s-+" footnote) nil t) - (let* ((start (match-beginning 0)) - (beg (goto-char (match-end 0))) - (end (save-excursion - (if (search-forward "\n\n" nil t) - (copy-marker (match-beginning 0)) - (goto-char (point-max)) - (skip-chars-backward "\n") - (point-marker))))) - (while (re-search-forward - (concat "^[" muse-regexp-blank "]+\\([^\n]\\)") - end t) - (replace-match "\\1" t)) - (let ((footnotemark-cmd (muse-markup-text 'footnotemark)) - (footnotemark-end-cmd (muse-markup-text 'footnotemark-end))) - (if (string= "" footnotemark-cmd) - (setq footnotemark - (concat (muse-markup-text 'footnote) - (muse-publish-escape-specials-in-string - (buffer-substring-no-properties beg end) - 'footnote) - (muse-markup-text 'footnote-end))) - (setq footnotemark (format footnotemark-cmd footnote - footnotemark-end-cmd)) - (unless muse-publish-footnotes - (set (make-local-variable 'muse-publish-footnotes) - (make-vector 256 nil))) - (unless (aref muse-publish-footnotes footnote) - (setq footnotemark - (concat - footnotemark - (concat (format (muse-markup-text 'footnotetext) - footnote) - (buffer-substring-no-properties beg end) - (muse-markup-text 'footnotetext-end)))) - (aset muse-publish-footnotes footnote footnotemark)))) - (goto-char end) - (skip-chars-forward "\n") - (delete-region start (point)) - (set-marker end nil)))) - (if footnotemark - (muse-insert-markup footnotemark) - (insert oldtext)))))) - -(defun muse-publish-markup-fn-sep () - (delete-region (match-beginning 0) (match-end 0)) - (muse-insert-markup (muse-markup-text 'fn-sep))) - -(defun muse-insert-markup-end-list (&rest args) - (let ((beg (point))) - (apply 'insert args) - (add-text-properties beg (point) '(muse-end-list t)) - (muse-publish-mark-read-only beg (point)))) - -(defun muse-publish-determine-dl-indent (continue indent-sym determine-sym) - ;; If the caller doesn't know how much indentation to use, figure it - ;; out ourselves. It is assumed that `muse-forward-list-item' has - ;; been called just before this to set the match data. - (when (and continue - (symbol-value determine-sym)) - (save-match-data - ;; snarf all leading whitespace - (let ((indent (and (match-beginning 2) - (buffer-substring (match-beginning 1) - (match-beginning 2))))) - (when (and indent - (not (string= indent ""))) - (set indent-sym indent) - (set determine-sym nil)))))) - -(defun muse-publish-surround-dl (indent post-indent) - (let* ((beg-item (muse-markup-text 'begin-dl-item)) - (end-item (muse-markup-text 'end-dl-item)) - (beg-ddt (muse-markup-text 'begin-ddt)) ;; term - (end-ddt (muse-markup-text 'end-ddt)) - (beg-dde (muse-markup-text 'begin-dde)) ;; definition - (end-dde (muse-markup-text 'end-dde)) - (continue t) - (no-terms t) - beg) - (while continue - ;; envelope this as one term+definitions unit -- HTML does not - ;; need this, but DocBook and Muse's custom XML format do - (muse-insert-markup beg-item) - (when (looking-at muse-dl-term-regexp) - ;; find the term and wrap it with published markup - (setq beg (point) - no-terms nil) - (goto-char (match-end 1)) - (delete-region (point) (match-end 0)) - (muse-insert-markup-end-list end-ddt) - ;; if definition is immediately after term, move to next line - (unless (eq (char-after) ?\n) - (insert ?\n)) - (save-excursion - (goto-char beg) - (delete-region (point) (match-beginning 1)) - (muse-insert-markup beg-ddt))) - ;; handle pathological edge case where there is no term -- I - ;; would prefer to just disallow this, but people seem to want - ;; this behavior - (when (and no-terms - (looking-at (concat "[" muse-regexp-blank "]*::" - "[" muse-regexp-blank "]*"))) - (delete-region (point) (match-end 0)) - ;; but only do this once - (setq no-terms nil)) - (setq beg (point) - ;; move past current item - continue (muse-forward-list-item 'dl-term indent)) - (save-restriction - (narrow-to-region beg (point)) - (goto-char (point-min)) - ;; publish each definition that we find, defaulting to an - ;; empty definition if none are found - (muse-publish-surround-text beg-dde end-dde - (lambda (indent) - (muse-forward-list-item 'dl-entry indent)) - indent post-indent - #'muse-publish-determine-dl-indent) - (goto-char (point-max)) - (skip-chars-backward (concat muse-regexp-blank "\n")) - (muse-insert-markup-end-list end-item) - (when continue - (goto-char (point-max))))))) - -(defun muse-publish-strip-list-indentation (list-item empty-line indent post-indent) - (let ((list-nested nil) - (indent-found nil)) - (while (< (point) (point-max)) - (when (and (looking-at list-item) - (not (or (get-text-property - (muse-list-item-critical-point) 'read-only) - (get-text-property - (muse-list-item-critical-point) 'muse-link)))) - ;; if we encounter a list item, allow no post-indent space - (setq list-nested t)) - (when (and (not (looking-at empty-line)) - (looking-at (concat indent "\\(" - (or (and list-nested "") - post-indent) - "\\)"))) - ;; if list is not nested, remove indentation - (unless indent-found - (setq post-indent (match-string 1) - indent-found t)) - (replace-match "")) - (forward-line 1)))) - -(defun muse-publish-surround-text (beg-tag end-tag move-func &optional indent post-indent determine-indent-func list-item) - (unless list-item - (setq list-item (format muse-list-item-regexp - (concat "[" muse-regexp-blank "]*")))) - (let ((continue t) - (empty-line (concat "^[" muse-regexp-blank "]*\n")) - (determine-indent (if determine-indent-func t nil)) - (new-indent indent) - (first t) - beg) - (unless indent - (setq indent (concat "[" muse-regexp-blank "]+"))) - (if post-indent - (setq post-indent (concat " \\{0," (number-to-string post-indent) - "\\}")) - (setq post-indent "")) - (while continue - (if (or (not end-tag) (string= end-tag "")) - ;; if no end of list item markup exists, treat the beginning - ;; of list item markup as it if it were the end -- this - ;; prevents multiple-level lists from being confused - (muse-insert-markup-end-list beg-tag) - (muse-insert-markup beg-tag)) - (setq beg (point) - ;; move past current item; continue is non-nil if there - ;; are more like items to be processed - continue (if (and determine-indent-func first) - (funcall move-func (concat indent post-indent)) - (funcall move-func indent))) - (when determine-indent-func - (funcall determine-indent-func continue 'new-indent 'determine-indent)) - (when continue - ;; remove list markup if we encountered another item of the - ;; same type - (replace-match "" t t nil 1)) - (save-restriction - ;; narrow to current item - (narrow-to-region beg (point)) - (goto-char (point-min)) - (if (looking-at empty-line) - ;; if initial line is blank, move to first non-blank line - (while (progn (forward-line 1) - (and (< (point) (point-max)) - (looking-at empty-line)))) - ;; otherwise, move to second line of text - (forward-line 1)) - ;; strip list indentation - (muse-publish-strip-list-indentation list-item empty-line - indent post-indent) - (skip-chars-backward (concat muse-regexp-blank "\n")) - (muse-insert-markup-end-list end-tag) - (when determine-indent-func - (setq indent new-indent)) - (when first - (setq first nil)) - (when continue - (goto-char (point-max))))))) - -(defun muse-publish-ensure-blank-line () - "Make sure that a blank line exists on the line before point." - (let ((pt (point-marker))) - (beginning-of-line) - (cond ((eq (point) (point-min)) nil) - ((prog2 (backward-char) (bolp) (forward-char)) nil) - (t (insert-before-markers "\n"))) - (goto-char pt) - (set-marker pt nil))) - -(defun muse-publish-markup-list () - "Markup a list entry. -This function works by marking up items of the same list level -and type, respecting the end-of-list property." - (let* ((str (match-string 1)) - (type (muse-list-item-type str)) - (indent (buffer-substring (muse-line-beginning-position) - (match-beginning 1))) - (post-indent (length str))) - (cond - ((or (get-text-property (muse-list-item-critical-point) 'read-only) - (get-text-property (muse-list-item-critical-point) 'muse-link)) - nil) - ((eq type 'ul) - (unless (eq (char-after (match-end 1)) ?-) - (delete-region (match-beginning 0) (match-end 0)) - (muse-publish-ensure-blank-line) - (muse-insert-markup (muse-markup-text 'begin-uli)) - (save-excursion - (muse-publish-surround-text - (muse-markup-text 'begin-uli-item) - (muse-markup-text 'end-uli-item) - (lambda (indent) - (muse-forward-list-item 'ul indent)) - indent post-indent) - (muse-insert-markup-end-list (muse-markup-text 'end-uli))) - (forward-line 1))) - ((eq type 'ol) - (delete-region (match-beginning 0) (match-end 0)) - (muse-publish-ensure-blank-line) - (muse-insert-markup (muse-markup-text 'begin-oli)) - (save-excursion - (muse-publish-surround-text - (muse-markup-text 'begin-oli-item) - (muse-markup-text 'end-oli-item) - (lambda (indent) - (muse-forward-list-item 'ol indent)) - indent post-indent) - (muse-insert-markup-end-list (muse-markup-text 'end-oli))) - (forward-line 1)) - (t - (goto-char (match-beginning 0)) - (muse-publish-ensure-blank-line) - (muse-insert-markup (muse-markup-text 'begin-dl)) - (save-excursion - (muse-publish-surround-dl indent post-indent) - (muse-insert-markup-end-list (muse-markup-text 'end-dl))) - (forward-line 1)))) - nil) - -(defun muse-publish-markup-quote () - "Markup a quoted paragraph. -The reason this function is so funky, is to prevent text properties -like read-only from being inadvertently deleted." - (let* ((ws (match-string 1)) - (centered (>= (string-width ws) 6)) - (begin-elem (if centered 'begin-center 'begin-quote-item)) - (end-elem (if centered 'end-center 'end-quote-item))) - (replace-match "" t t nil 1) - (unless centered - (muse-insert-markup (muse-markup-text 'begin-quote))) - (muse-publish-surround-text (muse-markup-text begin-elem) - (muse-markup-text end-elem) - (function (lambda (indent) - (muse-forward-paragraph) - nil))) - (unless centered - (muse-insert-markup (muse-markup-text 'end-quote))))) - -(defun muse-publish-markup-leading-space (markup-space multiple) - (let (count) - (when (and markup-space - (>= (setq count (skip-chars-forward " ")) 0)) - (delete-region (muse-line-beginning-position) (point)) - (while (> count 0) - (muse-insert-markup markup-space) - (setq count (- count multiple)))))) - -(defun muse-publish-markup-verse () - (let ((leader (match-string 0))) - (goto-char (match-beginning 0)) - (muse-insert-markup (muse-markup-text 'begin-verse)) - (while (looking-at leader) - (replace-match "") - (muse-publish-markup-leading-space (muse-markup-text 'verse-space) 2) - (let ((beg (point))) - (end-of-line) - (cond - ((bolp) - (let ((text (muse-markup-text 'empty-verse-line))) - (when text (muse-insert-markup text)))) - ((save-excursion - (save-match-data - (forward-line 1) - (or (looking-at (concat leader "[" - muse-regexp-blank - "]*$")) - (not (looking-at leader))))) - (let ((begin-text (muse-markup-text 'begin-last-stanza-line)) - (end-text (muse-markup-text 'end-last-stanza-line))) - (when end-text (muse-insert-markup end-text)) - (goto-char beg) - (when begin-text (muse-insert-markup begin-text)) - (end-of-line))) - (t - (let ((begin-text (muse-markup-text 'begin-verse-line)) - (end-text (muse-markup-text 'end-verse-line))) - (when end-text (muse-insert-markup end-text)) - (goto-char beg) - (when begin-text (muse-insert-markup begin-text)) - (end-of-line)))) - (forward-line 1)))) - (muse-insert-markup (muse-markup-text 'end-verse)) - (insert ?\n)) - -(defun muse-publish-trim-table (table) - "Remove completely blank columns from table, if at start or end of row." - ;; remove first - (catch 'found - (dolist (row (cdr table)) - (let ((el (cadr row))) - (when (and (stringp el) (not (string= el ""))) - (throw 'found t)))) - (dolist (row (cdr table)) - (setcdr row (cddr row))) - (setcar table (1- (car table)))) - ;; remove last - (catch 'found - (dolist (row (cdr table)) - (let ((el (car (last row)))) - (when (and (stringp el) (not (string= el ""))) - (throw 'found t)))) - (dolist (row (cdr table)) - (setcdr (last row 2) nil)) - (setcar table (1- (car table)))) - table) - -(defun muse-publish-table-fields (beg end) - "Parse given region as a table, returning a cons cell. -The car is the length of the longest row. - -The cdr is a list of the fields of the table, with the first -element indicating the type of the row: - 1: body, 2: header, 3: footer, hline: separator. - -The existing region will be removed, except for initial blank lines." - (unless (muse-publishing-directive "disable-tables") - (let ((longest 0) - (left 0) - (seen-hline nil) - fields field-list) - (save-restriction - (narrow-to-region beg end) - (goto-char (point-min)) - (while (looking-at (concat "^[" muse-regexp-blank "]*$")) - (forward-line 1)) - (setq beg (point)) - (while (= left 0) - (cond - ((looking-at muse-table-hline-regexp) - (when field-list ; skip if at the beginning of table - (if seen-hline - (setq field-list (cons (cons 'hline nil) field-list)) - (dolist (field field-list) - ;; the preceding fields are header lines - (setcar field 2)) - (setq seen-hline t)))) - ((looking-at muse-table-line-regexp) - (setq fields (cons (length (match-string 1)) - (mapcar #'muse-trim-whitespace - (split-string (match-string 0) - muse-table-field-regexp))) - field-list (cons fields field-list) - longest (max (length fields) longest)) - ;; strip initial bars, if they exist - (let ((first (cadr fields))) - (when (and first (string-match "\\`|+\\s-*" first)) - (setcar (cdr fields) (replace-match "" t t first)))))) - (setq left (forward-line 1)))) - (delete-region beg end) - (if (= longest 0) - (cons 0 nil) - ;; if the last line was an hline, remove it - (when (eq (caar field-list) 'hline) - (setq field-list (cdr field-list))) - (muse-publish-trim-table (cons (1- longest) (nreverse field-list))))))) - -(defun muse-publish-markup-table () - "Style does not support tables.\n") - -(defun muse-publish-table-el-table (variant) - "Publish table.el-style tables in the format given by VARIANT." - (when (condition-case nil - (progn (require 'table) - t) - (error nil)) - (let ((muse-buf (current-buffer))) - (save-restriction - (narrow-to-region (match-beginning 0) (match-end 0)) - (goto-char (point-min)) - (forward-line 1) - (when (search-forward "|" nil t) - (with-temp-buffer - (let ((temp-buf (current-buffer))) - (with-current-buffer muse-buf - (table-generate-source variant temp-buf)) - (with-current-buffer muse-buf - (delete-region (point-min) (point-max)) - (insert-buffer-substring temp-buf) - (muse-publish-mark-read-only (point-min) (point-max)))))))))) - -(defun muse-publish-markup-table-el () - "Mark up table.el-style tables." - (cond ((muse-style-derived-p 'html) - (muse-publish-table-el-table 'html)) - ((muse-style-derived-p 'latex) - (muse-publish-table-el-table 'latex)) - ((muse-style-derived-p 'docbook) - (muse-publish-table-el-table 'cals)) - (t "Style does not support table.el tables.\n"))) - -(defun muse-publish-escape-specials-in-string (string &optional context) - "Escape specials in STRING using style-specific :specials. -CONTEXT is used to figure out what kind of specials to escape. - -See the documentation of the `muse-publish-escape-specials' -function for the list of available contexts." - (unless string - (setq string "")) - (let ((specials (muse-style-element :specials nil t))) - (cond ((functionp specials) - (setq specials (funcall specials context))) - ((symbolp specials) - (setq specials (symbol-value specials)))) - (if (functionp specials) - (funcall specials string) - (apply (function concat) - (mapcar - (lambda (ch) - (let ((repl (or (assoc ch specials) - (assoc ch muse-publish-markup-specials)))) - (if (null repl) - (char-to-string ch) - (cdr repl)))) - (append string nil)))))) - -(defun muse-publish-markup-email () - (let* ((beg (match-end 1)) - (addr (buffer-substring-no-properties beg (match-end 0)))) - (setq addr (muse-publish-escape-specials-in-string addr 'email)) - (goto-char beg) - (delete-region beg (match-end 0)) - (if (or (eq (char-before (match-beginning 0)) ?\") - (eq (char-after (match-end 0)) ?\")) - (insert addr) - (insert (format (muse-markup-text 'email-addr) addr addr))) - (muse-publish-mark-read-only beg (point)))) - -(defun muse-publish-classify-url (target) - "Transform anchors and get published name, if TARGET is a page. -The return value is two linked cons cells. The car is the type -of link, the cadr is the page name, and the cddr is the anchor." - (save-match-data - (cond ((or (null target) (string= target "")) - nil) - ((string-match "\\`[uU][rR][lL]:\\(.+\\)\\'" target) - (cons 'url (cons (match-string 1 target) nil))) - ((string-match muse-image-regexp target) - (cons 'image (cons target nil))) - ((string-match muse-url-regexp target) - (cons 'url (cons target nil))) - ((string-match muse-file-regexp target) - (cons 'file (cons target nil))) - ((string-match "#" target) - (if (eq (aref target 0) ?\#) - (cons 'anchor-ref (cons nil (substring target 1))) - (cons 'link-and-anchor - ;; match-data is changed by - ;; `muse-publish-link-page' or descendants. - (cons (save-match-data - (muse-publish-link-page - (substring target 0 (match-beginning 0)))) - (substring target (match-end 0)))))) - (t - (cons 'link (cons (muse-publish-link-page target) nil)))))) - -(defun muse-publish-url-desc (desc explicit) - (when desc - (dolist (transform muse-publish-desc-transforms) - (setq desc (save-match-data - (when desc (funcall transform desc explicit))))) - (setq desc (muse-link-unescape desc)) - (muse-publish-escape-specials-in-string desc 'url-desc))) - -(defun muse-publish-url (url &optional desc orig-url explicit) - "Resolve a URL into its final form." - (let ((unesc-url url) - (unesc-orig-url orig-url) - (unesc-desc desc) - type anchor) - ;; Transform URL - (dolist (transform muse-publish-url-transforms) - (setq url (save-match-data (when url (funcall transform url explicit))))) - ;; Classify URL - (let ((target (muse-publish-classify-url url))) - (setq type (car target) - url (if (eq type 'image) - (muse-publish-escape-specials-in-string (cadr target) - 'image) - (muse-publish-escape-specials-in-string (cadr target) 'url)) - anchor (muse-publish-escape-specials-in-string - (cddr target) 'url))) - ;; Transform description - (if desc - (setq desc (muse-publish-url-desc desc explicit)) - (when orig-url - (setq orig-url (muse-publish-url-desc orig-url explicit)))) - ;; Act on URL classification - (cond ((eq type 'anchor-ref) - (muse-markup-text 'anchor-ref anchor (or desc orig-url))) - ((and unesc-desc (string-match muse-image-regexp unesc-desc)) - (let ((ext (or (file-name-extension desc) ""))) - (setq desc (muse-publish-escape-specials-in-string unesc-desc - 'image)) - (setq desc (muse-path-sans-extension desc)) - (muse-markup-text 'image-link url desc ext))) - ((string= url "") - desc) - ((eq type 'image) - (let ((ext (or (file-name-extension url) ""))) - (setq url (muse-path-sans-extension url)) - (if desc - (muse-markup-text 'image-with-desc url ext desc) - (muse-markup-text 'image url ext)))) - ((eq type 'link-and-anchor) - (muse-markup-text 'link-and-anchor url anchor - (or desc orig-url) - (muse-path-sans-extension url))) - ((eq type 'link) - (muse-markup-text 'link url (or desc orig-url))) - (t - (or (and (or desc - ;; compare the not-escaped versions of url and - ;; orig-url - (not (string= unesc-url unesc-orig-url))) - (let ((text (muse-markup-text 'url-and-desc url - (or desc orig-url)))) - (and (not (string= text "")) - text))) - (muse-markup-text 'url url (or desc orig-url))))))) - -(defun muse-publish-insert-url (url &optional desc orig-url explicit) - "Resolve a URL into its final form." - (delete-region (match-beginning 0) (match-end 0)) - (let ((text (muse-publish-url url desc orig-url explicit))) - (when text - (muse-insert-markup text)))) - -(defun muse-publish-markup-link () - (let (desc explicit orig-link link) - (setq explicit (save-match-data - (if (string-match muse-explicit-link-regexp - (match-string 0)) - t nil))) - (setq orig-link (if explicit (match-string 1) (match-string 0))) - (setq desc (when explicit (match-string 2))) - (setq link (if explicit - (muse-handle-explicit-link orig-link) - (muse-handle-implicit-link orig-link))) - (when (and link - (or explicit - (not (or (eq (char-before (match-beginning 0)) ?\") - (eq (char-after (match-end 0)) ?\"))))) - ;; if explicit link has no user-provided description, treat it - ;; as if it were an implicit link - (when (and explicit (not desc)) - (setq explicit nil)) - (muse-publish-insert-url link desc orig-link explicit)))) - -(defun muse-publish-markup-url () - (unless (or (eq (char-before (match-beginning 0)) ?\") - (eq (char-after (match-end 0)) ?\")) - (let ((url (match-string 0))) - (muse-publish-insert-url url nil url)))) - -;; Default publishing tags - -(defcustom muse-publish-contents-depth 2 - "The number of heading levels to include with tags." - :type 'integer - :group 'muse-publish) - -(defun muse-publish-contents-tag (beg end attrs) - (set (make-local-variable 'muse-publish-generate-contents) - (cons (copy-marker (point) t) - (let ((depth (cdr (assoc "depth" attrs)))) - (or (and depth (string-to-number depth)) - muse-publish-contents-depth))))) - -(defun muse-publish-verse-tag (beg end) - (muse-publish-ensure-block beg end) - (save-excursion - (save-restriction - (narrow-to-region beg end) - (goto-char (point-min)) - (delete-char 1) - (while (< (point) (point-max)) - (insert "> ") - (forward-line)) - (if (eq ?\ (char-syntax (char-before))) - (delete-char -1))))) - -(defun muse-publish-mark-read-only (beg end) - "Add read-only properties to the given region." - (add-text-properties beg end '(rear-nonsticky (read-only) read-only t)) - nil) - -(defun muse-publish-mark-link (&optional beg end) - "Indicate that the given region is a Muse link, so that other -markup elements respect it. If a region is not specified, use -the 0th match data to determine it. - -This is usually applied to explicit links." - (unless beg (setq beg (match-beginning 0))) - (unless end (setq end (match-end 0))) - (add-text-properties beg end '(muse-link t)) - nil) - -(defun muse-publish-quote-tag (beg end) - (muse-publish-ensure-block beg) - (save-excursion - (save-restriction - (narrow-to-region beg end) - (let ((quote-regexp "^\\(<\\(/?\\)quote>\\)")) - (muse-insert-markup (muse-markup-text 'begin-quote)) - (while (progn - (unless (looking-at (concat "[" muse-regexp-blank "\n]*" - "")) - (muse-publish-surround-text - (muse-markup-text 'begin-quote-item) - (muse-markup-text 'end-quote-item) - (function - (lambda (indent) - (muse-forward-paragraph) - (goto-char (match-end 0)) - (and (< (point) (point-max)) - (not (looking-at quote-regexp))))) - nil nil nil - quote-regexp)) - (if (>= (point) (point-max)) - t - (and (search-forward "" nil t) - (muse-goto-tag-end "quote" t) - (progn (forward-line 1) t) - (< (point) (point-max)))))) - (goto-char (point-max)) - (muse-insert-markup (muse-markup-text 'end-quote)))))) - -(defun muse-publish-code-tag (beg end) - (muse-publish-escape-specials beg end nil 'literal) - (goto-char beg) - (insert (muse-markup-text 'begin-literal)) - (goto-char end) - (insert (muse-markup-text 'end-literal)) - (muse-publish-mark-read-only beg (point))) - -(defun muse-publish-cite-tag (beg end attrs) - (let* ((type (muse-publish-get-and-delete-attr "type" attrs)) - (citetag (cond ((string-equal type "author") - 'begin-cite-author) - ((string-equal type "year") - 'begin-cite-year) - (t - 'begin-cite)))) - (goto-char beg) - (insert (muse-markup-text citetag (muse-publishing-directive "bibsource"))) - (goto-char end) - (insert (muse-markup-text 'end-cite)) - (muse-publish-mark-read-only beg (point)))) - -(defun muse-publish-src-tag (beg end attrs) - (muse-publish-example-tag beg end)) - -(defun muse-publish-example-tag (beg end) - (muse-publish-ensure-block beg end) - (muse-publish-escape-specials beg end nil 'example) - (goto-char beg) - (insert (muse-markup-text 'begin-example)) - (goto-char end) - (insert (muse-markup-text 'end-example)) - (muse-publish-mark-read-only beg (point))) - -(defun muse-publish-literal-tag (beg end attrs) - "Ensure that the text between BEG and END is not interpreted later on. - -ATTRS is an alist of attributes. - -If it contains a \"style\" element, delete the region if the -current style is neither derived from nor equal to this style. - -If it contains both a \"style\" element and an \"exact\" element -with the value \"t\", delete the region only if the current style -is exactly this style." - (let* ((style (cdr (assoc "style" attrs))) - (exact (cdr (assoc "exact" attrs))) - (exactp (and (stringp exact) (string= exact "t")))) - (if (or (not style) - (and exactp (equal (muse-style style) - muse-publishing-current-style)) - (and (not exactp) (muse-style-derived-p style))) - (muse-publish-mark-read-only beg end) - (delete-region beg end) - (when (and (bolp) (eolp) (not (eobp))) - (delete-char 1))))) - -(put 'muse-publish-literal-tag 'muse-dangerous-tag t) - -(defun muse-publish-verbatim-tag (beg end) - (muse-publish-escape-specials beg end nil 'verbatim) - (muse-publish-mark-read-only beg end)) - -(defun muse-publish-br-tag (beg end) - "Insert a line break." - (delete-region beg end) - (muse-insert-markup (muse-markup-text 'line-break))) - -(defalias 'muse-publish-class-tag 'ignore) -(defalias 'muse-publish-div-tag 'ignore) - -(defun muse-publish-call-tag-on-buffer (tag &optional attrs) - "Transform the current buffer as if it were surrounded by the tag TAG. -If attributes ATTRS are given, pass them to the tag function." - (let ((tag-info (muse-markup-tag-info tag))) - (when tag-info - (let* ((end (progn (goto-char (point-max)) (point-marker))) - (args (list (point-min) end)) - (muse-inhibit-style-tags nil)) - (when (nth 2 tag-info) - (nconc args (list attrs))) - (apply (nth 4 tag-info) args) - (set-marker end nil))))) - -(defun muse-publish-examplify-buffer (&optional attrs) - "Transform the current buffer as if it were an region." - (muse-publish-call-tag-on-buffer "example" attrs)) - -(defun muse-publish-srcify-buffer (&optional attrs) - "Transform the current buffer as if it were a region." - (muse-publish-call-tag-on-buffer "src" attrs)) - -(defun muse-publish-versify-buffer (&optional attrs) - "Transform the current buffer as if it were a region." - (muse-publish-call-tag-on-buffer "verse" attrs) - (muse-publish-markup "" - `((100 ,(concat "^[" muse-regexp-blank "]*> ") 0 - muse-publish-markup-verse))) - (goto-char (point-min))) - -(defmacro muse-publish-markup-attribute (beg end attrs reinterp &rest body) - "Evaluate BODY within the bounds of BEG and END. -ATTRS is an alist. Only the \"markup\" element of ATTRS is acted -on. - -If it is omitted, publish the region with the normal Muse rules. -If RE-INTERP is specified, this is done immediately in a new -publishing process. Currently, RE-INTERP is specified only by -the tag. - -If \"nil\", do not mark up the region at all, but prevent it from -being further interpreted by Muse. - -If \"example\", treat the region as if it was surrounded by the - tag. - -If \"src\", treat the region as if it was surrounded by the - tag. - -If \"verse\", treat the region as if it was surrounded by the - tag, to preserve newlines. - -Otherwise, it should be the name of a function to call in the -narrowed region after evaluating BODY. The function should -take the ATTRS parameter. - -BEG is modified to be the start of the published markup." - (let ((attrs-sym (make-symbol "attrs")) - (markup (make-symbol "markup")) - (markup-function (make-symbol "markup-function"))) - `(let* ((,attrs-sym ,attrs) - (,markup (muse-publish-get-and-delete-attr "markup" ,attrs-sym))) - (save-restriction - (narrow-to-region ,beg ,end) - (goto-char (point-min)) - ,@body - (if (not ,markup) - (when ,reinterp - (muse-publish-markup-region (point-min) (point-max)) - (muse-publish-mark-read-only (point-min) (point-max)) - (goto-char (point-max))) - (let ((,markup-function (read ,markup))) - (cond ((eq ,markup-function 'example) - (setq ,markup-function #'muse-publish-examplify-buffer)) - ((eq ,markup-function 'src) - (setq ,markup-function #'muse-publish-srcify-buffer)) - ((eq ,markup-function 'verse) - (setq ,markup-function #'muse-publish-versify-buffer)) - ((and ,markup-function (not (functionp ,markup-function))) - (error "Invalid markup function `%s'" ,markup)) - (t nil)) - (if ,markup-function - (funcall ,markup-function ,attrs-sym) - (muse-publish-mark-read-only (point-min) (point-max)) - (goto-char (point-max))))))))) - -(put 'muse-publish-markup-attribute 'lisp-indent-function 4) -(put 'muse-publish-markup-attribute 'edebug-form-spec - '(sexp sexp sexp sexp body)) - -(defun muse-publish-lisp-tag (beg end attrs) - (muse-publish-markup-attribute beg end attrs nil - (save-excursion - (save-restriction - (let ((str (muse-eval-lisp - (prog1 - (concat "(progn " - (buffer-substring-no-properties (point-min) - (point-max)) - ")") - (delete-region (point-min) (point-max)) - (widen))))) - (set-text-properties 0 (length str) nil str) - (insert str)))))) - -(put 'muse-publish-lisp-tag 'muse-dangerous-tag t) - -(defun muse-publish-command-tag (beg end attrs) - (muse-publish-markup-attribute beg end attrs nil - (while (looking-at "\\s-*$") - (forward-line)) - (let ((interp (muse-publish-get-and-delete-attr "interp" attrs))) - (if interp - (shell-command-on-region (point) (point-max) interp t t) - (shell-command - (prog1 - (buffer-substring-no-properties (point) (point-max)) - (delete-region (point-min) (point-max))) - t))) - ;; make sure there is a newline at end - (goto-char (point-max)) - (forward-line 0) - (unless (looking-at "\\s-*$") - (goto-char (point-max)) - (insert ?\n)) - (goto-char (point-min)))) - -(put 'muse-publish-command-tag 'muse-dangerous-tag t) - -(defun muse-publish-perl-tag (beg end attrs) - (muse-publish-command-tag beg end - (cons (cons "interp" (executable-find "perl")) - attrs))) - -(put 'muse-publish-perl-tag 'muse-dangerous-tag t) - -(defun muse-publish-php-tag (beg end attrs) - (muse-publish-command-tag beg end - (cons (cons "interp" (executable-find "php")) - attrs))) - -(put 'muse-publish-php-tag 'muse-dangerous-tag t) - -(defun muse-publish-python-tag (beg end attrs) - (muse-publish-command-tag beg end - (cons (cons "interp" (executable-find "python")) - attrs))) - -(put 'muse-publish-python-tag 'muse-dangerous-tag t) - -(defun muse-publish-ruby-tag (beg end attrs) - (muse-publish-command-tag beg end - (cons (cons "interp" (executable-find "ruby")) - attrs))) - -(put 'muse-publish-ruby-tag 'muse-dangerous-tag t) - -(defun muse-publish-comment-tag (beg end) - (if (null muse-publish-comments-p) - (delete-region beg end) - (goto-char end) - (muse-insert-markup (muse-markup-text 'comment-end)) - (muse-publish-mark-read-only beg end) - (goto-char beg) - (muse-insert-markup (muse-markup-text 'comment-begin)))) - -(defun muse-publish-include-tag (beg end attrs) - "Include the named file at the current location during publishing. - - - -The `markup' attribute controls how this file is marked up after -being inserted. See `muse-publish-markup-attribute' for an -explanation of how it works." - (let ((filename (muse-publish-get-and-delete-attr "file" attrs)) - (muse-publishing-directives (copy-alist muse-publishing-directives))) - (if filename - (setq filename (expand-file-name - filename - (file-name-directory muse-publishing-current-file))) - (error "No file attribute specified in tag")) - (muse-publish-markup-attribute beg end attrs t - (muse-insert-file-contents filename)))) - -(put 'muse-publish-include-tag 'muse-dangerous-tag t) - -(defun muse-publish-mark-up-tag (beg end attrs) - "Run an Emacs Lisp function on the region delimted by this tag. - - - -The optional \"function\" attribute controls how this section is -marked up. If used, it should be the name of a function to call -with the buffer narrowed to the delimited region. Note that no -further marking-up will be performed on this region. - -If \"function\" is omitted, use the standard Muse markup function. -This is useful for marking up content in headers and footers. - -The optional \"style\" attribute causes the region to be deleted -if the current style is neither derived from nor equal to this -style. - -If both a \"style\" attribute and an \"exact\" attribute are -provided, and \"exact\" is \"t\", delete the region only if the -current style is exactly this style." - (let* ((style (cdr (assoc "style" attrs))) - (exact (cdr (assoc "exact" attrs))) - (exactp (and (stringp exact) (string= exact "t")))) - (if (or (not style) - (and exactp (equal (muse-style style) - muse-publishing-current-style)) - (and (not exactp) (muse-style-derived-p style))) - (let* ((function (cdr (assoc "function" attrs))) - (muse-publish-use-header-footer-tags nil) - (markup-function (and function (intern-soft function)))) - (if (and markup-function (functionp markup-function)) - (save-restriction - (narrow-to-region beg end) - (funcall markup-function) - (goto-char (point-max))) - (let ((muse-publish-inhibit-style-hooks t)) - (muse-publish-markup-region beg end))) - (muse-publish-mark-read-only beg (point))) - (delete-region beg end)))) - -(put 'muse-publish-mark-up-tag 'muse-dangerous-tag t) - -;; Miscellaneous helper functions - -(defun muse-publish-strip-URL (string &rest ignored) - "If the text \"URL:\" exists at the beginning of STRING, remove it. -The text is removed regardless of whether and part of it is uppercase." - (save-match-data - (if (string-match "\\`[uU][rR][lL]:\\(.+\\)\\'" string) - (match-string 1 string) - string))) - -(defun muse-publish-markup-type (category default-func) - (let ((rule (muse-find-markup-element :overrides category (muse-style)))) - (funcall (or rule default-func)))) - -(defun muse-published-buffer-contents (buffer) - (with-current-buffer buffer - (goto-char (point-min)) - (let ((beg (and (search-forward "Emacs Muse begins here") - (muse-line-end-position))) - (end (and (search-forward "Emacs Muse ends here") - (muse-line-beginning-position)))) - (buffer-substring-no-properties beg end)))) - -(defun muse-published-contents (file) - (when (file-readable-p file) - (muse-with-temp-buffer - (muse-insert-file-contents file) - (muse-published-buffer-contents (current-buffer))))) - -(defun muse-publish-transform-output - (file temp-file output-path name gen-func &rest cleanup-exts) - "Transform the given TEMP-FILE into the OUTPUT-PATH, using GEN-FUNC." - (setq file (muse-page-name file)) - (message "Generating %s output for %s..." name file) - (if (not (funcall gen-func temp-file output-path)) - (message "Generating %s from %s...failed" name file) - (message "Generating %s output for %s...done" name file) - (muse-delete-file-if-exists temp-file) - (dolist (ext cleanup-exts) - (muse-delete-file-if-exists - (expand-file-name (concat file ext) - (file-name-directory output-path)))) - (message "Wrote %s" output-path))) - -(defun muse-publish-read-only (string) - (let ((end (1- (length string)))) - (add-text-properties 0 end - '(rear-nonsticky (read-only) read-only t) - string) - string)) - -;;; muse-publish.el ends here -- cgit v1.2.3-54-g00ecf