view lisp/cvs-status.el @ 98644:e1cc41b9282d

2008-10-12 Carsten Dominik <carsten.dominik@gmail.com> * org.el (org-edit-fixed-width-region): Exclude final newline from picture area. * org-export-latex.el (org-export-latex-subcontent): Add labels to sections, to make internal links work. (org-export-latex-fontify-headline): Do not remove all text properties, to make sure that target properties survive this process. * org-exp.el (org-export-preprocess-string): Change sequence of modifications, to make sure links are prepared before the LaTeX conversions do happen. * org-attach.el (org-attach-delete-all): Renamed from `org-attch-delete'. Add a security query before deleting the entire directory. New optional argument FORCE can overrule the security query. (org-attach-delete-one): New command. * org-attach.el (org-attach-file-list): Fix bug with directory. * org.el (org-apps-regexp-alist): New function. (org-file-apps): Add auto-mode to the default value. (org-open-file): Use the new structure of org-file-apps. * org-attach.el (org-attach): Support the new keys. (org-attach-method): New option. * org-bbdb.el (org-bbdb-anniversaries): Fix but with 29 Feb/1 March. * org.el (org-remove-uniherited-tags): Fix reverse interpretation of the list value o `org-use-tag-inheritance'. * org-attach.el (org-attach-auto-tag): New option. (org-attach-tag, org-attach-untag): New functions. (org-attach-attach, org-attach-new, org-attach-sync): Call `org-attach-tag'. (org-attach-delete): Call `org-attach-untag'. * org-attach.el: New file. * org-table.el (orgtbl-self-insert-command): Make this work for the keypad as well. * org.el (org-add-log-setup): Limit searc for drawers to entry text, not to subtree. * org-clock.el (org-clock-heading-for-remember): New variable. (org-clock-in): Set `org-clock-heading-for-remember'. 2008-10-12 James TD Smith <ahktenzero@mohorovi.cc> * org-remember.el (org-remember-apply-template): Add new expansions: %k, %K for currently clocked task and a link to the currently clocked task, and %< to file notes in the currently clocked task. 2008-10-12 Carsten Dominik <dominik@science.uva.nl> * org-export-latex.el (org-export-latex-make-header): Also insert the content of the property :latex-header-extra. * org-exp.el (org-infile-export-plist): Put the content of #+LATEX_HEADER: into the property :latex-header-extra. * org-colview.el (org-columns-get-format-and-top-level): Remove resetting the marker. * org-colview-xemacs.el (org-columns-get-format-and-top-level): Remove resetting the marker. * org.el (org-entry-property-inherited-from): Improve docstring. (org-entry-get-with-inheritance): Reset marker before starting the search. * org-exp.el (org-infile-export-plist): Allow multiple STYLE lines. * org.el (org-entry-get-multivalued-property) (org-entry-protect-space, org-entry-restore-space): New functions. (org-file-apps-defaults-macosx): Let postscript files be opened by preview. (org-time-stamp-inactive): Call `org-time-stamp'. (org-time-stamp): New argument `inactive'. Also edit inacive stamps. Convert time stamp type. (org-open-file): Interpret the `default' value for the `command' in `org-file-apps'. * org-id.el (org-id-int-to-b36-one-digit) (org-id-b36-to-int-one-digit, org-id-int-to-b36) (org-id-b36-to-int, org-id-time-to-b36): Modified from b62 to b36. * org-id.el (org-id-reverse-string): New function. (org-id-new): Use `org-id-reverse-string' to make sure the beginning chars of the ID are mutating fast. This allows to use a directory structure to spread things better. (org-id-prefix): Changed default to nil. * org-list.el (org-move-item-down, org-move-item-up): Remember and restore the column of the cursor position. * org-remember.el (org-remember-apply-template): Remove properties from `initial'. * org-wl.el (org-wl-open): Remove useless call to `wl-thread-open-all'. * org-remember.el (org-remember-handler): Fix bug with `bottom' location. * org-bbdb.el (org-bbdb-anniversaries): Require bbdb in `org-bbdb-anniversaries'. * org.el (org-get-next-sibling, org-forward-same-level): New functions, similar to the outline versions, but invisible headings are OK. 2008-10-12 Bastien Guerry <bzg@altern.org> * org.el (org-auto-repeat-maybe): Insert a space between the timestamp's type and the timestamp itself. 2008-10-12 Carsten Dominik <dominik@science.uva.nl> * org-table.el (org-table-sum): Do not format the result with %g, it does rounding when there are too many digits. * org.el (org-map-entries): Protect the keyword-selecting variables. 2008-10-12 Bastien Guerry <bzg@altern.org> * org-agenda.el (org-agenda-to-appt): Make sure the function check against all agenda files. 2008-10-12 Carsten Dominik <dominik@science.uva.nl> * org-list.el: New file, aggregating list functions from org.el and org-export-latex.el. * org.el (org-edit-src-region-extra): New option. * org-agenda.el (org-agenda-to-appt): Fix bug with appointment time before 1am. 2008-10-12 Bastien Guerry <bzg@altern.org> * org-export-latex.el (org-export-latex-keywords-maybe): Bug fix. 2008-10-12 James TA Smith <ahktenzero@mohorovi.cc> * org-plot.el (org-plot/gnuplot): Make tables starting with a hline work correctly. (org-plot/gnuplot-script): Put commas at the end of each script line. * org.el (org-get-refile-targets): Replace links with their descriptions (org-imenu-get-tree): Replace links with their descriptions. * org-remember.el (org-remember-apply-template): Add a new expansion for adding properties to remember items. * org.el (org-add-log-setup): Skip over drawers (properties, clocks etc) when adding notes. * org-agenda.el (org-agenda-get-closed): show durations of clocked items as well as the start and end times. * org-compat.el (org-get-x-clipboard-compat): Add a compat function for fetching the X clipboard on XEmacs and GNU Emacs 21. * org-remember.el (org-get-x-clipboard): Use the compat function to get clipboard values when x-selection-value is unavailable. Use substring-no-properties instead of set-text-properties to remove text properties from the clipboard value. * lisp/org-clock.el (org-update-mode-line): Support limiting the modeline clock string, and display the full todo value in the tooltip. Set a local keymap so mouse-3 on the clock string goes to the currently clocked task. (org-clock-string-limit): Add a custom value for the maximum length of the clock string in the modeline. (org-clock-mode-map): Add a keymap for the modeline string 2008-10-12 Carsten Dominik <dominik@science.uva.nl> * org-compat.el (org-propertize): New function. 2008-10-12 Bastien Guerry <bzg@altern.org> * org-export-latex.el (org-export-latex-tables): protect exported tables from further special chars conversion. (org-export-latex-preprocess): Preserve LaTeX environments. (org-list-parse-list): Parse descriptive lists. (org-list-to-generic, org-list-to-latex, org-list-to-html) (org-list-to-texinfo): Export descriptive lists. (org-quote-chars): Remove. (org-export-latex-keywords-maybe): Use `replace-regexp-in-string'. (org-export-latex-list-beginning-re): Rename to `org-list-beginning-re' (org-list-item-begin): Rename to `org-list-item-beginning' 2008-10-12 Carsten Dominik <dominik@science.uva.nl> * org.el (org-refile): Allow refiling to the last entry in the buffer. (org-get-tags-at): Fix bug when inheritance is turned off. * org.el (org-indent-line-function): No longer check for src regions, this is too much overhead. * org-agenda.el (org-agenda-highlight-todo): Fix bugs with keyword matching. * org.el (org-scan-tags): Make sure that tags matching is not case sensitive. TODO keyword matching is case sensitive, however, to avoid confusion with similar words that are not meant to be keywords. * org.el (org-get-local-tags-at): New function. (org-get-local-tags): New function. * org-exp.el (org-export-get-categories): New function. * org-agenda.el (org-sorting-choice) (org-agenda-sorting-strategy, org-agenda-get-todos) (org-agenda-get-timestamps, org-agenda-get-deadlines) (org-agenda-get-scheduled, org-agenda-get-blocks) (org-entries-lessp): Implement sorting by TODO state. (org-cmp-todo-state): New defsubst. * org-colview.el (org-colview-construct-allowed-dates): New function. (org-columns-next-allowed-value): Use `org-colview-construct-allowed-dates'. * org-colview-xemacs.el (org-colview-construct-allowed-dates): New function. (org-columns-next-allowed-value): Use `org-colview-construct-allowed-dates'. * org.el (org-protect-slash): New function. (org-get-refile-targets): Use `org-protect-slash'. * org-agenda.el (org-global-tags-completion-table): New variable. * org-exp.el (org-export-handle-export-tags): New function. (org-export-preprocess-string): Call `org-export-handle-export-tags'. * org-plot.el: New file. * org-publish.el (org-publish-expand-components): Function removed. (org-publish-expand-projects): Allow components to have components. * org.el (org-indent-line-function): Do not indent in regions that are external source code. (org-yank-and-fold-if-subtree): New function. * org-agenda.el (org-agenda-todayp): New function. (org-agenda-get-deadlines, org-agenda-get-scheduled): Use `org-agenda-todayp'. * org.el (org-insert-heading-respect-content) (org-insert-todo-heading-respect-content): New commands. (org-insert-heading-respect-content): New option. (org-insert-heading): Respect `org-insert-heading-respect-content'. * org-clock.el (org-clock-find-position): Make sure the note after the clock line gets moved into the new clock drawer. * org-id.el (org-id-new): New option. * org-table.el (org-table-copy-down): Avoid overflow during increment. Use prefix argument 0 to temporarily disable the increment. * org-exp.el (org-export-as-html): Do not turn on the major mode if the buffer will be killed anyway. (org-get-current-options): Exclude the #+TEXT field. (org-export-as-html): Make sure text before the first headline is a paragraph. * org-publish.el (org-publish-org-to): Tell the exporter that this buffer will be killed, so it is not necessary to do major mode initialization. * org-archive.el (org-archive-to-archive-sibling): Show empty lines after folding the archive sibling. * org.el (org-log-note-extra): New variable. 2008-10-12 Bastien Guerry <bzg@altern.org> * org.el (org-additional-option-like-keywords): Added keywords for the _QUOTE, _VERSE and _SRC environments. * org-export-latex.el (org-export-latex-preprocess): Fix bug when exporting _QUOTE and _VERSE environments. 2008-10-12 Carsten Dominik <dominik@science.uva.nl> * org-agenda.el (org-agenda-filter-by-tag): New command. * org-exp.el (org-get-current-options): Remove angular brackets from the date entry. * org.el (org-edit-fixed-width-region): New function. (org-edit-fixed-width-region): Also try `org-edit-fixed-width-region'. (org-edit-fixed-width-region-mode): New option. (org-activate-code): Only interprete lines starting with colon plus a space as example lines. * org-remember.el (org-remember-templates): Add nil instead of empty strings to fix the length of remember templates. * org-table.el (org-calc-default-modes): Fix the time format for calc, from 12 hour to 24 hour clock. * org-agenda.el (org-agenda-get-deadlines) (org-agenda-get-scheduled): Avoid `time-of-day' extraction for entries that are pre-warnings of deadlines or reminders. * org.el (org-sort-entries-or-items): Make numeric and alpha comparisons ignore any TODO keyword and priority cookie. * org-remember.el (org-remember-handler): Reinterpretation of the prefix argument. * org-agenda.el (org-agenda-get-scheduled): Use new `org-scheduled' face. * org-faces.el (org-scheduled): New face. * org-wl.el (org-wl-open): Remove incorrect declaration. * org-gnus.el (org-gnus-store-link): Support for :to information in gnus links. * org-exp.el (org-export-as-html): Fixed typo in creator information. (org-export-protect-examples): New parameter indent. Insert extra spaces only when this parameter is specified. (org-export-preprocess-string): Call `org-export-protect-examples' with an indentation parameter when exporting to ASCII. * org-remember.el (org-remember-templates) (org-remember-apply-template): Allow the file component to be a function. * org.el (org-goto-local-search-headings): Renamed from `org-goto-local-search-forward-headings'. Added the possibility to search backwards. * org-export-latex.el (org-export-latex): New customization group. * org-agenda.el (org-write-agenda): Erase buffer for txt export. * org-exp.el (org-html-do-expand): Allow {} to terminate tex macro * org.el (org-buffer-list): Select buffers based on major mode, not on file name. * org-agenda.el (org-agenda-align-tags): Fix bug with malformed face property. * org-colview.el (org-columns-display-here): Use `org-columns-modify-value-for-display-function'. * org-colview-xemacs.el (org-columns-display-here): Use `org-columns-modify-value-for-display-function'. * org.el (org-columns-modify-value-for-display-function): New option. * org-publish.el (org-publish-file): Make sure the directory match for the publishing directory works correctly. * org-agenda.el (org-agenda-execute-calendar-command) (org-agenda-diary-entry): Additional optional argument.
author Carsten Dominik <dominik@science.uva.nl>
date Sun, 12 Oct 2008 06:12:44 +0000
parents ee5932bf781d
children a9dc0e7c3f2b
line wrap: on
line source

;;; cvs-status.el --- major mode for browsing `cvs status' output -*- coding: utf-8 -*-

;; Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004,
;;   2005, 2006, 2007, 2008 Free Software Foundation, Inc.

;; Author: Stefan Monnier <monnier@iro.umontreal.ca>
;; Keywords: pcl-cvs cvs status tree tools

;; This file is part of GNU Emacs.

;; GNU Emacs is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.

;; GNU Emacs 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.  If not, see <http://www.gnu.org/licenses/>.

;;; Commentary:

;; Todo:

;; - Somehow allow cvs-status-tree to work on-the-fly

;;; Code:

(eval-when-compile (require 'cl))
(require 'pcvs-util)

;;;

(defgroup cvs-status nil
  "Major mode for browsing `cvs status' output."
  :group 'pcl-cvs
  :prefix "cvs-status-")

(easy-mmode-defmap cvs-status-mode-map
  '(("n"	. next-line)
    ("p"	. previous-line)
    ("N"	. cvs-status-next)
    ("P"	. cvs-status-prev)
    ("\M-n"	. cvs-status-next)
    ("\M-p"	. cvs-status-prev)
    ("t"	. cvs-status-cvstrees)
    ("T"	. cvs-status-trees)
    (">"        . cvs-mode-checkout))
  "CVS-Status' keymap."
  :group 'cvs-status
  :inherit 'cvs-mode-map)

;;(easy-menu-define cvs-status-menu cvs-status-mode-map
;;  "Menu for `cvs-status-mode'."
;;  '("CVS-Status"
;;    ["Show Tag Trees"	cvs-status-tree	t]
;;    ))

(defvar cvs-status-mode-hook nil
  "Hook run at the end of `cvs-status-mode'.")

(defconst cvs-status-tags-leader-re "^   Existing Tags:$")
(defconst cvs-status-entry-leader-re
  "^File:\\s-+\\(?:no file \\)?\\(.*\\S-\\)\\s-+Status: \\(.+\\)$")
(defconst cvs-status-dir-re "^cvs[.ex]* [a-z]+: Examining \\(.+\\)$")
(defconst cvs-status-rev-re "[0-9][.0-9]*\\.[.0-9]*[0-9]")
(defconst cvs-status-tag-re "[ \t]\\([a-zA-Z][^ \t\n.]*\\)")

(defconst cvs-status-font-lock-keywords
  `((,cvs-status-entry-leader-re
     (1 'cvs-filename)
     (2 'cvs-need-action))
    (,cvs-status-tags-leader-re
     (,cvs-status-rev-re
      (save-excursion (re-search-forward "^\n" nil 'move) (point))
      (progn (re-search-backward cvs-status-tags-leader-re nil t)
	     (forward-line 1))
      (0 font-lock-comment-face))
     (,cvs-status-tag-re
      (save-excursion (re-search-forward "^\n" nil 'move) (point))
      (progn (re-search-backward cvs-status-tags-leader-re nil t)
	     (forward-line 1))
      (1 font-lock-function-name-face)))))
(defconst cvs-status-font-lock-defaults
  '(cvs-status-font-lock-keywords t nil nil nil (font-lock-multiline . t)))

(defvar cvs-minor-wrap-function)
(put 'cvs-status-mode 'mode-class 'special)
;;;###autoload
(define-derived-mode cvs-status-mode fundamental-mode "CVS-Status"
  "Mode used for cvs status output."
  (set (make-local-variable 'font-lock-defaults) cvs-status-font-lock-defaults)
  (set (make-local-variable 'cvs-minor-wrap-function) 'cvs-status-minor-wrap))

;; Define cvs-status-next and cvs-status-prev
(easy-mmode-define-navigation cvs-status cvs-status-entry-leader-re "entry")

(defun cvs-status-current-file ()
  (save-excursion
    (forward-line 1)
    (or (re-search-backward cvs-status-entry-leader-re nil t)
	(re-search-forward cvs-status-entry-leader-re))
    (let* ((file (match-string 1))
	   (cvsdir (and (re-search-backward cvs-status-dir-re nil t)
			(match-string 1)))
	   (pcldir (and (if (boundp 'cvs-pcl-cvs-dirchange-re)
			    (re-search-backward cvs-pcl-cvs-dirchange-re nil t))
			(match-string 1)))
	   (dir ""))
      (let ((default-directory ""))
	(when pcldir (setq dir (expand-file-name pcldir dir)))
	(when cvsdir (setq dir (expand-file-name cvsdir dir)))
	(expand-file-name file dir)))))

(defun cvs-status-current-tag ()
  (save-excursion
    (let ((pt (point))
	  (col (current-column))
	  (start (progn (re-search-backward cvs-status-tags-leader-re nil t) (point)))
	  (end (progn (re-search-forward "^$" nil t) (point))))
      (when (and (< start pt) (> end pt))
	(goto-char pt)
	(end-of-line)
	(let ((tag nil) (dist pt) (end (point)))
	  (beginning-of-line)
	  (while (re-search-forward cvs-status-tag-re end t)
	    (let* ((cole (current-column))
		   (colb (save-excursion
			   (goto-char (match-beginning 1)) (current-column)))
		   (ndist (min (abs (- cole col)) (abs (- colb col)))))
	      (when (< ndist dist)
		(setq dist ndist)
		(setq tag (match-string 1)))))
	  tag)))))

(defun cvs-status-minor-wrap (buf f)
  (let ((data (with-current-buffer buf
		(cons
		 (cons (cvs-status-current-file)
		       (cvs-status-current-tag))
		 (when mark-active
		   (save-excursion
		     (goto-char (mark))
		     (cons (cvs-status-current-file)
			   (cvs-status-current-tag))))))))
    (let ((cvs-branch-prefix (cdar data))
	  (cvs-secondary-branch-prefix (and (cdar data) (cddr data)))
	  (cvs-minor-current-files
	   (cons (caar data)
		 (when (and (cadr data) (not (equal (caar data) (cadr data))))
		   (list (cadr data)))))
	  ;; FIXME:  I need to force because the fileinfos are UNKNOWN
	  (cvs-force-command "/F"))
      (funcall f))))

;;
;; Tagelt, tag element
;;

(defstruct (cvs-tag
	    (:constructor nil)
	    (:constructor cvs-tag-make
			  (vlist &optional name type))
	    (:conc-name cvs-tag->))
  vlist
  name
  type)

(defsubst cvs-status-vl-to-str (vl) (mapconcat 'number-to-string vl "."))

(defun cvs-tag->string (tag)
  (if (stringp tag) tag
    (let ((name (cvs-tag->name tag))
	   (vl (cvs-tag->vlist tag)))
      (if (null name) (cvs-status-vl-to-str vl)
	(let ((rev (if vl (concat " (" (cvs-status-vl-to-str vl) ")") "")))
	  (if (consp name) (mapcar (lambda (name) (concat name rev)) name)
	    (concat name rev)))))))

(defun cvs-tag-compare-1 (vl1 vl2)
  (cond
   ((and (null vl1) (null vl2)) 'equal)
   ((null vl1) 'more2)
   ((null vl2) 'more1)
   (t (let ((v1 (car vl1))
	    (v2 (car vl2)))
	(cond
	 ((> v1 v2) 'more1)
	 ((< v1 v2) 'more2)
	 (t (cvs-tag-compare-1 (cdr vl1) (cdr vl2))))))))

(defsubst cvs-tag-compare (tag1 tag2)
  (cvs-tag-compare-1 (cvs-tag->vlist tag1) (cvs-tag->vlist tag2)))

(defun cvs-tag-merge (tag1 tag2)
  "Merge TAG1 and TAG2 into one."
  (let ((type1 (cvs-tag->type tag1))
	(type2 (cvs-tag->type tag2))
	(name1 (cvs-tag->name tag1))
	(name2 (cvs-tag->name tag2)))
    (unless (equal (cvs-tag->vlist tag1) (cvs-tag->vlist tag2))
      (setf (cvs-tag->vlist tag1) nil))
    (if type1
	(unless (or (not type2) (equal type1 type2))
	  (setf (cvs-tag->type tag1) nil))
      (setf (cvs-tag->type tag1) type2))
    (if name1
	(setf (cvs-tag->name tag1) (cvs-append name1 name2))
      (setf (cvs-tag->name tag1) name2))
    tag1))

(defun cvs-tree-print (tags printer column)
  "Print the tree of TAGS where each tag's string is given by PRINTER.
PRINTER should accept both a tag (in which case it should return a string)
or a string (in which case it should simply return its argument).
A tag cannot be a CONS.  The return value can also be a list of strings,
if several nodes where merged into one.
The tree will be printed no closer than column COLUMN."

  (let* ((eol (save-excursion (end-of-line) (current-column)))
	 (column (max (+ eol 2) column)))
    (if (null tags) column
      ;;(move-to-column-force column)
      (let* ((rev (cvs-car tags))
	     (name (funcall printer (cvs-car rev)))
	     (rest (append (cvs-cdr name) (cvs-cdr tags)))
	     (prefix
	      (save-excursion
		(or (= (forward-line 1) 0) (insert "\n"))
		(cvs-tree-print rest printer column))))
	(assert (>= prefix column))
	(move-to-column prefix t)
	(assert (eolp))
	(insert (cvs-car name))
	(dolist (br (cvs-cdr rev))
	  (let* ((column (current-column))
		 (brrev (funcall printer (cvs-car br)))
		 (brlength (length (cvs-car brrev)))
		 (brfill (concat (make-string (/ brlength 2) ? ) "|"))
		 (prefix
		  (save-excursion
		    (insert " -- ")
		    (cvs-tree-print (cvs-append brrev brfill (cvs-cdr br))
				    printer (current-column)))))
	    (delete-region (save-excursion (move-to-column prefix) (point))
			   (point))
	    (insert " " (make-string (- prefix column 2) ?-) " ")
	    (end-of-line)))
	prefix))))

(defun cvs-tree-merge (tree1 tree2)
  "Merge tags trees TREE1 and TREE2 into one.
BEWARE:  because of stability issues, this is not a symetric operation."
  (assert (and (listp tree1) (listp tree2)))
  (cond
   ((null tree1) tree2)
   ((null tree2) tree1)
   (t
    (let* ((rev1 (car tree1))
	   (tag1 (cvs-car rev1))
	   (vl1 (cvs-tag->vlist tag1))
	   (l1 (length vl1))
	   (rev2 (car tree2))
	   (tag2 (cvs-car rev2))
	   (vl2 (cvs-tag->vlist tag2))
	   (l2 (length vl2)))
    (cond
     ((= l1 l2)
      (case (cvs-tag-compare tag1 tag2)
	(more1 (list* rev2 (cvs-tree-merge tree1 (cdr tree2))))
	(more2 (list* rev1 (cvs-tree-merge (cdr tree1) tree2)))
	(equal
	 (cons (cons (cvs-tag-merge tag1 tag2)
		     (cvs-tree-merge (cvs-cdr rev1) (cvs-cdr rev2)))
	       (cvs-tree-merge (cdr tree1) (cdr tree2))))))
     ((> l1 l2)
      (cvs-tree-merge
       (list (cons (cvs-tag-make (butlast vl1)) tree1)) tree2))
     ((< l1 l2)
      (cvs-tree-merge
       tree1 (list (cons (cvs-tag-make (butlast vl2)) tree2)))))))))

(defun cvs-tag-make-tag (tag)
  (let ((vl (mapcar 'string-to-number (split-string (nth 2 tag) "\\."))))
    (cvs-tag-make vl (nth 0 tag) (intern (nth 1 tag)))))

(defun cvs-tags->tree (tags)
  "Make a tree out of a list of TAGS."
  (let ((tags
	 (mapcar
	  (lambda (tag)
	    (let ((tag (cvs-tag-make-tag tag)))
	      (list (if (not (eq (cvs-tag->type tag) 'branch)) tag
		      (list (cvs-tag-make (butlast (cvs-tag->vlist tag)))
			    tag)))))
	  tags)))
    (while (cdr tags)
      (let (tl)
	(while tags
	  (push (cvs-tree-merge (pop tags) (pop tags)) tl))
	(setq tags (nreverse tl))))
    (car tags)))

(defun cvs-status-get-tags ()
  "Look for a list of tags, read them in and delete them.
Return nil if there was an empty list of tags and t if there wasn't
even a list.  Else, return the list of tags where each element of
the list is a three-string list TAG, KIND, REV."
  (let ((tags nil))
    (if (not (re-search-forward cvs-status-tags-leader-re nil t)) t
      (forward-char 1)
      (let ((pt (point))
	    (lastrev nil)
	    (case-fold-search t))
	(or
	 (looking-at "\\s-+no\\s-+tags")

	 (progn				; normal listing
	   (while (looking-at "^[ \t]+\\([^ \t\n]+\\)[ \t]+(\\([a-z]+\\): \\(.+\\))$")
	     (push (list (match-string 1) (match-string 2) (match-string 3)) tags)
	     (forward-line 1))
	   (unless (looking-at "^$") (setq tags nil) (goto-char pt))
	   tags)

	 (progn				; cvstree-style listing
	   (while (or (looking-at "^   .+\\(.\\)  \\([0-9.]+\\): \\([^\n\t .0-9][^\n\t ]*\\)?$")
		      (and lastrev
			   (looking-at "^   .+\\(\\)  \\(8\\)?  \\([^\n\t .0-9][^\n\t ]*\\)$")))
	     (setq lastrev (or (match-string 2) lastrev))
	     (push (list (match-string 3)
			 (if (equal (match-string 1) " ") "branch" "revision")
			 lastrev) tags)
	     (forward-line 1))
	   (unless (looking-at "^$") (setq tags nil) (goto-char pt))
	   (setq tags (nreverse tags)))

	 (progn				; new tree style listing
	   (let* ((re-lead "[ \t]*\\(-+\\)?\\(|\n?[ \t]+\\)*")
		  (re3 (concat re-lead "\\(\\.\\)?\\(" cvs-status-rev-re "\\)"))
		  (re2 (concat re-lead cvs-status-tag-re "\\(\\)"))
		  (re1 (concat re-lead cvs-status-tag-re
			       " (\\(" cvs-status-rev-re "\\))")))
	     (while (or (looking-at re1) (looking-at re2) (looking-at re3))
	       (push (list (match-string 3)
			   (if (match-string 1) "branch" "revision")
			   (match-string 4)) tags)
	       (goto-char (match-end 0))
	       (when (eolp) (forward-char 1))))
	   (unless (looking-at "^$") (setq tags nil) (goto-char pt))
	   (setq tags (nreverse tags))))

	(delete-region pt (point)))
      tags)))

(defvar font-lock-mode)
(defun cvs-refontify (beg end)
  (when (and (boundp 'font-lock-mode)
	     font-lock-mode
	     (fboundp 'font-lock-fontify-region))
    (font-lock-fontify-region (1- beg) (1+ end))))

(defun cvs-status-trees ()
  "Look for a lists of tags, and replace them with trees."
  (interactive)
  (save-excursion
    (goto-char (point-min))
    (let ((inhibit-read-only t)
	  (tags nil))
      (while (listp (setq tags (cvs-status-get-tags)))
	;;(let ((pt (save-excursion (forward-line -1) (point))))
	  (save-restriction
	    (narrow-to-region (point) (point))
	    ;;(newline)
	    (combine-after-change-calls
	      (cvs-tree-print (cvs-tags->tree tags) 'cvs-tag->string 3)))
	  ;;(cvs-refontify pt (point))
	  ;;(sit-for 0)
	  ;;)
	  ))))

;;;;
;;;; CVSTree-style trees
;;;;

(defvar cvs-tree-use-jisx0208 nil)	;Old compat var.
(defvar cvs-tree-use-charset
  (cond
   (cvs-tree-use-jisx0208 'jisx0208)
   ((char-displayable-p ?━) 'unicode)
   ((char-displayable-p (make-char 'japanese-jisx0208 40 44)) 'jisx0208))
  "*Non-nil if we should use the graphical glyphs from `japanese-jisx0208'.
Otherwise, default to ASCII chars like +, - and |.")

(defconst cvs-tree-char-space
  (case cvs-tree-use-charset
    (jisx0208 (make-char 'japanese-jisx0208 33 33))
    (unicode " ")
    (t "  ")))
(defconst cvs-tree-char-hbar
  (case cvs-tree-use-charset
    (jisx0208 (make-char 'japanese-jisx0208 40 44))
    (unicode "━")
    (t "--")))
(defconst cvs-tree-char-vbar
  (case cvs-tree-use-charset
    (jisx0208 (make-char 'japanese-jisx0208 40 45))
    (unicode "┃")
    (t "| ")))
(defconst cvs-tree-char-branch
  (case cvs-tree-use-charset
    (jisx0208 (make-char 'japanese-jisx0208 40 50))
    (unicode "┣")
    (t "+-")))
(defconst cvs-tree-char-eob		;end of branch
  (case cvs-tree-use-charset
    (jisx0208 (make-char 'japanese-jisx0208 40 49))
    (unicode "┗")
    (t "`-")))
(defconst cvs-tree-char-bob		;beginning of branch
  (case cvs-tree-use-charset
    (jisx0208 (make-char 'japanese-jisx0208 40 51))
    (unicode "┳")
    (t "+-")))

(defun cvs-tag-lessp (tag1 tag2)
  (eq (cvs-tag-compare tag1 tag2) 'more2))

(defvar cvs-tree-nomerge nil)

(defun cvs-status-cvstrees (&optional arg)
  "Look for a list of tags, and replace it with a tree.
Optional prefix ARG chooses between two representations."
  (interactive "P")
  (when (and cvs-tree-use-charset
	     (not enable-multibyte-characters))
    ;; We need to convert the buffer from unibyte to multibyte
    ;; since we'll use multibyte chars for the tree.
    (let ((modified (buffer-modified-p))
	  (inhibit-read-only t)
	  (inhibit-modification-hooks t))
      (unwind-protect
	  (progn
	    (decode-coding-region (point-min) (point-max) 'undecided)
	    (set-buffer-multibyte t))
	(restore-buffer-modified-p modified))))
  (save-excursion
    (goto-char (point-min))
    (let ((inhibit-read-only t)
	  (tags nil)
	  (cvs-tree-nomerge (if arg (not cvs-tree-nomerge) cvs-tree-nomerge)))
      (while (listp (setq tags (cvs-status-get-tags)))
	(let ((tags (mapcar 'cvs-tag-make-tag tags))
	      ;;(pt (save-excursion (forward-line -1) (point)))
	      )
	  (setq tags (sort tags 'cvs-tag-lessp))
	  (let* ((first (car tags))
		 (prev (if (cvs-tag-p first)
			   (list (car (cvs-tag->vlist first))) nil)))
	    (combine-after-change-calls
	      (cvs-tree-tags-insert tags prev))
	    ;;(cvs-refontify pt (point))
	    ;;(sit-for 0)
	    ))))))

(defun cvs-tree-tags-insert (tags prev)
  (when tags
    (let* ((tag (car tags))
	   (vlist (cvs-tag->vlist tag))
	   (nprev ;"next prev"
	    (let* ((next (cvs-car (cadr tags)))
		   (nprev (if (and cvs-tree-nomerge next
				   (equal vlist (cvs-tag->vlist next)))
			      prev vlist)))
	      (cvs-map (lambda (v p) v) nprev prev)))
	   (after (save-excursion
		   (newline)
		   (cvs-tree-tags-insert (cdr tags) nprev)))
	   (pe t)			;"prev equal"
	   (nas nil))			;"next afters" to be returned
      (insert "   ")
      (do* ((vs vlist (cdr vs))
	    (ps prev (cdr ps))
	    (as after (cdr as)))
	  ((and (null as) (null vs) (null ps))
	   (let ((revname (cvs-status-vl-to-str vlist)))
	     (if (cvs-every 'identity (cvs-map 'equal prev vlist))
		 (insert (make-string (+ 4 (length revname)) ? )
			 (or (cvs-tag->name tag) ""))
	       (insert "  " revname ": " (or (cvs-tag->name tag) "")))))
	(let* ((eq (and pe (equal (car ps) (car vs))))
	       (next-eq (equal (cadr ps) (cadr vs))))
	  (let* ((na+char
		  (if (car as)
		      (if eq
			  (if next-eq (cons t cvs-tree-char-vbar)
			    (cons t cvs-tree-char-branch))
			(cons nil cvs-tree-char-bob))
		    (if eq
			(if next-eq (cons nil cvs-tree-char-space)
			  (cons t cvs-tree-char-eob))
		      (cons nil (if (and (eq (cvs-tag->type tag) 'branch)
					 (cvs-every 'null as))
				    cvs-tree-char-space
				  cvs-tree-char-hbar))))))
	    (insert (cdr na+char))
	    (push (car na+char) nas))
	  (setq pe eq)))
      (nreverse nas))))

;;;;
;;;; Merged trees from different files
;;;;

(defun cvs-tree-fuzzy-merge-1 (trees tree prev)
  )

(defun cvs-tree-fuzzy-merge (trees tree)
  "Do the impossible:  merge TREE into TREES."
  ())

(defun cvs-tree ()
  "Get tags from the status output and merge tham all into a big tree."
  (save-excursion
    (goto-char (point-min))
    (let ((inhibit-read-only t)
	  (trees (make-vector 31 0)) tree)
      (while (listp (setq tree (cvs-tags->tree (cvs-status-get-tags))))
	(cvs-tree-fuzzy-merge trees tree))
      (erase-buffer)
      (let ((cvs-tag-print-rev nil))
	(cvs-tree-print tree 'cvs-tag->string 3)))))


(provide 'cvs-status)

;; arch-tag: db8b5094-d02a-473e-a476-544e89ff5ad0
;;; cvs-status.el ends here