home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / languages / elisp / epoch / markup.el < prev    next >
Encoding:
Text File  |  1993-05-28  |  53.2 KB  |  1,351 lines

  1. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  2. ;;;
  3. ;;; LISP CODE DIRECTORY INFORMATION
  4. ;;;
  5. ;;; LCD Archive Entry:
  6. ;;; markup|Bill Richardson|willrich@cs.utah.edu|
  7. ;;; Minor mode for electronic editorial markup|
  8. ;;; 26-May-1993|version 1.01|~/epoch/markup.el.Z|
  9. ;;;
  10. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  11. ;;;
  12. ;;; File:          markup.el
  13. ;;; Description:   Minor mode for electronic editorial markup.
  14. ;;; Requirements:  Epoch 4.2 or greater, compiled with DEFINE_CHANGE_FUNCTIONS.
  15. ;;; Author:        Bill Richardson (willrich@cs.utah.edu)
  16. ;;; Date:          April 9, 1993
  17. ;;; Copyright:     (C) Copyright 1993 William F. Richardson
  18. ;;;
  19. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  20. ;;;
  21. ;;; CHANGE HISTORY:
  22. ;;;
  23. ;;; 9-Apr-93  Version 1.0  willrich
  24. ;;;           First public release.
  25. ;;;
  26. ;;; 26-May-93 Version 1.01 willrich
  27. ;;;           Fixed bug in recognizing C-mode comments.
  28. ;;;
  29. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  30. ;;;
  31. ;;; COPYRIGHT NOTICE
  32. ;;;
  33. ;;; This program is free software; you can redistribute it and/or modify it
  34. ;;; under the terms of the GNU General Public License as published by the Free
  35. ;;; Software Foundation; either version 1, or (at your option) any later
  36. ;;; version.
  37. ;;;
  38. ;;; This program is distributed in the hope that it will be useful, but 
  39. ;;; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  40. ;;; or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  41. ;;; for more details.
  42. ;;;
  43. ;;; You should have received a copy of the GNU General Public License along
  44. ;;; with this program; if not, write to the Free Software Foundation, Inc.,
  45. ;;; 675 Mass Ave, Cambridge, MA 02139, USA.
  46. ;;;
  47. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  48. ;;;
  49. ;;; SUMMARY
  50. ;;;
  51. ;;; The markup package provides Epoch users the ability to make comments
  52. ;;; or changes to a document, while keeping those changes logically
  53. ;;; separate from the original text.  Several commands are supplied to
  54. ;;; operate on the marked text.  A minor mode is provided which will
  55. ;;; insure that the original text remains unchanged.  The package is
  56. ;;; written in Emacs Lisp, and requires Epoch 4.2 or later, compiled with
  57. ;;; DEFINE_CHANGE_FUNCTIONS defined.
  58. ;;;
  59. ;;; The markup package was written as an aid in publishing.  Often when
  60. ;;; an article is being written, part of the process is to have a
  61. ;;; colleague review the document and suggest changes.  Usually this is
  62. ;;; done by giving a printed copy of the article to the reviewer who
  63. ;;; marks all over it, and then returns it to the author.  The author
  64. ;;; then has to manually incorporate the comments and suggestions into
  65. ;;; the original document.
  66. ;;;
  67. ;;; With this package, the reviewing process is handled electronically.
  68. ;;; The rough draft of the article is sent to the reviewer directly, who
  69. ;;; edits the file while in markup mode.  This prevents him/her from
  70. ;;; accidently changing the original contents, while displaying all the
  71. ;;; suggested changes and deletions in different colors.  The modified
  72. ;;; file is saved and returned to the author.  The author can quickly
  73. ;;; move from one suggestion to another, and may incorporate the changes
  74. ;;; into the article with a minimum of effort.  The zone information
  75. ;;; which allows the author to see the reviewer's changes is saved as
  76. ;;; part of the file in a (hopefully) transparent fashion.
  77. ;;;
  78. ;;; Refer to the function documentation for 'markup-mode for more information.
  79. ;;;
  80. ;;; Binding markup functions to keys is done at the end of this file. I
  81. ;;; prefer the prefix "\C-c", but others may not.  Feel free to change it.
  82. ;;;
  83. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  84. ;;;
  85. ;;; INSTALLATION
  86. ;;;
  87. ;;; To install the markup package, just load it from your .emacs file. You
  88. ;;; should test for Epoch, since the package won't work under Emacs:
  89. ;;;
  90. ;;;     (when (boundp 'epoch::version)
  91. ;;;        (load "markup" t))
  92. ;;;
  93. ;;;
  94. ;;; You may also wish to change the default markup styles:
  95. ;;;
  96. ;;;     (when (and (boundp 'epoch::version)
  97. ;;;                (load "markup" t))
  98. ;;;        (setq markup::insert-style
  99. ;;;              (markup::make-style '(("background" . "green")
  100. ;;;                                    ("foreground" . "yellow"))))
  101. ;;;        (setq markup::delete-style
  102. ;;;              (markup::make-style '(("background" . "red")
  103. ;;;                                    ("foreground" . "white")))))
  104. ;;;
  105. ;;;
  106. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  107.  
  108. (require 'cl)                    ; distributed with Emacs
  109. (provide 'markup)
  110.  
  111. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  112. ;;;
  113. ;;; Stuff to deal with minor mode.
  114. ;;;
  115.  
  116. ;; Function to toggle or set markup-mode
  117. (defun markup-mode (&optional arg)
  118.   "Markup is a minor mode which allows editorial markup of files.
  119. An optional ARG turns markup-mode on if value is greater than 0, else off.
  120. In markup mode, the original text cannot be modified, but new text is
  121. inserted in a different style.  Several commands are provided to mark
  122. portions of the text in various ways, whether in markup mode or not, but
  123. some are restricted while in markup mode to prevent modifying original text.
  124.  
  125. The markup commands bound to keys are C-c followed by:
  126. \\{markup::command-prefix-key}
  127. While in markup mode, comments may be inserted with M-;
  128.  
  129. There are five types of markup regions. These are used by the reviewer to
  130. suggest changes to the original author.  The author may ignore the
  131. suggestion completely, or may Accept or Destroy it.  The key bindings for
  132. these actions are deliberately made difficult to type.  The functions
  133. markup-accept-all, markup-accept-region, markup-destroy-all, and
  134. markup-destroy-region may be used instead of keys.
  135.  
  136.  
  137. Inserted text -- This is new text which the reviewer has inserted. If this
  138.   change is Accepted, the text marked for insertion will be marked as
  139.   original text.  If this change is Destroyed, the new text is removed from
  140.   the buffer.
  141.  
  142. Deleted text -- This is text that the reviewer suggests be deleted. If
  143.   this change is Accepted, the text marked for deletion will be removed
  144.   from the buffer.  If this change is Destroyed, the text will be marked as
  145.   original.
  146.  
  147. Comment text -- This is text which contains comments from the reviewer to
  148.   the author.  It should not be part of the final document. If the buffer
  149.   has a defined comment syntax, the markup::insert-comment function will
  150.   attempt to use that syntax to delimit the reviewer's comments.  Comments
  151.   cannot be Accepted.  They can only be Destroyed or marked as original.
  152.  
  153. Replacement text -- This is text that has replaced original text.  This
  154.   should primarily be used to correct misspelled words, since massive
  155.   rewriting is not the job of the reviewer.  If this change is Accepted,
  156.   it becomes original text.  If it is Destroyed, the original text is
  157.   restored in place of the text marked as replacement.  Note that markup
  158.   mode will only allow one word at a time to be replaced.  There is a
  159.   limit to how much of the original text section is saved along with each
  160.   replacement text.  The amount is controlled by markup::save-text-length.
  161.  
  162. Highlighted text -- This has no editorial meaning, but is often useful as
  163.   a place holder, or to draw attention to specific sections.
  164.  
  165.  
  166. Purpose of the markup package:
  167.  
  168. The markup package allows users to make changes or additions to files in
  169. a way that makes the changes immediately obvious to another user, and
  170. also allows those changes to be selectively incorporated or ignored.
  171. However, files edited with the markup package may still be edited with
  172. editors other than Epoch.
  173.  
  174. Describing how the package may be useful is best done through an example:
  175.  
  176. Suppose I'm writing a paper to be published.  I get most of it written,
  177. and then give a printed copy to a friend to review.  My friend writes
  178. comments in the margins, crosses out words or paragraphs, inserts new
  179. text in a few places, and returns this mess back to me.  I then have to
  180. go through the document and transcribe any changes into my source file.
  181.  
  182. With the markup package, I can just email my friend the source file.  She
  183. would make her comments, additions, and deletions with Epoch using markup
  184. mode, and email the changed file back to me.  When I edit this new
  185. version in my Epoch buffer, I see her changes in different colors.  For
  186. example, her comments might be displayed in blue, her additions to my
  187. text in green, and her suggestions as to what to delete in red.  All of
  188. my original text would still be displayed in my default colors.
  189.  
  190. As I scan forward in the file, the colors make it easy to spot her
  191. suggestions.  There are also commands to move forward or backward to the
  192. next suggestion.  At each place, I can quickly accept, reject, or ignore
  193. her changes.  If I accept her change, a single command will make the
  194. correct changes to my file, by either deleting, replacing, or inserting
  195. the suggested version.  I don't have to retype anything.  If I reject a
  196. change, another command will discard inserted text or re-mark suggested
  197. deletions as original text.  I don't have to delete or rephrase anything
  198. here either.
  199.  
  200. The markup mode functions may be useful for just myself as well.  When my
  201. friend is editing the file in markup mode, she is prevented from actually
  202. changing my original text, and all of her comments and rephrasings appear
  203. in different colors.  If I edit a file with markup mode disabled, I can
  204. still mark sections of text for deletion, conditionally insert text, or
  205. add comments to myself.  This has no effect on the contents of the text
  206. file, so I can still print it out, or run it though LaTeX, or whatever.
  207. However, by marking sections in different colors, I can quickly find
  208. places in the file where I may wish to rethink some things.  So the
  209. markup package my be useful while composing articles as well.
  210.  
  211.  
  212. Saving markup information:
  213.  
  214. Loading the markup package installs functions to save and restore markup
  215. information automatically.  When a file is written, the markup data is
  216. saved by appending text to the end of the file.  If the buffer has a
  217. comment syntax defined, the markup information is commented out according
  218. to that syntax.  When a file is read, it is searched for the markup
  219. information and the appropriate zones are restored.
  220.  
  221. Further documentation is available through comments in the source file."
  222.   
  223.   ;; Here's the body of the markup-mode function
  224.   (interactive)
  225.   (setq markup-mode
  226.         (if (null arg) (not markup-mode)
  227.           (> (prefix-numeric-value arg) 0)))
  228.   (cond
  229.    (markup-mode                    ; just turned it on
  230.     (setq before-change-function 'markup::before-change)
  231.     (setq after-change-function 'markup::after-change)
  232.     (markup::turn-on-comment-key))
  233.    (t                        ; just turned it off
  234.     (setq before-change-function nil)
  235.     (setq after-change-function nil)
  236.     (markup::turn-off-comment-key))))
  237.  
  238.  
  239. ;; Variable to track state of markup-mode in each buffer.
  240. (defvar markup-mode nil
  241.   "Flag which is true when markup-mode is enabled.
  242. See also markup-mode function documentation.")
  243. (make-variable-buffer-local 'markup-mode)
  244. (setq-default markup-mode nil)
  245.  
  246. ;; Make before and after function hooks local to the buffer.
  247. (make-variable-buffer-local 'before-change-function)
  248. (make-variable-buffer-local 'after-change-function)
  249.  
  250. ;; Register markup-mode as a minor mode.
  251. (or (assq 'markup-mode minor-mode-alist)
  252.     (setq minor-mode-alist
  253.       (cons '(markup-mode " Markup")
  254.         minor-mode-alist)))
  255.  
  256.  
  257. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  258. ;;;
  259. ;;; Set up default styles for each type of text.  Suggested changes are
  260. ;;; indicated by regions of text which appearing in different styles.
  261. ;;; Epoch refers to these regions as zones.
  262. ;;;
  263.  
  264. ;; Creates a style from an alist of properties
  265. (defun markup::make-style (styles)
  266.   "Makes a new style with the parameters given in alist.
  267.  
  268. Each component of the alist looks like (\"style\" . \"value\").
  269.  
  270. For example, to set the foreground color to red and the font to 9x15bold, use
  271.         '((\"foreground\" . \"red\") (\"font\" . \"9x15bold\"))
  272.  
  273. The \"style\" is in fact concatenated onto \"set-style-\" and this function
  274. is called to set the style value.
  275.  
  276. The allowable style parameters are background, background-stipple,
  277. cursor-background, cursor-foreground, cursor-stipple, font, foreground,
  278. pixmap, stipple, underline and tag.  See the corresponding set-style-*
  279. functions for their effects."
  280.   
  281.   (let ((new-style (make-style)))
  282.     (while styles
  283.       (funcall (intern
  284.         (concat "set-style-" (car (car styles))))
  285.            new-style (cdr (car styles)))
  286.       (setq styles (cdr styles)))
  287.     new-style))
  288.  
  289.  
  290. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  291. ;;;
  292. ;;; User variable definitions.
  293. ;;;
  294.  
  295. ;;;
  296. ;;; Styles for various types of text.  These are customized for a screen
  297. ;;; which by default has yellow text on a navy blue background.
  298. ;;;
  299.  
  300. (defvar markup::insert-style
  301.   (markup::make-style '(("foreground" . "green")))
  302.   "*Markup style for inserted text.")
  303.  
  304. (defvar markup::delete-style
  305.   (markup::make-style '(("foreground" . "red")))
  306.   "*Markup style for deleted text.")
  307.  
  308. (defvar markup::replace-style
  309.   (markup::make-style '(("foreground" . "white")))
  310.   "*Markup style for replaced text.")
  311.  
  312. (defvar markup::comment-style
  313.   (markup::make-style '(("foreground" . "LightSkyBlue")))
  314.   "*Markup style for comment text.")
  315.  
  316. (defvar markup::highlight-style
  317.   (markup::make-style '(("background" . "gray70")))
  318.   "*Markup style for highlighted text.")
  319.  
  320. ;;;
  321. ;;; What happens when comments are inserted.
  322. ;;;
  323.  
  324. (defvar markup::comment-newline-before t
  325.   "*True if comments should insert newline first.")
  326. (make-variable-buffer-local 'markup::comment-newline-before)
  327.  
  328. (defvar markup::comment-newline-after nil
  329.   "*True if comments should insert newline afterwards.")
  330. (make-variable-buffer-local 'markup::comment-newline-after)
  331.  
  332. (defvar markup::comment-begin-at-eol t
  333.   "*True if comments should start at the end of the current line.")
  334. (make-variable-buffer-local 'markup::comment-begin-at-eol)
  335.  
  336. ;;;
  337. ;;; How inserted text is treated while in markup mode.
  338. ;;;
  339.  
  340. (defvar markup::insert-zone-soft t
  341.   "*True if inserted text should try to match the preceding zone.
  342. When new text is inserted at the very end of a comment zone,
  343. it normally would not be part of the comment.  Setting this to T
  344. checks to see if there is an adjacent zone to use instead.")
  345. (make-variable-buffer-local 'markup::insert-zone-soft)
  346.  
  347. ;;;
  348. ;;; How much replacement text should be saved with the file.
  349. ;;;
  350.  
  351. (defvar markup::save-text-length 500
  352.   "*Maximum length of original text to be saved with replace zones.
  353. Replacement text longer than this will not be saved along with the file.
  354. Replacing large sections of text should not be the responsibility of the
  355. reviewer.  To indicate that a major change is needed, insert a 
  356. comment indicating that a section should be rewritten by the author.")
  357. (make-variable-buffer-local 'markup::save-text-length)
  358.  
  359. ;;;
  360. ;;; Should we advance to the next change after each Accept or Destroy?
  361. ;;;
  362.  
  363. (defvar markup::advance-after-change t
  364.   "*True if point should advance after each Accept or Destroy.")
  365.  
  366.  
  367. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  368. ;;;
  369. ;;; Internal program variables.
  370. ;;;
  371.  
  372. (defconst markup::style-for-new-text 'insert
  373.   "Style used to insert new text in markup mode.
  374. This is an internal variable.  Don't screw with it.")
  375. (make-variable-buffer-local 'markup::style-for-new-text)
  376.  
  377. (defvar markup::old-local-map nil
  378.   "Holds local keymap in effect before entering markup mode.
  379. This is an internal variable.  Don't screw with it.")
  380. (make-variable-buffer-local 'markup::old-local-map)
  381. (setq-default markup::old-local-map nil)
  382.  
  383.  
  384. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  385. ;;;
  386. ;;; Functions to deal with zones.
  387. ;;;
  388. ;;; Each markup suggestion is indicated by epoch zones.  The data field of
  389. ;;; each markup zone contains a vector of information about the suggestion.
  390. ;;; The vector in the zone data field contains one of these six patterns:
  391. ;;; 
  392. ;;;   [ 'markup-d  'delete ]                    ; Original, unchangeable text
  393. ;;;   [ 'markup-d  'highlight ]                 ; Original, unchangeable text
  394. ;;;   [ 'markup    'insert ]            ; Changeable
  395. ;;;   [ 'markup    'comment ]            ; Changeable
  396. ;;;   [ 'markup    'replace  nil ]        ; Changeable, original lost
  397. ;;;   [ 'markup    'replace  "original text" ]    ; Changeable, original saved
  398. ;;; 
  399. ;;; The original text of a replacement zone should only be lost if the file
  400. ;;; is saved without very long text-saving enabled, and then reloaded.
  401. ;;; It is not good style to replace large quantities of original text,
  402. ;;; since that is the responsibility of the author, not the reviewer.
  403. ;;; 
  404.  
  405.  
  406. ;; Create a new zone.  This is a wrapper around the epoch function so that
  407. ;; the buffer is marked as modified when zones are changed by markup.
  408. (defun markup::add-zone (start end style &optional data buffer)
  409.   "Wrapper around epoch::add-zone so buffer is modified by markup zones."
  410.   (set-buffer-modified-p t)
  411.   (add-zone start end style data buffer))
  412.  
  413.  
  414. ;; Move a zone.  This is a wrapper around the epoch function so that
  415. ;; the buffer is marked as modified when zones are changed by markup.
  416. (defun markup::move-zone (zone &optional start end buffer)
  417.   "Wrapper around epoch::move-zone so buffer is modified by markup zones."
  418.   (set-buffer-modified-p t)
  419.   (move-zone zone start end buffer))
  420.  
  421.  
  422. ;; Delete a zone.  This is a wrapper around the epoch function so that
  423. ;; the buffer is marked as modified when zones are changed by markup.
  424. (defun markup::delete-zone (zone)
  425.   "Wrapper around epoch::delete-zone so buffer is modified by markup zones."
  426.   (set-buffer-modified-p t)
  427.   (delete-zone zone))
  428.  
  429.  
  430. ;; Make a new zone.  The vectors for non-replace zones are constants,
  431. ;; but the replace zone vector also contains the old/original text.
  432. ;; A zero-length old-text argument means no replacement text is known.
  433. (defun markup::make-zone (start end type &optional old-text)
  434.   "Makes a new markup zone for a given region."
  435.   (let ((zone (markup::add-zone start end nil)))
  436.     (cond
  437.      ((eq type 'delete)
  438.       (set-zone-style zone markup::delete-style)
  439.       (set-zone-data zone [ markup-d delete ]))
  440.      ((eq type 'highlight)
  441.       (set-zone-style zone markup::highlight-style)
  442.       (set-zone-data zone [ markup-d highlight ]))
  443.      ((eq type 'insert)
  444.       (set-zone-style zone markup::insert-style)
  445.       (set-zone-data zone [ markup insert ]))
  446.      ((eq type 'comment)
  447.       (set-zone-style zone markup::comment-style)
  448.       (set-zone-data zone [ markup comment ]))
  449.      ((eq type 'replace)
  450.       (set-zone-style zone markup::replace-style)
  451.       (set-zone-data zone (vector 'markup 'replace
  452.                   (cond
  453.                    ((null old-text) ; no text--get from buffer
  454.                     (buffer-substring start end))
  455.                    ((zerop (length old-text)) ; no string
  456.                     nil)    ; so no replacement
  457.                    (t        ; otherwise, use it
  458.                     old-text))))))))
  459.       
  460.  
  461. ;; Returns T if zone is a markup zone
  462. (defun markup::markup-zone-p (zone)
  463.   "Returns T if a zone is a markup zone."
  464.   (if zone                    ; if not zone, return nil
  465.       (let ((data (zone-data zone)))        ; get data field
  466.     (and (vectorp data)            ; must be vector
  467.          (or (eq 'markup (aref data 0))    ; must be 'markup or 'markup-d
  468.          (eq 'markup-d (aref data 0))))))) ; in first element
  469.  
  470.  
  471. ;; Returns T if zone is a changeable markup zone
  472. (defun markup::changeable-p (zone)
  473.   "Returns T if zone is a changeable markup zone."
  474.   (let ((data (zone-data zone)))        ; get data field
  475.     (and (vectorp data)                ; must be vector
  476.      (eq 'markup (aref data 0)))))        ; must be 'markup in first elt.
  477.  
  478.  
  479. ;; Returns T if zone is a non-changeable markup zone
  480. (defun markup::original-p (zone)
  481.   "Returns T if zone is a non-changeable markup zone."
  482.   (let ((data (zone-data zone)))        ; get data field
  483.     (and (vectorp data)                ; must be vector
  484.      (eq 'markup-d (aref data 0)))))    ; must be 'markup-d in first
  485.  
  486.  
  487. ;; Returns T if zone is a markup zone that can be accepted
  488. (defun markup::acceptable-p (zone)
  489.   "Returns T if zone is a markup zone that can be accepted."
  490.   (let ((data (zone-data zone)))        ; get data field
  491.     (and (vectorp data)                ; must be vector
  492.      (or
  493.       (and (eq 'markup-d (aref data 0))    ; delete zone
  494.            (eq 'delete (aref data 1)))
  495.       (and (eq 'markup (aref data 0))    ; insert
  496.            (eq 'insert (aref data 1)))
  497.       (and (eq 'markup (aref data 0))    ; replace
  498.            (eq 'replace (aref data 1)))))))
  499.  
  500.  
  501. ;; Returns T if zone is a markup zone that can be destroyed
  502. (defun markup::destroyable-p (zone)
  503.   "Returns T if zone is a markup zone that can be destroyed."
  504.   (let ((data (zone-data zone)))        ; get data field
  505.     (and (vectorp data)                ; must be vector
  506.      (or (eq 'markup-d (aref data 0))    ; original
  507.          (and (eq 'markup (aref data 0))    ; or modified
  508.           (not (and            ; except for
  509.             (eq 'replace (aref data 1)) ; replacement zone
  510.             (null (aref data 2))))))))) ; without original text
  511.  
  512.  
  513.  
  514. ;; This returns a list of the markup zones.
  515. (defun markup::zone-list ()
  516.   "Returns a list of all markup zones in the current buffer."
  517.   (let ((z-list nil))
  518.     (dolist (z (zone-list))            ; look at all zones
  519.       (if (and (markup::markup-zone-p z)    ; pick out markup zones
  520.            (/= (zone-start z) (zone-end z))) ; with some length
  521.       (push z z-list)))            ; save 'em
  522.     (reverse z-list)))                ; return 'em in order
  523.  
  524.  
  525. ;; Stretch an adjacent zone if possible, or make a new one,
  526. ;; so that the region is a zone of the correct style.
  527. (defun markup::insert-zone (start end type)
  528.   "Ensures that a region is in a particular style."
  529.   (let ((next-zone (zone-at (- start 1)))
  530.     (style (cond
  531.         ((eq type 'insert) markup::insert-style)
  532.         ((eq type 'comment) markup::comment-style)
  533.         ((eq type 'replace) markup::replace-style)
  534.         ((eq type 'delete) markup::delete-style)
  535.         ((eq type 'highlight) markup::highlight-style))))
  536.     (cond ((and next-zone            ; if there is one to the left
  537.         (eq style (zone-style next-zone))) ; and it's what we want
  538.        (markup::move-zone next-zone        ; just stretch it to cover
  539.                   (zone-start next-zone) end))
  540.       ((and (setq next-zone (zone-at end))    ; if there is one to the right
  541.         (eq style (zone-style next-zone))) ; and it's what we want
  542.        (markup::move-zone next-zone        ; just stretch it to cover
  543.                   start (zone-end next-zone)))
  544.       (t                    ; otherwise make a new one
  545.        (markup::make-zone start end type)))))
  546.  
  547.  
  548. ;; This is a soft version of markup::insert-zone.  If there is an
  549. ;; adjacent changeable markup zone, that zone is stretched instead of
  550. ;; using the given style.  This function is only called by
  551. ;; markup::after-change, when inserting into no zone at all.  This lets
  552. ;; text inserted at the end of a markup zone be in the same zone. It is
  553. ;; selectable with a user variable.
  554. (defun markup::insert-zone-soft (start end type)
  555.   "Suggests that a region be inserted in a particular style."
  556.   (let ((next-zone (zone-at (- start 1)))
  557.     (style (cond
  558.         ((eq type 'insert) markup::insert-style)
  559.         ((eq type 'comment) markup::comment-style)
  560.         ((eq type 'replace) markup::replace-style)
  561.         ((eq type 'delete) markup::delete-style)
  562.         ((eq type 'highlight) markup::highlight-style))))
  563.     (cond ((and next-zone            ; if there is one to the left
  564.         (markup::changeable-p next-zone)) ; and it's changeable
  565.        (markup::move-zone next-zone        ; just stretch it to cover
  566.                   (zone-start next-zone) end))
  567.       ((and (setq next-zone (zone-at end))    ; if there is one to the right
  568.         (eq style (zone-style next-zone))) ; and it's what we want
  569.        (markup::move-zone next-zone        ; just stretch it to cover
  570.                   start (zone-end next-zone)))
  571.       (t                    ; otherwise make a new one
  572.        (markup::make-zone start end type)))))
  573.  
  574.  
  575. ;; This splits a zone into two parts.  It should only be called if
  576. ;; it is certain that the zone entirely overlaps the region specified.
  577. (defun markup::split-zone (old-zone start stop)
  578.   "Splits an existing zone into two parts."
  579.   (let ((new-zone (markup::add-zone        ; make new zone for right side
  580.            stop
  581.            (zone-end old-zone)
  582.            (zone-style old-zone)
  583.            (zone-data old-zone))))
  584.     (set-zone-transient new-zone        ; copy the flags, too.
  585.             (zone-transient-p old-zone))
  586.     (set-zone-read-only new-zone
  587.             (zone-read-only-p old-zone))
  588.     (markup::move-zone old-zone            ; move original zone to left
  589.                (zone-start old-zone) start)))
  590.  
  591.  
  592. ;; This attempts to be polite about other zones, by moving them or
  593. ;; splitting them into two parts before creating a new zone in the middle.
  594. ;; This will only be called after inserting new text into a zone, so
  595. ;; we don't have to worry about partially or completely covered zones.
  596. (defun markup::split-and-insert-zone (start stop type)
  597.   "Splits a zone or region so that a markup zone appears inside it."
  598.   (let ((old-zone (zone-at start))
  599.     (new-zone nil))
  600.     (if (= start (zone-start old-zone))        ; if start is start of old zone
  601.     (markup::move-zone old-zone        ; just move old one to the end
  602.                stop        
  603.                (zone-end old-zone))
  604.       (markup::split-zone old-zone start stop))    ; else split it in two
  605.     (markup::insert-zone start stop type)))    ; make new one for me.
  606.  
  607.  
  608. ;; This removes or splits or stretches the zones in a list until
  609. ;; the specified region contains no zones.  Each zone in the list
  610. ;; must actually be visible from within the region.
  611. (defun markup::zap-zones-in-region (the-zones start stop)
  612.   "Removes all zones in a list from a given region."
  613.   (dolist (z the-zones)                ; look at each one
  614.     (cond ((and (>= (zone-start z) start)
  615.         (<= (zone-end z) stop))        ; if entirely inside
  616.        (markup::delete-zone z))        ; then delete it
  617.       ((>= (zone-start z) start)        ; if overlaps on right
  618.        (markup::move-zone z stop (zone-end z))) ; move it to the right
  619.       ((<= (zone-end z) stop)        ; if overlaps on the left
  620.        (markup::move-zone z (zone-start z) stop)) ; move it to the left
  621.       (t                    ; else it covers entire region
  622.        (markup::split-zone z start stop))))) ; so split it into two parts
  623.  
  624.  
  625. ;; This returns T if there are changeable markup zones contained
  626. ;; in a list of zones.
  627. (defun markup::changeable-zones-in (the-zones)
  628.   "Returns T if there are changeable zones in a zone list."
  629.   (cond ((null the-zones)            ; if empty list, nil
  630.      nil)
  631.     ((markup::changeable-p (car the-zones)) ; if found one, t
  632.      t)
  633.     (t                    ; else keep looking
  634.      (markup::changeable-zones-in (cdr the-zones)))))
  635.  
  636.  
  637. ;; This returns zones which may be affected by a text change in a given
  638. ;; region.  The Epoch function zones-in-region does not detect zones with
  639. ;; both endpoints outside the region.  If any zones completely span the
  640. ;; region, then adding the zones visible at the starting point to the
  641. ;; list within the region will provide all the affected zones.
  642. (defun markup::affected-zones (start end)
  643.   "Returns a list of zones visible in region."
  644.   (let ((zones-at-start (zones-at start))
  645.     (zone-list (zones-in-region start end t)))
  646.     (while zones-at-start
  647.       (if (not (memq (car zones-at-start) zone-list))
  648.     (setq zone-list (cons (car zones-at-start) zone-list)))
  649.       (setq zones-at-start (cdr zones-at-start)))
  650.     zone-list))
  651.  
  652.  
  653. ;; Mark a region with a given style
  654. (defun markup::mark-region-as-style (start stop style)
  655.   "Marks a region as belonging to a given style."
  656.   (let ((the-zones (markup::affected-zones start stop)))
  657.     (if (markup::changeable-zones-in the-zones)
  658.     (error "Region overlaps multiple markup zones."))
  659.     (markup::zap-zones-in-region the-zones start stop)
  660.     (markup::insert-zone start stop style)))
  661.  
  662.  
  663. ;; returns T if region contains original text
  664. (defun markup::bad-zone-to-change (start stop)
  665.   "Returns T if region contains original text."
  666.   (let (flag-str
  667.     beg
  668.     end
  669.     j
  670.     (the-zones (markup::affected-zones start stop))) ; get list of zones
  671.     (if the-zones                ; if got a list,
  672.     (progn
  673.       (setq flag-str (make-string (- stop start) ?x)) ; make a flag string
  674.       (dolist (zone the-zones)        ; look at each zone
  675.         (when (markup::changeable-p zone)    ; if it's okay to change
  676.           (setq beg (- (max (zone-start zone) start) start))
  677.           (setq end (- (min (zone-end zone) stop) start))
  678.           (setq j beg)
  679.           (dotimes (i (- end beg))        ; clear section of flag-str
  680.         (aset flag-str j ?o)
  681.         (incf j))))
  682.       (string-match "x" flag-str))        ; if any x's left, it's bad
  683.       t)))                    ; else, original text == bad
  684.  
  685.  
  686.  
  687.  
  688. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  689. ;;;
  690. ;;; These are the functions that handle implicit text changes.  They deal
  691. ;;; with text that is inserted or deleted by the normal Epoch editing
  692. ;;; commands, such as delete-char, yank, kill, undo, etc.  They are
  693. ;;; enabled in markup mode, and are the primary difference between markup
  694. ;;; mode and regular mode.  These won't work if DEFINE_CHANGE_FUNCTIONS
  695. ;;; was not #defined when Epoch was compiled.
  696. ;;;
  697.  
  698.  
  699. ;; This is called before any text is changed.  If the operation is an
  700. ;; insertion, it does nothing.  If the operation is a deletion or a
  701. ;; replacement, then it is only allowed if the affected text lies
  702. ;; entirely inside Replace, Comment, or Insert markup zones.  Deleting
  703. ;; or replacing text from the original document (which include text
  704. ;; inside a Delete zone) is not allowed while in markup mode.
  705. (defun markup::before-change (pos delpos) 
  706.   "Called automatically before any text changes in markup mode.
  707. It is active while in markup mode, and inactive otherwise.  It detects
  708. delete and replace operations and stops them if they will affect
  709. original text."
  710.   (if (and (/= pos delpos)            ; if deletion or replacement
  711.        (markup::bad-zone-to-change pos delpos)) ; and shouldn't be changed
  712.       (error "You can't modify original text while in markup mode.")))
  713.  
  714.  
  715. ;; This is called after any text is changed.  Since all Delete and
  716. ;; Replace operations which affect original text will be caught and
  717. ;; stopped before the change occurs, this only has to deal with Insert
  718. ;; operations which may require splitting original text and inserting a
  719. ;; new zone.
  720. (defun markup::after-change (pos inspos dellen) 
  721.   "Called automatically after each text change in markup mode.
  722. It is active while in markup mode, and inactive otherwise.  Inserted
  723. text is placed in a new markup zone if necessary.  Deleted and
  724. replaced text is prevented from affecting original text by the
  725. markup::before-change function."
  726.   (let (start-zone)
  727.     (condition-case err
  728.     (cond                    ; see what just happened
  729.      
  730.      ;; INSERTION:
  731.      ;; If inserting text in a changeable markup zone, fine.
  732.      ;; Otherwise, make a new zone containing the inserted text,
  733.      ;; splitting or moving other zones if necessary.
  734.      ((= dellen 0)                ; if insertion
  735.       (setq start-zone (zone-at pos))
  736.       (cond
  737.        ;; inserting inside no zone at all -- make or strech one.
  738.        ((null start-zone)
  739.         (if markup::insert-zone-soft
  740.         (markup::insert-zone-soft pos inspos
  741.                       markup::style-for-new-text)
  742.           (markup::insert-zone pos inspos markup::style-for-new-text)))
  743.        
  744.        ;; inserting inside a changeable markup zone -- do nothing
  745.        ((markup::changeable-p start-zone)
  746.         nil)
  747.        
  748.        ;; else it must be inserting in some other zone -- split it
  749.        (t
  750.         (markup::split-and-insert-zone pos inspos
  751.                        markup::style-for-new-text))))
  752.      
  753.      ;; REPLACEMENT:
  754.      ((= dellen (- inspos pos))        ; if replacement
  755.       nil)                    ; do nothing
  756.      
  757.      ;; DELETION:
  758.      ((= pos inspos)            ; if deletion
  759.       nil)                    ; do nothing
  760.      
  761.      ;; SOMETHING UNEXPECTED:
  762.      ;; I can't think what might happen to alter the text without
  763.      ;; using an insertion, deletion, or replacement.  If something
  764.      ;; does occur, print a message about it and hope for the best.
  765.      (t
  766.       (beep)
  767.       (message "strange change at %s: ins %s, del %s"
  768.            pos inspos dellen)))
  769.       (error
  770.        (beep)
  771.        (message "*After change error: %s" (prin1-to-string err))))))
  772.  
  773.  
  774.  
  775. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  776. ;;;
  777. ;;; These are functions to change and mark text explicitly.  They are
  778. ;;; always active, in markup mode or not.  Since original text should not
  779. ;;; be modified in markup mode, some of these functions are limited when
  780. ;;; markup mode is active.
  781. ;;;
  782.  
  783. ;; Insert a comment zone
  784. (defun markup::insert-comment ()
  785.   "Inserts a markup comment. If the major mode has a comment syntax
  786. defined, the insertion is delimited by the appropriate characters. The
  787. variables markup::comment-begin-at-eol, markup::comment-newline-before,
  788. and markup::comment-newline-after control how the comment is inserted."
  789.   (interactive)
  790.   (let ((c-start (if comment-start comment-start ""))
  791.     (c-end (if comment-end comment-end ""))
  792.     pos)
  793.     (unwind-protect
  794.     (progn
  795.       (setq markup::style-for-new-text 'comment)
  796.       (if markup::comment-begin-at-eol (end-of-line))
  797.       (if markup::comment-newline-before (insert "\n"))
  798.       (insert c-start " ")
  799.       (setq pos (point))
  800.       (insert " " c-end)
  801.       (if markup::comment-newline-after (insert "\n"))
  802.       (goto-char pos))
  803.       (setq markup::style-for-new-text 'insert))))
  804.  
  805.  
  806. ;; Mark a region as highlighted text
  807. (defun markup::mark-region-as-highlight (start stop)
  808.   "Marks the region as text to be highlighted.
  809. This has no editorial meaning, but is occasionally useful."
  810.   (interactive "r")
  811.   (markup::mark-region-as-style start stop 'highlight))
  812.  
  813.  
  814. ;; Mark a region as deleted text.
  815. (defun markup::mark-region-as-delete (start stop)
  816.   "Marks the region as text to be deleted."
  817.   (interactive "r")
  818.   (markup::mark-region-as-style start stop 'delete))
  819.  
  820.  
  821. ;; Mark from point to eol as deleted text.
  822. (defun markup::mark-to-eol-as-delete ()
  823.   "Marks from point to the end of the line as deleted text."
  824.   (interactive)
  825.   (let ((pos (point)))
  826.     (save-excursion
  827.       (end-of-line)
  828.       (markup::mark-region-as-style pos (point) 'delete))))
  829.  
  830.  
  831. ;; Mark a single word as deleted text.  This is useful for replacing
  832. ;; one word with another, which is a common need.
  833. (defun markup::mark-word-as-delete ()
  834.   "Mark the word under point for delete, then move to end of the word.
  835. If point is at the end of a word, the region marked for deletion is from
  836. point to the end of the next word.  The correct way to change a word is
  837. to mark it for deletion and then insert the new word immediately after.
  838. This command marks the current word and moves to the end so new text can
  839. be inserted.  By repeating this command, several words can be marked."
  840.   (interactive)
  841.   (let (start stop)
  842.     (cond
  843.      ((or (looking-at "\\w")        ; on a word now
  844.       (looking-at "\\b\\w"))    ; or at the beginning of it
  845.       (re-search-backward "\\b" nil)    ; go to start of it
  846.       (re-search-forward "\\w*\\b" nil)    ; go to end of it
  847.       (setq start (match-beginning 0))    ; and mark it
  848.       (setq stop (match-end 0))
  849.       (markup::mark-region-as-style start stop 'delete)
  850.       (goto-char stop))            ; and go to end
  851.      ((and (looking-at "\\b")        ; else if at the end of a word
  852.        (setq start (point))        ; (remember where I am)
  853.        (re-search-forward "\\w+\\b" nil)) ; and if another word follows
  854.       (setq stop (match-end 0))        ; then mark it
  855.       (markup::mark-region-as-style start stop 'delete)
  856.       (goto-char stop))            ; and go to end
  857.      (t
  858.       (error "Can't locate word under or before point.")))))
  859.  
  860.  
  861.  
  862. ;; Mark a region as replacement text -- not allowed in markup mode.
  863. (defun markup::mark-region-as-replace (start stop)
  864.   "Marks the region as replaced text.  Disabled in markup mode."
  865.   (interactive "r")
  866.   (if markup-mode
  867.     (error "You can't modify original text while in markup mode.")
  868.     (markup::mark-region-as-style start stop 'replace)))
  869.  
  870.  
  871. ;; Mark a region as inserted text -- not allowed in markup mode.
  872. (defun markup::mark-region-as-insert (start stop)
  873.   "Marks region as inserted text.  Disabled in markup mode."
  874.   (interactive "r")
  875.   (if markup-mode
  876.       (error "You can't modify original text while in markup mode.")
  877.     (markup::mark-region-as-style start stop 'insert)))
  878.  
  879.  
  880. ;; Mark a region as comment text -- not allowed in markup mode.
  881. (defun markup::mark-region-as-comment (start stop)
  882.   "Marks a region as a comment.  Disabled in markup mode."
  883.   (interactive "r")
  884.   (if markup-mode
  885.     (error "You can't modify original text while in markup mode.")
  886.     (markup::mark-region-as-style start stop 'comment)))
  887.  
  888.  
  889. ;; Mark a single word as replacement text.  This is useful for changing
  890. ;; misspelled words, so it is allowed even while in markup mode.  This is
  891. ;; the only case where original text can be changed in markup mode.
  892. (defun markup::mark-word-as-replace ()
  893.   "Mark the word under or before point for replacement.
  894. Used primarily to correct misspelled words.  This is the only function
  895. that allows changes to original text while in markup mode."
  896.   (interactive)
  897.   (let (start stop)
  898.     (save-excursion
  899.       (if (or (looking-at "\\w")
  900.           (re-search-backward "\\w" nil))
  901.       (progn
  902.         (re-search-backward "\\b" nil)
  903.         (re-search-forward "\\w*\\b" nil)
  904.         (setq start (match-beginning 0))
  905.         (setq stop (match-end 0))
  906.         (markup::mark-region-as-style start stop 'replace))
  907.     (error "Can't locate word under or before point.")))))
  908.  
  909.  
  910. ;; Mark current zone as original -- mostly for use outside of markup-mode
  911. (defun markup::mark-change-as-original ()
  912.   "Marks the current markup zone as original text.
  913. It won't let you make additions to original text while in markup mode."
  914.   (interactive)
  915.   (let ((z (zone-at)))
  916.     (cond ((null z)                ; original -- do nothing
  917.        (message "The text under point is already original."))
  918.       ((markup::changeable-p z)        ; changeable zone -- maybe
  919.        (if markup-mode
  920.            (error "You can't modify original text while in markup mode.")
  921.          (markup::delete-zone z)))
  922.       ((markup::original-p z)        ; delete zone -- okay
  923.        (markup::delete-zone z)))))
  924.  
  925.  
  926. ;; Accept changes suggested by current markup zone -- disabled in markup mode.
  927. (defun markup::accept-change ()
  928.   "Implements the current markup change.  Disabled in markup mode.
  929. Text marked for deletion is deleted.  Comments are unchanged.
  930. Text marked for insertion or replacement is re-marked as original.
  931. If markup::advance-after-change is true, point then advances to the
  932. next suggestion."
  933.   (interactive)
  934.   (if markup-mode
  935.       (error "You can't modify original text while in markup mode."))
  936.   (let ((z (zone-at)))
  937.     (cond ((null z)                ; no zone -- do nothing
  938.        nil)
  939.       ((markup::changeable-p z)        ; changeable zone
  940.        (if (eq 'comment (aref (zone-data z) 1)) ; if comment
  941.            (error "%s%s" "Comments can't be Accepted.  They can only be "
  942.               "Destroyed or marked as original.")
  943.          (markup::delete-zone z)))        ; else mark as original
  944.       ((markup::original-p z)        ; delete or highlight zone
  945.        (if (eq 'delete (aref (zone-data z) 1))
  946.            (kill-region (zone-start z) (zone-end z)) ; kill deletes only
  947.          (error "%s%s" "Highlit text can't be Accepted. It can only be "
  948.             "Destroyed or marked as original.")))))
  949.   (if (and markup::advance-after-change        ; skip to next one
  950.        (not (markup::markup-zone-p (zone-at)))) ; unless I'm already on it
  951.       (markup::next-change)))
  952.                 
  953.  
  954. ;; Deletes changes suggested by current markup zone.
  955. (defun markup::destroy-change ()
  956.   "Destroys the current markup change.
  957. Text marked for deletion is re-marked as original.  Comments and
  958. inserted text are deleted.  Replacement text reverts to the original.
  959. If markup::advance-after-change is true, point then advances to the
  960. next suggestion."
  961.   (interactive)
  962.   (let ((z (zone-at)))
  963.     (cond ((null z)                ; no zone -- do nothing
  964.        nil)
  965.       ((markup::original-p z)        ; original text -- unmark it
  966.        (markup::delete-zone z))
  967.       ((markup::changeable-p z)        ; changeable zone -- kill it
  968.        ;; replace zones should revert to original text
  969.        (if (eq 'replace (aref (zone-data z) 1))
  970.            (if (setq old-string (aref (zone-data z) 2))
  971.            ;; if we've got original text to replace it
  972.            (let ((prev-state markup-mode)) ; check current mode
  973.              (unwind-protect        ; and restore later
  974.              (progn
  975.                (markup-mode 0)    ; insert as original text
  976.                ;; kill suggested text
  977.                (kill-region (zone-start z) (zone-end z))
  978.                ;; insert original text
  979.                (insert old-string))
  980.                (if prev-state (markup-mode 1)))) ; restore mode
  981.          ;; else don't have original text for replacement
  982.          (error "I don't know what text this replaced."))
  983.          ;; not replacement zone -- just kill it
  984.          (kill-region (zone-start z) (zone-end z))))))
  985.   (if (and markup::advance-after-change        ; skip to next one
  986.        (not (markup::markup-zone-p (zone-at)))) ; unless I'm already on it
  987.       (markup::next-change)))
  988.  
  989.  
  990. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  991. ;;;
  992. ;;; Functions to move to next or previous markup zone.
  993. ;;;
  994.  
  995. ;; Move forward to the beginning of next markup zone.
  996. (defun markup::next-change ()
  997.   "Moves point to the beginning of the next markup zone.
  998. Returns nil if there is no next zone, else returns new point value."
  999.   (interactive)
  1000.   (let ((pos (point))
  1001.     (newpos nil)
  1002.     (the-zones (markup::zone-list)))    ; ordered list of markup zones
  1003.     (if the-zones
  1004.     (while the-zones            ; find first one > pos
  1005.       (if (> (zone-start (car the-zones)) pos)
  1006.           (progn
  1007.         (setq newpos (zone-start (car the-zones)))
  1008.         (setq the-zones nil))
  1009.         (setq the-zones (cdr the-zones)))))
  1010.     (if newpos
  1011.     (goto-char newpos)
  1012.       (message "No markup zones after point."))
  1013.     newpos))                    ; return new position
  1014.  
  1015.  
  1016. ;; Move backward to beginning of previous markup zone.
  1017. (defun markup::prev-change ()
  1018.   "Moves point to the beginning of the previous markup zone.
  1019. Returns nil if there is no previous zone, else returns new point value."
  1020.   (interactive)
  1021.   (let ((pos (point))
  1022.     (newpos nil)
  1023.     (the-zones (reverse (markup::zone-list)))) ; reverse ordered list
  1024.     (if the-zones
  1025.     (while the-zones            ; find first one < pos
  1026.       (if (< (zone-start (car the-zones)) pos)
  1027.           (progn
  1028.         (setq newpos (zone-start (car the-zones)))
  1029.         (setq the-zones nil))
  1030.         (setq the-zones (cdr the-zones)))))
  1031.     (if newpos
  1032.     (goto-char newpos)
  1033.       (message "No markup zones before point."))
  1034.     newpos))                    ; return new position
  1035.  
  1036.  
  1037. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1038. ;;;
  1039. ;;; Functions which affect all zones in a region.
  1040. ;;;
  1041.  
  1042. ;; accept all changes in region
  1043. (defun markup-accept-region (start end)
  1044.   "This Accepts all markup changes in the region.
  1045. Use caution. Not everything can be undone."
  1046.   (interactive "r")
  1047.   (save-excursion
  1048.     (goto-char end)
  1049.     (let (found)
  1050.       (while (and (setq found (markup::prev-change))
  1051.           (>= found start))
  1052.     (if (markup::acceptable-p (zone-at))
  1053.         (markup::accept-change))))))
  1054.  
  1055.  
  1056. ;; destroy all changes in region
  1057. (defun markup-destroy-region (start end)
  1058.   "This Destroys all markup changes in the region.
  1059. Use caution. Not everything can be undone."
  1060.   (interactive "r")
  1061.   (save-excursion
  1062.     (goto-char end)
  1063.     (let (found)
  1064.       (while (and (setq found (markup::prev-change))
  1065.           (>= found start))
  1066.     (if (markup::destroyable-p (zone-at))
  1067.         (markup::destroy-change))))))
  1068.  
  1069.  
  1070. ;; accept all changes in buffer
  1071. (defun markup-accept-all ()
  1072.   "This Accepts all markup changes in the current buffer.
  1073. Use caution. Not everything can be undone."
  1074.   (interactive)
  1075.   (when (y-or-n-p "Do you really want to Accept all changes? ")
  1076.     (markup-accept-region (point-min) (point-max))))
  1077.  
  1078.  
  1079. ;; destroy all changes in buffer
  1080. (defun markup-destroy-all ()
  1081.   "This Destroys all markup changes in the current buffer.
  1082. Use caution. Not everything can be undone."
  1083.   (interactive)
  1084.   (when (y-or-n-p "Do you really want to Destroy all changes? ")
  1085.     (markup-destroy-region (point-min) (point-max))))
  1086.  
  1087.  
  1088. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1089. ;;;
  1090. ;;; Stuff to save and reload zones.  Modified from save-zones.el,
  1091. ;;; which is included with Epoch.
  1092. ;;;
  1093. ;;; These functions will enable zone information in a buffer to be saved as
  1094. ;;; it is written to file, then restored when the file is loaded again.
  1095. ;;; Markup information is stored at the end of the buffer, commented out
  1096. ;;; appropriately according to the buffer's major mode.  If the major
  1097. ;;; mode does not support comments, the information is still stored. The
  1098. ;;; information being saved consists of the zone's start and end positions,
  1099. ;;; the zone's style, and the line number of the zone start.  The line
  1100. ;;; number is not used, but is included for those miserable wretches who
  1101. ;;; can't use this package but still want a hint as to where the changes are.
  1102. ;;; Also saved is the original text for replacment zones, provided the
  1103. ;;; original text is not longer than markup::save-text-length.  In
  1104. ;;; addition, the position of point after inserting the markup zone
  1105. ;;; header is saved as part of the information. This is done so that if a
  1106. ;;; mailer adds lines to the beginning of the file, the zones can be
  1107. ;;; restored anyway.  Otherwise you'd have to strip off the mail header
  1108. ;;; before you loaded the file.
  1109. ;;;
  1110.  
  1111. ;; Remove zone information from end of buffer.  If no-mod is t,
  1112. ;; do not change the modification status of the buffer.
  1113. (defun markup::purge-zone-info (&optional no-mod)
  1114.   "Removes markup zone information from end of buffer.
  1115. If optional argument is non-nil, don't change modification status."
  1116.   (let ((prev-state markup-mode))        ; save previous markup state
  1117.     (unwind-protect                ; to restore later
  1118.     (let* ((c-start (if comment-start comment-start ""))
  1119.            (c-end (if comment-end comment-end ""))
  1120.            (start
  1121.         (concat "\n" c-start " *-* Markup Info Start *-*" c-end))
  1122.            (finish
  1123.         (concat "\n" c-start " *-* Markup Info End *-*" c-end))
  1124.            (mod-state-before (buffer-modified-p))
  1125.            beg end)
  1126.       (save-excursion
  1127.         (goto-char (point-min))
  1128.         (if (search-forward start nil t)
  1129.         (progn
  1130.           (beginning-of-line)
  1131.           (setq beg (- (point) 1))
  1132.           (if (search-forward finish nil t)
  1133.               (progn
  1134.             (setq end (1+ (point)))    ; get final newline, too.
  1135.             (markup-mode 0)        ; turn markup off for delete
  1136.             (delete-region beg end))))))
  1137.       (if no-mod (set-buffer-modified-p mod-state-before)))
  1138.       (if prev-state (markup-mode 1)))))    ; restore markup state
  1139.  
  1140.  
  1141. ;; This is same as Emacs' what-line, but without printing the message.
  1142. (defun markup::what-line (pos)
  1143.   "Returns the line number of a given position."
  1144.   (save-restriction
  1145.     (widen)
  1146.     (save-excursion
  1147.       (goto-char pos)
  1148.       (beginning-of-line)
  1149.        (1+ (count-lines 1 (point))))))
  1150.  
  1151.  
  1152. ;; Save original text from replace zone in a readable form.  This is
  1153. ;; necessary to handle linefeed characters embedded in the text, which
  1154. ;; do not work correctly for most languages if just embedded as text.
  1155. ;; The string is represented as a list of strings, broken by linefeed
  1156. ;; characters, which are represented with a symbol. Null strings are saved.
  1157. (defun markup::save-text (text c-start c-end)
  1158.   "Writes the original text from a replace zone into the buffer."
  1159.   (let ((i 0)
  1160.     (j 0)
  1161.     (n (- (length text) 1)))
  1162.     (insert "\n" c-start "(" c-end)        ; always save something
  1163.     (if (and text                ; even if it's an empty list
  1164.          (< n markup::save-text-length))
  1165.     (progn
  1166.       (while (setq i (string-match "\n" text j))
  1167.         (if (/= i j)
  1168.         (insert "\n" c-start
  1169.             (prin1-to-string (substring text j i))
  1170.             c-end))
  1171.         (insert "\n" c-start "NL" c-end)
  1172.         (setq j (+ i 1)))
  1173.       (if (< j n)
  1174.           (insert "\n" c-start
  1175.               (prin1-to-string (substring text j (+ n 1)))
  1176.               c-end))))
  1177.     (insert "\n" c-start ")" c-end)))        ; close the list
  1178.  
  1179.  
  1180. ;; Save markup zones at end of the buffer.
  1181. (defun markup::save-zones ()
  1182.   "Saves markup zone information at the end of the buffer."
  1183.   (let ((here nil)                ; scratch
  1184.     (prev-state markup-mode))        ; save previous markup state
  1185.     (unwind-protect                ; to restore later
  1186.     (let (( zlist (markup::zone-list))
  1187.           (c-start (if comment-start comment-start ""))
  1188.           (c-end (if comment-end comment-end "")))
  1189.       (markup-mode 0)            ; turn markup off temporarily
  1190.       (markup::purge-zone-info)        ; clear zone info from buffer
  1191.       (when zlist                ; only deal with markup zones
  1192.         (save-excursion
  1193.           (goto-char (point-max))
  1194.           ;; begin markup section
  1195.           (insert "\n" c-start " *-* Markup Info Start *-*" c-end)
  1196.           (setq here (point))        ; save offset for mail headers
  1197.           (insert "\n" c-start "Ofs " (prin1-to-string here) " " c-end)
  1198.           (dolist (z zlist)            ; write out each zone
  1199.         (let ((start (zone-start z))
  1200.               (end (zone-end z))
  1201.               (z-data (zone-data z)))              
  1202.           (insert "\n" c-start        ; write the basic zone info
  1203.               (prin1-to-string
  1204.                (list start end
  1205.                  (aref z-data 1)
  1206.                  'Line (markup::what-line start)))
  1207.               c-end)
  1208.           ;; write the original text for replace zones
  1209.           (if (eq (aref z-data 1) 'replace)
  1210.               (markup::save-text (aref z-data 2) c-start c-end))))
  1211.           ;; end of markup section
  1212.           (insert "\n" c-start " *-* Markup Info End *-*" c-end "\n"))))
  1213.       (if prev-state (markup-mode 1))))        ; restore markup state
  1214.   nil)                        ; continue with write hooks
  1215.  
  1216.  
  1217. ;; Retrieve original text for replace zone from buffer.
  1218. (defun markup::restore-text (c-start c-end)
  1219.   "Returns original text for a replace zone, as found in the buffer."
  1220.   (let ((out nil)
  1221.     (start (concat "\n" c-start))
  1222.     (end (regexp-quote (concat ")" c-end))))
  1223.     (search-forward (concat c-start "(" c-end) )
  1224.     (while (and (search-forward start nil t)
  1225.         (not (looking-at end)))
  1226.       (setq out (cons (read (current-buffer)) out))
  1227.       (backward-char 1))
  1228.     (mapconcat '(lambda (x) (if (stringp x) x "\n")) (reverse out) "")))
  1229.  
  1230.  
  1231. ;; Restore zones to current buffer based on zone information at end of file.
  1232. ;; This is normally called only when files are loaded.
  1233. (defun markup::restore-zones ()
  1234.   "Restores markup zones based on information at end of buffer."
  1235.   (condition-case nil
  1236.       (let ((c-start (if comment-start comment-start ""))
  1237.         (c-end (if comment-end comment-end ""))
  1238.         (offset 0)
  1239.         look-here re-look-here here-now here-then
  1240.         hunt-for start end data type old-text style)
  1241.     (setq hunt-for
  1242.           (concat "\n" c-start " *-* Markup Info Start *-*" c-end))
  1243.     (setq look-here
  1244.           (concat "\n" c-start "Ofs "))
  1245.     (setq re-look-here
  1246.           (regexp-quote look-here))
  1247.     (save-excursion
  1248.       (goto-char (point-min))
  1249.       (when (search-forward hunt-for nil t)
  1250.         ;; compute offset for mail headers
  1251.         (when (looking-at re-look-here)
  1252.           (setq here-now (point))
  1253.           (search-forward look-here nil t)
  1254.           (setq here-then (read (current-buffer)))
  1255.           (setq offset (if (>= here-now here-then)
  1256.                    (- here-now here-then)
  1257.                  0)))
  1258.         ;; now load all markup ranges
  1259.         (while (search-forward "(" nil t)
  1260.           (backward-char 1)
  1261.           (setq data (read (current-buffer))) ; read data as list
  1262.           (when (listp data)
  1263.         (setq start (+ offset (nth 0 data)))
  1264.         (setq end (+ offset (nth 1 data)))
  1265.         (setq type (nth 2 data))
  1266.         (setq old-text
  1267.               (if (eq 'replace type)    ; if replace zone, get text
  1268.               (markup::restore-text c-start c-end)))
  1269.         (markup::make-zone start end type old-text)))))
  1270.     (markup::purge-zone-info t)        ; remove zone stuff
  1271.     (set-buffer-modified-p nil))        ; brand new file
  1272.     ;; catch errors here
  1273.     (error
  1274.      (beep)
  1275.      (message "Warning: Markup zone info is obsolete or garbled."))))
  1276.  
  1277.  
  1278. ;;;
  1279. ;;; Install hooks for automatic save/restore of markup zones.
  1280. ;;;
  1281.  
  1282. (or (memq 'markup::save-zones write-file-hooks)
  1283.     (setq write-file-hooks (cons 'markup::save-zones write-file-hooks)))
  1284. (or (memq 'markup::restore-zones find-file-hooks)
  1285.     (setq find-file-hooks (cons 'markup::restore-zones find-file-hooks)))
  1286.  
  1287.  
  1288. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1289. ;;;
  1290. ;;; Set up bindings for markup functions.  This will change the global
  1291. ;;; keymap so that the markup commands work even when not in markup mode.
  1292. ;;; The only key that is rebound while in markup mode is the one to insert
  1293. ;;; comments, and that is rebound locally.
  1294. ;;;
  1295.  
  1296. ;; Bind markup commands to keys following markup prefix
  1297. (define-prefix-command 'markup::command-prefix-key)
  1298. (define-key 'markup::command-prefix-key "k" 'markup::mark-to-eol-as-delete)
  1299. (define-key 'markup::command-prefix-key "d" 'markup::mark-region-as-delete)
  1300. (define-key 'markup::command-prefix-key "=" 'markup::mark-word-as-delete)
  1301. (define-key 'markup::command-prefix-key "h" 'markup::mark-region-as-highlight)
  1302. (define-key 'markup::command-prefix-key ";" 'markup::mark-region-as-comment)
  1303. (define-key 'markup::command-prefix-key "i" 'markup::mark-region-as-insert)
  1304. (define-key 'markup::command-prefix-key "r" 'markup::mark-region-as-replace)
  1305. (define-key 'markup::command-prefix-key "$" 'markup::mark-word-as-replace)
  1306. (define-key 'markup::command-prefix-key "o" 'markup::mark-change-as-original)
  1307. (define-key 'markup::command-prefix-key "A" 'markup::accept-change)
  1308. (define-key 'markup::command-prefix-key "D" 'markup::destroy-change)
  1309. (define-key 'markup::command-prefix-key "n" 'markup::next-change)
  1310. (define-key 'markup::command-prefix-key "p" 'markup::prev-change)
  1311. (define-key 'markup::command-prefix-key "\C-n" 'markup::next-change)
  1312. (define-key 'markup::command-prefix-key "\C-p" 'markup::prev-change)
  1313.  
  1314. ;; Copy function definition into variable definition, too,
  1315. ;; So that the documentation function can get at it.
  1316. (setq markup::command-prefix-key
  1317.       (symbol-function 'markup::command-prefix-key))
  1318. (put 'markup::command-prefix-key
  1319.      'variable-documentation
  1320.      "This is an internal variable.  Don't screw with it.")
  1321.  
  1322.  
  1323. ;; Bind local key to insert a markup comment
  1324. (defun markup::turn-on-comment-key ()
  1325.   "This binds a key to insert markup comments."
  1326.   (setq markup::old-local-map            ; old map is either
  1327.     (or (current-local-map)            ; whatever is in use
  1328.         (make-sparse-keymap)))        ; or a blank one
  1329.   (use-local-map (copy-keymap markup::old-local-map))
  1330.   (if (lookup-key (current-local-map) "\M-;")
  1331.       (local-unset-key "\M-;"))
  1332.   (local-set-key "\M-;" 'markup::insert-comment))
  1333.  
  1334.  
  1335. ;; Rebind comment key to original function
  1336. (defun markup::turn-off-comment-key ()
  1337.   "This rebinds the markup comment key to its original function."
  1338.   (when markup::old-local-map
  1339.     (use-local-map markup::old-local-map)
  1340.     (setq markup::old-local-map nil)))
  1341.  
  1342.  
  1343. ;; Finally, install key bindings in the global key map, with C-c prefix.
  1344. ;; Individual users may prefer to change this.  I like it here.
  1345. (define-key (current-global-map) "\C-c" 'markup::command-prefix-key)
  1346.  
  1347. ;;;
  1348. ;;; End of markup package.
  1349. ;;;
  1350. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1351.