SiteMap Search ElispArea HowTo Glossary RecentChanges News Problems Suggestions

rtf-mode.el

Download

;;; rtf-mode.el --- Emacs major mode for viewing/editing raw RTF source

;; Copyright (C) 2006 Sean M. Burke (but released under GPL)

;; Time-stamp: <2008-01-05 19:12:08 AKST sburke@cpan.org>
;; Author:     Sean M. Burke <sburke@cpan.org>
;; Maintainer: Sean M. Burke <sburke@cpan.org>
;; Keywords: wp, rtf, languages
;; Version: 1.1
;; X-URL: http://interglacial.com/rtf/emacs/

;;; Commentary:

;; Major mode for viewing/editing RTF source.  I wrote this mostly for
;; the sake of syntax-highlighting, and for the {}-matching.  It offers
;; little else in the way of conveniences (like key shortcuts.)
;; (If are interested in such conveniences, email me.)
;;
;; I maintain a homepage for this mode, at http://interglacial.com/rtf/emacs/
;;
;; Note that this mode doesn't (yet?) specially handle Help-RTF constructs.
;;
;; Also note that at time of writing (Oct 2006), Xemacs doesn't seem to
;; support some of the functions that this mode requires.  Sorry, Xemacs users!
;;
;; As with many programming modes, the default wrapping mechinisms
;; (whether via auto-fill-mode, or via fill-paragraph etc commands) do
;; not behave at all well.  So I've disabled wrapping as best I can,
;; so you don't end up corrupting documents.  Moreover, implementing
;; wrapping in RTF seems fairly difficult because of how spaces are
;; sometimes a significant token in RTF whereas newlines are not.
;;
;; For more on RTF, see http://interglacial.com/rtf/ and also be a chum and
;; go get this little book I wrote:  http://www.amazon.com/dp/0596004753
;;

;;; Boilerplate

;; This file is not part of GNU Emacs.
;;
;; This 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, version 2.
;;
;; This is distributed in the hope that it will be useful, but without any
;; warranty without even the implied warranty of merchantability or fitness for
;; a particular purpose.  See the GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License along with
;; GNU Emacs; see the file COPYING.  If not, write to the Free Software
;; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

;;; Change log:

;; 2006-10-23 version 1.1
;;  Public release.  Just a minor internal optimization.
;;  (We new use regexp-opt to build our regexp from rtf-loudcmds.)

;; 2006-10-22 version 1.0
;;  Public release.

;;; Code:
 
(require 'generic)

(defmacro rtf-mkface (name basis desc)
  `(defface
     ,name
     (list (list t :inherit ,basis))
     ,(concat "Face used to highlight rtf-mode " desc ".")
     :group 'rtf))

(rtf-mkface rtf-brace-face 'font-lock-string-face "braces")
(rtf-mkface rtf-charescape-face 'font-lock-function-name-face "numeric escapes")
(rtf-mkface rtf-loud-cword-face 'font-lock-warning-face "important commands")
(rtf-mkface rtf-cword-face 'font-lock-keyword-face "commands")
(rtf-mkface rtf-cword-param-face 'font-lock-builtin-face "command-parameters")
(rtf-mkface rtf-cword-endingspace-face 'font-lock-comment-face "command-ending spaces")
(rtf-mkface rtf-star-face 'font-lock-doc-face "\\* commands")
(rtf-mkface rtf-escnewline-face 'font-lock-constant-face "\\ [Newline] sequences")
(rtf-mkface rtf-esctab-face 'font-lock-warning-face "\\ [Tab] sequences")
(rtf-mkface rtf-escother-face 'font-lock-type-face "\\ [other] sequences")
(rtf-mkface rtf-trailing-whitespace-face 'highlight "trailing space")

(defvar rtf-loudcmds
  '(
    rtf1 page pard par line sectd sect footnote 
    cell row trowd 
    fonttbl info colortbl stylesheet
    )
  "A few structurally super-important (and parameterless) RTF commands.")

;======================================================================

(defun rtf-make-loudcmd-re (cmdlist)
  (concat "\\(\\\\\\)\\("
	  (regexp-opt (mapcar 'symbol-name cmdlist))
	  "\\)\\b\\( ?\\)"))

(define-generic-mode 'rtf-mode
  nil ; the comment syntax list -- nil because RTF has no comments!
  nil ; keyword-list -- nil because we do everything below

  (list   ; the font lock list...
 
    ; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    ; * Control words, \foo123, \foo-123, \foo

    ; A few structurally super-important (and parameterless) control words:
    (list (rtf-make-loudcmd-re rtf-loudcmds)
     '(1					'rtf-cword-face)
     '(2 					'rtf-loud-cword-face)
     '(3					'rtf-cword-endingspace-face)
     )
    
    '("\\(\\\\u-?[0-9]+ ?\\)" ; a unicodey escape
     1						'rtf-charescape-face)

    '("\\(\\\\[a-zA-Z]+\\)\\(-?[0-9]+\\)\\( ?\\)" ; with a num-param
     (1						'rtf-cword-face)
     (2						'rtf-cword-param-face)
     (3						'rtf-cword-endingspace-face)
     )

    '("\\(\\\\[a-zA-Z]+\\)\\( ?\\)" ; no param
     (1 					'rtf-cword-face)
     (2						'rtf-cword-endingspace-face)
     )
    ; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    ; * Control symbols
    ;  ~  the fancy \* thing...
    '("\\(\\\\\\*\\)" 1 'rtf-star-face)

    ;  ~  \'xx escape
    '("\\(\\\\'[a-fA-F0-9][a-fA-F0-9]\\)"  	1 'rtf-charescape-face)

    ;  ~ \[NEWLINE] which means \par
    '("\\(\\\\\012\\)" 				1 'rtf-escnewline-face)

    ;  ~ \[TAB]     which means \tab
    '("\\(\\\\\011\\)" 				1 'rtf-esctab-face)

    ;  ~  \_ where _ is anything else, but probably one of ' - : \ _ { | } ~
    '("\\(\\\\[^a-zA-Z'\011\012]\\)"		1 'rtf-escother-face)

    ; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    ; * '{' and '}' (unescaped, that is)
    '("\\({\\|}\\)" 				1 'rtf-brace-face)

    ; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    ; And, incidentally:
    ;  ~ trailing whitespace -- not an error, but which is /significant/!
    '("\\([ \011]+\\)$" 1			'rtf-trailing-whitespace-face)

    ;; You COULD highlight /all/ significant spaces in the document with this:
    ;("\\([ \011]+\\)" 1			'rtf-trailing-whitespace-face)
    ;; but that would probably be more confusing than useful.
 
    )

  (list ".rtf\\'" )  ; auto-mode list -- our extensions

  (list 'rtf-mode-setup-function)
  "A major mode for editing/viewing RTF source.")

; TODO: support for blobs?  Seems tricky.
;======================================================================

(defun rtf-mode-stultify-character (str)
  "Mark this one-character string as nonmagical in the syntax table."
  (modify-syntax-entry (string-to-char str) "."))

(defun rtf-mode-fill-paragraph (arg)
  (message "rtf-mode doesn't currently support filling paragraphs")
  ; This is just to inhibit the actual normal filler, because
  ; that's usually a really bad idea for RTF.  (I will have
  ; to write a replacement for it eventually, but it'll be
  ; quite tricky.)
  t)

(defun rtf-mode-setup-function ()
  (dolist (s (split-string "\"()[]\\" ""))
    (rtf-mode-stultify-character s))

  (set (make-local-variable 'require-final-newline) nil)
  ; Many RTF files end on a "}" without a newline after it

  (set (make-local-variable 'fill-paragraph-function) 'rtf-mode-fill-paragraph)

  ;(set (make-local-variable 'fill-prefix) " ")
  ; ^^ That'd be good for entering text content, but it inserts
  ;    significant spaces when you're entering commands!

  t
)

(provide 'rtf-mode)

;;; rtf-mode.el ends here