cvsdist e1e7868
;;; psvn.el --- Subversion interface for emacs
cvsdist e1e7868
;; Copyright (C) 2002-2004 by Stefan Reichoer
cvsdist e1e7868
357017e
;; Author: Stefan Reichoer, <stefan@xsteve.at>
357017e
;; $Id: psvn.el 11062 2004-09-21 20:12:42Z xsteve $
cvsdist e1e7868
cvsdist e1e7868
;; psvn.el is free software; you can redistribute it and/or modify
cvsdist e1e7868
;; it under the terms of the GNU General Public License as published by
cvsdist e1e7868
;; the Free Software Foundation; either version 2, or (at your option)
cvsdist e1e7868
;; any later version.
cvsdist e1e7868
cvsdist e1e7868
;; psvn.el is distributed in the hope that it will be useful,
cvsdist e1e7868
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
cvsdist e1e7868
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
cvsdist e1e7868
;; GNU General Public License for more details.
cvsdist e1e7868
cvsdist e1e7868
;; You should have received a copy of the GNU General Public License
cvsdist e1e7868
;; along with GNU Emacs; see the file COPYING.  If not, write to
cvsdist e1e7868
;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
cvsdist e1e7868
;; Boston, MA 02111-1307, USA.
cvsdist e1e7868
cvsdist e1e7868
;;; Commentary
cvsdist e1e7868
cvsdist e1e7868
;; psvn.el is tested with GNU Emacs 21.3 on windows, debian linux,
cvsdist e1e7868
;; freebsd5 with svn 1.05
cvsdist e1e7868
cvsdist e1e7868
;; psvn.el is an interface for the revision control tool subversion
cvsdist e1e7868
;; (see http://subversion.tigris.org)
cvsdist e1e7868
;; psvn.el provides a similar interface for subversion as pcl-cvs for cvs.
cvsdist e1e7868
;; At the moment the following commands are implemented:
cvsdist e1e7868
;; M-x svn-status: run 'svn -status -v'
cvsdist e1e7868
;; and show the result in the *svn-status* buffer.  This buffer uses
cvsdist e1e7868
;; svn-status mode in which the following keys are defined:
cvsdist e1e7868
;; g     - svn-status-update:               run 'svn status -v'
cvsdist e1e7868
;; C-u g - svn-status-update:               run 'svn status -vu'
cvsdist e1e7868
;; =     - svn-status-show-svn-diff         run 'svn diff'
cvsdist e1e7868
;; l     - svn-status-show-svn-log          run 'svn log'
cvsdist e1e7868
;; i     - svn-status-info                  run 'svn info'
cvsdist e1e7868
;; r     - svn-status-revert                run 'svn revert'
cvsdist e1e7868
;; V     - svn-status-resolved              run 'svn resolved'
cvsdist e1e7868
;; U     - svn-status-update-cmd            run 'svn update'
cvsdist e1e7868
;; c     - svn-status-commit-file           run 'svn commit'
cvsdist e1e7868
;; a     - svn-status-add-file              run 'svn add --non-recursive'
cvsdist e1e7868
;; A     - svn-status-add-file-recursively  run 'svn add'
cvsdist e1e7868
;; +     - svn-status-make-directory        run 'svn mkdir'
cvsdist e1e7868
;; R     - svn-status-mv                    run 'svn mv'
cvsdist e1e7868
;; C-d   - svn-status-rm                    run 'svn rm'
cvsdist e1e7868
;; M-c   - svn-status-cleanup               run 'svn cleanup'
cvsdist e1e7868
;; b     - svn-status-blame                 run 'svn blame'
cvsdist e1e7868
;; RET   - svn-status-find-file-or-examine-directory
cvsdist e1e7868
;; ^     - svn-status-examine-parent
cvsdist e1e7868
;; ~     - svn-status-get-specific-revision
cvsdist e1e7868
;; E     - svn-status-ediff-with-revision
cvsdist e1e7868
;; s     - svn-status-show-process-buffer
cvsdist e1e7868
;; e     - svn-status-toggle-edit-cmd-flag
cvsdist e1e7868
;; ?     - svn-status-toggle-hide-unknown
cvsdist e1e7868
;; _     - svn-status-toggle-hide-unmodified
cvsdist e1e7868
;; m     - svn-status-set-user-mark
cvsdist e1e7868
;; u     - svn-status-unset-user-mark
cvsdist e1e7868
;; $     - svn-status-toggle-elide
cvsdist e1e7868
;; DEL   - svn-status-unset-user-mark-backwards
cvsdist e1e7868
;; * !   - svn-status-unset-all-usermarks
cvsdist e1e7868
;; * ?   - svn-status-mark-unknown
cvsdist e1e7868
;; * A   - svn-status-mark-added
cvsdist e1e7868
;; * M   - svn-status-mark-modified
cvsdist e1e7868
;; .     - svn-status-goto-root-or-return
cvsdist e1e7868
;; f     - svn-status-find-file
cvsdist e1e7868
;; o     - svn-status-find-file-other-window
cvsdist e1e7868
;; v     - svn-status-view-file-other-window
cvsdist e1e7868
;; I     - svn-status-parse-info
cvsdist e1e7868
;; P l   - svn-status-property-list
cvsdist e1e7868
;; P s   - svn-status-property-set
cvsdist e1e7868
;; P d   - svn-status-property-delete
cvsdist e1e7868
;; P e   - svn-status-property-edit-one-entry
cvsdist e1e7868
;; P i   - svn-status-property-ignore-file
cvsdist e1e7868
;; P I   - svn-status-property-ignore-file-extension
cvsdist e1e7868
;; P C-i - svn-status-property-edit-svn-ignore
cvsdist e1e7868
;; P k   - svn-status-property-set-keyword-list
cvsdist e1e7868
;; P y   - svn-status-property-set-eol-style
cvsdist e1e7868
;; h     - svn-status-use-history
cvsdist e1e7868
;; q     - svn-status-bury-buffer
cvsdist e1e7868
cvsdist e1e7868
;; To use psvn.el put the following line in your .emacs:
cvsdist e1e7868
;; (require 'psvn)
cvsdist e1e7868
;; Start the svn interface with M-x svn-status
cvsdist e1e7868
cvsdist e1e7868
;; The latest version of psvn.el can be found at:
357017e
;;   http://www.xsteve.at/prg/emacs/psvn.el
cvsdist e1e7868
;; Or you can check it out from the subversion repository:
cvsdist e1e7868
;;   svn co http://svn.collab.net/repos/svn/trunk/contrib/client-side/psvn psvn
cvsdist e1e7868
cvsdist e1e7868
;; TODO:
cvsdist e1e7868
;; * shortcut for svn propset svn:keywords "Date" psvn.el
cvsdist e1e7868
;; * docstrings for the functions
cvsdist e1e7868
;; * perhaps shortcuts for ranges, dates
cvsdist e1e7868
;; * when editing the command line - offer help from the svn client
cvsdist e1e7868
;; * finish svn-status-property-set
cvsdist e1e7868
;; * eventually use the customize interface
cvsdist e1e7868
;; * interactive svn-status should complete existing directories only;
cvsdist e1e7868
;;   unfortunately `read-directory-name' doesn't exist in Emacs 21.3
cvsdist e1e7868
;; * Add repository browser
cvsdist e1e7868
;; * Improve support for svn blame
cvsdist e1e7868
;; * Support for editing the log file entries, e.g.:
cvsdist e1e7868
;;   svn propedit --revprop -r9821 svn:log
cvsdist e1e7868
;; * Better logview mode (allow to show the changeset for a given entry)
cvsdist e1e7868
cvsdist e1e7868
;; Overview over the implemented/not (yet) implemented svn sub-commands:
cvsdist e1e7868
;; * add                       implemented
cvsdist e1e7868
;; * blame                     implemented
cvsdist e1e7868
;; * cat                       implemented
cvsdist e1e7868
;; * checkout (co)
cvsdist e1e7868
;; * cleanup                   implemented
cvsdist e1e7868
;; * commit (ci)               implemented
cvsdist e1e7868
;; * copy (cp)
cvsdist e1e7868
;; * delete (del, remove, rm)  implemented
cvsdist e1e7868
;; * diff (di)                 implemented
cvsdist e1e7868
;; * export
cvsdist e1e7868
;; * help (?, h)
cvsdist e1e7868
;; * import
cvsdist e1e7868
;; * info                      implemented
cvsdist e1e7868
;; * list (ls)
cvsdist e1e7868
;; * log                       implemented
cvsdist e1e7868
;; * merge
cvsdist e1e7868
;; * mkdir                     implemented
cvsdist e1e7868
;; * move (mv, rename, ren)    implemented
cvsdist e1e7868
;; * propdel (pdel)            implemented
cvsdist e1e7868
;; * propedit (pedit, pe)      not needed
cvsdist e1e7868
;; * propget (pget, pg)        used
cvsdist e1e7868
;; * proplist (plist, pl)      implemented
cvsdist e1e7868
;; * propset (pset, ps)        used
cvsdist e1e7868
;; * resolved                  implemented
cvsdist e1e7868
;; * revert                    implemented
cvsdist e1e7868
;; * status (stat, st)         implemented
cvsdist e1e7868
;; * switch (sw)
cvsdist e1e7868
;; * update (up)               implemented
cvsdist e1e7868
cvsdist e1e7868
;; For the not yet implemented commands you should use the command line
cvsdist e1e7868
;; svn client. If there are user requests for any missing commands I will
cvsdist e1e7868
;; probably implement them.
cvsdist e1e7868
cvsdist e1e7868
;; Comments / suggestions and bug reports are welcome!
cvsdist e1e7868
cvsdist e1e7868
;;; Code:
cvsdist e1e7868
cvsdist e1e7868
;;; user setable variables
cvsdist e1e7868
(defvar svn-log-edit-file-name "++svn-log++" "*Name of a saved log file.")
357017e
(defvar svn-log-edit-insert-files-to-commit t "*Insert the filelist to commit in the *svn-log* buffer")
cvsdist e1e7868
(defvar svn-status-hide-unknown nil "*Hide unknown files in *svn-status* buffer.")
cvsdist e1e7868
(defvar svn-status-hide-unmodified nil "*Hide unmodified files in *svn-status* buffer.")
cvsdist e1e7868
(defvar svn-status-directory-history nil "*List of visited svn working directories.")
357017e
(defvar svn-status-sort-status-buffer t "Sort the *svn-status* buffer.
357017e
Setting this variable to nil speeds up M-x svn-status.
357017e
However, it is possible, that the sorting is wrong in this case.")
cvsdist e1e7868
cvsdist e1e7868
(defvar svn-status-unmark-files-after-list '(commit revert)
cvsdist e1e7868
  "*List of operations after which all user marks will be removed.
cvsdist e1e7868
Possible values are: commit, revert.")
cvsdist e1e7868
cvsdist e1e7868
;;; default arguments to pass to svn commands
cvsdist e1e7868
(defvar svn-status-default-log-arguments ""
cvsdist e1e7868
  "*Arguments to pass to svn log.
cvsdist e1e7868
\(used in `svn-status-show-svn-log'; override these by giving prefixes\).")
cvsdist e1e7868
cvsdist e1e7868
;;; hooks
cvsdist e1e7868
(defvar svn-log-edit-mode-hook nil "Hook run when entering `svn-log-edit-mode'.")
cvsdist e1e7868
cvsdist e1e7868
(defvar svn-status-wash-control-M-in-process-buffers
cvsdist e1e7868
  (eq system-type 'windows-nt)
cvsdist e1e7868
  "*Remove any trailing ^M from the *svn-process* buffer.")
cvsdist e1e7868
cvsdist e1e7868
;;; Customize group
cvsdist e1e7868
(defgroup psvn nil
cvsdist e1e7868
  "Subversion interface for Emacs."
cvsdist e1e7868
  :group 'tools)
cvsdist e1e7868
cvsdist e1e7868
(defgroup psvn-faces nil
cvsdist e1e7868
  "psvn faces."
cvsdist e1e7868
  :group 'psvn)
cvsdist e1e7868
cvsdist e1e7868
cvsdist e1e7868
(eval-and-compile
cvsdist e1e7868
  (require 'cl)
cvsdist e1e7868
  (defconst svn-xemacsp (featurep 'xemacs))
cvsdist e1e7868
  (if svn-xemacsp
cvsdist e1e7868
      (require 'overlay)
cvsdist e1e7868
    (require 'overlay nil t)))
cvsdist e1e7868
cvsdist e1e7868
;; Use the normally used mode for files ending in .~HEAD~, .~BASE~, ...
cvsdist e1e7868
(add-to-list 'auto-mode-alist '("\\.~?\\(HEAD\\|BASE\\|PREV\\)~?\\'" ignore t))
cvsdist e1e7868
cvsdist e1e7868
;;; internal variables
cvsdist e1e7868
(defvar svn-process-cmd nil)
cvsdist e1e7868
(defvar svn-status-info nil)
cvsdist e1e7868
(defvar svn-status-base-info nil)
cvsdist e1e7868
(defvar svn-status-initial-window-configuration nil)
cvsdist e1e7868
(defvar svn-status-default-column 23)
cvsdist e1e7868
(defvar svn-status-default-revision-width 4)
cvsdist e1e7868
(defvar svn-status-default-author-width 9)
cvsdist e1e7868
(defvar svn-status-line-format " %c%c %4s %4s %-9s")
cvsdist e1e7868
(defvar svn-status-short-mod-flag-p t)
cvsdist e1e7868
(defvar svn-start-of-file-list-line-number 0)
cvsdist e1e7868
(defvar svn-status-files-to-commit nil)
cvsdist e1e7868
(defvar svn-status-pre-commit-window-configuration nil)
cvsdist e1e7868
(defvar svn-status-pre-propedit-window-configuration nil)
cvsdist e1e7868
(defvar svn-status-head-revision nil)
cvsdist e1e7868
(defvar svn-status-root-return-info nil)
cvsdist e1e7868
(defvar svn-status-property-edit-must-match-flag nil)
cvsdist e1e7868
(defvar svn-status-propedit-property-name nil)
cvsdist e1e7868
(defvar svn-status-propedit-file-list nil)
cvsdist e1e7868
(defvar svn-status-mode-line-process "")
cvsdist e1e7868
(defvar svn-status-mode-line-process-status "")
cvsdist e1e7868
(defvar svn-status-mode-line-process-edit-flag "")
cvsdist e1e7868
(defvar svn-status-edit-svn-command nil)
cvsdist e1e7868
(defvar svn-status-update-previous-process-output nil)
cvsdist e1e7868
(defvar svn-status-temp-dir
cvsdist e1e7868
  (or
cvsdist e1e7868
   (when (boundp 'temporary-file-directory) temporary-file-directory) ;emacs
cvsdist e1e7868
   (when (boundp 'temp-directory) temp-directory)                     ;xemacs
cvsdist e1e7868
   "/tmp/"))
cvsdist e1e7868
(defvar svn-temp-suffix (make-temp-name "."))
cvsdist e1e7868
(defvar svn-status-temp-file-to-remove nil)
cvsdist e1e7868
(defvar svn-status-temp-arg-file (concat svn-status-temp-dir "svn.arg" svn-temp-suffix))
357017e
(defvar svn-status-options nil)
cvsdist e1e7868
cvsdist e1e7868
;;; faces
cvsdist e1e7868
(defface svn-status-marked-face
cvsdist e1e7868
  '((((type tty) (class color)) (:foreground "green" :weight light))
cvsdist e1e7868
    (((class color) (background light)) (:foreground "green3"))
cvsdist e1e7868
    (((class color) (background dark)) (:foreground "palegreen2"))
cvsdist e1e7868
    (t (:weight bold)))
cvsdist e1e7868
  "Face to highlight the mark for user marked files in svn status buffers."
cvsdist e1e7868
  :group 'psvn-faces)
cvsdist e1e7868
cvsdist e1e7868
(defface svn-status-modified-external-face
cvsdist e1e7868
  '((((type tty) (class color)) (:foreground "magenta" :weight light))
cvsdist e1e7868
    (((class color) (background light)) (:foreground "magenta"))
cvsdist e1e7868
    (((class color) (background dark)) (:foreground "yellow"))
cvsdist e1e7868
    (t (:weight bold)))
cvsdist e1e7868
  "Face to highlight the phrase \"externally modified\" in *svn-status* buffers."
cvsdist e1e7868
  :group 'psvn-faces)
cvsdist e1e7868
cvsdist e1e7868
;based on cvs-filename-face
cvsdist e1e7868
(defface svn-status-directory-face
cvsdist e1e7868
  '((((type tty) (class color)) (:foreground "lightblue" :weight light))
cvsdist e1e7868
    (((class color) (background light)) (:foreground "blue4"))
cvsdist e1e7868
    (((class color) (background dark)) (:foreground "lightskyblue1"))
cvsdist e1e7868
    (t (:weight bold)))
cvsdist e1e7868
  "Face for directories in svn status buffers.
cvsdist e1e7868
See `svn-status--line-info->directory-p' for what counts as a directory."
cvsdist e1e7868
  :group 'psvn-faces)
cvsdist e1e7868
cvsdist e1e7868
;based on font-lock-comment-face
cvsdist e1e7868
(defface svn-status-filename-face
cvsdist e1e7868
  '((((class color) (background light)) (:foreground "chocolate"))
cvsdist e1e7868
    (((class color) (background dark)) (:foreground "beige")))
cvsdist e1e7868
  "Face for non-directories in svn status buffers.
cvsdist e1e7868
See `svn-status--line-info->directory-p' for what counts as a directory."
cvsdist e1e7868
  :group 'psvn-faces)
cvsdist e1e7868
cvsdist e1e7868
(defvar svn-highlight t)
cvsdist e1e7868
;; stolen from PCL-CVS
cvsdist e1e7868
(defun svn-add-face (str face &optional keymap)
cvsdist e1e7868
  (when svn-highlight
cvsdist e1e7868
    ;; Do not use `list*'; cl.el might not have been loaded.  We could
cvsdist e1e7868
    ;; put (require 'cl) at the top but let's try to manage without.
cvsdist e1e7868
    (add-text-properties 0 (length str)
cvsdist e1e7868
                         `(face ,face
cvsdist e1e7868
                           ,@(when keymap
cvsdist e1e7868
                               `(mouse-face highlight
cvsdist e1e7868
                                 local-map ,keymap)))
cvsdist e1e7868
                         str))
cvsdist e1e7868
  str)
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-maybe-add-face (condition text face)
cvsdist e1e7868
  "If CONDITION then add FACE to TEXT.
cvsdist e1e7868
Else return TEXT unchanged."
cvsdist e1e7868
  (if condition
cvsdist e1e7868
      (svn-add-face text face)
cvsdist e1e7868
    text))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-choose-face-to-add (condition text face1 face2)
cvsdist e1e7868
  "If CONDITION then add FACE1 to TEXT, else add FACE2 to TEXT."
cvsdist e1e7868
  (if condition
cvsdist e1e7868
      (svn-add-face text face1)
cvsdist e1e7868
    (svn-add-face text face2)))
cvsdist e1e7868
cvsdist e1e7868
; compatibility
cvsdist e1e7868
; emacs 20
cvsdist e1e7868
(unless (fboundp 'point-at-eol) (defalias 'point-at-eol 'line-end-position))
cvsdist e1e7868
(unless (fboundp 'point-at-bol) (defalias 'point-at-bol 'line-beginning-position))
cvsdist e1e7868
(unless (functionp 'read-directory-name) (defalias 'read-directory-name 'read-file-name))
cvsdist e1e7868
cvsdist e1e7868
(eval-when-compile
cvsdist e1e7868
  (if (not (fboundp 'gethash))
cvsdist e1e7868
      (require 'cl-macs)))
cvsdist e1e7868
(if (not (fboundp 'puthash))
cvsdist e1e7868
    (defalias 'puthash 'cl-puthash))
cvsdist e1e7868
357017e
(defvar svn-status-display-new-status-buffer nil)
cvsdist e1e7868
;;;###autoload
cvsdist e1e7868
(defun svn-status (dir &optional arg)
cvsdist e1e7868
  "Examine the status of Subversion working copy in directory DIR.
cvsdist e1e7868
If ARG then pass the -u argument to `svn status'."
cvsdist e1e7868
  (interactive (list (read-directory-name "SVN status directory: "
cvsdist e1e7868
                                          nil default-directory nil)))
cvsdist e1e7868
  (unless (file-directory-p dir)
cvsdist e1e7868
    (error "%s is not a directory" dir))
cvsdist e1e7868
  (if (not (file-exists-p (concat dir "/.svn/")))
cvsdist e1e7868
      (when (y-or-n-p
cvsdist e1e7868
             (concat dir
cvsdist e1e7868
                     " does not seem to be a Subversion working copy (no .svn directory).  "
cvsdist e1e7868
                     "Run dired instead? "))
cvsdist e1e7868
        (dired dir))
cvsdist e1e7868
    (setq dir (file-name-as-directory dir))
cvsdist e1e7868
    (setq svn-status-directory-history (delete dir svn-status-directory-history))
cvsdist e1e7868
    (add-to-list 'svn-status-directory-history dir)
357017e
    (if (string= (buffer-name) "*svn-status*")
357017e
        (setq svn-status-display-new-status-buffer nil)
357017e
      (setq svn-status-display-new-status-buffer t)
357017e
      ;;(message "psvn: Saving initial window configuration")
cvsdist e1e7868
      (setq svn-status-initial-window-configuration (current-window-configuration)))
cvsdist e1e7868
    (let* ((status-buf (get-buffer-create "*svn-status*"))
cvsdist e1e7868
           (proc-buf (get-buffer-create "*svn-process*")))
cvsdist e1e7868
      (save-excursion
cvsdist e1e7868
        (set-buffer status-buf)
cvsdist e1e7868
        (setq default-directory dir)
cvsdist e1e7868
        (set-buffer proc-buf)
cvsdist e1e7868
        (setq default-directory dir)
cvsdist e1e7868
        (if arg
cvsdist e1e7868
            (svn-run-svn t t 'status "status" "-vu")
cvsdist e1e7868
          (svn-run-svn t t 'status "status" "-v"))))))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-use-history ()
cvsdist e1e7868
  (interactive)
cvsdist e1e7868
  (let* ((hist svn-status-directory-history)
cvsdist e1e7868
         (dir (read-from-minibuffer "svn-status on directory: "
cvsdist e1e7868
                              (cadr svn-status-directory-history)
cvsdist e1e7868
                              nil nil 'hist)))
cvsdist e1e7868
    (when (file-directory-p dir)
cvsdist e1e7868
      (svn-status dir))))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-run-svn (run-asynchron clear-process-buffer cmdtype &rest arglist)
cvsdist e1e7868
  "Run svn with arguments ARGLIST.
cvsdist e1e7868
cvsdist e1e7868
If RUN-ASYNCHRON is t then run svn asynchronously.
cvsdist e1e7868
cvsdist e1e7868
If CLEAR-PROCESS-BUFFER is t then erase the contents of the
cvsdist e1e7868
*svn-process* buffer before commencing.
cvsdist e1e7868
cvsdist e1e7868
CMDTYPE is a symbol such as 'mv, 'revert, or 'add, representing the
cvsdist e1e7868
command to run.
cvsdist e1e7868
cvsdist e1e7868
ARGLIST is a list of arguments \(which must include the command name,
cvsdist e1e7868
for  example: '(\"revert\" \"file1\"\)"
cvsdist e1e7868
  (if (eq (process-status "svn") nil)
cvsdist e1e7868
      (progn
cvsdist e1e7868
        (when svn-status-edit-svn-command
cvsdist e1e7868
          (setq arglist (append arglist
cvsdist e1e7868
                                (split-string
cvsdist e1e7868
                                 (read-from-minibuffer
cvsdist e1e7868
                                  (format "svn %s %S " cmdtype arglist)))))
cvsdist e1e7868
          (when (eq svn-status-edit-svn-command t)
cvsdist e1e7868
            (svn-status-toggle-edit-cmd-flag t))
cvsdist e1e7868
          (message "svn-run-svn %s: %S" cmdtype arglist))
cvsdist e1e7868
        (let* ((proc-buf (get-buffer-create "*svn-process*"))
cvsdist e1e7868
               (svn-proc))
cvsdist e1e7868
          (when (listp (car arglist))
cvsdist e1e7868
            (setq arglist (car arglist)))
cvsdist e1e7868
          (save-excursion
cvsdist e1e7868
            (set-buffer proc-buf)
cvsdist e1e7868
            (setq buffer-read-only nil)
cvsdist e1e7868
            (fundamental-mode)
cvsdist e1e7868
            (if clear-process-buffer
cvsdist e1e7868
                (delete-region (point-min) (point-max))
cvsdist e1e7868
              (goto-char (point-max)))
cvsdist e1e7868
            (setq svn-process-cmd cmdtype)
cvsdist e1e7868
            (setq svn-status-mode-line-process-status (format " running %s" cmdtype))
cvsdist e1e7868
            (svn-status-update-mode-line)
cvsdist e1e7868
            (sit-for 0.1)
cvsdist e1e7868
            (if run-asynchron
cvsdist e1e7868
                (progn
cvsdist e1e7868
                  (setq svn-proc (apply 'start-process "svn" proc-buf "svn" arglist))
cvsdist e1e7868
                  (set-process-sentinel svn-proc 'svn-process-sentinel))
cvsdist e1e7868
              ;;(message "running synchron: svn %S" arglist)
cvsdist e1e7868
              (apply 'call-process "svn" nil proc-buf nil arglist)
cvsdist e1e7868
              (setq svn-status-mode-line-process-status "")
cvsdist e1e7868
              (svn-status-update-mode-line)))))
cvsdist e1e7868
    (error "You can only run one svn process at once!")))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-process-sentinel (process event)
cvsdist e1e7868
  ;;(princ (format "Process: %s had the event `%s'" process event)))
cvsdist e1e7868
  ;;(save-excursion
cvsdist e1e7868
  (let ((act-buf (current-buffer)))
cvsdist e1e7868
    (set-buffer (process-buffer process))
cvsdist e1e7868
    (setq svn-status-mode-line-process-status "")
cvsdist e1e7868
    (svn-status-update-mode-line)
cvsdist e1e7868
    (cond ((string= event "finished\n")
cvsdist e1e7868
           (cond ((eq svn-process-cmd 'status)
cvsdist e1e7868
                  ;;(message "svn status finished")
cvsdist e1e7868
                  (if (eq system-type 'windows-nt)
cvsdist e1e7868
                      ;; convert path separator as UNIX style
cvsdist e1e7868
                      (save-excursion
cvsdist e1e7868
                        (goto-char (point-min))
cvsdist e1e7868
                        (while (search-forward "\\" nil t)
cvsdist e1e7868
                          (replace-match "/"))))
cvsdist e1e7868
                  (svn-parse-status-result)
cvsdist e1e7868
                  (set-buffer act-buf)
cvsdist e1e7868
                  (svn-status-update-buffer)
cvsdist e1e7868
                  (when svn-status-update-previous-process-output
cvsdist e1e7868
                    (set-buffer (process-buffer process))
cvsdist e1e7868
                    (delete-region (point-min) (point-max))
cvsdist e1e7868
                    (insert "Output from svn command:\n")
cvsdist e1e7868
                    (insert svn-status-update-previous-process-output)
cvsdist e1e7868
                    (goto-char (point-min))
357017e
                    (setq svn-status-update-previous-process-output nil))
357017e
                  (when svn-status-display-new-status-buffer
357017e
                    (set-window-configuration svn-status-initial-window-configuration)
357017e
                    (switch-to-buffer "*svn-status*")))
cvsdist e1e7868
                 ((eq svn-process-cmd 'log)
cvsdist e1e7868
                  (svn-status-show-process-buffer-internal t)
cvsdist e1e7868
                  (pop-to-buffer "*svn-process*")
cvsdist e1e7868
                  (switch-to-buffer (get-buffer-create "*svn-log*"))
cvsdist e1e7868
                  (let ((buffer-read-only nil))
cvsdist e1e7868
                    (delete-region (point-min) (point-max))
cvsdist e1e7868
                    (insert-buffer-substring "*svn-process*"))
cvsdist e1e7868
                  (svn-log-view-mode)
cvsdist e1e7868
                  (goto-char (point-min))
cvsdist e1e7868
                  (forward-line 3)
cvsdist e1e7868
                  (font-lock-fontify-buffer)
cvsdist e1e7868
                  (message "svn log finished"))
cvsdist e1e7868
                 ((eq svn-process-cmd 'info)
cvsdist e1e7868
                  (svn-status-show-process-buffer-internal t)
cvsdist e1e7868
                  (message "svn info finished"))
cvsdist e1e7868
                 ((eq svn-process-cmd 'parse-info)
cvsdist e1e7868
                  (svn-status-parse-info-result))
cvsdist e1e7868
                 ((eq svn-process-cmd 'blame)
cvsdist e1e7868
                  (svn-status-show-process-buffer-internal t)
cvsdist e1e7868
                  (message "svn blame finished"))
cvsdist e1e7868
                 ((eq svn-process-cmd 'commit)
cvsdist e1e7868
                  (svn-status-remove-temp-file-maybe)
cvsdist e1e7868
                  (svn-status-show-process-buffer-internal t)
cvsdist e1e7868
                  (when (member 'commit svn-status-unmark-files-after-list)
cvsdist e1e7868
                    (svn-status-unset-all-usermarks))
cvsdist e1e7868
                  (svn-status-update)
cvsdist e1e7868
                  (message "svn commit finished"))
cvsdist e1e7868
                 ((eq svn-process-cmd 'update)
cvsdist e1e7868
                  (svn-status-show-process-buffer-internal t)
cvsdist e1e7868
                  (svn-status-update)
cvsdist e1e7868
                  (message "svn update finished"))
cvsdist e1e7868
                 ((eq svn-process-cmd 'add)
cvsdist e1e7868
                  (svn-status-update)
cvsdist e1e7868
                  (message "svn add finished"))
cvsdist e1e7868
                 ((eq svn-process-cmd 'mkdir)
cvsdist e1e7868
                  (svn-status-update)
cvsdist e1e7868
                  (message "svn mkdir finished"))
cvsdist e1e7868
                 ((eq svn-process-cmd 'revert)
cvsdist e1e7868
                  (when (member 'revert svn-status-unmark-files-after-list)
cvsdist e1e7868
                    (svn-status-unset-all-usermarks))
cvsdist e1e7868
                  (svn-status-update)
cvsdist e1e7868
                  (message "svn revert finished"))
cvsdist e1e7868
                 ((eq svn-process-cmd 'resolved)
cvsdist e1e7868
                  (svn-status-update)
cvsdist e1e7868
                  (message "svn resolved finished"))
cvsdist e1e7868
                 ((eq svn-process-cmd 'mv)
cvsdist e1e7868
                  (svn-status-update)
cvsdist e1e7868
                  (message "svn mv finished"))
cvsdist e1e7868
                 ((eq svn-process-cmd 'rm)
cvsdist e1e7868
                  (svn-status-update)
cvsdist e1e7868
                  (message "svn rm finished"))
cvsdist e1e7868
                 ((eq svn-process-cmd 'cleanup)
cvsdist e1e7868
                  (message "svn cleanup finished"))
cvsdist e1e7868
                 ((eq svn-process-cmd 'proplist)
cvsdist e1e7868
                  (svn-status-show-process-buffer-internal t)
cvsdist e1e7868
                  (message "svn proplist finished"))
cvsdist e1e7868
                 ((eq svn-process-cmd 'proplist-parse)
cvsdist e1e7868
                  (svn-status-property-parse-property-names))
cvsdist e1e7868
                 ((eq svn-process-cmd 'propset)
cvsdist e1e7868
                  (svn-status-remove-temp-file-maybe)
cvsdist e1e7868
                  (svn-status-update))
cvsdist e1e7868
                 ((eq svn-process-cmd 'propdel)
cvsdist e1e7868
                  (svn-status-update))))
cvsdist e1e7868
          ((string= event "killed\n")
cvsdist e1e7868
           (message "svn process killed"))
cvsdist e1e7868
          ((string-match "exited abnormally" event)
cvsdist e1e7868
           (while (accept-process-output process 0 100))
cvsdist e1e7868
           ;; find last error message and show it.
cvsdist e1e7868
           (goto-char (point-max))
cvsdist e1e7868
           (message "svn failed: %s"
cvsdist e1e7868
                    (if (re-search-backward "^svn: \\(.*\\)" nil t)
cvsdist e1e7868
                        (match-string 1)
cvsdist e1e7868
                      event)))
cvsdist e1e7868
          (t
cvsdist e1e7868
           (message "svn process had unknown event: %s" event))
cvsdist e1e7868
          (svn-status-show-process-buffer-internal t))))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-parse-rev-num (str)
cvsdist e1e7868
  (if (and str (stringp str)
cvsdist e1e7868
           (save-match-data (string-match "^[0-9]+" str)))
cvsdist e1e7868
      (string-to-number str)
cvsdist e1e7868
    -1))
cvsdist e1e7868
cvsdist e1e7868
cvsdist e1e7868
(defun svn-parse-status-result ()
357017e
  "Parse the *svn-process* buffer.
cvsdist e1e7868
The results are used to build the `svn-status-info' variable."
cvsdist e1e7868
  (setq svn-status-head-revision nil)
cvsdist e1e7868
  (save-excursion
cvsdist e1e7868
    (let ((old-ui-information (svn-status-ui-information-hash-table))
cvsdist e1e7868
          (line-string)
cvsdist e1e7868
          (user-mark)
cvsdist e1e7868
          (svn-marks)
cvsdist e1e7868
          (svn-file-mark)
cvsdist e1e7868
          (svn-property-mark)
cvsdist e1e7868
          (svn-update-mark)
cvsdist e1e7868
          (local-rev)
cvsdist e1e7868
          (last-change-rev)
cvsdist e1e7868
          (author)
cvsdist e1e7868
          (path)
cvsdist e1e7868
          (user-elide nil)
357017e
          (ui-status '(nil nil))     ; contains (user-mark user-elide)
cvsdist e1e7868
          (revision-width svn-status-default-revision-width)
cvsdist e1e7868
          (author-width svn-status-default-author-width))
cvsdist e1e7868
      (set-buffer "*svn-process*")
cvsdist e1e7868
      (setq svn-status-info nil)
cvsdist e1e7868
      (goto-char (point-min))
cvsdist e1e7868
      (while (< (point) (point-max))
cvsdist e1e7868
        (cond
cvsdist e1e7868
         ((= (point-at-eol) (point-at-bol)) ;skip blank lines
cvsdist e1e7868
          nil)
357017e
         ((looking-at "Status against revision:[ ]+\\([0-9]+\\)")
cvsdist e1e7868
          ;; the above message appears for the main listing plus once for each svn:externals entry
cvsdist e1e7868
          (unless svn-status-head-revision
cvsdist e1e7868
            (setq svn-status-head-revision (match-string 1))))
cvsdist e1e7868
         ((looking-at "Performing status on external item at '\(.*\)'")
cvsdist e1e7868
          ;; The *next* line has info about the directory named in svn:externals
cvsdist e1e7868
          ;; we should parse it, and merge the info with what we have already know
cvsdist e1e7868
          ;; but for now just ignore the line completely
cvsdist e1e7868
          (forward-line)
cvsdist e1e7868
          )
cvsdist e1e7868
         (t
cvsdist e1e7868
          (setq svn-marks (buffer-substring (point) (+ (point) 8))
357017e
                svn-file-mark (elt svn-marks 0) ; 1st column
357017e
                svn-property-mark (elt svn-marks 1) ; 2nd column
cvsdist e1e7868
                ;;svn-locked-mark (elt svn-marks 2)            ; 3rd column
cvsdist e1e7868
                ;;svn-added-with-history-mark (elt svn-marks 3); 4th column
cvsdist e1e7868
                ;;svn-switched-mark (elt svn-marks 4)          ; 5th column
357017e
                svn-update-mark (elt svn-marks 7)) ; 8th column
cvsdist e1e7868
cvsdist e1e7868
          (when (eq svn-property-mark ?\ ) (setq svn-property-mark nil))
cvsdist e1e7868
          (when (eq svn-update-mark ?\ ) (setq svn-update-mark nil))
cvsdist e1e7868
          (forward-char 8)
cvsdist e1e7868
          (skip-chars-forward " ")
cvsdist e1e7868
          (cond
cvsdist e1e7868
           ((looking-at "\\([-?]\\|[0-9]+\\) +\\([-?]\\|[0-9]+\\) +\\([^ ]+\\) *\\(.+\\)")
cvsdist e1e7868
            (setq local-rev (svn-parse-rev-num (match-string 1))
cvsdist e1e7868
                  last-change-rev (svn-parse-rev-num (match-string 2))
cvsdist e1e7868
                  author (match-string 3)
cvsdist e1e7868
                  path (match-string 4)))
cvsdist e1e7868
           ((looking-at "\\(.*\\)")
cvsdist e1e7868
            (setq path (match-string 1)
cvsdist e1e7868
                  local-rev -1
cvsdist e1e7868
                  last-change-rev -1
cvsdist e1e7868
                  author (if (eq svn-file-mark 88) "" "?"))) ;clear author of svn:externals dirs
cvsdist e1e7868
           (t
cvsdist e1e7868
            (error "Unknown status line format")))
cvsdist e1e7868
          (unless path (setq path "."))
cvsdist e1e7868
          (setq ui-status (or (gethash path old-ui-information) (list user-mark user-elide)))
cvsdist e1e7868
          (setq svn-status-info (cons (list ui-status
cvsdist e1e7868
                                            svn-file-mark
cvsdist e1e7868
                                            svn-property-mark
cvsdist e1e7868
                                            path
cvsdist e1e7868
                                            local-rev
cvsdist e1e7868
                                            last-change-rev
cvsdist e1e7868
                                            author
cvsdist e1e7868
                                            svn-update-mark)
cvsdist e1e7868
                                      svn-status-info))
cvsdist e1e7868
          (setq revision-width (max revision-width
357017e
                                    (length (number-to-string local-rev))
357017e
                                    (length (number-to-string last-change-rev))))
cvsdist e1e7868
          (setq author-width (max author-width (length author)))))
357017e
        (forward-line 1))
cvsdist e1e7868
      ;; With subversion 0.29.0 and above, `svn -u st' returns files in
cvsdist e1e7868
      ;; a random order (especially if we have a mixed revision wc)
cvsdist e1e7868
      (setq svn-status-default-column
cvsdist e1e7868
            (+ 6 revision-width revision-width author-width
cvsdist e1e7868
               (if svn-status-short-mod-flag-p 3 0)))
cvsdist e1e7868
      (setq svn-status-line-format (format " %%c%%c %%%ds %%%ds %%-%ds"
cvsdist e1e7868
                                           revision-width
cvsdist e1e7868
                                           revision-width
cvsdist e1e7868
                                           author-width))
357017e
      (when svn-status-sort-status-buffer
357017e
        (setq svn-status-info (sort svn-status-info 'svn-status-sort-predicate))))))
cvsdist e1e7868
cvsdist e1e7868
;;(string-lessp "." "%") => nil
cvsdist e1e7868
;(svn-status-sort-predicate '(t t t ".") '(t t t "%")) => t
cvsdist e1e7868
(defun svn-status-sort-predicate (a b)
cvsdist e1e7868
  "Return t if A should appear before B in the *svn-status* buffer.
cvsdist e1e7868
A and B must be line-info's."
cvsdist e1e7868
  (string-lessp (concat (svn-status-line-info->full-path a) "/")
cvsdist e1e7868
                (concat (svn-status-line-info->full-path b) "/")))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-remove-temp-file-maybe ()
cvsdist e1e7868
  "Remove any (no longer required) temporary files created by psvn.el."
cvsdist e1e7868
  (when svn-status-temp-file-to-remove
cvsdist e1e7868
    (when (file-exists-p svn-status-temp-file-to-remove)
cvsdist e1e7868
      (delete-file svn-status-temp-file-to-remove))
cvsdist e1e7868
    (when (file-exists-p svn-status-temp-arg-file)
cvsdist e1e7868
      (delete-file svn-status-temp-arg-file))
cvsdist e1e7868
    (setq svn-status-temp-file-to-remove nil)))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-remove-control-M ()
cvsdist e1e7868
  "Remove ^M at end of line in the whole buffer."
cvsdist e1e7868
  (interactive)
cvsdist e1e7868
  (let ((buffer-read-only nil))
cvsdist e1e7868
    (save-match-data
cvsdist e1e7868
      (save-excursion
cvsdist e1e7868
        (goto-char (point-min))
cvsdist e1e7868
        (while (re-search-forward "\r$" (point-max) t)
cvsdist e1e7868
          (replace-match "" nil nil))))))
cvsdist e1e7868
cvsdist e1e7868
(condition-case nil
cvsdist e1e7868
    ;;(easy-menu-add-item nil '("tools") ["SVN Status" svn-status t] "PCL-CVS")
cvsdist e1e7868
    (easy-menu-add-item nil '("tools") ["SVN Status" svn-status t])
cvsdist e1e7868
  (error (message "psvn: could not install menu")))
cvsdist e1e7868
cvsdist e1e7868
(defvar svn-status-mode-map () "Keymap used in `svn-status-mode' buffers.")
cvsdist e1e7868
(defvar svn-status-mode-property-map ()
cvsdist e1e7868
  "Subkeymap used in `svn-status-mode' for property commands.")
357017e
(defvar svn-status-mode-options-map ()
357017e
  "Subkeymap used in `svn-status-mode' for option commands.")
cvsdist e1e7868
cvsdist e1e7868
(when (not svn-status-mode-map)
cvsdist e1e7868
  (setq svn-status-mode-map (make-sparse-keymap))
cvsdist e1e7868
  (suppress-keymap svn-status-mode-map)
cvsdist e1e7868
  ;; Don't use (kbd "<return>"); it's unreachable with GNU Emacs 21.3 on a TTY.
cvsdist e1e7868
  (define-key svn-status-mode-map (kbd "RET") 'svn-status-find-file-or-examine-directory)
cvsdist e1e7868
  (define-key svn-status-mode-map (kbd "^") 'svn-status-examine-parent)
cvsdist e1e7868
  (define-key svn-status-mode-map (kbd "s") 'svn-status-show-process-buffer)
cvsdist e1e7868
  (define-key svn-status-mode-map (kbd "f") 'svn-status-find-files)
cvsdist e1e7868
  (define-key svn-status-mode-map (kbd "o") 'svn-status-find-file-other-window)
cvsdist e1e7868
  (define-key svn-status-mode-map (kbd "v") 'svn-status-view-file-other-window)
cvsdist e1e7868
  (define-key svn-status-mode-map (kbd "e") 'svn-status-toggle-edit-cmd-flag)
cvsdist e1e7868
  (define-key svn-status-mode-map (kbd "g") 'svn-status-update)
cvsdist e1e7868
  (define-key svn-status-mode-map (kbd "q") 'svn-status-bury-buffer)
cvsdist e1e7868
  (define-key svn-status-mode-map (kbd "h") 'svn-status-use-history)
cvsdist e1e7868
  (define-key svn-status-mode-map (kbd "m") 'svn-status-set-user-mark)
cvsdist e1e7868
  (define-key svn-status-mode-map (kbd "u") 'svn-status-unset-user-mark)
cvsdist e1e7868
  ;; This matches a binding of `dired-unmark-all-files' in `dired-mode-map'
cvsdist e1e7868
  ;; of both GNU Emacs and XEmacs.  It seems unreachable with XEmacs on
cvsdist e1e7868
  ;; TTY, but if that's a problem then its Dired needs fixing too.
cvsdist e1e7868
  ;; Or you could just use "*!".
cvsdist e1e7868
  (define-key svn-status-mode-map "\M-\C-?" 'svn-status-unset-all-usermarks)
cvsdist e1e7868
  ;; The key that normally deletes characters backwards should here
cvsdist e1e7868
  ;; instead unmark files backwards.  In GNU Emacs, that would be (kbd
cvsdist e1e7868
  ;; "DEL") aka [?\177], but XEmacs treats those as [(delete)] and
cvsdist e1e7868
  ;; would bind a key that normally deletes forwards.  [(backspace)]
cvsdist e1e7868
  ;; is unreachable with GNU Emacs on a tty.  Try to recognize the
cvsdist e1e7868
  ;; dialect and act accordingly.
cvsdist e1e7868
  ;;
cvsdist e1e7868
  ;; XEmacs has a `delete-forward-p' function that checks the
cvsdist e1e7868
  ;; `delete-key-deletes-forward' option.  We don't use those, for two
cvsdist e1e7868
  ;; reasons: psvn.el may be loaded before user customizations, and
cvsdist e1e7868
  ;; XEmacs allows simultaneous connections to multiple devices with
cvsdist e1e7868
  ;; different keyboards.
cvsdist e1e7868
  (define-key svn-status-mode-map
cvsdist e1e7868
              (if (member (kbd "DEL") '([(delete)] [delete]))
cvsdist e1e7868
                  [(backspace)]         ; XEmacs
cvsdist e1e7868
                (kbd "DEL"))            ; GNU Emacs
cvsdist e1e7868
              'svn-status-unset-user-mark-backwards)
cvsdist e1e7868
  (define-key svn-status-mode-map (kbd "$") 'svn-status-toggle-elide)
cvsdist e1e7868
  (define-key svn-status-mode-map (kbd ".") 'svn-status-goto-root-or-return)
cvsdist e1e7868
  (define-key svn-status-mode-map (kbd "I") 'svn-status-parse-info)
cvsdist e1e7868
  (define-key svn-status-mode-map (kbd "?") 'svn-status-toggle-hide-unknown)
cvsdist e1e7868
  (define-key svn-status-mode-map (kbd "_") 'svn-status-toggle-hide-unmodified)
cvsdist e1e7868
  (define-key svn-status-mode-map (kbd "a") 'svn-status-add-file)
cvsdist e1e7868
  (define-key svn-status-mode-map (kbd "A") 'svn-status-add-file-recursively)
cvsdist e1e7868
  (define-key svn-status-mode-map (kbd "+") 'svn-status-make-directory)
cvsdist e1e7868
  (define-key svn-status-mode-map (kbd "R") 'svn-status-mv)
cvsdist e1e7868
  (define-key svn-status-mode-map (kbd "D") 'svn-status-rm)
cvsdist e1e7868
  (define-key svn-status-mode-map (kbd "c") 'svn-status-commit-file)
cvsdist e1e7868
  (define-key svn-status-mode-map (kbd "M-c") 'svn-status-cleanup)
cvsdist e1e7868
  (define-key svn-status-mode-map (kbd "U") 'svn-status-update-cmd)
cvsdist e1e7868
  (define-key svn-status-mode-map (kbd "r") 'svn-status-revert)
cvsdist e1e7868
  (define-key svn-status-mode-map (kbd "l") 'svn-status-show-svn-log)
cvsdist e1e7868
  (define-key svn-status-mode-map (kbd "i") 'svn-status-info)
cvsdist e1e7868
  (define-key svn-status-mode-map (kbd "b") 'svn-status-blame)
cvsdist e1e7868
  (define-key svn-status-mode-map (kbd "=") 'svn-status-show-svn-diff)
cvsdist e1e7868
  ;; [(control ?=)] is unreachable on TTY, but you can use "*u" instead.
cvsdist e1e7868
  ;; (Is the "u" mnemonic for something?)
cvsdist e1e7868
  (define-key svn-status-mode-map (kbd "C-=") 'svn-status-show-svn-diff-for-marked-files)
cvsdist e1e7868
  (define-key svn-status-mode-map (kbd "~") 'svn-status-get-specific-revision)
cvsdist e1e7868
  (define-key svn-status-mode-map (kbd "E") 'svn-status-ediff-with-revision)
cvsdist e1e7868
  (define-key svn-status-mode-map (kbd "C-n") 'svn-status-next-line)
cvsdist e1e7868
  (define-key svn-status-mode-map (kbd "C-p") 'svn-status-previous-line)
cvsdist e1e7868
  (define-key svn-status-mode-map (kbd "<down>") 'svn-status-next-line)
cvsdist e1e7868
  (define-key svn-status-mode-map (kbd "<up>") 'svn-status-previous-line)
cvsdist e1e7868
  (setq svn-status-mode-mark-map (make-sparse-keymap))
cvsdist e1e7868
  (define-key svn-status-mode-map (kbd "*") svn-status-mode-mark-map)
cvsdist e1e7868
  (define-key svn-status-mode-mark-map (kbd "!") 'svn-status-unset-all-usermarks)
cvsdist e1e7868
  (define-key svn-status-mode-mark-map (kbd "?") 'svn-status-mark-unknown)
cvsdist e1e7868
  (define-key svn-status-mode-mark-map (kbd "A") 'svn-status-mark-added)
cvsdist e1e7868
  (define-key svn-status-mode-mark-map (kbd "M") 'svn-status-mark-modified)
cvsdist e1e7868
  (define-key svn-status-mode-mark-map (kbd "V") 'svn-status-resolved)
cvsdist e1e7868
  (define-key svn-status-mode-mark-map (kbd "u") 'svn-status-show-svn-diff-for-marked-files))
cvsdist e1e7868
(when (not svn-status-mode-property-map)
cvsdist e1e7868
  (setq svn-status-mode-property-map (make-sparse-keymap))
cvsdist e1e7868
  (define-key svn-status-mode-property-map (kbd "l") 'svn-status-property-list)
cvsdist e1e7868
  (define-key svn-status-mode-property-map (kbd "s") 'svn-status-property-set)
cvsdist e1e7868
  (define-key svn-status-mode-property-map (kbd "d") 'svn-status-property-delete)
cvsdist e1e7868
  (define-key svn-status-mode-property-map (kbd "e") 'svn-status-property-edit-one-entry)
cvsdist e1e7868
  (define-key svn-status-mode-property-map (kbd "i") 'svn-status-property-ignore-file)
cvsdist e1e7868
  (define-key svn-status-mode-property-map (kbd "I") 'svn-status-property-ignore-file-extension)
cvsdist e1e7868
  ;; XEmacs 21.4.15 on TTY (vt420) converts `C-i' to `TAB',
cvsdist e1e7868
  ;; which [(control ?i)] won't match.  Handle it separately.
cvsdist e1e7868
  ;; On GNU Emacs, the following two forms bind the same key,
cvsdist e1e7868
  ;; reducing clutter in `where-is'.
cvsdist e1e7868
  (define-key svn-status-mode-property-map [(control ?i)] 'svn-status-property-edit-svn-ignore)
cvsdist e1e7868
  (define-key svn-status-mode-property-map (kbd "TAB") 'svn-status-property-edit-svn-ignore)
cvsdist e1e7868
  (define-key svn-status-mode-property-map (kbd "k") 'svn-status-property-set-keyword-list)
cvsdist e1e7868
  (define-key svn-status-mode-property-map (kbd "y") 'svn-status-property-set-eol-style)
cvsdist e1e7868
  (define-key svn-status-mode-property-map (kbd "p") 'svn-status-property-parse)
cvsdist e1e7868
  ;; TODO: Why is `svn-status-select-line' in `svn-status-mode-property-map'?
cvsdist e1e7868
  (define-key svn-status-mode-property-map (kbd "RET") 'svn-status-select-line)
cvsdist e1e7868
  (define-key svn-status-mode-map (kbd "P") svn-status-mode-property-map))
357017e
(when (not svn-status-mode-options-map)
357017e
  (setq svn-status-mode-options-map (make-sparse-keymap))
357017e
  (define-key svn-status-mode-options-map (kbd "s") 'svn-status-save-state)
357017e
  (define-key svn-status-mode-options-map (kbd "l") 'svn-status-load-state)
357017e
  (define-key svn-status-mode-options-map (kbd "x") 'svn-status-toggle-sort-status-buffer)
357017e
  (define-key svn-status-mode-map (kbd "O") svn-status-mode-options-map))
cvsdist e1e7868
cvsdist e1e7868
(easy-menu-define svn-status-mode-menu svn-status-mode-map
cvsdist e1e7868
  "'svn-status-mode' menu"
cvsdist e1e7868
  '("SVN"
cvsdist e1e7868
    ["svn status" svn-status-update t]
cvsdist e1e7868
    ["svn update" svn-status-update-cmd t]
cvsdist e1e7868
    ["svn commit" svn-status-commit-file t]
cvsdist e1e7868
    ["svn log" svn-status-show-svn-log t]
cvsdist e1e7868
    ["svn info" svn-status-info t]
cvsdist e1e7868
    ["svn blame" svn-status-blame t]
cvsdist e1e7868
    ("Diff"
cvsdist e1e7868
     ["svn diff current file" svn-status-show-svn-diff t]
cvsdist e1e7868
     ["svn diff marked files" svn-status-show-svn-diff-for-marked-files t]
cvsdist e1e7868
     ["svn ediff current file" svn-status-ediff-with-revision t]
cvsdist e1e7868
     )
cvsdist e1e7868
    ["svn cat ..." svn-status-get-specific-revision t]
cvsdist e1e7868
    ["svn add" svn-status-add-file t]
cvsdist e1e7868
    ["svn mkdir..." svn-status-make-directory t]
cvsdist e1e7868
    ["svn mv..." svn-status-mv t]
cvsdist e1e7868
    ["svn rm..." svn-status-rm t]
cvsdist e1e7868
    ["Up Directory" svn-status-examine-parent t]
cvsdist e1e7868
    ["Elide Directory" svn-status-toggle-elide t]
cvsdist e1e7868
    ["svn revert" svn-status-revert t]
cvsdist e1e7868
    ["svn resolved" svn-status-resolved t]
cvsdist e1e7868
    ["svn cleanup" svn-status-cleanup t]
cvsdist e1e7868
    ["Show Process Buffer" svn-status-show-process-buffer t]
cvsdist e1e7868
    ("Property"
cvsdist e1e7868
     ["svn proplist" svn-status-property-list t]
cvsdist e1e7868
     ["Set Multiple Properties..." svn-status-property-set t]
cvsdist e1e7868
     ["Edit One Property..." svn-status-property-edit-one-entry t]
cvsdist e1e7868
     ["svn propdel..." svn-status-property-delete t]
cvsdist e1e7868
     "---"
cvsdist e1e7868
     ["svn:ignore File..." svn-status-property-ignore-file t]
cvsdist e1e7868
     ["svn:ignore File Extension..." svn-status-property-ignore-file-extension t]
cvsdist e1e7868
     ["Edit svn:ignore Property" svn-status-property-edit-svn-ignore t]
cvsdist e1e7868
     "---"
cvsdist e1e7868
     ["Set svn:keywords List" svn-status-property-set-keyword-list t]
cvsdist e1e7868
     ["Set svn:eol-style" svn-status-property-set-eol-style t]
cvsdist e1e7868
     )
357017e
    ("Options"
357017e
     ["Save Options" svn-status-save-state t]
357017e
     ["Load Options" svn-status-load-state t]
357017e
     ["Toggle sorting of *svn-status* buffer" svn-status-toggle-sort-status-buffer
357017e
      :style toggle :selected svn-status-sort-status-buffer]
357017e
     )
cvsdist e1e7868
    "---"
cvsdist e1e7868
    ["Edit Next SVN Cmd Line" svn-status-toggle-edit-cmd-flag t]
cvsdist e1e7868
    ["Work Directory History..." svn-status-use-history t]
cvsdist e1e7868
    ["Mark" svn-status-set-user-mark t]
cvsdist e1e7868
    ["Unmark" svn-status-unset-user-mark t]
cvsdist e1e7868
    ("Mark / Unmark"
cvsdist e1e7868
     ["Unmark all" svn-status-unset-all-usermarks t]
cvsdist e1e7868
     ["Mark/Unmark unknown" svn-status-mark-unknown t]
cvsdist e1e7868
     ["Mark/Unmark added" svn-status-mark-added t]
cvsdist e1e7868
     ["Mark/Unmark modified" svn-status-mark-modified t]
cvsdist e1e7868
     )
cvsdist e1e7868
    ["Hide Unknown" svn-status-toggle-hide-unknown
cvsdist e1e7868
     :style toggle :selected svn-status-hide-unknown]
cvsdist e1e7868
    ["Hide Unmodified" svn-status-toggle-hide-unmodified
cvsdist e1e7868
     :style toggle :selected svn-status-hide-unmodified]
cvsdist e1e7868
    ))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-mode ()
cvsdist e1e7868
  "Major mode used by  psvn.el to process the output of \"svn status\".
cvsdist e1e7868
cvsdist e1e7868
psvn.el is an interface for the revision control tool subversion
cvsdist e1e7868
\(see http://subversion.tigris.org).
cvsdist e1e7868
psvn.el provides a similar interface for subversion as pcl-cvs does for cvs.
cvsdist e1e7868
At the moment the following commands are implemented:
cvsdist e1e7868
  M-x svn-status: run 'svn -status -v'
cvsdist e1e7868
  and show the result in the *svn-status* buffer, this buffer uses the
cvsdist e1e7868
  svn-status mode. In this mode the following keys are defined:
cvsdist e1e7868
\\{svn-status-mode-map}"
cvsdist e1e7868
  (interactive)
cvsdist e1e7868
  (kill-all-local-variables)
cvsdist e1e7868
cvsdist e1e7868
  (use-local-map svn-status-mode-map)
cvsdist e1e7868
  (easy-menu-add svn-status-mode-menu)
cvsdist e1e7868
cvsdist e1e7868
  (setq major-mode 'svn-status-mode)
cvsdist e1e7868
  (setq mode-name "svn-status")
cvsdist e1e7868
  (setq mode-line-process 'svn-status-mode-line-process)
cvsdist e1e7868
  (let ((view-read-only nil))
cvsdist e1e7868
    (toggle-read-only 1)))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-update-mode-line ()
cvsdist e1e7868
  (setq svn-status-mode-line-process
cvsdist e1e7868
        (concat svn-status-mode-line-process-edit-flag svn-status-mode-line-process-status))
cvsdist e1e7868
  (force-mode-line-update))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-bury-buffer (arg)
cvsdist e1e7868
  "Bury the *svn-status* buffer.
cvsdist e1e7868
When called with a prefix argument, switch back to the window configuration that was
cvsdist e1e7868
in use before `svn-status' was called."
cvsdist e1e7868
  (interactive "P")
cvsdist e1e7868
  (cond (arg
cvsdist e1e7868
         (when svn-status-initial-window-configuration
cvsdist e1e7868
           (set-window-configuration svn-status-initial-window-configuration)))
cvsdist e1e7868
        (t
cvsdist e1e7868
         (let ((bl '("*svn-log-edit*" "*svn-property-edit*" "*svn-process*")))
cvsdist e1e7868
           (while bl
cvsdist e1e7868
             (when (get-buffer (car bl))
cvsdist e1e7868
               (bury-buffer (car bl)))
cvsdist e1e7868
             (setq bl (cdr bl)))
cvsdist e1e7868
           (when (string= (buffer-name) "*svn-status*")
cvsdist e1e7868
             (bury-buffer))))))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-find-files ()
cvsdist e1e7868
  "Open selected file(s) for editing.
cvsdist e1e7868
See `svn-status-marked-files' for what counts as selected."
cvsdist e1e7868
  (interactive)
cvsdist e1e7868
  (let ((fnames (mapcar 'svn-status-line-info->full-path (svn-status-marked-files))))
cvsdist e1e7868
    (mapc 'find-file fnames)))
cvsdist e1e7868
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-find-file-other-window ()
cvsdist e1e7868
  "Open the file in the other window for editing."
cvsdist e1e7868
  (interactive)
cvsdist e1e7868
  (find-file-other-window (svn-status-line-info->filename
cvsdist e1e7868
                           (svn-status-get-line-information))))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-view-file-other-window ()
cvsdist e1e7868
  "Open the file in the other window for viewing."
cvsdist e1e7868
  (interactive)
cvsdist e1e7868
  (view-file-other-window (svn-status-line-info->filename
cvsdist e1e7868
                           (svn-status-get-line-information))))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-find-file-or-examine-directory ()
cvsdist e1e7868
  "If point is on a directory, run `svn-status' on that directory.
cvsdist e1e7868
Otherwise run `find-file'."
cvsdist e1e7868
  (interactive)
cvsdist e1e7868
  (let ((line-info (svn-status-get-line-information)))
cvsdist e1e7868
    (if (svn-status-line-info->directory-p line-info)
cvsdist e1e7868
        (svn-status (svn-status-line-info->full-path line-info))
cvsdist e1e7868
      (find-file (svn-status-line-info->filename line-info)))))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-examine-parent ()
cvsdist e1e7868
  "Run `svn-status' on the parent of the current directory."
cvsdist e1e7868
  (interactive)
cvsdist e1e7868
  (svn-status (expand-file-name "../")))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-line-info->ui-status (line-info) (nth 0 line-info))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-line-info->has-usermark (line-info) (nth 0 (nth 0 line-info)))
cvsdist e1e7868
(defun svn-status-line-info->user-elide (line-info) (nth 1 (nth 0 line-info)))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-line-info->filemark (line-info) (nth 1 line-info))
cvsdist e1e7868
(defun svn-status-line-info->propmark (line-info) (nth 2 line-info))
cvsdist e1e7868
(defun svn-status-line-info->filename (line-info) (nth 3 line-info))
cvsdist e1e7868
(defun svn-status-line-info->filename-nondirectory (line-info)
cvsdist e1e7868
  (file-name-nondirectory (svn-status-line-info->filename line-info)))
cvsdist e1e7868
(defun svn-status-line-info->localrev (line-info)
cvsdist e1e7868
  (if (>= (nth 4 line-info) 0)
cvsdist e1e7868
      (nth 4 line-info)
cvsdist e1e7868
    nil))
cvsdist e1e7868
(defun svn-status-line-info->lastchangerev (line-info)
cvsdist e1e7868
  "Return the last revision in which LINE-INFO was modified."
cvsdist e1e7868
  (if (>= (nth 5 line-info) 0)
cvsdist e1e7868
      (nth 5 line-info)
cvsdist e1e7868
    nil))
cvsdist e1e7868
(defun svn-status-line-info->author (line-info) (nth 6 line-info))
cvsdist e1e7868
(defun svn-status-line-info->modified-external (line-info) (nth 7 line-info))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-line-info->is-visiblep (line-info)
cvsdist e1e7868
  (not (or (svn-status-line-info->hide-because-unknown line-info)
cvsdist e1e7868
           (svn-status-line-info->hide-because-unmodified line-info)
cvsdist e1e7868
           (svn-status-line-info->hide-because-user-elide line-info))))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-line-info->hide-because-unknown (line-info)
cvsdist e1e7868
  (and svn-status-hide-unknown
cvsdist e1e7868
       (eq (svn-status-line-info->filemark line-info) ??)))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-line-info->hide-because-unmodified (line-info)
cvsdist e1e7868
  ;;(message " %S %S %S %S - %s" svn-status-hide-unmodified (svn-status-line-info->propmark line-info) ?_
cvsdist e1e7868
  ;;         (svn-status-line-info->filemark line-info) (svn-status-line-info->filename line-info))
cvsdist e1e7868
  (and svn-status-hide-unmodified
cvsdist e1e7868
       (and (or (eq (svn-status-line-info->filemark line-info) ?_)
cvsdist e1e7868
                (eq (svn-status-line-info->filemark line-info) ? ))
cvsdist e1e7868
            (or (eq (svn-status-line-info->propmark line-info) ?_)
cvsdist e1e7868
                (eq (svn-status-line-info->propmark line-info) ? )
cvsdist e1e7868
                (eq (svn-status-line-info->propmark line-info) nil)))))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-line-info->hide-because-user-elide (line-info)
cvsdist e1e7868
  (eq (svn-status-line-info->user-elide line-info) t))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-line-info->show-user-elide-continuation (line-info)
cvsdist e1e7868
  (eq (svn-status-line-info->user-elide line-info) 'directory))
cvsdist e1e7868
357017e
;; modify the line-info
357017e
(defun svn-status-line-info->set-filemark (line-info value)
357017e
  (setcar (nthcdr 1 line-info) value))
357017e
cvsdist e1e7868
(defun svn-status-toggle-elide ()
cvsdist e1e7868
  (interactive)
cvsdist e1e7868
  (let ((st-info svn-status-info)
cvsdist e1e7868
        (fname)
cvsdist e1e7868
        (test (svn-status-line-info->filename (svn-status-get-line-information)))
cvsdist e1e7868
        (len-test)
cvsdist e1e7868
        (len-fname)
cvsdist e1e7868
        (new-elide-mark t)
cvsdist e1e7868
        (elide-mark))
cvsdist e1e7868
    (when (string= test ".")
cvsdist e1e7868
      (setq test ""))
cvsdist e1e7868
    (setq len-test (length test))
cvsdist e1e7868
    (while st-info
cvsdist e1e7868
      (setq fname (svn-status-line-info->filename (car st-info)))
cvsdist e1e7868
      (setq len-fname (length fname))
cvsdist e1e7868
      (when (and (>= len-fname len-test)
cvsdist e1e7868
                 (string= (substring fname 0 len-test) test))
cvsdist e1e7868
        ;;(message "elide: %s %s" fname (svn-status-line-info->user-elide (car st-info)))
cvsdist e1e7868
        (setq elide-mark new-elide-mark)
cvsdist e1e7868
        (when (or (string= fname ".")
cvsdist e1e7868
                  (and (= len-fname len-test) (svn-status-line-info->directory-p (car st-info))))
cvsdist e1e7868
          (message "Elide directory %s and all its files." fname)
cvsdist e1e7868
          (setq new-elide-mark (not (svn-status-line-info->user-elide (car st-info))))
cvsdist e1e7868
          (setq elide-mark (if new-elide-mark 'directory nil)))
cvsdist e1e7868
        (setcar (nthcdr 1 (svn-status-line-info->ui-status (car st-info))) elide-mark))
cvsdist e1e7868
      (setq st-info (cdr st-info))))
cvsdist e1e7868
  (svn-status-update-buffer))
cvsdist e1e7868
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-line-info->directory-p (line-info)
cvsdist e1e7868
  "Return t if LINE-INFO refers to a directory, nil otherwise.
cvsdist e1e7868
Symbolic links to directories count as directories (see `file-directory-p')."
cvsdist e1e7868
  (file-directory-p (svn-status-line-info->filename line-info)))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-line-info->full-path (line-info)
cvsdist e1e7868
  "Return the full path of the file represented by LINE-INFO."
cvsdist e1e7868
  (expand-file-name
cvsdist e1e7868
   (svn-status-line-info->filename line-info)))
cvsdist e1e7868
cvsdist e1e7868
;;Not convinced that this is the fastest way, but...
cvsdist e1e7868
(defun svn-status-count-/ (string)
cvsdist e1e7868
  "Return number of \"/\"'s in STRING."
cvsdist e1e7868
  (let ((n 0)
cvsdist e1e7868
        (last 0))
cvsdist e1e7868
    (while (setq last (string-match "/" string (1+ last)))
cvsdist e1e7868
      (setq n (1+ n)))
cvsdist e1e7868
    n))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-insert-line-in-status-buffer (line-info)
cvsdist e1e7868
  "Format LINE-INFO and insert the result in the current buffer."
cvsdist e1e7868
  (let ((usermark (if (svn-status-line-info->has-usermark line-info) "*" " "))
cvsdist e1e7868
        (external (if (svn-status-line-info->modified-external line-info)
cvsdist e1e7868
                      (svn-add-face (if svn-status-short-mod-flag-p
cvsdist e1e7868
                                        "** "
cvsdist e1e7868
                                      " (modified external)")
cvsdist e1e7868
                                    'svn-status-modified-external-face)
cvsdist e1e7868
                    (if svn-status-short-mod-flag-p "   " "")))
cvsdist e1e7868
        ;; To add indentation based on the
cvsdist e1e7868
        ;; directory that the file is in, we just insert 2*(number of "/" in
cvsdist e1e7868
        ;; filename) spaces, which is rather hacky (but works)!
cvsdist e1e7868
        (filename (svn-status-choose-face-to-add
cvsdist e1e7868
                   (svn-status-line-info->directory-p line-info)
cvsdist e1e7868
                   (concat (make-string
cvsdist e1e7868
                            (* 2 (svn-status-count-/
cvsdist e1e7868
                                  (svn-status-line-info->filename line-info)))
cvsdist e1e7868
                            32)
cvsdist e1e7868
                           (if svn-status-hide-unmodified
cvsdist e1e7868
                               (svn-status-line-info->filename line-info)
cvsdist e1e7868
                             (svn-status-line-info->filename-nondirectory line-info)))
cvsdist e1e7868
                   'svn-status-directory-face
cvsdist e1e7868
                   'svn-status-filename-face))
cvsdist e1e7868
        (elide-hint (if (svn-status-line-info->show-user-elide-continuation line-info) " ..." "")))
cvsdist e1e7868
    (insert (svn-status-maybe-add-face
cvsdist e1e7868
             (svn-status-line-info->has-usermark line-info)
cvsdist e1e7868
             (concat usermark
cvsdist e1e7868
                     (format svn-status-line-format
cvsdist e1e7868
                             (svn-status-line-info->filemark line-info)
cvsdist e1e7868
                             (or (svn-status-line-info->propmark line-info) ? )
cvsdist e1e7868
                             (or (svn-status-line-info->localrev line-info) "")
cvsdist e1e7868
                             (or (svn-status-line-info->lastchangerev line-info) "")
cvsdist e1e7868
                             (svn-status-line-info->author line-info)))
cvsdist e1e7868
             'svn-status-marked-face)
cvsdist e1e7868
            (if svn-status-short-mod-flag-p external filename)
cvsdist e1e7868
            (if svn-status-short-mod-flag-p filename external)
cvsdist e1e7868
            elide-hint
cvsdist e1e7868
            "\n")))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-update-buffer ()
cvsdist e1e7868
  (interactive)
cvsdist e1e7868
  ;(message (format "buffer-name: %s" (buffer-name)))
cvsdist e1e7868
  (unless (string= (buffer-name) "*svn-status*")
cvsdist e1e7868
    (delete-other-windows)
cvsdist e1e7868
    (split-window-vertically)
cvsdist e1e7868
    (switch-to-buffer "*svn-status*"))
cvsdist e1e7868
  (svn-status-mode)
cvsdist e1e7868
  (let ((st-info svn-status-info)
cvsdist e1e7868
        (buffer-read-only nil)
cvsdist e1e7868
        (start-pos)
cvsdist e1e7868
        (overlay)
cvsdist e1e7868
        (unmodified-count 0)
cvsdist e1e7868
        (unknown-count 0)
cvsdist e1e7868
        (marked-count 0)
cvsdist e1e7868
        (fname (svn-status-line-info->filename (svn-status-get-line-information)))
cvsdist e1e7868
        (fname-pos (point))
cvsdist e1e7868
        (column (current-column)))
cvsdist e1e7868
    (delete-region (point-min) (point-max))
cvsdist e1e7868
    (insert "\n")
cvsdist e1e7868
    ;; Insert all files and directories
cvsdist e1e7868
    (while st-info
cvsdist e1e7868
      (setq start-pos (point))
cvsdist e1e7868
      (cond ((svn-status-line-info->has-usermark (car st-info))
cvsdist e1e7868
             ;; Show a marked file always
cvsdist e1e7868
             (svn-insert-line-in-status-buffer (car st-info)))
cvsdist e1e7868
            ((svn-status-line-info->hide-because-user-elide (car st-info))
cvsdist e1e7868
             );(message "user wanted to hide %s" (svn-status-line-info->filename (car st-info))))
cvsdist e1e7868
            ((svn-status-line-info->hide-because-unknown (car st-info))
cvsdist e1e7868
             (setq unknown-count (+ unknown-count 1)))
cvsdist e1e7868
            ((svn-status-line-info->hide-because-unmodified (car st-info))
cvsdist e1e7868
             (setq unmodified-count (+ unmodified-count 1)))
cvsdist e1e7868
            (t
cvsdist e1e7868
             (svn-insert-line-in-status-buffer (car st-info))))
cvsdist e1e7868
      (when (svn-status-line-info->has-usermark (car st-info))
cvsdist e1e7868
        (setq marked-count (+ marked-count 1)))
cvsdist e1e7868
      (setq overlay (make-overlay start-pos (point)))
cvsdist e1e7868
      (overlay-put overlay 'svn-info (car st-info))
cvsdist e1e7868
      (setq st-info (cdr st-info)))
cvsdist e1e7868
    ;; Insert status information at the buffer beginning
cvsdist e1e7868
    (goto-char (point-min))
cvsdist e1e7868
    (insert (format "svn status for directory %s%s\n"
cvsdist e1e7868
                    default-directory
cvsdist e1e7868
                    (if svn-status-head-revision (format " (status against revision: %s)"
cvsdist e1e7868
                                                         svn-status-head-revision)
cvsdist e1e7868
                      "")))
cvsdist e1e7868
    (when svn-status-base-info
cvsdist e1e7868
      (insert (concat "Repository: " (svn-status-base-info->url) "\n")))
cvsdist e1e7868
    (when svn-status-hide-unknown
cvsdist e1e7868
      (insert
cvsdist e1e7868
       (format "%d Unknown files are hidden - press ? to toggle hiding\n"
cvsdist e1e7868
               unknown-count)))
cvsdist e1e7868
    (when svn-status-hide-unmodified
cvsdist e1e7868
      (insert
cvsdist e1e7868
       (format "%d Unmodified files are hidden - press _ to toggle hiding\n"
cvsdist e1e7868
               unmodified-count)))
cvsdist e1e7868
    (insert (format "%d files marked\n" marked-count))
cvsdist e1e7868
    (setq svn-start-of-file-list-line-number (+ (count-lines (point-min) (point)) 1))
cvsdist e1e7868
    (if fname
cvsdist e1e7868
        (progn
cvsdist e1e7868
          (goto-char fname-pos)
cvsdist e1e7868
          (svn-status-goto-file-name fname)
cvsdist e1e7868
          (goto-char (+ column (point-at-bol))))
cvsdist e1e7868
      (goto-char (+ (next-overlay-change (point-min)) svn-status-default-column)))))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-parse-info (arg)
cvsdist e1e7868
  "Parse the svn info output for the base directory.
cvsdist e1e7868
Show the repository url after this call in the *svn-status* buffer.
cvsdist e1e7868
When called with the prefix argument 0, reset the information to nil.
cvsdist e1e7868
This hides the repository information again."
cvsdist e1e7868
  (interactive "P")
cvsdist e1e7868
  (if (eq arg 0)
cvsdist e1e7868
      (setq svn-status-base-info nil)
cvsdist e1e7868
    (svn-run-svn nil t 'parse-info "info" ".")
cvsdist e1e7868
    (svn-status-parse-info-result))
cvsdist e1e7868
  (svn-status-update-buffer))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-parse-info-result ()
cvsdist e1e7868
  (let ((url))
cvsdist e1e7868
    (save-excursion
cvsdist e1e7868
      (set-buffer "*svn-process*")
cvsdist e1e7868
      (goto-char (point-min))
cvsdist e1e7868
      (search-forward "Url: ")
cvsdist e1e7868
      (setq url (buffer-substring-no-properties (point) (point-at-eol))))
cvsdist e1e7868
    (setq svn-status-base-info `((url ,url)))))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-base-info->url ()
cvsdist e1e7868
  (if svn-status-base-info
cvsdist e1e7868
      (cadr (assoc 'url svn-status-base-info))
cvsdist e1e7868
    ""))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-toggle-edit-cmd-flag (&optional reset)
cvsdist e1e7868
  (interactive)
cvsdist e1e7868
  (cond ((or reset (eq svn-status-edit-svn-command 'sticky))
cvsdist e1e7868
         (setq svn-status-edit-svn-command nil))
cvsdist e1e7868
        ((eq svn-status-edit-svn-command nil)
cvsdist e1e7868
         (setq svn-status-edit-svn-command t))
cvsdist e1e7868
        ((eq svn-status-edit-svn-command t)
cvsdist e1e7868
         (setq svn-status-edit-svn-command 'sticky)))
cvsdist e1e7868
  (cond ((eq svn-status-edit-svn-command t)
cvsdist e1e7868
         (setq svn-status-mode-line-process-edit-flag " EditCmd"))
cvsdist e1e7868
        ((eq svn-status-edit-svn-command 'sticky)
cvsdist e1e7868
         (setq svn-status-mode-line-process-edit-flag " EditCmd#"))
cvsdist e1e7868
        (t
cvsdist e1e7868
         (setq svn-status-mode-line-process-edit-flag "")))
cvsdist e1e7868
  (svn-status-update-mode-line))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-goto-root-or-return ()
cvsdist e1e7868
  "Bounce point between the root (\".\") and the current line."
cvsdist e1e7868
  (interactive)
cvsdist e1e7868
  (if (string= (svn-status-line-info->filename (svn-status-get-line-information)) ".")
cvsdist e1e7868
      (when svn-status-root-return-info
cvsdist e1e7868
        (svn-status-goto-file-name
cvsdist e1e7868
         (svn-status-line-info->filename svn-status-root-return-info)))
cvsdist e1e7868
    (setq svn-status-root-return-info (svn-status-get-line-information))
cvsdist e1e7868
    (svn-status-goto-file-name ".")))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-next-line (nr-of-lines)
cvsdist e1e7868
  (interactive "p")
cvsdist e1e7868
  (next-line nr-of-lines)
cvsdist e1e7868
  (when (svn-status-get-line-information)
cvsdist e1e7868
    (goto-char (+ (point-at-bol) svn-status-default-column))))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-previous-line (nr-of-lines)
cvsdist e1e7868
  (interactive "p")
cvsdist e1e7868
  (previous-line nr-of-lines)
cvsdist e1e7868
  (when (svn-status-get-line-information)
cvsdist e1e7868
    (goto-char (+ (point-at-bol) svn-status-default-column))))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-update (&optional arg)
cvsdist e1e7868
  "Run 'svn status -v'.
cvsdist e1e7868
When called with a prefix argument run 'svn status -vu'."
cvsdist e1e7868
  (interactive "P")
cvsdist e1e7868
  (unless (interactive-p)
cvsdist e1e7868
    (save-excursion
cvsdist e1e7868
      (set-buffer "*svn-process*")
cvsdist e1e7868
      (setq svn-status-update-previous-process-output (buffer-substring (point-min) (point-max)))))
cvsdist e1e7868
  (svn-status default-directory arg))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-get-line-information ()
cvsdist e1e7868
  "Find out about the file under point.
cvsdist e1e7868
The result may be parsed with the various `svn-status-line-info->...' functions."
cvsdist e1e7868
  (let ((overlay (car (overlays-at (point)))))
cvsdist e1e7868
    (when overlay
cvsdist e1e7868
      (overlay-get overlay 'svn-info))))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-get-file-list (use-marked-files)
cvsdist e1e7868
  "Get either the marked files or the files, where the cursor is on."
cvsdist e1e7868
  (if use-marked-files
cvsdist e1e7868
      (svn-status-marked-files)
cvsdist e1e7868
    (list (svn-status-get-line-information))))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-get-file-list-names (use-marked-files)
cvsdist e1e7868
  (mapcar 'svn-status-line-info->filename (svn-status-get-file-list use-marked-files)))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-select-line ()
cvsdist e1e7868
  (interactive)
cvsdist e1e7868
  (let ((info (svn-status-get-line-information)))
cvsdist e1e7868
    (if info
cvsdist e1e7868
        (message "%S %S %S" info (svn-status-line-info->hide-because-unknown info)
cvsdist e1e7868
                                 (svn-status-line-info->hide-because-unmodified info))
cvsdist e1e7868
      (message "No file on this line"))))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-directory-containing-point (allow-self)
cvsdist e1e7868
  "Find the (full path of) directory containing the file under point.
cvsdist e1e7868
cvsdist e1e7868
If ALLOW-SELF and the file is a directory, return that directory,
cvsdist e1e7868
otherwise return the directory containing the file under point."
cvsdist e1e7868
  ;;the first `or' below is because s-s-g-l-i returns `nil' if
cvsdist e1e7868
  ;;point was outside the file list, but we need
cvsdist e1e7868
  ;;s-s-l-i->f to return a string to add to `default-directory'.
cvsdist e1e7868
  (let ((line-info (or (svn-status-get-line-information)
cvsdist e1e7868
                       '(nil nil nil ""))))
cvsdist e1e7868
    (file-name-as-directory
cvsdist e1e7868
     (expand-file-name
cvsdist e1e7868
      (if (and allow-self (svn-status-line-info->directory-p line-info))
cvsdist e1e7868
          (svn-status-line-info->filename line-info)
cvsdist e1e7868
        ;;The next `or' is because (file-name-directory "file") returns nil
cvsdist e1e7868
        (or (file-name-directory (svn-status-line-info->filename line-info))
cvsdist e1e7868
            "."))))))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-set-user-mark (arg)
cvsdist e1e7868
  "Set a user mark on the current file or directory.
cvsdist e1e7868
If the cursor is on a file this file is marked and the cursor advances to the next line.
cvsdist e1e7868
If the cursor is on a directory all files in this directory are marked.
cvsdist e1e7868
cvsdist e1e7868
If this function is called with a prefix argument, only the current line is
cvsdist e1e7868
marked, even if it is a directory."
cvsdist e1e7868
  (interactive "P")
cvsdist e1e7868
  (let ((info (svn-status-get-line-information)))
cvsdist e1e7868
    (if info
cvsdist e1e7868
        (progn
cvsdist e1e7868
          (svn-status-apply-usermark t arg)
cvsdist e1e7868
          (svn-status-next-line 1))
cvsdist e1e7868
      (message "No file on this line - cannot set a mark"))))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-unset-user-mark (arg)
cvsdist e1e7868
  "Remove a user mark on the current file or directory.
cvsdist e1e7868
If the cursor is on a file, this file is unmarked and the cursor advances to the next line.
cvsdist e1e7868
If the cursor is on a directory, all files in this directory are unmarked.
cvsdist e1e7868
cvsdist e1e7868
If this function is called with a prefix argument, only the current line is
cvsdist e1e7868
unmarked, even if is a directory."
cvsdist e1e7868
  (interactive "P")
cvsdist e1e7868
  (let ((info (svn-status-get-line-information)))
cvsdist e1e7868
    (if info
cvsdist e1e7868
        (progn
cvsdist e1e7868
          (svn-status-apply-usermark nil arg)
cvsdist e1e7868
          (svn-status-next-line 1))
cvsdist e1e7868
      (message "No file on this line - cannot unset a mark"))))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-unset-user-mark-backwards ()
cvsdist e1e7868
  "Remove a user mark from the previous file.
cvsdist e1e7868
Then move to that line."
cvsdist e1e7868
  ;; This is consistent with `dired-unmark-backward' and
cvsdist e1e7868
  ;; `cvs-mode-unmark-up'.
cvsdist e1e7868
  (interactive)
cvsdist e1e7868
  (let ((info (save-excursion
cvsdist e1e7868
                (svn-status-next-line -1)
cvsdist e1e7868
                (svn-status-get-line-information))))
cvsdist e1e7868
    (if info
cvsdist e1e7868
        (progn
cvsdist e1e7868
          (svn-status-next-line -1)
cvsdist e1e7868
          (svn-status-apply-usermark nil t))
cvsdist e1e7868
      (message "No file on previous line - cannot unset a mark"))))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-apply-usermark (set-mark only-this-line)
cvsdist e1e7868
  "Do the work for the various marking/unmarking functions."
cvsdist e1e7868
  (let* ((st-info svn-status-info)
cvsdist e1e7868
         (line-info (svn-status-get-line-information))
cvsdist e1e7868
         (file-name (svn-status-line-info->filename line-info))
cvsdist e1e7868
     (sub-file-regexp (concat "^" (regexp-quote
cvsdist e1e7868
                       (file-name-as-directory file-name))))
cvsdist e1e7868
         (newcursorpos-fname)
cvsdist e1e7868
         (i-fname)
cvsdist e1e7868
         (current-line svn-start-of-file-list-line-number))
cvsdist e1e7868
    (while st-info
cvsdist e1e7868
      (when (svn-status-line-info->is-visiblep (car st-info))
cvsdist e1e7868
        (setq current-line (1+ current-line)))
cvsdist e1e7868
      (setq i-fname (svn-status-line-info->filename (car st-info)))
cvsdist e1e7868
      (when (or (string= file-name i-fname)
cvsdist e1e7868
        (string-match sub-file-regexp i-fname))
cvsdist e1e7868
        (when (svn-status-line-info->is-visiblep (car st-info))
cvsdist e1e7868
          (when (or (not only-this-line) (string= file-name i-fname))
cvsdist e1e7868
            (setq newcursorpos-fname i-fname)
cvsdist e1e7868
            (if set-mark
cvsdist e1e7868
                (message "marking: %s" i-fname)
cvsdist e1e7868
              (message "unmarking: %s" i-fname))
cvsdist e1e7868
            ;;(message "ui-status: %S" (svn-status-line-info->ui-status (car st-info)))
cvsdist e1e7868
            (setcar (svn-status-line-info->ui-status (car st-info)) set-mark)
cvsdist e1e7868
            (save-excursion
cvsdist e1e7868
              (let ((buffer-read-only nil))
cvsdist e1e7868
                (goto-line current-line)
cvsdist e1e7868
                (delete-region (point-at-bol) (point-at-eol))
cvsdist e1e7868
                (svn-insert-line-in-status-buffer (car st-info))
cvsdist e1e7868
                (delete-char 1))))))
cvsdist e1e7868
      (setq st-info (cdr st-info)))
cvsdist e1e7868
    ;;(svn-status-update-buffer)
cvsdist e1e7868
    (svn-status-goto-file-name newcursorpos-fname)))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-apply-usermark-checked (check-function set-mark)
cvsdist e1e7868
  "Mark or unmark files, whether a given function returns t.
cvsdist e1e7868
The function is called with the line information. Therefore the svnstatus-line-info->* functions can be
cvsdist e1e7868
used in the check."
cvsdist e1e7868
  (let ((st-info svn-status-info))
cvsdist e1e7868
    (while st-info
cvsdist e1e7868
      (when (apply check-function (list (car st-info)))
cvsdist e1e7868
        (if set-mark
cvsdist e1e7868
            (when (not (svn-status-line-info->has-usermark (car st-info)))
cvsdist e1e7868
              (message "marking: %s" (svn-status-line-info->filename (car st-info))))
cvsdist e1e7868
          (when (svn-status-line-info->has-usermark (car st-info))
cvsdist e1e7868
            (message "unmarking: %s" (svn-status-line-info->filename (car st-info)))))
cvsdist e1e7868
        (setcar (svn-status-line-info->ui-status (car st-info)) set-mark))
cvsdist e1e7868
      (setq st-info (cdr st-info)))
cvsdist e1e7868
    (svn-status-update-buffer)))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-mark-unknown (arg)
cvsdist e1e7868
  "Mark all unknown files.
cvsdist e1e7868
These are the files marked with '?' in the *svn-status* buffer.
cvsdist e1e7868
If the function is called with a prefix arg, unmark all these files."
cvsdist e1e7868
  (interactive "P")
cvsdist e1e7868
  (svn-status-apply-usermark-checked '(lambda (info) (eq (svn-status-line-info->filemark info) ??)) (not arg)))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-mark-added (arg)
cvsdist e1e7868
  "Mark all added files.
cvsdist e1e7868
These are the files marked with 'A' in the *svn-status* buffer.
cvsdist e1e7868
If the function is called with a prefix arg, unmark all these files."
cvsdist e1e7868
  (interactive "P")
cvsdist e1e7868
  (svn-status-apply-usermark-checked '(lambda (info) (eq (svn-status-line-info->filemark info) ?A)) (not arg)))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-mark-modified (arg)
cvsdist e1e7868
  "Mark all modified files.
cvsdist e1e7868
These are the files marked with 'M' in the *svn-status* buffer.
cvsdist e1e7868
If the function is called with a prefix arg, unmark all these files."
cvsdist e1e7868
  (interactive "P")
cvsdist e1e7868
  (svn-status-apply-usermark-checked '(lambda (info) (eq (svn-status-line-info->filemark info) ?M)) (not arg)))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-unset-all-usermarks ()
cvsdist e1e7868
  (interactive)
cvsdist e1e7868
  (svn-status-apply-usermark-checked '(lambda (info) t) nil))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-toggle-hide-unknown ()
cvsdist e1e7868
  (interactive)
cvsdist e1e7868
  (setq svn-status-hide-unknown (not svn-status-hide-unknown))
cvsdist e1e7868
  (svn-status-update-buffer))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-toggle-hide-unmodified ()
cvsdist e1e7868
  (interactive)
cvsdist e1e7868
  (setq svn-status-hide-unmodified (not svn-status-hide-unmodified))
cvsdist e1e7868
  (svn-status-update-buffer))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-goto-file-name (name)
cvsdist e1e7868
  ;; (message "svn-status-goto-file-name: %s %d" name (point))
cvsdist e1e7868
  (let ((start-pos (point)))
cvsdist e1e7868
    (goto-char (point-min))
cvsdist e1e7868
    (while (< (point) (point-max))
cvsdist e1e7868
      (goto-char (next-overlay-change (point)))
cvsdist e1e7868
      (when (string= name (svn-status-line-info->filename
cvsdist e1e7868
                           (svn-status-get-line-information)))
cvsdist e1e7868
        (setq start-pos (+ (point) svn-status-default-column))))
cvsdist e1e7868
    (goto-char start-pos)))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-find-info-for-file-name (name)
357017e
  (let* ((st-info svn-status-info)
357017e
         (info))
357017e
    (while st-info
357017e
      (when (string= name (svn-status-line-info->filename (car st-info)))
357017e
        (setq info (car st-info))
357017e
        (setq st-info nil)) ; terminate loop
357017e
      (setq st-info (cdr st-info)))
357017e
    info))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-marked-files ()
cvsdist e1e7868
  "Return all files marked by `svn-status-set-user-mark',
cvsdist e1e7868
or (if no files were marked) the file under point."
cvsdist e1e7868
  (let* ((st-info svn-status-info)
cvsdist e1e7868
         (file-list))
cvsdist e1e7868
    (while st-info
cvsdist e1e7868
      (when (svn-status-line-info->has-usermark (car st-info))
cvsdist e1e7868
        (setq file-list (append file-list (list (car st-info)))))
cvsdist e1e7868
      (setq st-info (cdr st-info)))
cvsdist e1e7868
    (or file-list
cvsdist e1e7868
        (if (svn-status-get-line-information)
cvsdist e1e7868
            (list (svn-status-get-line-information))
cvsdist e1e7868
          nil))))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-marked-file-names ()
cvsdist e1e7868
  (mapcar 'svn-status-line-info->filename (svn-status-marked-files)))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-ui-information-hash-table ()
cvsdist e1e7868
  (let ((st-info svn-status-info)
cvsdist e1e7868
        (svn-status-ui-information (make-hash-table :test 'equal)))
cvsdist e1e7868
    (while st-info
cvsdist e1e7868
      (puthash (svn-status-line-info->filename (car st-info))
cvsdist e1e7868
               (svn-status-line-info->ui-status (car st-info))
cvsdist e1e7868
               svn-status-ui-information)
cvsdist e1e7868
      (setq st-info (cdr st-info)))
cvsdist e1e7868
    svn-status-ui-information))
cvsdist e1e7868
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-create-arg-file (file-name prefix file-info-list postfix)
cvsdist e1e7868
  (with-temp-file file-name
cvsdist e1e7868
    (insert prefix)
cvsdist e1e7868
    (let ((st-info file-info-list))
cvsdist e1e7868
      (while st-info
cvsdist e1e7868
        (insert (svn-status-line-info->filename (car st-info)))
cvsdist e1e7868
        (insert "\n")
cvsdist e1e7868
        (setq st-info (cdr st-info)))
cvsdist e1e7868
cvsdist e1e7868
    (insert postfix))))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-show-process-buffer-internal (&optional scroll-to-top)
cvsdist e1e7868
  (when (eq (current-buffer) "*svn-status*")
cvsdist e1e7868
    (delete-other-windows))
cvsdist e1e7868
  (pop-to-buffer "*svn-process*")
cvsdist e1e7868
  (when svn-status-wash-control-M-in-process-buffers
cvsdist e1e7868
    (svn-status-remove-control-M))
cvsdist e1e7868
  (when scroll-to-top
cvsdist e1e7868
    (goto-char (point-min)))
cvsdist e1e7868
  (other-window 1))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-show-svn-log (arg)
cvsdist e1e7868
  "Run `svn log' on selected files.
cvsdist e1e7868
When called with a prefix argument add the following command switches:
cvsdist e1e7868
 no prefix:              use whatever is in the string `svn-status-default-log-arguments'
cvsdist e1e7868
 prefix argument of -1:  use no arguments
cvsdist e1e7868
 prefix argument of 0:   use the -q switch (quiet)
cvsdist e1e7868
 other prefix arguments: use the -v switch (verbose)
cvsdist e1e7868
cvsdist e1e7868
See `svn-status-marked-files' for what counts as selected."
cvsdist e1e7868
  (interactive "P")
cvsdist e1e7868
  (let ((switch (cond ((eq arg 0) "-q")
cvsdist e1e7868
                      ((eq arg -1) "")
cvsdist e1e7868
                      (arg        "-v")
cvsdist e1e7868
                      (t          svn-status-default-log-arguments))))
cvsdist e1e7868
    ;;(message "show log info for: %S" (svn-status-marked-files))
cvsdist e1e7868
    (svn-status-create-arg-file svn-status-temp-arg-file "" (svn-status-marked-files) "")
cvsdist e1e7868
    (if (> (length switch) 0)
cvsdist e1e7868
        (svn-run-svn t t 'log "log" "--targets" svn-status-temp-arg-file switch)
cvsdist e1e7868
      (svn-run-svn t t 'log "log" "--targets" svn-status-temp-arg-file))
cvsdist e1e7868
    (save-excursion
cvsdist e1e7868
      (set-buffer "*svn-process*")
cvsdist e1e7868
      (svn-log-view-mode))))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-info ()
cvsdist e1e7868
  "Run `svn info' on all selected files.
cvsdist e1e7868
See `svn-status-marked-files' for what counts as selected."
cvsdist e1e7868
  (interactive)
cvsdist e1e7868
  (svn-status-create-arg-file svn-status-temp-arg-file "" (svn-status-marked-files) "")
cvsdist e1e7868
  (svn-run-svn t t 'info "info" "--targets" svn-status-temp-arg-file))
cvsdist e1e7868
cvsdist e1e7868
;; Todo: add possiblity to specify the revision
cvsdist e1e7868
(defun svn-status-blame ()
cvsdist e1e7868
  "Run `svn blame' on the current file."
cvsdist e1e7868
  (interactive)
cvsdist e1e7868
  ;;(svn-run-svn t t 'blame "blame" "-r" "BASE" (svn-status-line-info->filename (svn-status-get-line-information))))
cvsdist e1e7868
  (svn-run-svn t t 'blame "blame" (svn-status-line-info->filename (svn-status-get-line-information))))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-show-svn-diff (arg)
cvsdist e1e7868
  "Run `svn diff' on the current file.
cvsdist e1e7868
If there is a newer revision in the repository, the diff is done against HEAD, otherwise
cvsdist e1e7868
compare the working copy with BASE.
cvsdist e1e7868
If ARG then prompt for revision to diff against."
cvsdist e1e7868
  (interactive "P")
cvsdist e1e7868
  (svn-status-show-svn-diff-internal arg nil))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-show-svn-diff-for-marked-files (arg)
cvsdist e1e7868
  "Run `svn diff' on all selected files.
cvsdist e1e7868
See `svn-status-marked-files' for what counts as selected.
cvsdist e1e7868
If ARG then prompt for revision to diff against, else compare working copy with BASE."
cvsdist e1e7868
  (interactive "P")
cvsdist e1e7868
  (svn-status-show-svn-diff-internal arg t))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-show-svn-diff-internal (arg &optional use-all-marked-files)
cvsdist e1e7868
  (let* ((fl (if use-all-marked-files
cvsdist e1e7868
                 (svn-status-marked-files)
cvsdist e1e7868
               (list (svn-status-get-line-information))))
cvsdist e1e7868
         (clear-buf t)
cvsdist e1e7868
         (revision (if arg
cvsdist e1e7868
                       (svn-status-read-revision-string "Diff with files for version: " "PREV")
cvsdist e1e7868
                     (if use-all-marked-files
cvsdist e1e7868
                         "BASE"
cvsdist e1e7868
                       (if (svn-status-line-info->modified-external (car fl)) "HEAD" "BASE")))))
cvsdist e1e7868
    (while fl
cvsdist e1e7868
      (svn-run-svn nil clear-buf 'diff "diff" "-r" revision (svn-status-line-info->filename (car fl)))
cvsdist e1e7868
      (setq clear-buf nil)
cvsdist e1e7868
      (setq fl (cdr fl))))
cvsdist e1e7868
  (svn-status-show-process-buffer-internal t)
cvsdist e1e7868
  (save-excursion
cvsdist e1e7868
    (set-buffer "*svn-process*")
cvsdist e1e7868
    (diff-mode)
cvsdist e1e7868
    (font-lock-fontify-buffer)))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-show-process-buffer ()
cvsdist e1e7868
  (interactive)
cvsdist e1e7868
  (svn-status-show-process-buffer-internal))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-add-file-recursively (arg)
cvsdist e1e7868
  "Run `svn add' on all selected files.
cvsdist e1e7868
When a directory is added, add files recursively.
cvsdist e1e7868
See `svn-status-marked-files' for what counts as selected.
cvsdist e1e7868
When this function is called with a prefix argument, use the actual file instead."
cvsdist e1e7868
  (interactive "P")
cvsdist e1e7868
  (message "adding: %S" (svn-status-get-file-list-names (not arg)))
cvsdist e1e7868
  (svn-status-create-arg-file svn-status-temp-arg-file "" (svn-status-get-file-list (not arg)) "")
cvsdist e1e7868
  (svn-run-svn t t 'add "add" "--targets" svn-status-temp-arg-file))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-add-file (arg)
cvsdist e1e7868
  "Run `svn add' on all selected files.
cvsdist e1e7868
When a directory is added, don't add the files of the directory
cvsdist e1e7868
 (svn add --non-recursive <file-list> is called).
cvsdist e1e7868
See `svn-status-marked-files' for what counts as selected.
cvsdist e1e7868
When this function is called with a prefix argument, use the actual file instead."
cvsdist e1e7868
  (interactive "P")
cvsdist e1e7868
  (message "adding: %S" (svn-status-get-file-list-names (not arg)))
cvsdist e1e7868
  (svn-status-create-arg-file svn-status-temp-arg-file "" (svn-status-get-file-list (not arg)) "")
cvsdist e1e7868
  (svn-run-svn t t 'add "add" "--non-recursive" "--targets" svn-status-temp-arg-file))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-make-directory (dir)
cvsdist e1e7868
  "Run `svn mkdir DIR'."
cvsdist e1e7868
  ;; TODO: Allow entering a URI interactively.
cvsdist e1e7868
  ;; Currently, `read-file-name' corrupts it.
cvsdist e1e7868
  (interactive (list (read-file-name "Make directory: "
cvsdist e1e7868
                                     (svn-status-directory-containing-point t))))
cvsdist e1e7868
  (unless (string-match "^[^:/]+://" dir) ; Is it a URI?
cvsdist e1e7868
    (setq dir (file-relative-name dir)))
cvsdist e1e7868
  (svn-run-svn t t 'mkdir "mkdir" "--" dir))
cvsdist e1e7868
cvsdist e1e7868
;;TODO: write a svn-status-cp similar to this---maybe a common
cvsdist e1e7868
;;function to do both?
cvsdist e1e7868
(defun svn-status-mv ()
cvsdist e1e7868
  "Prompt for a destination, and `svn mv' selected files there.
cvsdist e1e7868
See `svn-status-marked-files' for what counts as `selected'.
cvsdist e1e7868
cvsdist e1e7868
If one file was selected then the destination DEST should be a
cvsdist e1e7868
filename to rename the selected file to, or a directory to move the
cvsdist e1e7868
file into; if multiple files were selected then DEST should be a
cvsdist e1e7868
directory to move the selected files into.
cvsdist e1e7868
cvsdist e1e7868
The default DEST is the directory containing point.
cvsdist e1e7868
cvsdist e1e7868
BUG: If we've marked some directory containging a file as well as the
cvsdist e1e7868
file itself, then we should just mv the directory, but this implementation
cvsdist e1e7868
doesn't check for that.
cvsdist e1e7868
SOLUTION: for each dir, umark all its contents (but not the dir
cvsdist e1e7868
itself) before running mv."
cvsdist e1e7868
  (interactive)
cvsdist e1e7868
  (let* ((marked-files (svn-status-marked-files))
cvsdist e1e7868
         (num-of-files (length marked-files))
cvsdist e1e7868
         original
cvsdist e1e7868
         dest)
cvsdist e1e7868
    (if (= 1 num-of-files)
cvsdist e1e7868
        ;; one file to rename, prompt for new name, or directory to move the
cvsdist e1e7868
        ;; file into.
cvsdist e1e7868
        (setq dest (read-file-name (format "Rename %s to: "
cvsdist e1e7868
                                           (svn-status-line-info->filename (car marked-files)))
cvsdist e1e7868
                                   (svn-status-directory-containing-point t)))
cvsdist e1e7868
      ;;multiple files selected, so prompt for existing directory to mv them into.
cvsdist e1e7868
      (setq dest (read-directory-name (format "Move %d files to directory: " num-of-files)
cvsdist e1e7868
                                      (svn-status-directory-containing-point t) nil t))
cvsdist e1e7868
      (unless (file-directory-p dest)
cvsdist e1e7868
        (error "%s is not a directory" dest)))
cvsdist e1e7868
    (when (string= dest "")
cvsdist e1e7868
      (error "No destination entered; no files moved"))
cvsdist e1e7868
    (unless (string-match "^[^:/]+://" dest) ; Is it a URI?
cvsdist e1e7868
      (setq dest (file-relative-name dest)))
cvsdist e1e7868
;
cvsdist e1e7868
    ;;do the move: svn mv only lets us move things once at a time, so
cvsdist e1e7868
    ;;we need to run svn mv once for each file (hence second arg to
cvsdist e1e7868
    ;;svn-run-svn is nil.)
cvsdist e1e7868
cvsdist e1e7868
    ;;TODO: before doing any moving, For every marked directory,
cvsdist e1e7868
    ;;ensure none of its contents are also marked, since we dont want
cvsdist e1e7868
    ;;to move both file *and* its parent...
cvsdist e1e7868
    ;; what about hidden files?? what if user marks a dir+contents, then presses `_' ??
cvsdist e1e7868
;;   ;one solution:
cvsdist e1e7868
;;      (dolist (original marked-files)
cvsdist e1e7868
;;          (when (svn-status-line-info->directory-p original)
cvsdist e1e7868
;;              ;; run  svn-status-goto-file-name to move point to line of file
cvsdist e1e7868
;;              ;; run  svn-status-unset-user-mark to unmark dir+all contents
cvsdist e1e7868
;;              ;; run  svn-status-set-user-mark   to remark dir
cvsdist e1e7868
;;              ;; maybe check for local mods here, and unmark if user does't say --force?
cvsdist e1e7868
;;              ))
cvsdist e1e7868
        (dolist (original marked-files)
cvsdist e1e7868
      (let ((original-name (svn-status-line-info->filename original))
cvsdist e1e7868
                        (original-filemarks (svn-status-line-info->filemark original))
cvsdist e1e7868
                        (original-propmarks (svn-status-line-info->propmark original)))
cvsdist e1e7868
        (cond
cvsdist e1e7868
         ((or (eq original-filemarks 77)  ;;original has local mods: maybe do `svn mv --force'
cvsdist e1e7868
              (eq original-propmarks 77)) ;;original has local prop mods: maybe do `svn mv --force'
cvsdist e1e7868
          (if (yes-or-no-p (format "%s has local modifications; use `--force' to really move it? "
cvsdist e1e7868
                                   original-name))
cvsdist e1e7868
              (svn-run-svn nil t 'mv "mv" "--force" "--" original-name dest)
cvsdist e1e7868
            (message "Not moving %s" original-name)))
cvsdist e1e7868
         ((eq original-filemarks 63) ;;original is unversioned: maybe do plain `mv'
cvsdist e1e7868
          (if (yes-or-no-p (format "%s is unversioned.  Use plain `mv -i %s %s'? "
cvsdist e1e7868
                                   original-name original-name dest))
cvsdist e1e7868
              (call-process "mv" nil (get-buffer-create "*svn-process*") nil "-i" original-name dest)
cvsdist e1e7868
            (message "Not moving %s" original-name)))
cvsdist e1e7868
cvsdist e1e7868
         ((eq original-filemarks 65) ;;original has `A' mark (eg it was `svn add'ed, but not committed)
cvsdist e1e7868
          (message "Not moving %s (try committing it first)" original-name))
cvsdist e1e7868
cvsdist e1e7868
         ((eq original-filemarks 32) ;;original is unmodified: can use `svn mv'
cvsdist e1e7868
          (svn-run-svn nil t 'mv "mv" "--" original-name dest))
cvsdist e1e7868
cvsdist e1e7868
         ;;file is conflicted in some way?
cvsdist e1e7868
         (t
cvsdist e1e7868
          (if (yes-or-no-p (format "The status of %s looks scary.  Risk moving it anyway? " original-name))
cvsdist e1e7868
              (svn-run-svn nil t 'mv "mv" "--" original-name dest)
cvsdist e1e7868
            (message "Not moving %s" original-name))))))
cvsdist e1e7868
        (svn-status-update)))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-revert ()
cvsdist e1e7868
  "Run `svn revert' on all selected files.
cvsdist e1e7868
See `svn-status-marked-files' for what counts as selected."
cvsdist e1e7868
  (interactive)
cvsdist e1e7868
  (let* ((marked-files (svn-status-marked-files))
cvsdist e1e7868
         (num-of-files (length marked-files)))
cvsdist e1e7868
    (when (yes-or-no-p
cvsdist e1e7868
           (if (= 1 num-of-files)
cvsdist e1e7868
               (format "Revert %s? " (svn-status-line-info->filename (car marked-files)))
cvsdist e1e7868
             (format "Revert %d files? " num-of-files)))
cvsdist e1e7868
      (message "reverting: %S" (svn-status-marked-file-names))
cvsdist e1e7868
      (svn-status-create-arg-file svn-status-temp-arg-file "" (svn-status-marked-files) "")
cvsdist e1e7868
      (svn-run-svn t t 'revert "revert" "--targets" svn-status-temp-arg-file))))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-rm (force)
cvsdist e1e7868
  "Run `svn rm' on all selected files.
cvsdist e1e7868
See `svn-status-marked-files' for what counts as selected.
cvsdist e1e7868
When called with a prefix argument add the command line switch --force."
cvsdist e1e7868
  (interactive "P")
cvsdist e1e7868
  (let* ((marked-files (svn-status-marked-files))
cvsdist e1e7868
         (num-of-files (length marked-files)))
cvsdist e1e7868
    (when (yes-or-no-p
cvsdist e1e7868
           (if (= 1 num-of-files)
cvsdist e1e7868
               (format "%sRemove %s? " (if force "Force " "") (svn-status-line-info->filename (car marked-files)))
cvsdist e1e7868
             (format "%sRemove %d files? " (if force "Force " "") num-of-files)))
cvsdist e1e7868
      (message "removing: %S" (svn-status-marked-file-names))
cvsdist e1e7868
      (svn-status-create-arg-file svn-status-temp-arg-file "" (svn-status-marked-files) "")
cvsdist e1e7868
      (if force
cvsdist e1e7868
          (svn-run-svn t t 'rm "rm" "--force" "--targets" svn-status-temp-arg-file)
cvsdist e1e7868
        (svn-run-svn t t 'rm "rm" "--targets" svn-status-temp-arg-file)))))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-update-cmd ()
cvsdist e1e7868
  (interactive)
cvsdist e1e7868
  ;TODO: use file names also
cvsdist e1e7868
  (svn-run-svn t t 'update "update"))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-commit-file ()
cvsdist e1e7868
  (interactive)
cvsdist e1e7868
  (let* ((marked-files (svn-status-marked-files)))
cvsdist e1e7868
    (setq svn-status-files-to-commit marked-files)
cvsdist e1e7868
    (svn-log-edit-show-files-to-commit)
357017e
    (svn-status-pop-to-commit-buffer)
357017e
    (when svn-log-edit-insert-files-to-commit
357017e
      (svn-log-edit-insert-files-to-commit))))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-pop-to-commit-buffer ()
cvsdist e1e7868
  (interactive)
cvsdist e1e7868
  (setq svn-status-pre-commit-window-configuration (current-window-configuration))
cvsdist e1e7868
  (let* ((use-existing-buffer (get-buffer "*svn-log-edit*"))
cvsdist e1e7868
         (commit-buffer (get-buffer-create "*svn-log-edit*"))
cvsdist e1e7868
         (dir default-directory))
cvsdist e1e7868
    (pop-to-buffer commit-buffer)
cvsdist e1e7868
    (setq default-directory dir)
cvsdist e1e7868
    (unless use-existing-buffer
cvsdist e1e7868
      (when (and svn-log-edit-file-name (file-readable-p svn-log-edit-file-name))
cvsdist e1e7868
        (insert-file svn-log-edit-file-name)))
cvsdist e1e7868
    (svn-log-edit-mode)))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-cleanup ()
cvsdist e1e7868
  (interactive)
cvsdist e1e7868
  (let ((file-names (svn-status-marked-file-names)))
cvsdist e1e7868
    (if file-names
cvsdist e1e7868
        (progn
cvsdist e1e7868
          ;(message "svn-status-cleanup %S" file-names))
cvsdist e1e7868
          (svn-run-svn t t 'cleanup (append (list "cleanup") file-names)))
cvsdist e1e7868
      (message "No valid file selected - No status cleanup possible"))))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-resolved ()
cvsdist e1e7868
  "Run `svn resolved' on all selected files.
cvsdist e1e7868
See `svn-status-marked-files' for what counts as selected."
cvsdist e1e7868
  (interactive)
cvsdist e1e7868
  (let* ((marked-files (svn-status-marked-files))
cvsdist e1e7868
         (num-of-files (length marked-files)))
cvsdist e1e7868
    (when (yes-or-no-p
cvsdist e1e7868
           (if (= 1 num-of-files)
cvsdist e1e7868
               (format "Resolve %s? " (svn-status-line-info->filename (car marked-files)))
cvsdist e1e7868
             (format "Resolve %d files? " num-of-files)))
cvsdist e1e7868
      (message "resolving: %S" (svn-status-marked-file-names))
cvsdist e1e7868
      (svn-status-create-arg-file svn-status-temp-arg-file "" (svn-status-marked-files) "")
cvsdist e1e7868
      (svn-run-svn t t 'resolved "resolved" "--targets" svn-status-temp-arg-file))))
cvsdist e1e7868
357017e
;; --------------------------------------------------------------------------------
357017e
;; Update the *svn-status* buffer, when a file is saved
357017e
;; --------------------------------------------------------------------------------
357017e
357017e
(defvar svn-status-file-modified-after-save-flag ?m
357017e
  "The flag, that is shown, in the *svn-status* buffer, after
357017e
a file is changed and saved in emacs.
357017e
Recommended values are ?m or ?M.")
357017e
(defun svn-status-after-save-hook ()
357017e
  "Set a modified indication, when a file is saved from a svn working copy."
357017e
  (let* ((svn-dir (car-safe svn-status-directory-history))
357017e
         (svn-dir (when svn-dir (expand-file-name svn-dir)))
357017e
         (file-dir (file-name-directory (buffer-file-name)))
357017e
         (svn-dir-len (length (or svn-dir "")))
357017e
         (file-dir-len (length file-dir))
357017e
         (file-name))
357017e
    (when (and svn-dir
357017e
               (>= file-dir-len svn-dir-len)
357017e
               (string= (substring file-dir 0 svn-dir-len) svn-dir))
357017e
      (setq file-name (substring (buffer-file-name) svn-dir-len))
357017e
      ;;(message (format "In svn-status directory %S" file-name))
357017e
      (let ((st-info svn-status-info)
357017e
            (i-fname))
357017e
        (while st-info
357017e
          (setq i-fname (svn-status-line-info->filename (car st-info)))
357017e
          ;;(message (format "i-fname=%S" i-fname))
357017e
          (when (and (string= file-name i-fname)
357017e
                     (not (eq (svn-status-line-info->filemark (car st-info)) ??)))
357017e
            (svn-status-line-info->set-filemark (car st-info)
357017e
                                                svn-status-file-modified-after-save-flag)
357017e
            (save-excursion
357017e
              (set-buffer "*svn-status*")
357017e
              (svn-status-goto-file-name i-fname)
357017e
              (let ((buffer-read-only nil))
357017e
                (delete-region (point-at-bol) (point-at-eol))
357017e
                (svn-insert-line-in-status-buffer (car st-info))
357017e
                (delete-char 1))))
357017e
          (setq st-info (cdr st-info))))))
357017e
  nil)
357017e
357017e
(add-hook 'after-save-hook 'svn-status-after-save-hook)
cvsdist e1e7868
cvsdist e1e7868
;; --------------------------------------------------------------------------------
cvsdist e1e7868
;; Getting older revisions
cvsdist e1e7868
;; --------------------------------------------------------------------------------
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-get-specific-revision (arg)
cvsdist e1e7868
  "Retrieve older revisions.
cvsdist e1e7868
The older revisions are stored in backup files named F.~REVISION~.
cvsdist e1e7868
cvsdist e1e7868
When the function is called without a prefix argument: get all marked files.
cvsdist e1e7868
Otherwise get only the actual file."
cvsdist e1e7868
  (interactive "P")
cvsdist e1e7868
  (svn-status-get-specific-revision-internal (not arg) t))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-get-specific-revision-internal (&optional only-actual-file arg)
cvsdist e1e7868
  (let* ((file-names (if only-actual-file
cvsdist e1e7868
                         (list (svn-status-line-info->filename (svn-status-get-line-information)))
cvsdist e1e7868
                       (svn-status-marked-file-names)))
cvsdist e1e7868
         (revision (if arg (svn-status-read-revision-string "Get files for version: " "PREV") "BASE"))
cvsdist e1e7868
         (file-name)
cvsdist e1e7868
         (file-name-with-revision))
cvsdist e1e7868
    (message "Getting revision %s for %S" revision file-names)
cvsdist e1e7868
    (setq svn-status-get-specific-revision-file-info nil)
cvsdist e1e7868
    (while file-names
cvsdist e1e7868
      (setq file-name (car file-names))
cvsdist e1e7868
      (setq file-name-with-revision (concat file-name ".~" revision "~"))
cvsdist e1e7868
      (add-to-list 'svn-status-get-specific-revision-file-info
cvsdist e1e7868
                   (cons file-name file-name-with-revision))
cvsdist e1e7868
      (save-excursion
cvsdist e1e7868
        (find-file file-name-with-revision)
cvsdist e1e7868
        (setq buffer-read-only nil)
cvsdist e1e7868
        (delete-region (point-min) (point-max))
cvsdist e1e7868
        (svn-run-svn nil t 'cat (append (list "cat" "-r" revision) (list file-name)))
cvsdist e1e7868
        ;;todo: error processing
cvsdist e1e7868
        ;;svn: Filesystem has no item
cvsdist e1e7868
        ;;svn: file not found: revision `15', path `/trunk/file.txt'
cvsdist e1e7868
        (insert-buffer-substring "*svn-process*")
cvsdist e1e7868
        (save-buffer))
cvsdist e1e7868
      (setq file-names (cdr file-names)))
cvsdist e1e7868
    (setq svn-status-get-specific-revision-file-info
cvsdist e1e7868
      (nreverse svn-status-get-specific-revision-file-info))
cvsdist e1e7868
    (message "svn-status-get-specific-revision-file-info: %S"
cvsdist e1e7868
             svn-status-get-specific-revision-file-info)))
cvsdist e1e7868
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-ediff-with-revision (arg)
cvsdist e1e7868
  "Run ediff on the current file with a previous revision.
cvsdist e1e7868
If ARG then prompt for revision to diff against."
cvsdist e1e7868
  (interactive "P")
cvsdist e1e7868
  (svn-status-get-specific-revision-internal t arg)
cvsdist e1e7868
  (let* ((ediff-after-quit-destination-buffer (current-buffer))
cvsdist e1e7868
         (my-buffer (find-file-noselect (caar svn-status-get-specific-revision-file-info)))
cvsdist e1e7868
         (base-buff (find-file-noselect (cdar svn-status-get-specific-revision-file-info)))
cvsdist e1e7868
         (svn-transient-buffers (list base-buff ))
cvsdist e1e7868
         (startup-hook '(svn-ediff-startup-hook)))
cvsdist e1e7868
    (ediff-buffers my-buffer base-buff  startup-hook)))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-ediff-startup-hook ()
cvsdist e1e7868
  (add-hook 'ediff-after-quit-hook-internal
cvsdist e1e7868
        `(lambda ()
cvsdist e1e7868
           (svn-ediff-exit-hook
cvsdist e1e7868
        ',ediff-after-quit-destination-buffer ',svn-transient-buffers))
cvsdist e1e7868
        nil 'local))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-ediff-exit-hook (svn-buf tmp-bufs)
cvsdist e1e7868
  ;; kill the temp buffers (and their associated windows)
cvsdist e1e7868
  (dolist (tb tmp-bufs)
cvsdist e1e7868
    (when (and tb (buffer-live-p tb) (not (buffer-modified-p tb)))
cvsdist e1e7868
      (let ((win (get-buffer-window tb t)))
cvsdist e1e7868
    (when win (delete-window win))
cvsdist e1e7868
    (kill-buffer tb))))
cvsdist e1e7868
  ;; switch back to the *svn* buffer
cvsdist e1e7868
  (when (and svn-buf (buffer-live-p svn-buf)
cvsdist e1e7868
         (not (get-buffer-window svn-buf t)))
cvsdist e1e7868
    (ignore-errors (switch-to-buffer svn-buf))))
cvsdist e1e7868
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-read-revision-string (prompt &optional default-value)
cvsdist e1e7868
  "Prompt the user for a svn revision number."
cvsdist e1e7868
  (interactive)
cvsdist e1e7868
  (read-string prompt default-value))
cvsdist e1e7868
cvsdist e1e7868
;; --------------------------------------------------------------------------------
cvsdist e1e7868
;; SVN process handling
cvsdist e1e7868
;; --------------------------------------------------------------------------------
cvsdist e1e7868
cvsdist e1e7868
(defun svn-process-kill ()
cvsdist e1e7868
  "Kill the current running svn process."
cvsdist e1e7868
  (interactive)
cvsdist e1e7868
  (let ((process (get-process "svn")))
cvsdist e1e7868
    (if process
cvsdist e1e7868
        (delete-process process)
cvsdist e1e7868
      (message "No running svn process"))))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-process-send-string (string)
cvsdist e1e7868
  "Send a string to the running svn process.
cvsdist e1e7868
This is useful, if the running svn process asks the user a question.
cvsdist e1e7868
Note: use C-q C-j to send a line termination character."
cvsdist e1e7868
  (interactive "sSend string to svn process: ")
cvsdist e1e7868
  (save-excursion
cvsdist e1e7868
    (set-buffer "*svn-process*")
cvsdist e1e7868
    (let ((buffer-read-only nil))
cvsdist e1e7868
      (insert string))
cvsdist e1e7868
    (set-marker (process-mark (get-process "svn")) (point)))
cvsdist e1e7868
  (process-send-string "svn" string))
cvsdist e1e7868
cvsdist e1e7868
;; --------------------------------------------------------------------------------
cvsdist e1e7868
;; Property List stuff
cvsdist e1e7868
;; --------------------------------------------------------------------------------
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-property-list ()
cvsdist e1e7868
  (interactive)
cvsdist e1e7868
  (let ((file-names (svn-status-marked-file-names)))
cvsdist e1e7868
    (if file-names
cvsdist e1e7868
        (progn
cvsdist e1e7868
          (svn-run-svn t t 'proplist (append (list "proplist" "-v") file-names)))
cvsdist e1e7868
      (message "No valid file selected - No property listing possible"))))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-proplist-start ()
cvsdist e1e7868
  (svn-run-svn t t 'proplist-parse "proplist" (svn-status-line-info->filename
cvsdist e1e7868
                                               (svn-status-get-line-information))))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-property-parse ()
cvsdist e1e7868
  (interactive)
cvsdist e1e7868
  (svn-status-proplist-start))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-property-edit-one-entry (arg)
cvsdist e1e7868
  "Edit a property.
cvsdist e1e7868
When called with a prefix argument, it is possible to enter a new property."
cvsdist e1e7868
  (interactive "P")
cvsdist e1e7868
  (setq svn-status-property-edit-must-match-flag (not arg))
cvsdist e1e7868
  (svn-status-proplist-start))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-property-set ()
cvsdist e1e7868
  (interactive)
cvsdist e1e7868
  (setq svn-status-property-edit-must-match-flag nil)
cvsdist e1e7868
  (svn-status-proplist-start))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-property-delete ()
cvsdist e1e7868
  (interactive)
cvsdist e1e7868
  (setq svn-status-property-edit-must-match-flag t)
cvsdist e1e7868
  (svn-status-proplist-start))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-property-parse-property-names ()
cvsdist e1e7868
  ;(svn-status-show-process-buffer-internal t)
cvsdist e1e7868
  (message "svn-status-property-parse-property-names")
cvsdist e1e7868
  (let ((pl)
cvsdist e1e7868
        (pfl)
cvsdist e1e7868
        (prop-name)
cvsdist e1e7868
        (prop-value))
cvsdist e1e7868
    (save-excursion
cvsdist e1e7868
      (set-buffer "*svn-process*")
cvsdist e1e7868
      (goto-char (point-min))
cvsdist e1e7868
      (forward-line 1)
cvsdist e1e7868
      (while (looking-at "  \\(.+\\)")
cvsdist e1e7868
        (setq pl (append pl (list (match-string 1))))
cvsdist e1e7868
        (forward-line 1)))
cvsdist e1e7868
    ;(cond last-command: svn-status-property-set, svn-status-property-edit-one-entry
cvsdist e1e7868
    ;svn-status-property-parse:
cvsdist e1e7868
    (cond ((eq last-command 'svn-status-property-parse)
cvsdist e1e7868
           ;(message "%S %S" pl last-command)
cvsdist e1e7868
           (while pl
cvsdist e1e7868
             (svn-run-svn nil t 'propget-parse "propget" (car pl)
cvsdist e1e7868
                          (svn-status-line-info->filename
cvsdist e1e7868
                           (svn-status-get-line-information)))
cvsdist e1e7868
             (save-excursion
cvsdist e1e7868
               (set-buffer "*svn-process*")
cvsdist e1e7868
               (setq pfl (append pfl (list
cvsdist e1e7868
                                      (list
cvsdist e1e7868
                                       (car pl)
cvsdist e1e7868
                                       (buffer-substring
cvsdist e1e7868
                                        (point-min) (- (point-max) 1)))))))
cvsdist e1e7868
             (setq pl (cdr pl))
cvsdist e1e7868
             (message "%S" pfl)))
cvsdist e1e7868
          ((eq last-command 'svn-status-property-edit-one-entry)
cvsdist e1e7868
           ;;(message "svn-status-property-edit-one-entry")
cvsdist e1e7868
           (setq prop-name
cvsdist e1e7868
                 (completing-read "Set Property - Name: " (mapcar 'list pl)
cvsdist e1e7868
                                  nil svn-status-property-edit-must-match-flag))
cvsdist e1e7868
           (unless (string= prop-name "")
cvsdist e1e7868
             (save-excursion
cvsdist e1e7868
               (set-buffer "*svn-status*")
cvsdist e1e7868
               (svn-status-property-edit (list (svn-status-get-line-information))
cvsdist e1e7868
                                         prop-name))))
cvsdist e1e7868
          ((eq last-command 'svn-status-property-set)
cvsdist e1e7868
           (message "svn-status-property-set")
cvsdist e1e7868
           (setq prop-name
cvsdist e1e7868
                 (completing-read "Set Property - Name: " (mapcar 'list pl) nil nil))
cvsdist e1e7868
           (setq prop-value (read-from-minibuffer "Property value: "))
cvsdist e1e7868
           (unless (string= prop-name "")
cvsdist e1e7868
             (save-excursion
cvsdist e1e7868
               (set-buffer "*svn-status*")
cvsdist e1e7868
               (message "setting property %s := %s for %S" prop-name prop-value
cvsdist e1e7868
                        (svn-status-marked-files)))))
cvsdist e1e7868
          ((eq last-command 'svn-status-property-delete)
cvsdist e1e7868
           (setq prop-name
cvsdist e1e7868
                 (completing-read "Delete Property - Name: " (mapcar 'list pl) nil t))
cvsdist e1e7868
           (unless (string= prop-name "")
cvsdist e1e7868
             (save-excursion
cvsdist e1e7868
               (set-buffer "*svn-status*")
cvsdist e1e7868
               (let ((file-names (svn-status-marked-file-names)))
cvsdist e1e7868
                 (when file-names
cvsdist e1e7868
                   (message "Going to delete prop %s for %s" prop-name file-names)
cvsdist e1e7868
                   (svn-run-svn t t 'propdel
cvsdist e1e7868
                                (append (list "propdel" prop-name) file-names))))))))))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-property-edit (file-info-list prop-name &optional new-prop-value)
cvsdist e1e7868
  (let* ((commit-buffer (get-buffer-create "*svn-property-edit*"))
cvsdist e1e7868
         (dir default-directory)
cvsdist e1e7868
         ;; now only one file is implemented ...
cvsdist e1e7868
         (file-name (svn-status-line-info->filename (car file-info-list)))
cvsdist e1e7868
         (prop-value))
cvsdist e1e7868
    (message "Edit property %s for file %s" prop-name file-name)
cvsdist e1e7868
    (svn-run-svn nil t 'propget-parse "propget" prop-name file-name)
cvsdist e1e7868
    (save-excursion
cvsdist e1e7868
      (set-buffer "*svn-process*")
cvsdist e1e7868
      (setq prop-value (if (> (point-max) 1)
cvsdist e1e7868
                           (buffer-substring (point-min) (- (point-max) 1))
cvsdist e1e7868
                         "")))
cvsdist e1e7868
    (setq svn-status-propedit-property-name prop-name)
cvsdist e1e7868
    (setq svn-status-propedit-file-list file-info-list)
cvsdist e1e7868
    (setq svn-status-pre-propedit-window-configuration (current-window-configuration))
cvsdist e1e7868
    (pop-to-buffer commit-buffer)
cvsdist e1e7868
    (delete-region (point-min) (point-max))
cvsdist e1e7868
    (setq default-directory dir)
cvsdist e1e7868
    (insert prop-value)
cvsdist e1e7868
    (svn-status-remove-control-M)
cvsdist e1e7868
    (when new-prop-value
cvsdist e1e7868
      (when (listp new-prop-value)
cvsdist e1e7868
        (message "Adding new prop values %S " new-prop-value)
cvsdist e1e7868
        (while new-prop-value
cvsdist e1e7868
          (goto-char (point-min))
cvsdist e1e7868
          (unless (re-search-forward
cvsdist e1e7868
                   (concat "^" (regexp-quote (car new-prop-value)) "$") nil t)
cvsdist e1e7868
            (goto-char (point-max))
cvsdist e1e7868
            (when (> (current-column) 0) (insert "\n"))
cvsdist e1e7868
            (insert (car new-prop-value)))
cvsdist e1e7868
          (setq new-prop-value (cdr new-prop-value)))))
cvsdist e1e7868
    (svn-prop-edit-mode)))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-property-set-property (file-info-list prop-name prop-value)
cvsdist e1e7868
  "Set a property on a given file list."
cvsdist e1e7868
  (save-excursion
cvsdist e1e7868
    (set-buffer (get-buffer "*svn-property-edit*"))
cvsdist e1e7868
    (delete-region (point-min) (point-max))
cvsdist e1e7868
    (insert prop-value))
cvsdist e1e7868
  (setq svn-status-propedit-file-list (svn-status-marked-files))
cvsdist e1e7868
  (setq svn-status-propedit-property-name prop-name)
cvsdist e1e7868
  (svn-prop-edit-do-it nil)
cvsdist e1e7868
  (svn-status-update))
cvsdist e1e7868
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-get-directory (line-info)
cvsdist e1e7868
  (let* ((file-name (svn-status-line-info->filename line-info))
cvsdist e1e7868
         (file-dir (file-name-directory file-name)))
cvsdist e1e7868
    ;;(message "file-dir: %S" file-dir)
cvsdist e1e7868
    (if file-dir
cvsdist e1e7868
        (substring file-dir 0 (- (length file-dir) 1))
cvsdist e1e7868
      ".")))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-get-file-list-per-directory (files)
cvsdist e1e7868
  ;;(message "%S" files)
cvsdist e1e7868
  (let ((dir-list nil)
cvsdist e1e7868
        (i files)
cvsdist e1e7868
        (j)
cvsdist e1e7868
        (dir))
cvsdist e1e7868
    (while i
cvsdist e1e7868
      (setq dir (svn-status-get-directory (car i)))
cvsdist e1e7868
      (setq j (assoc dir dir-list))
cvsdist e1e7868
      (if j
cvsdist e1e7868
          (progn
cvsdist e1e7868
            ;;(message "dir already present %S %s" j dir)
cvsdist e1e7868
            (setcdr j (append (cdr j) (list (car i)))))
cvsdist e1e7868
        (setq dir-list (append dir-list (list (list dir (car i))))))
cvsdist e1e7868
      (setq i (cdr i)))
cvsdist e1e7868
    ;;(message "svn-status-get-file-list-per-directory: %S" dir-list)
cvsdist e1e7868
    dir-list))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-property-ignore-file ()
cvsdist e1e7868
  (interactive)
cvsdist e1e7868
  (let ((d-list (svn-status-get-file-list-per-directory (svn-status-marked-files)))
cvsdist e1e7868
        (dir)
cvsdist e1e7868
        (f-info)
cvsdist e1e7868
        (ext-list))
cvsdist e1e7868
    (while d-list
cvsdist e1e7868
      (setq dir (caar d-list))
cvsdist e1e7868
      (setq f-info (cdar d-list))
cvsdist e1e7868
      (setq ext-list (mapcar '(lambda (i)
cvsdist e1e7868
                                (svn-status-line-info->filename-nondirectory i)) f-info))
cvsdist e1e7868
      ;;(message "ignore in dir %s: %S" dir f-info)
cvsdist e1e7868
      (save-window-excursion
cvsdist e1e7868
        (when (y-or-n-p (format "Ignore %S for %s? " ext-list dir))
cvsdist e1e7868
          (svn-status-property-edit
cvsdist e1e7868
           (list (svn-status-find-info-for-file-name dir)) "svn:ignore" ext-list)
cvsdist e1e7868
          (svn-prop-edit-do-it nil)))   ; synchronous
cvsdist e1e7868
      (setq d-list (cdr d-list)))
cvsdist e1e7868
    (svn-status-update)))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-property-ignore-file-extension ()
cvsdist e1e7868
  (interactive)
cvsdist e1e7868
  (let ((d-list (svn-status-get-file-list-per-directory (svn-status-marked-files)))
cvsdist e1e7868
        (dir)
cvsdist e1e7868
        (f-info)
cvsdist e1e7868
        (ext-list))
cvsdist e1e7868
    (while d-list
cvsdist e1e7868
      (setq dir (caar d-list))
cvsdist e1e7868
      (setq f-info (cdar d-list))
cvsdist e1e7868
      ;;(message "ignore in dir %s: %S" dir f-info)
cvsdist e1e7868
      (setq ext-list nil)
cvsdist e1e7868
      (while f-info
cvsdist e1e7868
        (add-to-list 'ext-list (concat "*."
cvsdist e1e7868
                                       (file-name-extension
cvsdist e1e7868
                                        (svn-status-line-info->filename (car f-info)))))
cvsdist e1e7868
        (setq f-info (cdr f-info)))
cvsdist e1e7868
      ;;(message "%S" ext-list)
cvsdist e1e7868
      (save-window-excursion
cvsdist e1e7868
        (when (y-or-n-p (format "Ignore %S for %s? " ext-list dir))
cvsdist e1e7868
          (svn-status-property-edit
cvsdist e1e7868
           (list (svn-status-find-info-for-file-name dir)) "svn:ignore"
cvsdist e1e7868
           ext-list)
cvsdist e1e7868
          (svn-prop-edit-do-it nil)))
cvsdist e1e7868
      (setq d-list (cdr d-list)))
cvsdist e1e7868
    (svn-status-update)))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-property-edit-svn-ignore ()
cvsdist e1e7868
  (interactive)
cvsdist e1e7868
  (let* ((line-info (svn-status-get-line-information))
cvsdist e1e7868
         (dir (if (svn-status-line-info->directory-p line-info)
cvsdist e1e7868
                  (svn-status-line-info->filename line-info)
cvsdist e1e7868
                (svn-status-get-directory line-info))))
cvsdist e1e7868
    (svn-status-property-edit
cvsdist e1e7868
     (list (svn-status-find-info-for-file-name dir)) "svn:ignore")
cvsdist e1e7868
    (message "Edit svn:ignore on %s" dir)))
cvsdist e1e7868
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-property-set-keyword-list ()
cvsdist e1e7868
  "Edit the svn:keywords property on the marked files."
cvsdist e1e7868
  (interactive)
cvsdist e1e7868
  ;;(message "Set svn:keywords for %S" (svn-status-marked-file-names))
cvsdist e1e7868
  (svn-status-property-edit (svn-status-marked-files) "svn:keywords"))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-status-property-set-eol-style ()
cvsdist e1e7868
  "Edit the svn:eol-style property on the marked files."
cvsdist e1e7868
  (interactive)
cvsdist e1e7868
  (svn-status-property-set-property
cvsdist e1e7868
   (svn-status-marked-files) "svn:eol-style"
cvsdist e1e7868
   (completing-read "Set svn:eol-style for the marked files: "
cvsdist e1e7868
                    (mapcar 'list '("native" "CRLF" "LF" "CR"))
cvsdist e1e7868
                    nil t)))
cvsdist e1e7868
cvsdist e1e7868
;; --------------------------------------------------------------------------------
cvsdist e1e7868
;; svn-prop-edit-mode:
cvsdist e1e7868
;; --------------------------------------------------------------------------------
cvsdist e1e7868
cvsdist e1e7868
(defvar svn-prop-edit-mode-map () "Keymap used in `svn-prop-edit-mode' buffers.")
cvsdist e1e7868
cvsdist e1e7868
(when (not svn-prop-edit-mode-map)
cvsdist e1e7868
  (setq svn-prop-edit-mode-map (make-sparse-keymap))
cvsdist e1e7868
  (define-key svn-prop-edit-mode-map [(control ?c) (control ?c)] 'svn-prop-edit-done)
cvsdist e1e7868
  (define-key svn-prop-edit-mode-map [(control ?c) (control ?d)] 'svn-prop-edit-svn-diff)
cvsdist e1e7868
  (define-key svn-prop-edit-mode-map [(control ?c) (control ?s)] 'svn-prop-edit-svn-status)
cvsdist e1e7868
  (define-key svn-prop-edit-mode-map [(control ?c) (control ?l)] 'svn-prop-edit-svn-log)
cvsdist e1e7868
  (define-key svn-prop-edit-mode-map [(control ?c) (control ?q)] 'svn-prop-edit-abort))
cvsdist e1e7868
cvsdist e1e7868
(easy-menu-define svn-prop-edit-mode-menu svn-prop-edit-mode-map
cvsdist e1e7868
"'svn-prop-edit-mode' menu"
cvsdist e1e7868
                  '("SVN-PropEdit"
cvsdist e1e7868
                    ["Commit" svn-prop-edit-done t]
cvsdist e1e7868
                    ["Show Diff" svn-prop-edit-svn-diff t]
cvsdist e1e7868
                    ["Show Status" svn-prop-edit-svn-status t]
cvsdist e1e7868
                    ["Show Log" svn-prop-edit-svn-log t]
cvsdist e1e7868
                    ["Abort" svn-prop-edit-abort t]))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-prop-edit-mode ()
cvsdist e1e7868
  "Major Mode to edit file properties of files under svn control.
cvsdist e1e7868
Commands:
cvsdist e1e7868
\\{svn-prop-edit-mode-map}"
cvsdist e1e7868
  (interactive)
cvsdist e1e7868
  (kill-all-local-variables)
cvsdist e1e7868
  (use-local-map svn-prop-edit-mode-map)
cvsdist e1e7868
  (easy-menu-add svn-prop-edit-mode-menu)
cvsdist e1e7868
  (setq major-mode 'svn-prop-edit-mode)
cvsdist e1e7868
  (setq mode-name "svn-prop-edit"))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-prop-edit-abort ()
cvsdist e1e7868
  (interactive)
cvsdist e1e7868
  (bury-buffer)
cvsdist e1e7868
  (set-window-configuration svn-status-pre-propedit-window-configuration))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-prop-edit-done ()
cvsdist e1e7868
  (interactive)
cvsdist e1e7868
  (svn-prop-edit-do-it t))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-prop-edit-do-it (async)
cvsdist e1e7868
  (message "svn propset %s on %s"
cvsdist e1e7868
           svn-status-propedit-property-name
cvsdist e1e7868
           (mapcar 'svn-status-line-info->filename svn-status-propedit-file-list))
cvsdist e1e7868
  (save-excursion
cvsdist e1e7868
    (set-buffer (get-buffer "*svn-property-edit*"))
cvsdist e1e7868
    (set-buffer-file-coding-system 'undecided-unix nil)
cvsdist e1e7868
    (setq svn-status-temp-file-to-remove
cvsdist e1e7868
          (concat svn-status-temp-dir "svn-prop-edit.txt" svn-temp-suffix))
cvsdist e1e7868
    (write-region (point-min) (point-max) svn-status-temp-file-to-remove nil 1))
cvsdist e1e7868
  (when svn-status-propedit-file-list ; there are files to change properties
cvsdist e1e7868
    (svn-status-create-arg-file svn-status-temp-arg-file ""
cvsdist e1e7868
                                svn-status-propedit-file-list "")
cvsdist e1e7868
    (setq svn-status-propedit-file-list nil)
cvsdist e1e7868
    (svn-run-svn async t 'propset "propset"
cvsdist e1e7868
         svn-status-propedit-property-name
cvsdist e1e7868
                 "--targets" svn-status-temp-arg-file
cvsdist e1e7868
                 "-F" (concat svn-status-temp-dir "svn-prop-edit.txt" svn-temp-suffix))
cvsdist e1e7868
    (unless async (svn-status-remove-temp-file-maybe)))
cvsdist e1e7868
  (set-window-configuration svn-status-pre-propedit-window-configuration))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-prop-edit-svn-diff (arg)
cvsdist e1e7868
  (interactive "P")
cvsdist e1e7868
  (set-buffer "*svn-status*")
cvsdist e1e7868
  (svn-status-show-svn-diff-for-marked-files arg))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-prop-edit-svn-log (arg)
cvsdist e1e7868
  (interactive "P")
cvsdist e1e7868
  (set-buffer "*svn-status*")
cvsdist e1e7868
  (svn-status-show-svn-log arg))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-prop-edit-svn-status ()
cvsdist e1e7868
  (interactive)
cvsdist e1e7868
  (pop-to-buffer "*svn-status*")
cvsdist e1e7868
  (other-window 1))
cvsdist e1e7868
cvsdist e1e7868
;; --------------------------------------------------------------------------------
cvsdist e1e7868
;; svn-log-edit-mode:
cvsdist e1e7868
;; --------------------------------------------------------------------------------
cvsdist e1e7868
cvsdist e1e7868
(defvar svn-log-edit-mode-map () "Keymap used in `svn-log-edit-mode' buffers.")
cvsdist e1e7868
cvsdist e1e7868
(when (not svn-log-edit-mode-map)
cvsdist e1e7868
  (setq svn-log-edit-mode-map (make-sparse-keymap))
cvsdist e1e7868
  (define-key svn-log-edit-mode-map (kbd "C-c C-c") 'svn-log-edit-done)
cvsdist e1e7868
  (define-key svn-log-edit-mode-map (kbd "C-c C-d") 'svn-log-edit-svn-diff)
cvsdist e1e7868
  (define-key svn-log-edit-mode-map (kbd "C-c C-s") 'svn-log-edit-save-message)
cvsdist e1e7868
  (define-key svn-log-edit-mode-map (kbd "C-c C-i") 'svn-log-edit-svn-status)
cvsdist e1e7868
  (define-key svn-log-edit-mode-map (kbd "C-c C-l") 'svn-log-edit-svn-log)
cvsdist e1e7868
  (define-key svn-log-edit-mode-map (kbd "C-c C-?") 'svn-log-edit-show-files-to-commit)
cvsdist e1e7868
  (define-key svn-log-edit-mode-map (kbd "C-c C-z") 'svn-log-edit-erase-edit-buffer)
cvsdist e1e7868
  (define-key svn-log-edit-mode-map (kbd "C-c C-q") 'svn-log-edit-abort))
cvsdist e1e7868
cvsdist e1e7868
(easy-menu-define svn-log-edit-mode-menu svn-log-edit-mode-map
cvsdist e1e7868
"'svn-log-edit-mode' menu"
cvsdist e1e7868
                  '("SVN-Log"
cvsdist e1e7868
                    ["Save to disk" svn-log-edit-save-message t]
cvsdist e1e7868
                    ["Commit" svn-log-edit-done t]
cvsdist e1e7868
                    ["Show Diff" svn-log-edit-svn-diff t]
cvsdist e1e7868
                    ["Show Status" svn-log-edit-svn-status t]
cvsdist e1e7868
                    ["Show Log" svn-log-edit-svn-log t]
cvsdist e1e7868
                    ["Show files to commit" svn-log-edit-show-files-to-commit t]
cvsdist e1e7868
                    ["Erase buffer" svn-log-edit-erase-edit-buffer]
cvsdist e1e7868
                    ["Abort" svn-log-edit-abort t]))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-log-edit-mode ()
cvsdist e1e7868
  "Major Mode to edit svn log messages.
cvsdist e1e7868
Commands:
cvsdist e1e7868
\\{svn-log-edit-mode-map}"
cvsdist e1e7868
  (interactive)
cvsdist e1e7868
  (kill-all-local-variables)
cvsdist e1e7868
  (use-local-map svn-log-edit-mode-map)
cvsdist e1e7868
  (easy-menu-add svn-log-edit-mode-menu)
cvsdist e1e7868
  (setq major-mode 'svn-log-edit-mode)
cvsdist e1e7868
  (setq mode-name "svn-log-edit")
cvsdist e1e7868
  (run-hooks 'svn-log-edit-mode-hook))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-log-edit-abort ()
cvsdist e1e7868
  (interactive)
cvsdist e1e7868
  (bury-buffer)
cvsdist e1e7868
  (set-window-configuration svn-status-pre-commit-window-configuration))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-log-edit-done ()
cvsdist e1e7868
  (interactive)
cvsdist e1e7868
  (message "svn-log editing done")
cvsdist e1e7868
  (save-excursion
cvsdist e1e7868
    (set-buffer (get-buffer "*svn-log-edit*"))
357017e
    (when svn-log-edit-insert-files-to-commit
357017e
      (svn-log-edit-remove-comment-lines))
cvsdist e1e7868
    (set-buffer-file-coding-system 'undecided-unix nil)
cvsdist e1e7868
    (write-region (point-min) (point-max)
cvsdist e1e7868
                  (concat svn-status-temp-dir "svn-log-edit.txt" svn-temp-suffix) nil 1))
cvsdist e1e7868
  (when svn-status-files-to-commit ; there are files to commit
cvsdist e1e7868
    (svn-status-create-arg-file svn-status-temp-arg-file ""
cvsdist e1e7868
                                svn-status-files-to-commit "")
cvsdist e1e7868
    (setq svn-status-files-to-commit nil)
cvsdist e1e7868
    (setq svn-status-temp-file-to-remove (concat svn-status-temp-dir "svn-log-edit.txt" svn-temp-suffix))
cvsdist e1e7868
    (svn-run-svn t t 'commit "commit" "--targets" svn-status-temp-arg-file
cvsdist e1e7868
                 "-F" svn-status-temp-file-to-remove))
cvsdist e1e7868
  (set-window-configuration svn-status-pre-commit-window-configuration))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-log-edit-svn-diff (arg)
cvsdist e1e7868
  "Show the diff we are about to commit.
cvsdist e1e7868
If ARG then show diff between some other version of the selected files."
cvsdist e1e7868
  (interactive "P")
cvsdist e1e7868
  (set-buffer "*svn-status*")
cvsdist e1e7868
  (svn-status-show-svn-diff-for-marked-files arg))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-log-edit-svn-log (arg)
cvsdist e1e7868
  (interactive "P")
cvsdist e1e7868
  (set-buffer "*svn-status*")
cvsdist e1e7868
  (svn-status-show-svn-log arg))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-log-edit-svn-status ()
cvsdist e1e7868
  (interactive)
cvsdist e1e7868
  (pop-to-buffer "*svn-status*")
cvsdist e1e7868
  (other-window 1))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-log-edit-show-files-to-commit ()
cvsdist e1e7868
  (interactive)
cvsdist e1e7868
  (message "Files to commit: %S"
cvsdist e1e7868
           (mapcar 'svn-status-line-info->filename svn-status-files-to-commit)))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-log-edit-save-message ()
cvsdist e1e7868
  "Save the current log message to the file `svn-log-edit-file-name'."
cvsdist e1e7868
  (interactive)
cvsdist e1e7868
  (write-region (point-min) (point-max) svn-log-edit-file-name))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-log-edit-erase-edit-buffer ()
cvsdist e1e7868
  "Delete everything in the *svn-log-edit* buffer."
cvsdist e1e7868
  (interactive)
cvsdist e1e7868
  (set-buffer "*svn-log-edit*")
cvsdist e1e7868
  (erase-buffer))
cvsdist e1e7868
357017e
(defun svn-log-edit-insert-files-to-commit ()
357017e
  (interactive)
357017e
  (svn-log-edit-remove-comment-lines)
357017e
  (let ((buf-size (- (point-max) (point-min))))
357017e
    (save-excursion
357017e
      (goto-char (point-min))
357017e
      (insert "## Lines starting with '## ' will be removed from the log message.\n")
357017e
      (insert "## File(s) to commit:\n")
357017e
      (let ((file-list svn-status-files-to-commit))
357017e
        (while file-list
357017e
          (insert (concat "## " (svn-status-line-info->filename (car file-list)) "\n"))
357017e
          (setq file-list (cdr file-list)))))
357017e
    (when (= 0 buf-size)
357017e
      (goto-char (point-max)))))
357017e
357017e
(defun svn-log-edit-remove-comment-lines ()
357017e
  (interactive)
357017e
  (save-excursion
357017e
    (goto-char (point-min))
357017e
    (flush-lines "^## .*")))
357017e
cvsdist e1e7868
cvsdist e1e7868
;; --------------------------------------------------------------------------------
cvsdist e1e7868
;; svn-log-view-mode:
cvsdist e1e7868
;; --------------------------------------------------------------------------------
cvsdist e1e7868
cvsdist e1e7868
(defvar svn-log-view-mode-map () "Keymap used in `svn-log-view-mode' buffers.")
cvsdist e1e7868
cvsdist e1e7868
(when (not svn-log-view-mode-map)
cvsdist e1e7868
  (setq svn-log-view-mode-map (make-sparse-keymap))
cvsdist e1e7868
  (define-key svn-log-view-mode-map (kbd "p") 'svn-log-view-prev)
cvsdist e1e7868
  (define-key svn-log-view-mode-map (kbd "n") 'svn-log-view-next)
cvsdist e1e7868
  (define-key svn-log-view-mode-map (kbd "=") 'svn-log-view-diff)
cvsdist e1e7868
  (define-key svn-log-view-mode-map (kbd "q") 'bury-buffer))
cvsdist e1e7868
(easy-menu-define svn-log-view-mode-menu svn-log-view-mode-map
cvsdist e1e7868
"'svn-log-view-mode' menu"
cvsdist e1e7868
                  '("SVN-LogView"
cvsdist e1e7868
                    ["Show Changeset" svn-log-view-diff t]))
cvsdist e1e7868
cvsdist e1e7868
(defvar svn-log-view-font-lock-keywords
cvsdist e1e7868
  '(("^r.+" . font-lock-keyword-face)
cvsdist e1e7868
  "Keywords in svn-log-view-mode."))
cvsdist e1e7868
cvsdist e1e7868
(define-derived-mode svn-log-view-mode log-view-mode "svn-log-view"
cvsdist e1e7868
  "Major Mode to show the output from svn log.
cvsdist e1e7868
Commands:
cvsdist e1e7868
\\{svn-log-view-mode-map}
cvsdist e1e7868
"
cvsdist e1e7868
  (use-local-map svn-log-view-mode-map)
cvsdist e1e7868
  (easy-menu-add svn-log-view-mode-menu)
cvsdist e1e7868
  (set (make-local-variable 'font-lock-defaults) '(svn-log-view-font-lock-keywords t)))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-log-view-next ()
cvsdist e1e7868
  (interactive)
cvsdist e1e7868
  (when (re-search-forward "^r[0-9]+" nil t)
cvsdist e1e7868
    (beginning-of-line 3)))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-log-view-prev ()
cvsdist e1e7868
  (interactive)
cvsdist e1e7868
  (when (re-search-backward "^r[0-9]+" nil t 2)
cvsdist e1e7868
    (beginning-of-line 3)))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-log-revision-at-point ()
cvsdist e1e7868
  (save-excursion
cvsdist e1e7868
    (re-search-backward "^r\\([0-9]+\\)")
cvsdist e1e7868
    (match-string-no-properties 1)))
cvsdist e1e7868
cvsdist e1e7868
(defun svn-log-view-diff (arg)
cvsdist e1e7868
  "Show the changeset for a given log entry.
cvsdist e1e7868
When called with a prefix argument, ask the user for the revision."
cvsdist e1e7868
  (interactive "P")
cvsdist e1e7868
  (let* ((upper-rev (svn-log-revision-at-point))
cvsdist e1e7868
        (lower-rev (number-to-string (- (string-to-number upper-rev) 1)))
cvsdist e1e7868
        (rev-arg (concat lower-rev ":" upper-rev)))
cvsdist e1e7868
    (when arg
cvsdist e1e7868
      (setq rev-arg (read-string "Revision for changeset: " rev-arg)))
cvsdist e1e7868
    (svn-run-svn nil t 'diff "diff" (concat "-r" rev-arg))
cvsdist e1e7868
    (svn-status-show-process-buffer-internal t)
cvsdist e1e7868
    (save-excursion
cvsdist e1e7868
      (set-buffer "*svn-process*")
cvsdist e1e7868
      (diff-mode)
cvsdist e1e7868
      (font-lock-fontify-buffer))))
cvsdist e1e7868
357017e
;; --------------------------------------------------------------------------------
357017e
;; svn status persistent options
357017e
;; --------------------------------------------------------------------------------
357017e
357017e
(defun svn-status-base-dir ()
357017e
  (let ((base-dir default-directory)
357017e
        (dot-svn-dir)
357017e
        (dir-below default-directory))
357017e
    (setq dot-svn-dir (concat base-dir ".svn"))
357017e
    (while (when (file-exists-p dot-svn-dir)
357017e
             (setq base-dir (file-name-directory dot-svn-dir))
357017e
             (string-match "\\(.+/\\).+/" dir-below)
357017e
             (setq dir-below (match-string 1 dir-below))
357017e
             (setq dot-svn-dir (concat dir-below ".svn"))))
357017e
    base-dir))
357017e
357017e
(defun svn-status-save-state ()
357017e
  (interactive)
357017e
  (let ((buf (find-file (concat (svn-status-base-dir) "++psvn.state"))))
357017e
    (delete-region (point-min) (point-max))
357017e
    (setq svn-status-options
357017e
          (list
357017e
           (list "sort-status-buffer" svn-status-sort-status-buffer)))
357017e
    (insert (pp-to-string svn-status-options))
357017e
    (save-buffer)
357017e
    (kill-buffer buf)))
357017e
357017e
(defun svn-status-load-state ()
357017e
  (interactive)
357017e
  (let ((file (concat (svn-status-base-dir) "++psvn.state")))
357017e
    (if (file-readable-p file)
357017e
        (with-temp-buffer
357017e
          (insert-file-contents file)
357017e
          (setq svn-status-options (read (current-buffer)))
357017e
          (setq svn-status-sort-status-buffer
357017e
                (nth 1 (assoc "sort-status-buffer" svn-status-options))))
357017e
      (error "%s is not readable." file))
357017e
    (message "Loaded %s" file)))
357017e
357017e
(defun svn-status-toggle-sort-status-buffer ()
357017e
  "If you turn off sorting, you can speed up M-x svn-status.
357017e
However, the buffer is not correct sorted then.
357017e
This function will be removed again, when a faster parsing and
357017e
display routine for svn-status is available."
357017e
  (interactive)
357017e
  (setq svn-status-sort-status-buffer (not svn-status-sort-status-buffer))
357017e
  (message (concat "The *svn-status* buffer will be"
357017e
                   (if svn-status-sort-status-buffer "" " not")
357017e
                   " sorted.")))
357017e
357017e
cvsdist e1e7868
(provide 'psvn)
cvsdist e1e7868
cvsdist e1e7868
;;; psvn.el ends here