Mercurial > hg > xemacs-beta
diff lisp/hyperbole/kotl/kimport.el @ 0:376386a54a3c r19-14
Import from CVS: tag r19-14
author | cvs |
---|---|
date | Mon, 13 Aug 2007 08:45:50 +0200 |
parents | |
children | c53a95d3c46d |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lisp/hyperbole/kotl/kimport.el Mon Aug 13 08:45:50 2007 +0200 @@ -0,0 +1,588 @@ +;;!emacs +;; +;; FILE: kimport.el +;; SUMMARY: Convert and insert other outline file formats into koutlines. +;; USAGE: GNU Emacs V19 Lisp Library +;; KEYWORDS: data, outlines, wp +;; +;; AUTHOR: Bob Weiner & Kellie Clark +;; +;; ORIG-DATE: 15-Nov-93 at 11:57:05 +;; LAST-MOD: 1-Nov-95 at 23:19:09 by Bob Weiner +;;; ************************************************************************ +;;; Other required Elisp libraries +;;; ************************************************************************ + +;; kfile.el requires kotl-mode.el which requires kimport.el. +(require 'wrolo) + +;;; ************************************************************************ +;;; Public variables +;;; ************************************************************************ + +;; kimport:mode-alist and kimport:suffix-alist are defined in +;; "../hyperbole.el". + +;;; ************************************************************************ +;;; Public functions +;;; ************************************************************************ + +;;;###autoload +(defun kimport:file (import-from output-to &optional children-p) + "Import a buffer or file IMPORT-FROM into the koutline in buffer or file OUTPUT-TO. + +Any suffix in IMPORT-FROM's buffer name is used to determine the type of +importation. All others are imported as text, one paragraph per cell. + +See the documentation for the variable, `kimport:suffix-alist' for +information on specific importation formats." + (interactive "FImport from buffer/file: \nFInsert into koutline buffer/file: \nP") + (let ((import-buf-name + (cond ((or (bufferp import-from) + (get-buffer import-from)) + (buffer-name (get-buffer import-from))) + ((get-file-buffer import-from) + (buffer-name (get-file-buffer import-from))) + ((stringp import-from) + (file-name-nondirectory import-from)) + (t (error "(kimport:buffer): `%s' is an invalid `import-from' argument")))) + (function)) + + (set-buffer import-buf-name) + (if (setq function (cdr (assq major-mode kimport:mode-alist))) + nil + (let ((import-suffix (if (string-match "\\..+\\'" import-buf-name) + (match-string 0 import-buf-name))) + (suffix-alist kimport:suffix-alist) + suffix-regexp) + (while (and import-suffix suffix-alist) + (setq suffix-regexp (car (car suffix-alist)) + function (cdr (car suffix-alist)) + suffix-alist (cdr suffix-alist)) + (if (string-match suffix-regexp import-suffix) + nil + (setq function nil))) + (if function nil (setq function (cdr (assq t kimport:mode-alist)))))) + (funcall function import-from output-to children-p))) + +;;; Augment right-side numbered files, blank line between cells +;;; + +;;;###autoload +(defun kimport:aug-post-outline (import-from output-to &optional children-p) + "Insert Augment outline statements from IMPORT-FROM into koutline OUTPUT-TO. +Displays and leaves point in OUTPUT-TO. See documentation for +`kimport:initialize' for valid values of IMPORT-FROM and OUTPUT-TO and for +an explanation of where imported cells are placed. + +If OUTPUT-TO is a new koutline, the first statement inserted will be the +first cell. Otherwise, it will be the successor of the current cell. + +Each statement to be imported is delimited by an Augment relative id at the +end of the statement. \"1\" = level 1, \"1a\" = level 2 in outline and so +on." + (interactive "FImport from Augment post-numbered buffer/file: \nFBuffer/file to insert cells into: \nP") + (let ((output-level 1) (klabel "1") + initially-empty-output no-renumber orig-point count total) + ;; Don't change the order of import-from and output-to inits here. + (setq import-from (kimport:copy-and-set-buffer import-from) + output-to (kimport:initialize output-to) + orig-point (point) + initially-empty-output (zerop (- (point-max) (point-min))) + no-renumber (or initially-empty-output + (not (if children-p + (kcell-view:child-p) + (kcell-view:sibling-p))))) + + (if (eq import-from output-to) + (error "(kimport:aug-post-outline): Import and output buffers may not be the same.")) + + (set-buffer import-from) + (show-all) + (save-excursion + (goto-char (point-min)) + ;; Total number of Augement statements. + (setq total (read (count-matches + " +\\([0-9][0-9a-z]*\\)\n\\(\n\\|\\'\\)"))) + (if initially-empty-output + nil + ;; Insert first cell as sibling of current cell. + (set-buffer output-to) + (if children-p + ;; Insert as children. + (progn (setq klabel (klabel:child (kcell-view:label)) + output-level (klabel:level klabel)) + ;; Move to end of this cell since cell insertion will + ;; occur at point. + (goto-char (kcell-view:end))) + ;; Insert as successors. + (setq klabel (klabel:increment (kcell-view:label)) + output-level (klabel:level klabel)) + ;; Move to start of line of next tree since cell insertion will occur + ;; at point. + (goto-char (kotl-mode:tree-end)))) + (setq count (kimport:aug-post-statements + import-from output-to klabel output-level 1 0 total))) + (pop-to-buffer output-to) + (kfile:narrow-to-kcells) + (if no-renumber nil (klabel-type:update-labels klabel)) + (goto-char orig-point) + (if (kotl-mode:buffer-empty-p) + nil + (kotl-mode:to-valid-position)) + (message "Imported %d of %d Augment statements." count total))) + +;;; +;;; Emacs outliner style files, leading '*' cell delimiters +;;; + +;;;###autoload +(defun kimport:star-outline (import-from output-to &optional children-p) + "Insert star outline nodes from IMPORT-FROM into koutline OUTPUT-TO. +Displays and leaves point in OUTPUT-TO. See documentation for +`kimport:initialize' for valid values of IMPORT-FROM and OUTPUT-TO and for +an explanation of where imported cells are placed. + +\"* \" = level 1, \"** \" = level 2 in outline and so on." + (interactive "FImport from star delimited cells buffer/file: \nFBuffer/file to insert cells into: \nP") + (let ((output-level 1) (klabel "1") + initially-empty-output no-renumber orig-point count total) + ;; Don't change the order of import-from and output-to inits here. + (setq import-from (kimport:copy-and-set-buffer import-from) + output-to (kimport:initialize output-to) + orig-point (point) + initially-empty-output (zerop (- (point-max) (point-min))) + no-renumber (or initially-empty-output + (not (if children-p + (kcell-view:child-p) + (kcell-view:sibling-p))))) + + (if (eq import-from output-to) + (error "(kimport:star-outline): Import and output buffers may not be the same.")) + + (set-buffer import-from) + (show-all) + (save-excursion + (goto-char (point-min)) + ;; If initial text in buffer is not an star outline node, add a star to + ;; make it one, so it is not deleted from the import. + (if (not (looking-at "[ \t]*\\*")) + (insert "* ")) + (goto-char (point-min)) + ;; Total number of top-level cells. + (setq total (read (count-matches "^[ \t]*\\*[ \t\n]"))) + (if initially-empty-output + nil + ;; Insert first cell as sibling of current cell. + (set-buffer output-to) + (if children-p + ;; Insert as children. + (progn (setq klabel (klabel:child (kcell-view:label)) + output-level (klabel:level klabel)) + ;; Move to end of this cell since cell insertion will + ;; occur at point. + (goto-char (kcell-view:end))) + ;; Insert as successors. + (setq klabel (klabel:increment (kcell-view:label)) + output-level (klabel:level klabel)) + ;; Move to start of line of next tree since cell insertion will occur + ;; at point. + (goto-char (kotl-mode:tree-end)))) + (setq count (kimport:star-entries + import-from output-to klabel output-level 1 0 total))) + (pop-to-buffer output-to) + (kfile:narrow-to-kcells) + (if no-renumber nil (klabel-type:update-labels klabel)) + (goto-char orig-point) + (if (kotl-mode:buffer-empty-p) + nil + (kotl-mode:to-valid-position)) + (message "Imported %d of %d star outline trees." count total))) + +;;; +;;; Generic text file import or koutline insertion. +;;; + +;;;###autoload +(defun kimport:text (import-from output-to &optional children-p) + "Insert text paragraphs from IMPORT-FROM into koutline OUTPUT-TO. +Displays and leaves point in OUTPUT-TO. See documentation for +`kimport:initialize' for valid values of IMPORT-FROM and OUTPUT-TO and for +an explanation of where imported cells are placed. + +Text paragraphs are imported as a sequence of same level cells. Koutlines +are imported with their structure intact. + +The variable, 'paragraph-start,' is used to determine paragraphs." + (interactive "FImport from text/koutline buffer/file: \nFInsert cells into koutline buffer/file: \nP") + (let ((klabel "1") (output-level 1) (count 0) initially-empty-output + no-renumber orig-point total) + ;; Don't change the order of import-from and output-to inits here. + (setq import-from (kimport:copy-and-set-buffer import-from) + output-to (kimport:initialize output-to) + orig-point (point) + initially-empty-output (zerop (- (point-max) (point-min))) + no-renumber (or initially-empty-output + (not (if children-p + (kcell-view:child-p) + (kcell-view:sibling-p))))) + + (if (eq import-from output-to) + (error "(kimport:text): Import and output buffers may not be the same.")) + + (set-buffer import-from) + (let ((kotl-import (eq major-mode 'kotl-mode)) + visible-cells) + (save-excursion + (if initially-empty-output + nil + ;; Insert first cell as sibling of current cell. + (set-buffer output-to) + (if children-p + ;; Insert as children. + (progn (setq klabel (klabel:child (kcell-view:label)) + output-level (klabel:level klabel)) + ;; Move to end of this cell since cell insertion will + ;; occur at point. + (goto-char (kcell-view:end))) + ;; Insert as successors. + (setq klabel (klabel:increment (kcell-view:label)) + output-level (klabel:level klabel)) + ;; Move to start of line of next tree since cell insertion will occur + ;; at point. + (goto-char (kotl-mode:tree-end))) + (set-buffer import-from)) + + (if kotl-import + ;; Importing from a koutline, so handle specially. + (progn (kotl-mode:beginning-of-buffer) + ;; Total number of cells. + (setq total (read (count-matches "[\n\r][\n\r]")) + visible-cells (read (count-matches "\n\n")) + count (save-excursion + ;; Incredible non-local exit to ensure that + ;; recursion ends at the right time. + (catch 'end + (kimport:kcells import-from output-to klabel + output-level 1 + count total))))) + + (show-all) + (goto-char (point-min)) + ;; Total number of paragraphs. + (setq total (read (count-matches paragraph-start)) + count (kimport:text-paragraphs import-from output-to klabel + output-level count total)))) + (pop-to-buffer output-to) + (kfile:narrow-to-kcells) + (if no-renumber nil (klabel-type:update-labels klabel)) + (goto-char orig-point) + (if (kotl-mode:buffer-empty-p) + nil + (kotl-mode:to-valid-position)) + (if kotl-import + (message "Imported %d of %d visible cells from a %d cell outline." + count visible-cells total) + (message "Imported %d of %d paragraphs." count total))))) + +;;; ************************************************************************ +;;; Private functions - Don't call these functions from outside of this +;;; module or you may misuse them and cause data corruption. +;;; ************************************************************************ + +(defun kimport:aug-label-lessp (label1 label2) + "Return non-nil iff Augment-style LABEL1 is less than LABEL2." + (let ((lev1 (klabel:level-alpha label1)) + (lev2 (klabel:level-alpha label2))) + (cond ((< lev1 lev2)) + ((= lev1 lev2) (string-lessp label1 label2)) + (t nil)))) + +(defun kimport:aug-post-statements (import-from output-to klabel output-level + import-level count total) + "Insert post-numbered Augment statements (contents only) from IMPORT-FROM into existing OUTPUT-TO. + +KLABEL is the label to use for the first imported statement. +OUTPUT-LEVEL is the level at which to insert the first statement. +IMPORT-LEVEL is the depth of the current statement in the import file, +\(initially 1). + +COUNT of inserted cells starts at 0. TOTAL is the total number of statements +in IMPORT-FROM, used to show a running tally of the imported statements." + (set-buffer import-from) + (let ((cell-end-regexp " +\\([0-9][0-9a-z]*\\)\n\\(\n+\\|\\'\\)") + contents start subtree-p end end-contents statement-level + child-label) + ;; While find cells at import-level or deeper ... + (while (and (setq start (point)) + (re-search-forward cell-end-regexp nil t) + (<= import-level + (setq statement-level + (klabel:level-alpha + (buffer-substring + (match-beginning 1) (match-end 1)))))) + (setq end-contents (match-beginning 0) + end (match-end 0)) + (goto-char start) + (skip-chars-forward " ") + (setq contents (kimport:unindent-region (point) end-contents)) + (goto-char end) + (setq subtree-p (save-excursion + (if (re-search-forward cell-end-regexp nil t) + (< statement-level + (klabel:level-alpha + (buffer-substring + (match-beginning 1) (match-end 1))))))) + (save-excursion + (set-buffer output-to) + ;; Add the cell starting at point. + (kview:add-cell klabel output-level contents nil t) + (if subtree-p (setq child-label (klabel:child klabel))) + (message "%d of %d statements converted..." + (setq count (1+ count)) total) + (setq klabel (klabel:increment klabel))) + ;; + ;; Current buffer returns to `import-from' here. + ;; Handle each sub-level through recursion. + (if subtree-p + ;; Subtree exists so insert its cells. + (setq count + (kimport:aug-post-statements + import-from output-to child-label (1+ output-level) + (1+ import-level) count total)))) + (goto-char start)) + count) + +(defun kimport:copy-and-set-buffer (source) + "Copy and untabify SOURCE, set copy buffer as current buffer for this command and return the copy buffer. +SOURCE may be a buffer name, a buffer or a file name. +If SOURCE buffer name begins with a space, it is not copied under the +assumption that it already has been. If SOURCE is a koutline, it is not +copied since there is no need to copy it to import it." + ;; This buffer name format is used so that we can easily + ;; extract any file name suffix from the buffer name. + (setq source (set-buffer (or (get-buffer source) + (find-file-noselect source)))) + (let ((mode (or (if (boundp 'kotl-previous-mode) kotl-previous-mode) + major-mode)) + copy) + (if (or (eq mode 'kotl-mode) + (= ?\ (aref (buffer-name source) 0))) + source + (setq copy (get-buffer-create + (concat " " (if (string-match ".+[|<]" (buffer-name)) + (substring (buffer-name) + 0 (1- (match-end 0))) + (buffer-name))))) + (set-buffer copy) + (setq buffer-read-only nil + major-mode mode) + (erase-buffer) + (insert-buffer source) + (untabify (point-min) (point-max)) + ;; Ensure buffer ends with a newline so that we don't miss the last + ;; element during the import. + (goto-char (point-max)) + (if (/= (preceding-char) ?\n) (insert "\n")) + (set-buffer-modified-p nil) + copy))) + +(defun kimport:initialize (output-to) + "Setup to import elements into koutline OUTPUT-TO. +Return OUTPUT-TO buffer and set current buffer for the current command +to OUTPUT-TO. + +OUTPUT-TO may be a buffer, buffer-name or file name. If OUTPUT-TO exists +already, it must be a koutline or an error will be signaled. For an existing +OUTPUT-TO, the text cells are inserted after the cell at point or after the +first cell for a newly loaded koutline. If OUTPUT-TO is nil, the current +buffer is used. + +If OUTPUT-TO is an existing koutline, the first cell imported will be added +as the successor of the current cell. If an existing file is read in as +OUTPUT-TO within this function, point is left at the end of this buffer so +that imported cells will be appended to the buffer. For a new file, this +means the first cell imported will become the first outline cell. + +If a non-nil third argument, CHILDREN-P, is given to the caller of this +function and OUTPUT-TO contains at least one cell, then the imported cells +will be added as children of the cell where this function leaves point +\(either the current cell or for a newly read in outline, the last cell)." + (let* ((output-existing-buffer-p + (if output-to + (or (get-buffer output-to) (get-file-buffer output-to)))) + (output-exists-p + (if output-to + (or output-existing-buffer-p (file-exists-p output-to)) + ;; current buffer will be used for output and it exists. + t))) + (setq output-to (if output-to + (or (get-buffer output-to) + (find-file-noselect output-to)) + (current-buffer))) + (set-buffer output-to) + (if output-exists-p + (if (eq major-mode 'kotl-mode) + (if (kotl-mode:buffer-empty-p) + nil + ;; Make imported cells be appended if the output buffer was + ;; just read in. + (if output-existing-buffer-p nil (goto-char (point-max))) + (kotl-mode:to-valid-position)) + (error + "(kimport:initialize): Second arg, %s, must be a koutline file." + (buffer-name output-to))) + (if (eq major-mode 'kotl-mode) + nil + (setq kview nil) + (kotl-mode)) + (delete-region (point-min) (point-max)))) + output-to) + +(defun kimport:kcells (import-from output-to klabel output-level + import-level count total) + "Insert visible koutline cells (contents and attributes) from IMPORT-FROM into existing OUTPUT-TO. + +KLABEL is the label to use for the first imported cell. +OUTPUT-LEVEL is the level at which to insert the first cell. +IMPORT-LEVEL is the depth of the current cell in the import file, +\(initially 1). + +COUNT of inserted cells starts at 0. TOTAL is the total number of cells +in IMPORT-FROM, used to show a running tally of the imported cells." + (set-buffer import-from) + (goto-char (kcell-view:start)) + (let ((again t) contents subtree-p child-label) + ;; While find cells at import-level or deeper ... + (while (<= import-level (kcell-view:level)) + (setq subtree-p (kcell-view:child-p nil t) + contents (kcell-view:contents)) + (goto-char (kcell-view:end-contents)) + (save-excursion + (set-buffer output-to) + ;; Add the cell starting at point. + (kview:add-cell klabel output-level contents nil t) + (if subtree-p (setq child-label (klabel:child klabel))) + (message "%d of %d cells inserted..." + (setq count (1+ count)) total) + (setq klabel (klabel:increment klabel))) + ;; + ;; Current buffer returns to `import-from' here. + ;; Handle each sub-level through recursion. + (if (and (setq again (kcell-view:next t)) subtree-p) + ;; Subtree exists so insert its cells. + (setq count + (kimport:kcells + import-from output-to child-label (1+ output-level) + (1+ import-level) count total))) + (if again nil (throw 'end count)))) + count) + +(defun kimport:star-entries (import-from output-to klabel output-level + import-level count total) + "Insert visible star outline entries from IMPORT-FROM into existing OUTPUT-TO. + +KLABEL is the label to use for the first imported entry. +OUTPUT-LEVEL is the level at which to insert the first entry. +IMPORT-LEVEL is the depth of the current entry in the import file, +\(initially 1). + +COUNT of inserted entries starts at 0. TOTAL is the total number of entries +in IMPORT-FROM, used to show a running tally of the imported entries." + (set-buffer import-from) + (let ((start (point)) + (rolo-entry-regexp "^[ \t]*\\(\\*+\\)") + subtree-p end contents node-level child-label) + ;; While find cells at import-level or deeper ... + (while (and (re-search-forward rolo-entry-regexp nil t) + (<= import-level + (setq node-level + (length + (buffer-substring + (match-beginning 1) (match-end 1)))))) + (skip-chars-forward " \t") + (setq start (point) + end (rolo-to-entry-end) + subtree-p (if (looking-at rolo-entry-regexp) + (< node-level + (length (buffer-substring + (match-beginning 1) (match-end 1)))))) + (skip-chars-backward "\n\r") + (setq contents (kimport:unindent-region start (point))) + (save-excursion + (set-buffer output-to) + ;; Add the cell starting at point. + (kview:add-cell klabel output-level contents nil t) + (if subtree-p (setq child-label (klabel:child klabel))) + (message "%d of %d trees converted..." + (if (= node-level 1) (setq count (1+ count)) count) + total) + (setq klabel (klabel:increment klabel))) + ;; + ;; Current buffer returns to `import-from' here. + (goto-char end) + ;; + ;; Handle each sub-level through recursion. + (if subtree-p + ;; Subtree exists so insert its cells. + (setq count + (kimport:star-entries import-from output-to child-label + (1+ output-level) (1+ import-level) + count total)))) + (goto-char start)) + count) + +(defun kimport:text-paragraphs (import-from output-to klabel + output-level count total) + "Insert text paragraphs from IMPORT-FROM into existing OUTPUT-TO. +First cell is inserted with KLABEL at OUTPUT-LEVEL, as the sibling of the +previous cell, with the COUNT of inserted paragraphs starting at 0. TOTAL is +the total number of paragraphs in IMPORT-FROM, used to show a running tally +of the imported paragraphs. + +The variable, 'paragraph-start' is used to determine paragraphs." + (set-buffer import-from) + (let* ((count 0) start end contents) + ;; Next line is needed when importing into an existing kview. + (goto-char (point-min)) + ;; Move past blank lines at point. + (skip-chars-forward " \t\n\r") + (beginning-of-line) + (while (and (setq start (point) + end (re-search-forward paragraph-start nil t)) + (/= start end)) + (setq contents (kimport:unindent-region start end)) + (set-buffer output-to) + ;; Add the cell starting at point. + (kview:add-cell klabel output-level contents nil t) + (setq count (1+ count)) + (message "%d of %d paragraphs converted..." + count total) + (setq klabel (klabel:increment klabel)) + (set-buffer import-from) + (goto-char end) + ;; Move past blank lines separating paragraphs. + (skip-chars-forward " \t\n\r") + (beginning-of-line)) + (message "%d of %d paragraphs converted" count total) + count)) + +(defun kimport:unindent-region (start end) + "Calculate indent based upon the second line within the region START to END. +Remove the indent and return the remaining region as a string." + (save-excursion + (let (indent-regexp) + (goto-char start) + ;; Remove leading indent from lines in paragraph. Base paragraph + ;; indent on the 2nd paragraph line since the first line might be + ;; further indented or outdented. + (setq indent-regexp + (if (re-search-forward "[\n\r][ \t]+" end t) + (concat "^" (make-string (current-column) ?\ )))) + (if indent-regexp + (hypb:replace-match-string + indent-regexp (buffer-substring start end) "" t) + (buffer-substring start end))))) + +(provide 'kimport) +