~svn/ubuntu/raring/subversion/ppa

« back to all changes in this revision

Viewing changes to contrib/client-side/psvn/psvn.el

  • Committer: Bazaar Package Importer
  • Author(s): Adam Conrad
  • Date: 2005-12-05 01:26:14 UTC
  • mfrom: (1.1.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20051205012614-qom4xfypgtsqc2xq
Tags: 1.2.3dfsg1-3ubuntu1
Merge with the final Debian release of 1.2.3dfsg1-3, bringing in
fixes to the clean target, better documentation of the libdb4.3
upgrade and build fixes to work with swig1.3_1.3.27.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
;;; psvn.el --- Subversion interface for emacs
 
2
;; Copyright (C) 2002-2005 by Stefan Reichoer
 
3
 
 
4
;; Author: Stefan Reichoer, <stefan@xsteve.at>
 
5
;; $Id: psvn.el 13841 2005-04-01 20:55:27Z xsteve $
 
6
 
 
7
;; psvn.el is free software; you can redistribute it and/or modify
 
8
;; it under the terms of the GNU General Public License as published by
 
9
;; the Free Software Foundation; either version 2, or (at your option)
 
10
;; any later version.
 
11
 
 
12
;; psvn.el is distributed in the hope that it will be useful,
 
13
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
 
14
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
15
;; GNU General Public License for more details.
 
16
 
 
17
;; You should have received a copy of the GNU General Public License
 
18
;; along with GNU Emacs; see the file COPYING.  If not, write to
 
19
;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 
20
;; Boston, MA 02111-1307, USA.
 
21
 
 
22
;;; Commentary
 
23
 
 
24
;; psvn.el is tested with GNU Emacs 21.3 on windows, debian linux,
 
25
;; freebsd5, red hat el3 with svn 1.1.1
 
26
 
 
27
;; psvn.el is an interface for the revision control tool subversion
 
28
;; (see http://subversion.tigris.org)
 
29
;; psvn.el provides a similar interface for subversion as pcl-cvs for cvs.
 
30
;; At the moment the following commands are implemented:
 
31
;; M-x svn-status: run 'svn -status -v'
 
32
;; and show the result in the *svn-status* buffer.
 
33
;; If svn-status-verbose is set to nil, only "svn status" without "-v"
 
34
;; is run. Currently you have to toggle this variable manually.
 
35
;; This buffer uses svn-status mode in which the following keys are defined:
 
36
;; g     - svn-status-update:               run 'svn status -v'
 
37
;; C-u g - svn-status-update:               run 'svn status -vu'
 
38
;; =     - svn-status-show-svn-diff         run 'svn diff'
 
39
;; l     - svn-status-show-svn-log          run 'svn log'
 
40
;; i     - svn-status-info                  run 'svn info'
 
41
;; r     - svn-status-revert                run 'svn revert'
 
42
;; X v   - svn-status-resolved              run 'svn resolved'
 
43
;; U     - svn-status-update-cmd            run 'svn update'
 
44
;; c     - svn-status-commit-file           run 'svn commit'
 
45
;; a     - svn-status-add-file              run 'svn add --non-recursive'
 
46
;; A     - svn-status-add-file-recursively  run 'svn add'
 
47
;; +     - svn-status-make-directory        run 'svn mkdir'
 
48
;; R     - svn-status-mv                    run 'svn mv'
 
49
;; C-d   - svn-status-rm                    run 'svn rm'
 
50
;; M-c   - svn-status-cleanup               run 'svn cleanup'
 
51
;; b     - svn-status-blame                 run 'svn blame'
 
52
;; RET   - svn-status-find-file-or-examine-directory
 
53
;; ^     - svn-status-examine-parent
 
54
;; ~     - svn-status-get-specific-revision
 
55
;; E     - svn-status-ediff-with-revision
 
56
;; X X   - svn-status-resolve-conflicts
 
57
;; s     - svn-status-show-process-buffer
 
58
;; e     - svn-status-toggle-edit-cmd-flag
 
59
;; ?     - svn-status-toggle-hide-unknown
 
60
;; _     - svn-status-toggle-hide-unmodified
 
61
;; m     - svn-status-set-user-mark
 
62
;; u     - svn-status-unset-user-mark
 
63
;; $     - svn-status-toggle-elide
 
64
;; w     - svn-status-copy-filename-as-kill
 
65
;; DEL   - svn-status-unset-user-mark-backwards
 
66
;; * !   - svn-status-unset-all-usermarks
 
67
;; * ?   - svn-status-mark-unknown
 
68
;; * A   - svn-status-mark-added
 
69
;; * M   - svn-status-mark-modified
 
70
;; * D   - svn-status-mark-deleted
 
71
;; * *   - svn-status-mark-changed
 
72
;; .     - svn-status-goto-root-or-return
 
73
;; f     - svn-status-find-file
 
74
;; o     - svn-status-find-file-other-window
 
75
;; v     - svn-status-view-file-other-window
 
76
;; I     - svn-status-parse-info
 
77
;; V     - svn-status-svnversion
 
78
;; P l   - svn-status-property-list
 
79
;; P s   - svn-status-property-set
 
80
;; P d   - svn-status-property-delete
 
81
;; P e   - svn-status-property-edit-one-entry
 
82
;; P i   - svn-status-property-ignore-file
 
83
;; P I   - svn-status-property-ignore-file-extension
 
84
;; P C-i - svn-status-property-edit-svn-ignore
 
85
;; P k   - svn-status-property-set-keyword-list
 
86
;; P y   - svn-status-property-set-eol-style
 
87
;; P x   - svn-status-property-set-executable
 
88
;; h     - svn-status-use-history
 
89
;; q     - svn-status-bury-buffer
 
90
 
 
91
;; The output in the buffer contains this header to ease reading
 
92
;; of svn output:
 
93
;;   FPH BASE CMTD Author   em File
 
94
;; F = Filemark
 
95
;; P = Property mark
 
96
;; H = History mark
 
97
;; BASE = local base revision
 
98
;; CMTD = last committed revision
 
99
;; Author = author of change
 
100
;; em = "**" or "(Update Available)" [see `svn-status-short-mod-flag-p']
 
101
;;      if file can be updated
 
102
;; File = path/filename
 
103
;;
 
104
 
 
105
;; To use psvn.el put the following line in your .emacs:
 
106
;; (require 'psvn)
 
107
;; Start the svn interface with M-x svn-status
 
108
 
 
109
;; The latest version of psvn.el can be found at:
 
110
;;   http://www.xsteve.at/prg/emacs/psvn.el
 
111
;; Or you can check it out from the subversion repository:
 
112
;;   svn co http://svn.collab.net/repos/svn/trunk/contrib/client-side/psvn psvn
 
113
 
 
114
;; TODO:
 
115
;; * shortcut for svn propset svn:keywords "Date" psvn.el
 
116
;; * docstrings for the functions
 
117
;; * perhaps shortcuts for ranges, dates
 
118
;; * when editing the command line - offer help from the svn client
 
119
;; * finish svn-status-property-set
 
120
;; * eventually use the customize interface
 
121
;; * interactive svn-status should complete existing directories only;
 
122
;;   unfortunately `read-directory-name' doesn't exist in Emacs 21.3
 
123
;; * Add repository browser
 
124
;; * Improve support for svn blame
 
125
 
 
126
;; Overview over the implemented/not (yet) implemented svn sub-commands:
 
127
;; * add                       implemented
 
128
;; * blame                     implemented
 
129
;; * cat                       implemented
 
130
;; * checkout (co)
 
131
;; * cleanup                   implemented
 
132
;; * commit (ci)               implemented
 
133
;; * copy (cp)
 
134
;; * delete (del, remove, rm)  implemented
 
135
;; * diff (di)                 implemented
 
136
;; * export
 
137
;; * help (?, h)
 
138
;; * import
 
139
;; * info                      implemented
 
140
;; * list (ls)
 
141
;; * log                       implemented
 
142
;; * merge
 
143
;; * mkdir                     implemented
 
144
;; * move (mv, rename, ren)    implemented
 
145
;; * propdel (pdel)            implemented
 
146
;; * propedit (pedit, pe)      not needed
 
147
;; * propget (pget, pg)        used
 
148
;; * proplist (plist, pl)      implemented
 
149
;; * propset (pset, ps)        used
 
150
;; * resolved                  implemented
 
151
;; * revert                    implemented
 
152
;; * status (stat, st)         implemented
 
153
;; * switch (sw)
 
154
;; * update (up)               implemented
 
155
 
 
156
;; For the not yet implemented commands you should use the command line
 
157
;; svn client. If there are user requests for any missing commands I will
 
158
;; probably implement them.
 
159
 
 
160
;; Comments / suggestions and bug reports are welcome!
 
161
 
 
162
;;; Code:
 
163
 
 
164
(require 'easymenu)
 
165
 
 
166
;;; user setable variables
 
167
(defvar svn-status-verbose t "*Add '-v' to svn status call.")
 
168
(defvar svn-log-edit-file-name "++svn-log++" "*Name of a saved log file.")
 
169
(defvar svn-log-edit-insert-files-to-commit t "*Insert the filelist to commit in the *svn-log* buffer")
 
170
(defvar svn-status-hide-unknown nil "*Hide unknown files in *svn-status* buffer.")
 
171
(defvar svn-status-hide-unmodified nil "*Hide unmodified files in *svn-status* buffer.")
 
172
(defvar svn-status-directory-history nil "*List of visited svn working directories.")
 
173
(defvar svn-status-sort-status-buffer t "Sort the *svn-status* buffer.
 
174
Setting this variable to nil speeds up M-x svn-status.
 
175
However, it is possible, that the sorting is wrong in this case.")
 
176
 
 
177
(defvar svn-status-unmark-files-after-list '(commit revert)
 
178
  "*List of operations after which all user marks will be removed.
 
179
Possible values are: commit, revert.")
 
180
 
 
181
(defvar svn-status-negate-meaning-of-arg-commands nil
 
182
  "*List of operations that sould use a negated meaning of the prefix argument.
 
183
The only supported function is 'svn-status.")
 
184
 
 
185
(defvar svn-status-svn-executable "svn" "*The name of the svn executable.")
 
186
 
 
187
(defvar svn-status-svn-environment-var-list nil
 
188
  "*A list of environment variables that should be set for that svn process.
 
189
If you set that variable, svn is called with that environment variables set.
 
190
That is done via the env program.
 
191
 
 
192
You could set it for example to '(\"LANG=C\")")
 
193
 
 
194
(defvar svn-status-window-alist
 
195
  '((diff "*svn-diff*") (log "*svn-log*") (info t) (blame t) (proplist t) (update t))
 
196
  "An alist to specify which windows should be used for svn command outputs.
 
197
The following keys are supported: diff, log, info, blame, proplist, update.
 
198
The follwing values can be given:
 
199
nil       ... show in *svn-process* buffer
 
200
t         ... show in dedicated *svn-info* buffer
 
201
invisible ... don't show the buffer (eventually useful for update)
 
202
a string  ... show in a buffer named string")
 
203
 
 
204
(defvar svn-status-short-mod-flag-p t
 
205
  "*Whether the mark for out of date files is short or long.
 
206
 
 
207
If this variable is is t, and a file is out of date (i.e., there is a newer
 
208
version in the repository than the working copy), then the file will
 
209
be marked by \"**\"
 
210
 
 
211
If this variale is nil, and the file is out of date then the longer phrase
 
212
\"(Update Available)\" is used.
 
213
 
 
214
In either case the mark gets the face
 
215
`svn-status-update-available-face', and will only be visible if
 
216
`\\[svn-status-update]' is run with a prefix argument")
 
217
 
 
218
(defvar svn-status-use-header-line t
 
219
  "*Whether a header line should be used.
 
220
When t: Use the emacs header line
 
221
When 'inline: Insert the header line in the *svn-status* buffer
 
222
Otherwise: Don't display a header line")
 
223
 
 
224
;;; default arguments to pass to svn commands
 
225
(defvar svn-status-default-log-arguments ""
 
226
  "*Arguments to pass to svn log.
 
227
\(used in `svn-status-show-svn-log'; override these by giving prefixes\).")
 
228
 
 
229
(defvar svn-trac-project-root nil "Path for an eventual existing trac issue tracker.")
 
230
 
 
231
(defvar svn-status-module-name nil "A nice short name for the actual project.")
 
232
 
 
233
(defvar svn-status-load-state-before-svn-status t "Load the ++psvn.state file, before running svn-status")
 
234
 
 
235
;;; hooks
 
236
(defvar svn-log-edit-mode-hook nil "Hook run when entering `svn-log-edit-mode'.")
 
237
(defvar svn-log-edit-done-hook nil "Hook run after commiting files via svn.")
 
238
 
 
239
(defvar svn-status-wash-control-M-in-process-buffers
 
240
  (eq system-type 'windows-nt)
 
241
  "*Remove any trailing ^M from the *svn-process* buffer.")
 
242
 
 
243
;;; experimental features
 
244
(defvar svn-status-use-process-filter nil "Use the svn process filter for
 
245
asynchronous calls to svn.")
 
246
 
 
247
;;; Customize group
 
248
(defgroup psvn nil
 
249
  "Subversion interface for Emacs."
 
250
  :group 'tools)
 
251
 
 
252
(defgroup psvn-faces nil
 
253
  "psvn faces."
 
254
  :group 'psvn)
 
255
 
 
256
 
 
257
(eval-and-compile
 
258
  (require 'cl)
 
259
  (defconst svn-xemacsp (featurep 'xemacs))
 
260
  (if svn-xemacsp
 
261
      (require 'overlay)
 
262
    (require 'overlay nil t)))
 
263
 
 
264
;; Use the normally used mode for files ending in .~HEAD~, .~BASE~, ...
 
265
(add-to-list 'auto-mode-alist '("\\.~?\\(HEAD\\|BASE\\|PREV\\)~?\\'" ignore t))
 
266
 
 
267
;;; internal variables
 
268
(defvar svn-process-cmd nil)
 
269
(defvar svn-status-info nil)
 
270
(defvar svn-status-base-info nil)
 
271
(defvar svn-status-initial-window-configuration nil)
 
272
(defvar svn-status-default-column 23)
 
273
(defvar svn-status-default-revision-width 4)
 
274
(defvar svn-status-default-author-width 9)
 
275
(defvar svn-status-line-format " %c%c%c %4s %4s %-9s")
 
276
(defvar svn-start-of-file-list-line-number 0)
 
277
(defvar svn-status-files-to-commit nil)
 
278
(defvar svn-status-pre-commit-window-configuration nil)
 
279
(defvar svn-status-pre-propedit-window-configuration nil)
 
280
(defvar svn-status-head-revision nil)
 
281
(defvar svn-status-root-return-info nil)
 
282
(defvar svn-status-property-edit-must-match-flag nil)
 
283
(defvar svn-status-propedit-property-name nil)
 
284
(defvar svn-status-propedit-file-list nil)
 
285
(defvar svn-status-mode-line-process "")
 
286
(defvar svn-status-mode-line-process-status "")
 
287
(defvar svn-status-mode-line-process-edit-flag "")
 
288
(defvar svn-status-edit-svn-command nil)
 
289
(defvar svn-status-update-previous-process-output nil)
 
290
(defvar svn-status-temp-dir
 
291
  (or
 
292
   (when (boundp 'temporary-file-directory) temporary-file-directory) ;emacs
 
293
   (when (boundp 'temp-directory) temp-directory)                     ;xemacs
 
294
   "/tmp/"))
 
295
(defvar svn-temp-suffix (make-temp-name "."))
 
296
(defvar svn-status-temp-file-to-remove nil)
 
297
(defvar svn-status-temp-arg-file (concat svn-status-temp-dir "svn.arg" svn-temp-suffix))
 
298
(defvar svn-status-options nil)
 
299
(defvar svn-status-commit-rev-number nil)
 
300
(defvar svn-status-operated-on-dot nil)
 
301
(defvar svn-status-elided-list nil)
 
302
 
 
303
;;; faces
 
304
(defface svn-status-marked-face
 
305
  '((((type tty) (class color)) (:foreground "green" :weight light))
 
306
    (((class color) (background light)) (:foreground "green3"))
 
307
    (((class color) (background dark)) (:foreground "palegreen2"))
 
308
    (t (:weight bold)))
 
309
  "Face to highlight the mark for user marked files in svn status buffers."
 
310
  :group 'psvn-faces)
 
311
 
 
312
(defface svn-status-marked-popup-face
 
313
  '((((type tty) (class color)) (:foreground "green" :weight light))
 
314
    (((class color) (background light)) (:foreground "green3"))
 
315
    (((class color) (background dark)) (:foreground "palegreen2"))
 
316
    (t (:weight bold)))
 
317
  "Face to highlight the actual file, if a popup menu is activated."
 
318
  :group 'psvn-faces)
 
319
 
 
320
(defface svn-status-update-available-face
 
321
  '((((type tty) (class color)) (:foreground "magenta" :weight light))
 
322
    (((class color) (background light)) (:foreground "magenta"))
 
323
    (((class color) (background dark)) (:foreground "yellow"))
 
324
    (t (:weight bold)))
 
325
  "Face used to highlight the 'out of date' mark.
 
326
\(i.e., the mark used when there is a newer version in the repository
 
327
than the working copy.\)
 
328
 
 
329
See also `svn-status-short-mod-flag-p'."
 
330
  :group 'psvn-faces)
 
331
 
 
332
;based on cvs-filename-face
 
333
(defface svn-status-directory-face
 
334
  '((((type tty) (class color)) (:foreground "lightblue" :weight light))
 
335
    (((class color) (background light)) (:foreground "blue4"))
 
336
    (((class color) (background dark)) (:foreground "lightskyblue1"))
 
337
    (t (:weight bold)))
 
338
  "Face for directories in svn status buffers.
 
339
See `svn-status--line-info->directory-p' for what counts as a directory."
 
340
  :group 'psvn-faces)
 
341
 
 
342
;based on font-lock-comment-face
 
343
(defface svn-status-filename-face
 
344
  '((((class color) (background light)) (:foreground "chocolate"))
 
345
    (((class color) (background dark)) (:foreground "beige")))
 
346
  "Face for non-directories in svn status buffers.
 
347
See `svn-status--line-info->directory-p' for what counts as a directory."
 
348
  :group 'psvn-faces)
 
349
 
 
350
;based on font-lock-warning-face
 
351
(defface svn-status-locked-face
 
352
  '((t
 
353
     (:weight bold :foreground "Red")))
 
354
  "Face for the phrase \"[ LOCKED ]\" *svn-status* buffers."
 
355
  :group 'psvn-faces)
 
356
 
 
357
;based on vhdl-font-lock-directive-face
 
358
(defface svn-status-switched-face
 
359
  '((((class color)
 
360
      (background light))
 
361
     (:foreground "CadetBlue"))
 
362
    (((class color)
 
363
      (background dark))
 
364
     (:foreground "Aquamarine"))
 
365
    (t
 
366
     (:bold t :italic t)))
 
367
  "Face for the phrase \"(switched)\" non-directories in svn status buffers."
 
368
  :group 'psvn-faces)
 
369
 
 
370
(defvar svn-highlight t)
 
371
;; stolen from PCL-CVS
 
372
(defun svn-add-face (str face &optional keymap)
 
373
  "Return string STR decorated with the specified FACE.
 
374
If `svn-highlight' is nil then just return STR."
 
375
  (when svn-highlight
 
376
    ;; Do not use `list*'; cl.el might not have been loaded.  We could
 
377
    ;; put (require 'cl) at the top but let's try to manage without.
 
378
    (add-text-properties 0 (length str)
 
379
                         `(face ,face
 
380
                                mouse-face highlight)
 
381
;; 18.10.2004: the keymap parameter is not used (yet) in psvn.el
 
382
;;                           ,@(when keymap
 
383
;;                               `(mouse-face highlight
 
384
;;                                 local-map ,keymap)))
 
385
                         str))
 
386
  str)
 
387
 
 
388
(defun svn-status-maybe-add-face (condition text face)
 
389
  "If CONDITION then add FACE to TEXT.
 
390
Else return TEXT unchanged."
 
391
  (if condition
 
392
      (svn-add-face text face)
 
393
    text))
 
394
 
 
395
(defun svn-status-choose-face-to-add (condition text face1 face2)
 
396
  "If CONDITION then add FACE1 to TEXT, else add FACE2 to TEXT."
 
397
  (if condition
 
398
      (svn-add-face text face1)
 
399
    (svn-add-face text face2)))
 
400
 
 
401
(defun svn-status-maybe-add-string (condition string face)
 
402
  "If CONDITION then return STRING decorated with FACE.
 
403
Otherwise, return \"\"."
 
404
  (if condition
 
405
      (svn-add-face string face)
 
406
    ""))
 
407
 
 
408
; compatibility
 
409
; emacs 20
 
410
(unless (fboundp 'point-at-eol) (defalias 'point-at-eol 'line-end-position))
 
411
(unless (fboundp 'point-at-bol) (defalias 'point-at-bol 'line-beginning-position))
 
412
(unless (functionp 'read-directory-name) (defalias 'read-directory-name 'read-file-name))
 
413
 
 
414
(eval-when-compile
 
415
  (if (not (fboundp 'gethash))
 
416
      (require 'cl-macs)))
 
417
(if (not (fboundp 'puthash))
 
418
    (defalias 'puthash 'cl-puthash))
 
419
 
 
420
; xemacs
 
421
(if (fboundp 'match-string-no-properties)
 
422
    nil ;; great
 
423
  (defsubst match-string-no-properties (match)
 
424
    (buffer-substring-no-properties (match-beginning match) (match-end match))))
 
425
 
 
426
(defvar svn-status-display-new-status-buffer nil)
 
427
;;;###autoload
 
428
(defun svn-status (dir &optional arg)
 
429
  "Examine the status of Subversion working copy in directory DIR.
 
430
If ARG then pass the -u argument to `svn status'."
 
431
  (interactive (list (read-directory-name "SVN status directory: "
 
432
                                          nil default-directory nil)
 
433
                     current-prefix-arg))
 
434
  (setq arg (svn-status-possibly-negate-meaning-of-arg arg 'svn-status))
 
435
  (unless (file-directory-p dir)
 
436
    (error "%s is not a directory" dir))
 
437
  (if (not (file-exists-p (concat dir "/.svn/")))
 
438
      (when (y-or-n-p
 
439
             (concat dir
 
440
                     " does not seem to be a Subversion working copy (no .svn directory).  "
 
441
                     "Run dired instead? "))
 
442
        (dired dir))
 
443
    (setq dir (file-name-as-directory dir))
 
444
    (when svn-status-load-state-before-svn-status
 
445
      (unless (string= dir (car svn-status-directory-history))
 
446
        (svn-status-load-state t)))
 
447
    (setq svn-status-directory-history (delete dir svn-status-directory-history))
 
448
    (add-to-list 'svn-status-directory-history dir)
 
449
    (if (string= (buffer-name) "*svn-status*")
 
450
        (setq svn-status-display-new-status-buffer nil)
 
451
      (setq svn-status-display-new-status-buffer t)
 
452
      ;;(message "psvn: Saving initial window configuration")
 
453
      (setq svn-status-initial-window-configuration (current-window-configuration)))
 
454
    (let* ((status-buf (get-buffer-create "*svn-status*"))
 
455
           (proc-buf (get-buffer-create "*svn-process*"))
 
456
           (status-option (if svn-status-verbose
 
457
                              (if arg "-uv" "-v")
 
458
                            (if arg "-u" ""))))
 
459
      (save-excursion
 
460
        (set-buffer status-buf)
 
461
        (setq default-directory dir)
 
462
        (set-buffer proc-buf)
 
463
        (setq default-directory dir
 
464
              svn-status-remote (when arg t))
 
465
        (svn-run-svn t t 'status "status" status-option)))))
 
466
 
 
467
(defun svn-status-use-history ()
 
468
  (interactive)
 
469
  (let* ((hist svn-status-directory-history)
 
470
         (dir (read-from-minibuffer "svn-status on directory: "
 
471
                              (cadr svn-status-directory-history)
 
472
                              nil nil 'hist)))
 
473
    (if (file-directory-p dir)
 
474
        (svn-status dir)
 
475
      (error "%s is not a directory" dir))))
 
476
 
 
477
(defun svn-run-svn (run-asynchron clear-process-buffer cmdtype &rest arglist)
 
478
  "Run svn with arguments ARGLIST.
 
479
 
 
480
If RUN-ASYNCHRON is t then run svn asynchronously.
 
481
 
 
482
If CLEAR-PROCESS-BUFFER is t then erase the contents of the
 
483
*svn-process* buffer before commencing.
 
484
 
 
485
CMDTYPE is a symbol such as 'mv, 'revert, or 'add, representing the
 
486
command to run.
 
487
 
 
488
ARGLIST is a list of arguments \(which must include the command name,
 
489
for example: '(\"revert\" \"file1\"\)
 
490
 
 
491
If the variable `svn-status-edit-svn-command' is non-nil then the user
 
492
is prompted for give extra arguments, which are appended to ARGLIST."
 
493
  (if (eq (process-status "svn") nil)
 
494
      (progn
 
495
        (when svn-status-edit-svn-command
 
496
          (setq arglist (append arglist
 
497
                                (split-string
 
498
                                 (read-from-minibuffer
 
499
                                  (format "Run `svn %s' with extra arguments: "
 
500
                                          (mapconcat 'identity arglist " "))))))
 
501
          (when (eq svn-status-edit-svn-command t)
 
502
            (svn-status-toggle-edit-cmd-flag t))
 
503
          (message "svn-run-svn %s: %S" cmdtype arglist))
 
504
        (let* ((proc-buf (get-buffer-create "*svn-process*"))
 
505
               (svn-exe svn-status-svn-executable)
 
506
               (svn-proc))
 
507
          (when (listp (car arglist))
 
508
            (setq arglist (car arglist)))
 
509
          (save-excursion
 
510
            (set-buffer proc-buf)
 
511
            (setq buffer-read-only nil)
 
512
            (fundamental-mode)
 
513
            (if clear-process-buffer
 
514
                (delete-region (point-min) (point-max))
 
515
              (goto-char (point-max)))
 
516
            (setq svn-process-cmd cmdtype)
 
517
            (setq svn-status-mode-line-process-status (format " running %s" cmdtype))
 
518
            (svn-status-update-mode-line)
 
519
            (sit-for 0.1)
 
520
            (when svn-status-svn-environment-var-list
 
521
              (setq arglist (append svn-status-svn-environment-var-list
 
522
                                    (list svn-status-svn-executable)
 
523
                                    arglist))
 
524
              (setq svn-exe "env"))
 
525
            (if run-asynchron
 
526
                (progn
 
527
                  ;;(message "running asynchron: %s %S" svn-exe arglist)
 
528
                  (setq svn-proc (apply 'start-process "svn" proc-buf svn-exe arglist))
 
529
                  (set-process-sentinel svn-proc 'svn-process-sentinel)
 
530
                  (when svn-status-use-process-filter
 
531
                    (set-process-filter svn-proc 'svn-process-filter)))
 
532
              ;;(message "running synchron: %s %S" svn-exe arglist)
 
533
              (apply 'call-process svn-exe nil proc-buf nil arglist)
 
534
              (setq svn-status-mode-line-process-status "")
 
535
              (svn-status-update-mode-line)))))
 
536
    (error "You can only run one svn process at once!")))
 
537
 
 
538
(defun svn-process-sentinel-fixup-path-seperators()
 
539
  (when (eq system-type 'windows-nt)
 
540
      ;; convert path separator to UNIX style
 
541
      (save-excursion
 
542
        (goto-char (point-min))
 
543
        (while (search-forward "\\" nil t)
 
544
          (replace-match "/")))))
 
545
 
 
546
(defun svn-process-sentinel (process event)
 
547
  ;;(princ (format "Process: %s had the event `%s'" process event)))
 
548
  ;;(save-excursion
 
549
  (let ((act-buf (current-buffer)))
 
550
    (set-buffer (process-buffer process))
 
551
    (setq svn-status-mode-line-process-status "")
 
552
    (svn-status-update-mode-line)
 
553
    (cond ((string= event "finished\n")
 
554
           (cond ((eq svn-process-cmd 'status)
 
555
                  ;;(message "svn status finished")
 
556
                  (svn-process-sentinel-fixup-path-seperators)
 
557
                  (svn-parse-status-result)
 
558
                  (set-buffer act-buf)
 
559
                  (svn-status-update-buffer)
 
560
                  (when svn-status-update-previous-process-output
 
561
                    (set-buffer (process-buffer process))
 
562
                    (delete-region (point-min) (point-max))
 
563
                    (insert "Output from svn command:\n")
 
564
                    (insert svn-status-update-previous-process-output)
 
565
                    (goto-char (point-min))
 
566
                    (setq svn-status-update-previous-process-output nil))
 
567
                  (when svn-status-display-new-status-buffer
 
568
                    (set-window-configuration svn-status-initial-window-configuration)
 
569
                    (switch-to-buffer "*svn-status*")))
 
570
                 ((eq svn-process-cmd 'log)
 
571
                  (svn-status-show-process-output 'log t)
 
572
                  (pop-to-buffer svn-status-last-output-buffer-name)
 
573
                  (svn-log-view-mode)
 
574
                  (forward-line 3)
 
575
                  (font-lock-fontify-buffer)
 
576
                  (message "svn log finished"))
 
577
                 ((eq svn-process-cmd 'info)
 
578
                  (svn-status-show-process-output 'info t)
 
579
                  (message "svn info finished"))
 
580
                 ((eq svn-process-cmd 'parse-info)
 
581
                  (svn-status-parse-info-result))
 
582
                 ((eq svn-process-cmd 'blame)
 
583
                  (svn-status-show-process-output 'blame t)
 
584
                  (message "svn blame finished"))
 
585
                 ((eq svn-process-cmd 'commit)
 
586
                  (svn-process-sentinel-fixup-path-seperators)
 
587
                  (svn-status-remove-temp-file-maybe)
 
588
                  (when (member 'commit svn-status-unmark-files-after-list)
 
589
                    (svn-status-unset-all-usermarks))
 
590
                  (svn-status-update-with-command-list (svn-status-parse-commit-output))
 
591
                  (run-hooks 'svn-log-edit-done-hook)
 
592
                  (setq svn-status-files-to-commit nil)
 
593
                  (message "svn commit finished"))
 
594
                 ((eq svn-process-cmd 'update)
 
595
                  (svn-status-show-process-output 'update t)
 
596
                  (svn-status-update)
 
597
                  (message "svn update finished"))
 
598
                 ((eq svn-process-cmd 'add)
 
599
                  (svn-status-update)
 
600
                  (message "svn add finished"))
 
601
                 ((eq svn-process-cmd 'mkdir)
 
602
                  (svn-status-update)
 
603
                  (message "svn mkdir finished"))
 
604
                 ((eq svn-process-cmd 'revert)
 
605
                  (when (member 'revert svn-status-unmark-files-after-list)
 
606
                    (svn-status-unset-all-usermarks))
 
607
                  (svn-status-update)
 
608
                  (message "svn revert finished"))
 
609
                 ((eq svn-process-cmd 'resolved)
 
610
                  (svn-status-update)
 
611
                  (message "svn resolved finished"))
 
612
                 ((eq svn-process-cmd 'mv)
 
613
                  (svn-status-update)
 
614
                  (message "svn mv finished"))
 
615
                 ((eq svn-process-cmd 'rm)
 
616
                  (svn-status-update)
 
617
                  (message "svn rm finished"))
 
618
                 ((eq svn-process-cmd 'cleanup)
 
619
                  (message "svn cleanup finished"))
 
620
                 ((eq svn-process-cmd 'proplist)
 
621
                  (svn-status-show-process-output 'proplist t)
 
622
                  (message "svn proplist finished"))
 
623
                 ((eq svn-process-cmd 'proplist-parse)
 
624
                  (svn-status-property-parse-property-names))
 
625
                 ((eq svn-process-cmd 'propset)
 
626
                  (svn-status-remove-temp-file-maybe)
 
627
                  (svn-status-update))
 
628
                 ((eq svn-process-cmd 'propdel)
 
629
                  (svn-status-update))))
 
630
          ((string= event "killed\n")
 
631
           (message "svn process killed"))
 
632
          ((string-match "exited abnormally" event)
 
633
           (while (accept-process-output process 0 100))
 
634
           ;; find last error message and show it.
 
635
           (goto-char (point-max))
 
636
           (message "svn failed: %s"
 
637
                    (if (re-search-backward "^svn: \\(.*\\)" nil t)
 
638
                        (match-string 1)
 
639
                      event)))
 
640
          (t
 
641
           (message "svn process had unknown event: %s" event))
 
642
          (svn-status-show-process-output nil t))))
 
643
 
 
644
(defun svn-process-filter (process str)
 
645
  (save-window-excursion
 
646
    (set-buffer "*svn-process*")
 
647
    (message "svn-process-filter: %s" str)
 
648
    (insert str)))
 
649
 
 
650
(defun svn-parse-rev-num (str)
 
651
  (if (and str (stringp str)
 
652
           (save-match-data (string-match "^[0-9]+" str)))
 
653
      (string-to-number str)
 
654
    -1))
 
655
 
 
656
 
 
657
(defun svn-status-make-dummy-dirs (dir-list)
 
658
  (append (mapcar (lambda (dir)
 
659
                    (list ui-status 32 nil dir -1 -1 "?" nil nil nil nil))
 
660
                  dir-list)
 
661
          svn-status-info))
 
662
 
 
663
 
 
664
(defun svn-parse-status-result ()
 
665
  "Parse the *svn-process* buffer.
 
666
The results are used to build the `svn-status-info' variable."
 
667
  (setq svn-status-head-revision nil)
 
668
  (save-excursion
 
669
    (let ((old-ui-information (svn-status-ui-information-hash-table))
 
670
          (line-string)
 
671
          (user-mark)
 
672
          (svn-marks)
 
673
          (svn-file-mark)
 
674
          (svn-property-mark)
 
675
          (svn-locked-mark)
 
676
          (svn-with-history-mark)
 
677
          (svn-switched-mark)
 
678
          (svn-update-mark)
 
679
          (local-rev)
 
680
          (last-change-rev)
 
681
          (author)
 
682
          (path)
 
683
          (user-elide nil)
 
684
          (ui-status '(nil nil))     ; contains (user-mark user-elide)
 
685
          (revision-width svn-status-default-revision-width)
 
686
          (author-width svn-status-default-author-width)
 
687
          (svn-marks-length (if (and svn-status-verbose svn-status-remote)
 
688
                                8 5))
 
689
          (dir-set '(".")))
 
690
      (set-buffer "*svn-process*")
 
691
      (setq svn-status-info nil)
 
692
      (goto-char (point-min))
 
693
      (while (< (point) (point-max))
 
694
        (cond
 
695
         ((= (point-at-eol) (point-at-bol)) ;skip blank lines
 
696
          nil)
 
697
         ((looking-at "Status against revision:[ ]+\\([0-9]+\\)")
 
698
          ;; the above message appears for the main listing plus once for each svn:externals entry
 
699
          (unless svn-status-head-revision
 
700
            (setq svn-status-head-revision (match-string 1))))
 
701
         ((looking-at "Performing status on external item at '\\(.*\\)'")
 
702
          ;; The *next* line has info about the directory named in svn:externals
 
703
          ;; [ie the directory in (match-string 1)]
 
704
          ;; we should parse it, and merge the info with what we have already know
 
705
          ;; but for now just ignore the line completely
 
706
          (forward-line)
 
707
          )
 
708
         (t
 
709
          (setq svn-marks (buffer-substring (point) (+ (point) svn-marks-length))
 
710
                svn-file-mark (elt svn-marks 0)         ; 1st column - M,A,C,D,G,? etc
 
711
                svn-property-mark (elt svn-marks 1)     ; 2nd column - M,C (properties)
 
712
                svn-locked-mark (elt svn-marks 2)       ; 3rd column - L or blank
 
713
                svn-with-history-mark (elt svn-marks 3) ; 4th column - + or blank
 
714
                svn-switched-mark (elt svn-marks 4))     ; 5th column - S or blank
 
715
          (if (and svn-status-verbose svn-status-remote)
 
716
              (setq svn-update-mark (elt svn-marks 7))) ; 8th column - * or blank
 
717
          (when (eq svn-property-mark ?\ )     (setq svn-property-mark nil))
 
718
          (when (eq svn-locked-mark ?\ )       (setq svn-locked-mark nil))
 
719
          (when (eq svn-with-history-mark ?\ ) (setq svn-with-history-mark nil))
 
720
          (when (eq svn-switched-mark ?\ )     (setq svn-switched-mark nil))
 
721
          (when (eq svn-update-mark ?\ )       (setq svn-update-mark nil))
 
722
          (forward-char svn-marks-length)
 
723
          (skip-chars-forward " ")
 
724
          (cond
 
725
           ((looking-at "\\([-?]\\|[0-9]+\\) +\\([-?]\\|[0-9]+\\) +\\([^ ]+\\) *\\(.+\\)$")
 
726
            (setq local-rev (svn-parse-rev-num (match-string 1))
 
727
                  last-change-rev (svn-parse-rev-num (match-string 2))
 
728
                  author (match-string 3)
 
729
                  path (match-string 4)))
 
730
           ((looking-at "\\([-?]\\|[0-9]+\\) +\\([^ ]+\\)$")
 
731
            (setq local-rev (svn-parse-rev-num (match-string 1))
 
732
                  last-change-rev -1
 
733
                  author "?"
 
734
                  path (match-string 2)))
 
735
           ((looking-at "\\(.*\\)")
 
736
            (setq path (match-string 1)
 
737
                  local-rev -1
 
738
                  last-change-rev -1
 
739
                  author (if (eq svn-file-mark 88) "" "?"))) ;clear author of svn:externals dirs
 
740
           (t
 
741
            (error "Unknown status line format")))
 
742
          (unless path (setq path "."))
 
743
          (setq dir (file-name-directory path))
 
744
          (if (and (not svn-status-verbose) dir)
 
745
              (let ((dirname (directory-file-name dir)))
 
746
                (if (not (member dirname dir-set))
 
747
                    (setq dir-set (cons dirname dir-set)))))
 
748
          (setq ui-status (or (gethash path old-ui-information) (list user-mark user-elide)))
 
749
          (setq svn-status-info (cons (list ui-status
 
750
                                            svn-file-mark
 
751
                                            svn-property-mark
 
752
                                            path
 
753
                                            local-rev
 
754
                                            last-change-rev
 
755
                                            author
 
756
                                            svn-update-mark
 
757
                                            svn-locked-mark
 
758
                                            svn-with-history-mark
 
759
                                            svn-switched-mark)
 
760
                                      svn-status-info))
 
761
          (setq revision-width (max revision-width
 
762
                                    (length (number-to-string local-rev))
 
763
                                    (length (number-to-string last-change-rev))))
 
764
          (setq author-width (max author-width (length author)))))
 
765
        (forward-line 1))
 
766
      (unless svn-status-verbose
 
767
        (setq svn-status-info (svn-status-make-dummy-dirs dir-set)))
 
768
      (setq svn-status-default-column
 
769
            (+ 6 revision-width revision-width author-width
 
770
               (if svn-status-short-mod-flag-p 3 0)))
 
771
      (setq svn-status-line-format (format " %%c%%c%%c %%%ds %%%ds %%-%ds"
 
772
                                           revision-width
 
773
                                           revision-width
 
774
                                           author-width))
 
775
      (setq svn-status-info (nreverse svn-status-info))
 
776
      (when svn-status-sort-status-buffer
 
777
        (setq svn-status-info (sort svn-status-info 'svn-status-sort-predicate))))))
 
778
 
 
779
;;(string-lessp "." "%") => nil
 
780
;(svn-status-sort-predicate '(t t t ".") '(t t t "%")) => t
 
781
(defun svn-status-sort-predicate (a b)
 
782
  "Return t if A should appear before B in the *svn-status* buffer.
 
783
A and B must be line-info's."
 
784
  (string-lessp (concat (svn-status-line-info->full-path a) "/")
 
785
                (concat (svn-status-line-info->full-path b) "/")))
 
786
 
 
787
(defun svn-status-remove-temp-file-maybe ()
 
788
  "Remove any (no longer required) temporary files created by psvn.el."
 
789
  (when svn-status-temp-file-to-remove
 
790
    (when (file-exists-p svn-status-temp-file-to-remove)
 
791
      (delete-file svn-status-temp-file-to-remove))
 
792
    (when (file-exists-p svn-status-temp-arg-file)
 
793
      (delete-file svn-status-temp-arg-file))
 
794
    (setq svn-status-temp-file-to-remove nil)))
 
795
 
 
796
(defun svn-status-remove-control-M ()
 
797
  "Remove ^M at end of line in the whole buffer."
 
798
  (interactive)
 
799
  (let ((buffer-read-only nil))
 
800
    (save-match-data
 
801
      (save-excursion
 
802
        (goto-char (point-min))
 
803
        (while (re-search-forward "\r$" (point-max) t)
 
804
          (replace-match "" nil nil))))))
 
805
 
 
806
(condition-case nil
 
807
    ;;(easy-menu-add-item nil '("tools") ["SVN Status" svn-status t] "PCL-CVS")
 
808
    (easy-menu-add-item nil '("tools") ["SVN Status" svn-status t])
 
809
  (error (message "psvn: could not install menu")))
 
810
 
 
811
(defvar svn-status-mode-map () "Keymap used in `svn-status-mode' buffers.")
 
812
(defvar svn-status-mode-property-map ()
 
813
  "Subkeymap used in `svn-status-mode' for property commands.")
 
814
(defvar svn-status-mode-options-map ()
 
815
  "Subkeymap used in `svn-status-mode' for option commands.")
 
816
(defvar svn-status-mode-trac-map ()
 
817
  "Subkeymap used in `svn-status-mode' for trac issue tracker commands.")
 
818
(defvar svn-status-mode-extension-map ()
 
819
  "Subkeymap used in `svn-status-mode' for some seldom used commands.")
 
820
 
 
821
(when (not svn-status-mode-map)
 
822
  (setq svn-status-mode-map (make-sparse-keymap))
 
823
  (suppress-keymap svn-status-mode-map)
 
824
  ;; Don't use (kbd "<return>"); it's unreachable with GNU Emacs 21.3 on a TTY.
 
825
  (define-key svn-status-mode-map (kbd "RET") 'svn-status-find-file-or-examine-directory)
 
826
  (define-key svn-status-mode-map (kbd "<mouse-2>") 'svn-status-mouse-find-file-or-examine-directory)
 
827
  (define-key svn-status-mode-map (kbd "^") 'svn-status-examine-parent)
 
828
  (define-key svn-status-mode-map (kbd "s") 'svn-status-show-process-buffer)
 
829
  (define-key svn-status-mode-map (kbd "f") 'svn-status-find-files)
 
830
  (define-key svn-status-mode-map (kbd "o") 'svn-status-find-file-other-window)
 
831
  (define-key svn-status-mode-map (kbd "v") 'svn-status-view-file-other-window)
 
832
  (define-key svn-status-mode-map (kbd "e") 'svn-status-toggle-edit-cmd-flag)
 
833
  (define-key svn-status-mode-map (kbd "g") 'svn-status-update)
 
834
  (define-key svn-status-mode-map (kbd "q") 'svn-status-bury-buffer)
 
835
  (define-key svn-status-mode-map (kbd "h") 'svn-status-use-history)
 
836
  (define-key svn-status-mode-map (kbd "m") 'svn-status-set-user-mark)
 
837
  (define-key svn-status-mode-map (kbd "u") 'svn-status-unset-user-mark)
 
838
  ;; This matches a binding of `dired-unmark-all-files' in `dired-mode-map'
 
839
  ;; of both GNU Emacs and XEmacs.  It seems unreachable with XEmacs on
 
840
  ;; TTY, but if that's a problem then its Dired needs fixing too.
 
841
  ;; Or you could just use "*!".
 
842
  (define-key svn-status-mode-map "\M-\C-?" 'svn-status-unset-all-usermarks)
 
843
  ;; The key that normally deletes characters backwards should here
 
844
  ;; instead unmark files backwards.  In GNU Emacs, that would be (kbd
 
845
  ;; "DEL") aka [?\177], but XEmacs treats those as [(delete)] and
 
846
  ;; would bind a key that normally deletes forwards.  [(backspace)]
 
847
  ;; is unreachable with GNU Emacs on a tty.  Try to recognize the
 
848
  ;; dialect and act accordingly.
 
849
  ;;
 
850
  ;; XEmacs has a `delete-forward-p' function that checks the
 
851
  ;; `delete-key-deletes-forward' option.  We don't use those, for two
 
852
  ;; reasons: psvn.el may be loaded before user customizations, and
 
853
  ;; XEmacs allows simultaneous connections to multiple devices with
 
854
  ;; different keyboards.
 
855
  (define-key svn-status-mode-map
 
856
              (if (member (kbd "DEL") '([(delete)] [delete]))
 
857
                  [(backspace)]         ; XEmacs
 
858
                (kbd "DEL"))            ; GNU Emacs
 
859
              'svn-status-unset-user-mark-backwards)
 
860
  (define-key svn-status-mode-map (kbd "$") 'svn-status-toggle-elide)
 
861
  (define-key svn-status-mode-map (kbd "w") 'svn-status-copy-filename-as-kill)
 
862
  (define-key svn-status-mode-map (kbd ".") 'svn-status-goto-root-or-return)
 
863
  (define-key svn-status-mode-map (kbd "I") 'svn-status-parse-info)
 
864
  (define-key svn-status-mode-map (kbd "V") 'svn-status-svnversion)
 
865
  (define-key svn-status-mode-map (kbd "?") 'svn-status-toggle-hide-unknown)
 
866
  (define-key svn-status-mode-map (kbd "_") 'svn-status-toggle-hide-unmodified)
 
867
  (define-key svn-status-mode-map (kbd "a") 'svn-status-add-file)
 
868
  (define-key svn-status-mode-map (kbd "A") 'svn-status-add-file-recursively)
 
869
  (define-key svn-status-mode-map (kbd "+") 'svn-status-make-directory)
 
870
  (define-key svn-status-mode-map (kbd "R") 'svn-status-mv)
 
871
  (define-key svn-status-mode-map (kbd "D") 'svn-status-rm)
 
872
  (define-key svn-status-mode-map (kbd "c") 'svn-status-commit-file)
 
873
  (define-key svn-status-mode-map (kbd "M-c") 'svn-status-cleanup)
 
874
  (define-key svn-status-mode-map (kbd "U") 'svn-status-update-cmd)
 
875
  (define-key svn-status-mode-map (kbd "r") 'svn-status-revert)
 
876
  (define-key svn-status-mode-map (kbd "l") 'svn-status-show-svn-log)
 
877
  (define-key svn-status-mode-map (kbd "i") 'svn-status-info)
 
878
  (define-key svn-status-mode-map (kbd "b") 'svn-status-blame)
 
879
  (define-key svn-status-mode-map (kbd "=") 'svn-status-show-svn-diff)
 
880
  ;; [(control ?=)] is unreachable on TTY, but you can use "*u" instead.
 
881
  ;; (Is the "u" mnemonic for something?)
 
882
  (define-key svn-status-mode-map (kbd "C-=") 'svn-status-show-svn-diff-for-marked-files)
 
883
  (define-key svn-status-mode-map (kbd "~") 'svn-status-get-specific-revision)
 
884
  (define-key svn-status-mode-map (kbd "E") 'svn-status-ediff-with-revision)
 
885
 
 
886
  (define-key svn-status-mode-map (kbd "C-n") 'svn-status-next-line)
 
887
  (define-key svn-status-mode-map (kbd "C-p") 'svn-status-previous-line)
 
888
  (define-key svn-status-mode-map (kbd "<down>") 'svn-status-next-line)
 
889
  (define-key svn-status-mode-map (kbd "<up>") 'svn-status-previous-line)
 
890
  (define-key svn-status-mode-map [down-mouse-3] 'svn-status-popup-menu)
 
891
  (setq svn-status-mode-mark-map (make-sparse-keymap))
 
892
  (define-key svn-status-mode-map (kbd "*") svn-status-mode-mark-map)
 
893
  (define-key svn-status-mode-mark-map (kbd "!") 'svn-status-unset-all-usermarks)
 
894
  (define-key svn-status-mode-mark-map (kbd "?") 'svn-status-mark-unknown)
 
895
  (define-key svn-status-mode-mark-map (kbd "A") 'svn-status-mark-added)
 
896
  (define-key svn-status-mode-mark-map (kbd "M") 'svn-status-mark-modified)
 
897
  (define-key svn-status-mode-mark-map (kbd "D") 'svn-status-mark-deleted)
 
898
  (define-key svn-status-mode-mark-map (kbd "*") 'svn-status-mark-changed)
 
899
  (define-key svn-status-mode-mark-map (kbd "u") 'svn-status-show-svn-diff-for-marked-files))
 
900
(when (not svn-status-mode-property-map)
 
901
  (setq svn-status-mode-property-map (make-sparse-keymap))
 
902
  (define-key svn-status-mode-property-map (kbd "l") 'svn-status-property-list)
 
903
  (define-key svn-status-mode-property-map (kbd "s") 'svn-status-property-set)
 
904
  (define-key svn-status-mode-property-map (kbd "d") 'svn-status-property-delete)
 
905
  (define-key svn-status-mode-property-map (kbd "e") 'svn-status-property-edit-one-entry)
 
906
  (define-key svn-status-mode-property-map (kbd "i") 'svn-status-property-ignore-file)
 
907
  (define-key svn-status-mode-property-map (kbd "I") 'svn-status-property-ignore-file-extension)
 
908
  ;; XEmacs 21.4.15 on TTY (vt420) converts `C-i' to `TAB',
 
909
  ;; which [(control ?i)] won't match.  Handle it separately.
 
910
  ;; On GNU Emacs, the following two forms bind the same key,
 
911
  ;; reducing clutter in `where-is'.
 
912
  (define-key svn-status-mode-property-map [(control ?i)] 'svn-status-property-edit-svn-ignore)
 
913
  (define-key svn-status-mode-property-map (kbd "TAB") 'svn-status-property-edit-svn-ignore)
 
914
  (define-key svn-status-mode-property-map (kbd "k") 'svn-status-property-set-keyword-list)
 
915
  (define-key svn-status-mode-property-map (kbd "y") 'svn-status-property-set-eol-style)
 
916
  (define-key svn-status-mode-property-map (kbd "x") 'svn-status-property-set-executable)
 
917
  ;; TODO: Why is `svn-status-select-line' in `svn-status-mode-property-map'?
 
918
  (define-key svn-status-mode-property-map (kbd "RET") 'svn-status-select-line)
 
919
  (define-key svn-status-mode-map (kbd "P") svn-status-mode-property-map))
 
920
(when (not svn-status-mode-extension-map)
 
921
  (setq svn-status-mode-extension-map (make-sparse-keymap))
 
922
  (define-key svn-status-mode-extension-map (kbd "v") 'svn-status-resolved)
 
923
  (define-key svn-status-mode-extension-map (kbd "X") 'svn-status-resolve-conflicts)
 
924
  (define-key svn-status-mode-map (kbd "X") svn-status-mode-extension-map))
 
925
(when (not svn-status-mode-options-map)
 
926
  (setq svn-status-mode-options-map (make-sparse-keymap))
 
927
  (define-key svn-status-mode-options-map (kbd "s") 'svn-status-save-state)
 
928
  (define-key svn-status-mode-options-map (kbd "l") 'svn-status-load-state)
 
929
  (define-key svn-status-mode-options-map (kbd "x") 'svn-status-toggle-sort-status-buffer)
 
930
  (define-key svn-status-mode-options-map (kbd "t") 'svn-status-set-trac-project-root)
 
931
  (define-key svn-status-mode-options-map (kbd "n") 'svn-status-set-module-name)
 
932
  (define-key svn-status-mode-map (kbd "O") svn-status-mode-options-map))
 
933
(when (not svn-status-mode-trac-map)
 
934
  (setq svn-status-mode-trac-map (make-sparse-keymap))
 
935
  (define-key svn-status-mode-trac-map (kbd "t") 'svn-trac-browse-timeline)
 
936
  (define-key svn-status-mode-map (kbd "T") svn-status-mode-trac-map))
 
937
 
 
938
(easy-menu-define svn-status-mode-menu svn-status-mode-map
 
939
  "'svn-status-mode' menu"
 
940
  '("SVN"
 
941
    ["svn status" svn-status-update t]
 
942
    ["svn update" svn-status-update-cmd t]
 
943
    ["svn commit" svn-status-commit-file t]
 
944
    ["svn log" svn-status-show-svn-log t]
 
945
    ["svn info" svn-status-info t]
 
946
    ["svn blame" svn-status-blame t]
 
947
    ("Diff"
 
948
     ["svn diff current file" svn-status-show-svn-diff t]
 
949
     ["svn diff marked files" svn-status-show-svn-diff-for-marked-files t]
 
950
     ["svn ediff current file" svn-status-ediff-with-revision t]
 
951
     ["svn resolve conflicts" svn-status-resolve-conflicts]
 
952
     )
 
953
    ["svn cat ..." svn-status-get-specific-revision t]
 
954
    ["svn add" svn-status-add-file t]
 
955
    ["svn add recursively" svn-status-add-file-recursively t]
 
956
    ["svn mkdir..." svn-status-make-directory t]
 
957
    ["svn mv..." svn-status-mv t]
 
958
    ["svn rm..." svn-status-rm t]
 
959
    ["Up Directory" svn-status-examine-parent t]
 
960
    ["Elide Directory" svn-status-toggle-elide t]
 
961
    ["svn revert" svn-status-revert t]
 
962
    ["svn resolved" svn-status-resolved t]
 
963
    ["svn cleanup" svn-status-cleanup t]
 
964
    ["Show Process Buffer" svn-status-show-process-buffer t]
 
965
    ("Property"
 
966
     ["svn proplist" svn-status-property-list t]
 
967
     ["Set Multiple Properties..." svn-status-property-set t]
 
968
     ["Edit One Property..." svn-status-property-edit-one-entry t]
 
969
     ["svn propdel..." svn-status-property-delete t]
 
970
     "---"
 
971
     ["svn:ignore File..." svn-status-property-ignore-file t]
 
972
     ["svn:ignore File Extension..." svn-status-property-ignore-file-extension t]
 
973
     ["Edit svn:ignore Property" svn-status-property-edit-svn-ignore t]
 
974
     "---"
 
975
     ["Edit svn:keywords List" svn-status-property-set-keyword-list t]
 
976
     ["Select svn:eol-style" svn-status-property-set-eol-style t]
 
977
     ["Set svn:executable" svn-status-property-set-executable t]
 
978
     )
 
979
    ("Options"
 
980
     ["Save Options" svn-status-save-state t]
 
981
     ["Load Options" svn-status-load-state t]
 
982
     ["Set Trac project root" svn-status-set-trac-project-root t]
 
983
     ["Set Short module name" svn-status-set-module-name t]
 
984
     ["Toggle sorting of *svn-status* buffer" svn-status-toggle-sort-status-buffer
 
985
      :style toggle :selected svn-status-sort-status-buffer]
 
986
     )
 
987
    ("Trac"
 
988
     ["Browse timeline" svn-trac-browse-timeline t]
 
989
     ["Set Trac project root" svn-status-set-trac-project-root t]
 
990
     )
 
991
    "---"
 
992
    ["Edit Next SVN Cmd Line" svn-status-toggle-edit-cmd-flag t]
 
993
    ["Work Directory History..." svn-status-use-history t]
 
994
    ("Mark / Unmark"
 
995
     ["Mark" svn-status-set-user-mark t]
 
996
     ["Unmark" svn-status-unset-user-mark t]
 
997
     ["Unmark all" svn-status-unset-all-usermarks t]
 
998
     "---"
 
999
     ["Mark/Unmark unknown" svn-status-mark-unknown t]
 
1000
     ["Mark/Unmark added" svn-status-mark-added t]
 
1001
     ["Mark/Unmark modified" svn-status-mark-modified t]
 
1002
     ["Mark/Unmark deleted" svn-status-mark-deleted t]
 
1003
     ["Mark/Unmark modified/added/deleted" svn-status-mark-changed t]
 
1004
     )
 
1005
    ["Hide Unknown" svn-status-toggle-hide-unknown
 
1006
     :style toggle :selected svn-status-hide-unknown]
 
1007
    ["Hide Unmodified" svn-status-toggle-hide-unmodified
 
1008
     :style toggle :selected svn-status-hide-unmodified]
 
1009
    ))
 
1010
 
 
1011
 
 
1012
(defun svn-status-popup-menu (event)
 
1013
  (interactive "e")
 
1014
  (mouse-set-point event)
 
1015
  (let* ((line-info (svn-status-get-line-information))
 
1016
         (name (svn-status-line-info->filename line-info)))
 
1017
    (when line-info
 
1018
      (easy-menu-define svn-status-actual-popup-menu nil nil
 
1019
        (list name
 
1020
               ["svn diff" svn-status-show-svn-diff t]
 
1021
               ["svn commit" svn-status-commit-file t]
 
1022
               ["svn log" svn-status-show-svn-log t]
 
1023
               ["svn info" svn-status-info t]
 
1024
               ["svn blame" svn-status-blame t]))
 
1025
      (svn-status-face-set-temporary-during-popup
 
1026
       'svn-status-marked-popup-face (line-beginning-position) (line-end-position)
 
1027
       svn-status-actual-popup-menu))))
 
1028
 
 
1029
(defun svn-status-face-set-temporary-during-popup (face begin end menu &optional prefix)
 
1030
  "Put FACE on BEGIN and END in the buffer during Popup MENU.
 
1031
PREFIX is passed to `popup-menu'."
 
1032
  (let (o)
 
1033
    (unwind-protect
 
1034
        (progn
 
1035
          (setq o (make-overlay begin end))
 
1036
          (overlay-put o 'face face)
 
1037
          (sit-for 0)
 
1038
          (popup-menu menu prefix))
 
1039
      (delete-overlay o))))
 
1040
 
 
1041
(defun svn-status-mode ()
 
1042
  "Major mode used by psvn.el to display the output of \"svn status\".
 
1043
 
 
1044
The Output has the following format:
 
1045
  FPH BASE CMTD Author   em File
 
1046
F = Filemark
 
1047
P = Property mark
 
1048
H = History mark
 
1049
BASE = local base revision
 
1050
CMTD = last committed revision
 
1051
Author = author of change
 
1052
em = \"**\" or \"(Update Available)\" [see `svn-status-short-mod-flag-p']
 
1053
     if file can be updated
 
1054
File = path/filename
 
1055
 
 
1056
The following keys are defined:
 
1057
\\{svn-status-mode-map}"
 
1058
  (interactive)
 
1059
  (kill-all-local-variables)
 
1060
 
 
1061
  (use-local-map svn-status-mode-map)
 
1062
  (easy-menu-add svn-status-mode-menu)
 
1063
 
 
1064
  (setq major-mode 'svn-status-mode)
 
1065
  (setq mode-name "svn-status")
 
1066
  (setq mode-line-process 'svn-status-mode-line-process)
 
1067
  (let ((view-read-only nil))
 
1068
    (toggle-read-only 1)))
 
1069
 
 
1070
(defun svn-status-update-mode-line ()
 
1071
  (setq svn-status-mode-line-process
 
1072
        (concat svn-status-mode-line-process-edit-flag svn-status-mode-line-process-status))
 
1073
  (force-mode-line-update))
 
1074
 
 
1075
(defun svn-status-bury-buffer (arg)
 
1076
  "Bury the buffers used by psvn.el
 
1077
Currently this is:
 
1078
  *svn-status*
 
1079
  *svn-log-edit*
 
1080
  *svn-property-edit*
 
1081
  *svn-log*
 
1082
  *svn-process*
 
1083
When called with a prefix argument, ARG, switch back to the window configuration that was
 
1084
in use before `svn-status' was called."
 
1085
  (interactive "P")
 
1086
  (cond (arg
 
1087
         (when svn-status-initial-window-configuration
 
1088
           (set-window-configuration svn-status-initial-window-configuration)))
 
1089
        (t
 
1090
         (let ((bl '("*svn-log-edit*" "*svn-property-edit*" "*svn-log*" "*svn-process*")))
 
1091
           (while bl
 
1092
             (when (get-buffer (car bl))
 
1093
               (bury-buffer (car bl)))
 
1094
             (setq bl (cdr bl)))
 
1095
           (when (string= (buffer-name) "*svn-status*")
 
1096
             (bury-buffer))))))
 
1097
 
 
1098
(defun svn-status-find-files ()
 
1099
  "Open selected file(s) for editing.
 
1100
See `svn-status-marked-files' for what counts as selected."
 
1101
  (interactive)
 
1102
  (let ((fnames (mapcar 'svn-status-line-info->full-path (svn-status-marked-files))))
 
1103
    (mapc 'find-file fnames)))
 
1104
 
 
1105
 
 
1106
(defun svn-status-find-file-other-window ()
 
1107
  "Open the file in the other window for editing."
 
1108
  (interactive)
 
1109
  (svn-status-ensure-cursor-on-file)
 
1110
  (find-file-other-window (svn-status-line-info->filename
 
1111
                           (svn-status-get-line-information))))
 
1112
 
 
1113
(defun svn-status-view-file-other-window ()
 
1114
  "Open the file in the other window for viewing."
 
1115
  (interactive)
 
1116
  (svn-status-ensure-cursor-on-file)
 
1117
  (view-file-other-window (svn-status-line-info->filename
 
1118
                           (svn-status-get-line-information))))
 
1119
 
 
1120
(defun svn-status-find-file-or-examine-directory ()
 
1121
  "If point is on a directory, run `svn-status' on that directory.
 
1122
Otherwise run `find-file'."
 
1123
  (interactive)
 
1124
  (svn-status-ensure-cursor-on-file)
 
1125
  (let ((line-info (svn-status-get-line-information)))
 
1126
    (if (svn-status-line-info->directory-p line-info)
 
1127
        (svn-status (svn-status-line-info->full-path line-info))
 
1128
      (find-file (svn-status-line-info->filename line-info)))))
 
1129
 
 
1130
(defun svn-status-examine-parent ()
 
1131
  "Run `svn-status' on the parent of the current directory."
 
1132
  (interactive)
 
1133
  (svn-status (expand-file-name "../")))
 
1134
 
 
1135
(defun svn-status-mouse-find-file-or-examine-directory (event)
 
1136
  "Move point to where EVENT occurred, and do `svn-status-find-file-or-examine-directory'
 
1137
EVENT could be \"mouse clicked\" or similar."
 
1138
  (interactive "e")
 
1139
  (mouse-set-point event)
 
1140
  (svn-status-find-file-or-examine-directory))
 
1141
 
 
1142
(defun svn-status-line-info->ui-status (line-info) (nth 0 line-info))
 
1143
 
 
1144
(defun svn-status-line-info->has-usermark (line-info) (nth 0 (nth 0 line-info)))
 
1145
(defun svn-status-line-info->user-elide (line-info) (nth 1 (nth 0 line-info)))
 
1146
 
 
1147
(defun svn-status-line-info->filemark (line-info) (nth 1 line-info))
 
1148
(defun svn-status-line-info->propmark (line-info) (nth 2 line-info))
 
1149
(defun svn-status-line-info->filename (line-info) (nth 3 line-info))
 
1150
(defun svn-status-line-info->filename-nondirectory (line-info)
 
1151
  (file-name-nondirectory (svn-status-line-info->filename line-info)))
 
1152
(defun svn-status-line-info->localrev (line-info)
 
1153
  (if (>= (nth 4 line-info) 0)
 
1154
      (nth 4 line-info)
 
1155
    nil))
 
1156
(defun svn-status-line-info->lastchangerev (line-info)
 
1157
  "Return the last revision in which LINE-INFO was modified."
 
1158
  (let ((l (nth 5 line-info)))
 
1159
    (if (and l (>= l 0))
 
1160
        l
 
1161
      nil)))
 
1162
(defun svn-status-line-info->author (line-info) (nth 6 line-info))
 
1163
(defun svn-status-line-info->update-available (line-info)
 
1164
  "Return whether LINE-INFO is out of date.
 
1165
In other words, whether there is a newer version available in the
 
1166
repository than the working copy."
 
1167
  (nth 7 line-info))
 
1168
(defun svn-status-line-info->locked (line-info)
 
1169
  "Return whether LINE-INFO represents a locked file.
 
1170
This is column three of the `svn status' output.
 
1171
The result will be nil or \"L\".
 
1172
\(A file becomes locked when an operation is interupted; run \\[svn-status-cleanup]'
 
1173
to unlock it.\)"
 
1174
  (nth 8 line-info))
 
1175
(defun svn-status-line-info->historymark (line-info)
 
1176
  "Mark from column four of output from `svn status'.
 
1177
This will be nil unless the file is scheduled for addition with
 
1178
history, when it will be \"+\"."
 
1179
  (nth 9 line-info))
 
1180
(defun svn-status-line-info->switched (line-info)
 
1181
  "Return whether LINE-INFO is switched relative to its parent.
 
1182
This is column five of the output from `svn status'.
 
1183
The result will be nil or \"S\"."
 
1184
  (nth 10 line-info))
 
1185
 
 
1186
(defun svn-status-line-info->is-visiblep (line-info)
 
1187
  (not (or (svn-status-line-info->hide-because-unknown line-info)
 
1188
           (svn-status-line-info->hide-because-unmodified line-info)
 
1189
           (svn-status-line-info->hide-because-user-elide line-info))))
 
1190
 
 
1191
(defun svn-status-line-info->hide-because-unknown (line-info)
 
1192
  (and svn-status-hide-unknown
 
1193
       (eq (svn-status-line-info->filemark line-info) ??)))
 
1194
 
 
1195
(defun svn-status-line-info->hide-because-unmodified (line-info)
 
1196
  ;;(message " %S %S %S %S - %s" svn-status-hide-unmodified (svn-status-line-info->propmark line-info) ?_
 
1197
  ;;         (svn-status-line-info->filemark line-info) (svn-status-line-info->filename line-info))
 
1198
  (and svn-status-hide-unmodified
 
1199
       (and (or (eq (svn-status-line-info->filemark line-info) ?_)
 
1200
                (eq (svn-status-line-info->filemark line-info) ? ))
 
1201
            (or (eq (svn-status-line-info->propmark line-info) ?_)
 
1202
                (eq (svn-status-line-info->propmark line-info) ? )
 
1203
                (eq (svn-status-line-info->propmark line-info) nil)))))
 
1204
 
 
1205
(defun svn-status-line-info->hide-because-user-elide (line-info)
 
1206
  (eq (svn-status-line-info->user-elide line-info) t))
 
1207
 
 
1208
(defun svn-status-line-info->show-user-elide-continuation (line-info)
 
1209
  (eq (svn-status-line-info->user-elide line-info) 'directory))
 
1210
 
 
1211
;; modify the line-info
 
1212
(defun svn-status-line-info->set-filemark (line-info value)
 
1213
  (setcar (nthcdr 1 line-info) value))
 
1214
 
 
1215
(defun svn-status-line-info->set-propmark (line-info value)
 
1216
  (setcar (nthcdr 2 line-info) value))
 
1217
 
 
1218
(defun svn-status-line-info->set-localrev (line-info value)
 
1219
  (setcar (nthcdr 4 line-info) value))
 
1220
 
 
1221
(defun svn-status-line-info->set-lastchangerev (line-info value)
 
1222
  (setcar (nthcdr 5 line-info) value))
 
1223
 
 
1224
(defun svn-status-copy-filename-as-kill (arg)
 
1225
  "Copy the actual file name to the kill-ring.
 
1226
When called with the prefix argument 0, use the full path name."
 
1227
  (interactive "P")
 
1228
  (let ((str (if (eq arg 0)
 
1229
                 (svn-status-line-info->full-path (svn-status-get-line-information))
 
1230
               (svn-status-line-info->filename (svn-status-get-line-information)))))
 
1231
    (kill-new str)
 
1232
    (message "Copied %s" str)))
 
1233
 
 
1234
(defun svn-status-toggle-elide ()
 
1235
  (interactive)
 
1236
  (let ((st-info svn-status-info)
 
1237
        (fname)
 
1238
        (test (svn-status-line-info->filename (svn-status-get-line-information)))
 
1239
        (len-test)
 
1240
        (len-fname)
 
1241
        (new-elide-mark t)
 
1242
        (elide-mark))
 
1243
    (if (member test svn-status-elided-list)
 
1244
        (setq svn-status-elided-list (delete test svn-status-elided-list))
 
1245
      (add-to-list 'svn-status-elided-list test))
 
1246
    (when (string= test ".")
 
1247
      (setq test ""))
 
1248
    (setq len-test (length test))
 
1249
    (while st-info
 
1250
      (setq fname (svn-status-line-info->filename (car st-info)))
 
1251
      (setq len-fname (length fname))
 
1252
      (when (and (>= len-fname len-test)
 
1253
                 (string= (substring fname 0 len-test) test))
 
1254
        (setq elide-mark new-elide-mark)
 
1255
        (when (or (string= fname ".")
 
1256
                  (and (= len-fname len-test) (svn-status-line-info->directory-p (car st-info))))
 
1257
          (message "Elided directory %s and all its files." fname)
 
1258
          (setq new-elide-mark (not (svn-status-line-info->user-elide (car st-info))))
 
1259
          (setq elide-mark (if new-elide-mark 'directory nil)))
 
1260
        ;;(message "elide-mark: %S member: %S" elide-mark (member fname svn-status-elided-list))
 
1261
        (when (and (member fname svn-status-elided-list) (not elide-mark))
 
1262
          (setq svn-status-elided-list (delete fname svn-status-elided-list)))
 
1263
        (setcar (nthcdr 1 (svn-status-line-info->ui-status (car st-info))) elide-mark))
 
1264
      (setq st-info (cdr st-info))))
 
1265
  ;;(message "svn-status-elided-list: %S" svn-status-elided-list)
 
1266
  (svn-status-update-buffer))
 
1267
 
 
1268
(defun svn-status-apply-elide-list ()
 
1269
  "Elide files/directories according to `svn-status-elided-list'."
 
1270
  (interactive)
 
1271
  (let ((st-info svn-status-info)
 
1272
        (fname)
 
1273
        (len-fname)
 
1274
        (test)
 
1275
        (len-test)
 
1276
        (elide-mark))
 
1277
    (while st-info
 
1278
      (setq fname (svn-status-line-info->filename (car st-info)))
 
1279
      (setq len-fname (length fname))
 
1280
      (setq elided-list svn-status-elided-list)
 
1281
      (setq elide-mark nil)
 
1282
      (while elided-list
 
1283
        (setq test (car elided-list))
 
1284
        (when (string= test ".")
 
1285
          (setq test ""))
 
1286
        (setq len-test (length test))
 
1287
        (when (and (>= len-fname len-test)
 
1288
                   (string= (substring fname 0 len-test) test))
 
1289
          (setq elide-mark t)
 
1290
          (when (or (string= fname ".")
 
1291
                    (and (= len-fname len-test) (svn-status-line-info->directory-p (car st-info))))
 
1292
            (setq elide-mark 'directory)))
 
1293
        (setq elided-list (cdr elided-list)))
 
1294
      ;;(message "fname: %s elide-mark: %S" fname elide-mark)
 
1295
      (setcar (nthcdr 1 (svn-status-line-info->ui-status (car st-info))) elide-mark)
 
1296
      (setq st-info (cdr st-info))))
 
1297
  (svn-status-update-buffer))
 
1298
 
 
1299
(defun svn-status-update-with-command-list (cmd-list)
 
1300
  (save-excursion
 
1301
    (set-buffer "*svn-status*")
 
1302
    (let ((st-info)
 
1303
          (found)
 
1304
          (action))
 
1305
      (setq cmd-list (sort cmd-list '(lambda (item1 item2) (string-lessp (car item1) (car item2)))))
 
1306
      (while cmd-list
 
1307
        (unless st-info (setq st-info svn-status-info))
 
1308
        ;;(message "%S" (caar cmd-list))
 
1309
        (setq found nil)
 
1310
        (while (and (not found) st-info)
 
1311
          (setq found (string= (caar cmd-list) (svn-status-line-info->filename (car st-info))))
 
1312
          ;;(message "found: %S" found)
 
1313
          (unless found (setq st-info (cdr st-info))))
 
1314
        (unless found
 
1315
          (message "continue to search for %s" (caar cmd-list))
 
1316
          (setq st-info svn-status-info)
 
1317
          (while (and (not found) st-info)
 
1318
            (setq found (string= (caar cmd-list) (svn-status-line-info->filename (car st-info))))
 
1319
            (unless found (setq st-info (cdr st-info)))))
 
1320
        (if found
 
1321
            ;;update the info line
 
1322
            (progn
 
1323
              (setq action (cadar cmd-list))
 
1324
              ;;(message "found %s, action: %S" (caar cmd-list) action)
 
1325
              (svn-status-annotate-status-buffer-entry action (car st-info)))
 
1326
          (message "did not find %s" (caar cmd-list)))
 
1327
        (setq cmd-list (cdr cmd-list))))))
 
1328
 
 
1329
(defun svn-status-annotate-status-buffer-entry (action line-info)
 
1330
  (let ((tag-string))
 
1331
    (svn-status-goto-file-name (svn-status-line-info->filename line-info))
 
1332
    (when (and (member action '(committed added))
 
1333
               svn-status-commit-rev-number)
 
1334
      (svn-status-line-info->set-localrev line-info svn-status-commit-rev-number)
 
1335
      (svn-status-line-info->set-lastchangerev line-info svn-status-commit-rev-number))
 
1336
    (cond ((equal action 'committed)
 
1337
           (setq tag-string " <committed>"))
 
1338
          ((equal action 'added)
 
1339
           (setq tag-string " <added>"))
 
1340
          ((equal action 'deleted)
 
1341
           (setq tag-string " <deleted>"))
 
1342
          (t
 
1343
           (message "Unknown action '%s for %s" action (svn-status-line-info->filename line-info))))
 
1344
    (when tag-string
 
1345
      (svn-status-line-info->set-filemark line-info ? )
 
1346
      (svn-status-line-info->set-propmark line-info ? )
 
1347
      (let ((buffer-read-only nil))
 
1348
        (delete-region (point-at-bol) (point-at-eol))
 
1349
        (svn-insert-line-in-status-buffer line-info)
 
1350
        (backward-char 1)
 
1351
        (insert tag-string)
 
1352
        (delete-char 1)))))
 
1353
 
 
1354
 
 
1355
 
 
1356
;; (svn-status-update-with-command-list '(("++ideas" committed) ("a.txt" committed) ("alf")))
 
1357
;; (svn-status-update-with-command-list (svn-status-parse-commit-output))
 
1358
 
 
1359
 
 
1360
(defun svn-status-parse-commit-output ()
 
1361
  "Parse the output of svn commit.
 
1362
Return a list that is suitable for `svn-status-update-with-command-list'"
 
1363
  (save-excursion
 
1364
    (set-buffer "*svn-process*")
 
1365
    (let ((action)
 
1366
          (name)
 
1367
          (skip)
 
1368
          (result))
 
1369
      (goto-char (point-min))
 
1370
      (setq svn-status-commit-rev-number nil)
 
1371
      (setq skip nil) ; set to t whenever we find a line not about a committed file
 
1372
      (while (< (point) (point-max))
 
1373
        (cond ((= (point-at-eol) (point-at-bol)) ;skip blank lines
 
1374
               (setq skip t))
 
1375
              ((looking-at "Sending")
 
1376
               (setq action 'committed))
 
1377
              ((looking-at "Adding")
 
1378
               (setq action 'added))
 
1379
              ((looking-at "Deleting")
 
1380
               (setq action 'deleted))
 
1381
              ((looking-at "Transmitting file data")
 
1382
               (setq skip t))
 
1383
              ((looking-at "Committed revision \\([0-9]+\\)")
 
1384
               (setq svn-status-commit-rev-number
 
1385
                     (string-to-number (match-string-no-properties 1)))
 
1386
               (setq skip t))
 
1387
              (t ;; this should never be needed(?)
 
1388
               (setq action 'unknown)))
 
1389
        (unless skip                                ;found an interesting line
 
1390
          (forward-char 15)
 
1391
          (when svn-status-operated-on-dot
 
1392
            ;; when the commit used . as argument, delete the trailing directory
 
1393
            ;; from the svn output
 
1394
            (search-forward "/" nil t))
 
1395
          (setq name (buffer-substring-no-properties (point) (point-at-eol)))
 
1396
          (setq result (cons (list name action)
 
1397
                             result))
 
1398
          (setq skip nil))
 
1399
        (forward-line 1))
 
1400
      result)))
 
1401
;;(svn-status-parse-commit-output)
 
1402
;;(svn-status-annotate-status-buffer-entry)
 
1403
 
 
1404
(defun svn-status-line-info->directory-p (line-info)
 
1405
  "Return t if LINE-INFO refers to a directory, nil otherwise.
 
1406
Symbolic links to directories count as directories (see `file-directory-p')."
 
1407
  (file-directory-p (svn-status-line-info->filename line-info)))
 
1408
 
 
1409
(defun svn-status-line-info->full-path (line-info)
 
1410
  "Return the full path of the file represented by LINE-INFO."
 
1411
  (expand-file-name
 
1412
   (svn-status-line-info->filename line-info)))
 
1413
 
 
1414
;;Not convinced that this is the fastest way, but...
 
1415
(defun svn-status-count-/ (string)
 
1416
  "Return number of \"/\"'s in STRING."
 
1417
  (let ((n 0)
 
1418
        (last 0))
 
1419
    (while (setq last (string-match "/" string (1+ last)))
 
1420
      (setq n (1+ n)))
 
1421
    n))
 
1422
 
 
1423
(defun svn-insert-line-in-status-buffer (line-info)
 
1424
  "Format LINE-INFO and insert the result in the current buffer."
 
1425
  (let ((usermark (if (svn-status-line-info->has-usermark line-info) "*" " "))
 
1426
        (update-available (if (svn-status-line-info->update-available line-info)
 
1427
                              (svn-add-face (if svn-status-short-mod-flag-p
 
1428
                                                "** "
 
1429
                                              " (Update Available)")
 
1430
                                            'svn-status-update-available-face)
 
1431
                            (if svn-status-short-mod-flag-p "   " "")))
 
1432
        (filename  ;; <indentation>file or /path/to/file
 
1433
                     (concat
 
1434
                      (if svn-status-hide-unmodified
 
1435
                          (svn-add-face
 
1436
                           (file-name-as-directory
 
1437
                            (svn-status-line-info->directory-containing-line-info line-info nil))
 
1438
                           'svn-status-directory-face)
 
1439
                        ;; showing all files, so add indentation
 
1440
                        (make-string (* 2 (svn-status-count-/
 
1441
                                           (svn-status-line-info->filename line-info)))
 
1442
                                     32))
 
1443
                      (svn-status-choose-face-to-add
 
1444
                       (svn-status-line-info->directory-p line-info)
 
1445
                       (svn-status-line-info->filename-nondirectory line-info)
 
1446
                       'svn-status-directory-face
 
1447
                       'svn-status-filename-face)))
 
1448
        (elide-hint (if (svn-status-line-info->show-user-elide-continuation line-info) " ..." "")))
 
1449
    (insert (svn-status-maybe-add-face
 
1450
             (svn-status-line-info->has-usermark line-info)
 
1451
             (concat usermark
 
1452
                     (format svn-status-line-format
 
1453
                             (svn-status-line-info->filemark line-info)
 
1454
                             (or (svn-status-line-info->propmark line-info) ? )
 
1455
                             (or (svn-status-line-info->historymark line-info) ? )
 
1456
                             (or (svn-status-line-info->localrev line-info) "")
 
1457
                             (or (svn-status-line-info->lastchangerev line-info) "")
 
1458
                             (svn-status-line-info->author line-info))
 
1459
                     (if svn-status-short-mod-flag-p update-available filename)
 
1460
                     (if svn-status-short-mod-flag-p filename update-available)
 
1461
                     (svn-status-maybe-add-string (svn-status-line-info->locked line-info)
 
1462
                                                  " [ LOCKED ]" 'svn-status-locked-face)
 
1463
                     (svn-status-maybe-add-string (svn-status-line-info->switched line-info)
 
1464
                                                  " (switched)" 'svn-status-switched-face)
 
1465
                     elide-hint)
 
1466
             'svn-status-marked-face)
 
1467
            "\n")))
 
1468
 
 
1469
(defun svn-status-update-buffer ()
 
1470
  "Update the *svn-status* buffer, using `svn-status-info'."
 
1471
  (interactive)
 
1472
  ;(message (format "buffer-name: %s" (buffer-name)))
 
1473
  (unless (string= (buffer-name) "*svn-status*")
 
1474
    (set-buffer "*svn-status*"))
 
1475
  (svn-status-mode)
 
1476
  (let ((st-info svn-status-info)
 
1477
        (buffer-read-only nil)
 
1478
        (start-pos)
 
1479
        (overlay)
 
1480
        (unmodified-count 0)            ;how many unmodified files are hidden
 
1481
        (unknown-count 0)               ;how many unknown files are hidden
 
1482
        (marked-count 0)                ;how many files are elided
 
1483
        (user-elide-count 0)
 
1484
        (fname (svn-status-line-info->filename (svn-status-get-line-information)))
 
1485
        (fname-pos (point))
 
1486
        (header-line-string)
 
1487
        (column (current-column)))
 
1488
    (delete-region (point-min) (point-max))
 
1489
    (insert "\n")
 
1490
    ;; Insert all files and directories
 
1491
    (while st-info
 
1492
      (setq start-pos (point))
 
1493
      (cond ((svn-status-line-info->has-usermark (car st-info))
 
1494
             ;; Show a marked file always
 
1495
             (svn-insert-line-in-status-buffer (car st-info)))
 
1496
            ((svn-status-line-info->update-available (car st-info))
 
1497
             (svn-insert-line-in-status-buffer (car st-info)))
 
1498
            ((svn-status-line-info->hide-because-user-elide (car st-info))
 
1499
             (setq user-elide-count (1+ user-elide-count)))
 
1500
            ((svn-status-line-info->hide-because-unknown (car st-info))
 
1501
             (setq unknown-count (1+ unknown-count)))
 
1502
            ((svn-status-line-info->hide-because-unmodified (car st-info))
 
1503
             (setq unmodified-count (1+ unmodified-count)))
 
1504
            (t
 
1505
             (svn-insert-line-in-status-buffer (car st-info))))
 
1506
      (when (svn-status-line-info->has-usermark (car st-info))
 
1507
        (setq marked-count (+ marked-count 1)))
 
1508
      (setq overlay (make-overlay start-pos (point)))
 
1509
      (overlay-put overlay 'svn-info (car st-info))
 
1510
      (setq st-info (cdr st-info)))
 
1511
    ;; Insert status information at the buffer beginning
 
1512
    (goto-char (point-min))
 
1513
    (insert (format "svn status for directory %s%s\n"
 
1514
                    default-directory
 
1515
                    (if svn-status-head-revision (format " (status against revision: %s)"
 
1516
                                                         svn-status-head-revision)
 
1517
                      "")))
 
1518
    (when svn-status-module-name
 
1519
      (insert (format "Project name: %s\n" svn-status-module-name)))
 
1520
    (when svn-status-base-info
 
1521
      (insert (concat "Repository: " (svn-status-base-info->url) "\n")))
 
1522
    (when svn-status-hide-unknown
 
1523
      (insert
 
1524
       (format "%d Unknown file(s) are hidden - press `?' to toggle hiding\n"
 
1525
               unknown-count)))
 
1526
    (when svn-status-hide-unmodified
 
1527
      (insert
 
1528
       (format "%d Unmodified file(s) are hidden - press `_' to toggle hiding\n"
 
1529
               unmodified-count)))
 
1530
    (when (> user-elide-count 0)
 
1531
      (insert (format "%d file(s) elided\n" user-elide-count)))
 
1532
    (insert (format "%d file(s) marked\n" marked-count))
 
1533
    (setq header-line-string (concat (format svn-status-line-format
 
1534
                                             70 80 72 "BASE" "CMTD" "Author")
 
1535
                                     (if svn-status-short-mod-flag-p "em " "")
 
1536
                                     "File"))
 
1537
    (cond ((eq svn-status-use-header-line t)
 
1538
           (setq header-line-format (concat "    " header-line-string)))
 
1539
          ((eq svn-status-use-header-line 'inline)
 
1540
           (insert "\n " header-line-string "\n")))
 
1541
    (setq svn-start-of-file-list-line-number (+ (count-lines (point-min) (point)) 1))
 
1542
    (if fname
 
1543
        (progn
 
1544
          (goto-char fname-pos)
 
1545
          (svn-status-goto-file-name fname)
 
1546
          (goto-char (+ column (point-at-bol))))
 
1547
      (goto-char (+ (next-overlay-change (point-min)) svn-status-default-column)))))
 
1548
 
 
1549
(defun svn-status-parse-info (arg)
 
1550
  "Parse the svn info output for the base directory.
 
1551
Show the repository url after this call in the *svn-status* buffer.
 
1552
When called with the prefix argument 0, reset the information to nil.
 
1553
This hides the repository information again.
 
1554
 
 
1555
When ARG is t, don't update the svn status buffer. This useful for
 
1556
non-interactive use."
 
1557
  (interactive "P")
 
1558
  (if (eq arg 0)
 
1559
      (setq svn-status-base-info nil)
 
1560
    (svn-run-svn nil t 'parse-info "info" ".")
 
1561
    (svn-status-parse-info-result))
 
1562
  (unless (eq arg t)
 
1563
    (svn-status-update-buffer)))
 
1564
 
 
1565
(defun svn-status-parse-info-result ()
 
1566
  (let ((url))
 
1567
    (save-excursion
 
1568
      (set-buffer "*svn-process*")
 
1569
      (goto-char (point-min))
 
1570
      (search-forward "Url: ")
 
1571
      (setq url (buffer-substring-no-properties (point) (point-at-eol))))
 
1572
    (setq svn-status-base-info `((url ,url)))))
 
1573
 
 
1574
(defun svn-status-base-info->url ()
 
1575
  (if svn-status-base-info
 
1576
      (cadr (assoc 'url svn-status-base-info))
 
1577
    ""))
 
1578
 
 
1579
(defun svn-status-toggle-edit-cmd-flag (&optional reset)
 
1580
  (interactive)
 
1581
  (cond ((or reset (eq svn-status-edit-svn-command 'sticky))
 
1582
         (setq svn-status-edit-svn-command nil))
 
1583
        ((eq svn-status-edit-svn-command nil)
 
1584
         (setq svn-status-edit-svn-command t))
 
1585
        ((eq svn-status-edit-svn-command t)
 
1586
         (setq svn-status-edit-svn-command 'sticky)))
 
1587
  (cond ((eq svn-status-edit-svn-command t)
 
1588
         (setq svn-status-mode-line-process-edit-flag " EditCmd"))
 
1589
        ((eq svn-status-edit-svn-command 'sticky)
 
1590
         (setq svn-status-mode-line-process-edit-flag " EditCmd#"))
 
1591
        (t
 
1592
         (setq svn-status-mode-line-process-edit-flag "")))
 
1593
  (svn-status-update-mode-line))
 
1594
 
 
1595
(defun svn-status-goto-root-or-return ()
 
1596
  "Bounce point between the root (\".\") and the current line."
 
1597
  (interactive)
 
1598
  (if (string= (svn-status-line-info->filename (svn-status-get-line-information)) ".")
 
1599
      (when svn-status-root-return-info
 
1600
        (svn-status-goto-file-name
 
1601
         (svn-status-line-info->filename svn-status-root-return-info)))
 
1602
    (setq svn-status-root-return-info (svn-status-get-line-information))
 
1603
    (svn-status-goto-file-name ".")))
 
1604
 
 
1605
(defun svn-status-next-line (nr-of-lines)
 
1606
  (interactive "p")
 
1607
  (next-line nr-of-lines)
 
1608
  (when (svn-status-get-line-information)
 
1609
    (goto-char (+ (point-at-bol) svn-status-default-column))))
 
1610
 
 
1611
(defun svn-status-previous-line (nr-of-lines)
 
1612
  (interactive "p")
 
1613
  (previous-line nr-of-lines)
 
1614
  (when (svn-status-get-line-information)
 
1615
    (goto-char (+ (point-at-bol) svn-status-default-column))))
 
1616
 
 
1617
(defun svn-status-possibly-negate-meaning-of-arg (arg &optional command)
 
1618
  "Negate arg, if this-command is a member of svn-status-possibly-negate-meaning-of-arg."
 
1619
  (unless command
 
1620
    (setq command this-command))
 
1621
  (if (member command svn-status-negate-meaning-of-arg-commands)
 
1622
      (not arg)
 
1623
    arg))
 
1624
 
 
1625
(defun svn-status-update (&optional arg)
 
1626
  "Run 'svn status -v'.
 
1627
When called with a prefix argument run 'svn status -vu'."
 
1628
  (interactive "P")
 
1629
  (unless (interactive-p)
 
1630
    (save-excursion
 
1631
      (set-buffer "*svn-process*")
 
1632
      (setq svn-status-update-previous-process-output
 
1633
            (buffer-substring (point-min) (point-max)))))
 
1634
  (svn-status default-directory arg))
 
1635
 
 
1636
(defun svn-status-get-line-information ()
 
1637
  "Find out about the file under point.
 
1638
The result may be parsed with the various `svn-status-line-info->...' functions."
 
1639
  (let ((svn-info nil))
 
1640
    (dolist (overlay (overlays-at (point)))
 
1641
      (setq svn-info (or svn-info
 
1642
                         (overlay-get overlay 'svn-info))))
 
1643
    svn-info))
 
1644
 
 
1645
(defun svn-status-get-file-list (use-marked-files)
 
1646
  "Get either the marked files or the files, where the cursor is on."
 
1647
  (if use-marked-files
 
1648
      (svn-status-marked-files)
 
1649
    (list (svn-status-get-line-information))))
 
1650
 
 
1651
(defun svn-status-get-file-list-names (use-marked-files)
 
1652
  (mapcar 'svn-status-line-info->filename (svn-status-get-file-list use-marked-files)))
 
1653
 
 
1654
(defun svn-status-select-line ()
 
1655
  (interactive)
 
1656
  (let ((info (svn-status-get-line-information)))
 
1657
    (if info
 
1658
        (message "%S %S %S" info (svn-status-line-info->hide-because-unknown info)
 
1659
                                 (svn-status-line-info->hide-because-unmodified info))
 
1660
      (message "No file on this line"))))
 
1661
 
 
1662
(defun svn-status-ensure-cursor-on-file ()
 
1663
  (unless (svn-status-get-line-information)
 
1664
    (error "No file on the current line")))
 
1665
 
 
1666
(defun svn-status-directory-containing-point (allow-self)
 
1667
  "Find the (full path of) directory containing the file under point.
 
1668
 
 
1669
If ALLOW-SELF and the file is a directory, return that directory,
 
1670
otherwise return the directory containing the file under point."
 
1671
  ;;the first `or' below is because s-s-g-l-i returns `nil' if
 
1672
  ;;point was outside the file list, but we need
 
1673
  ;;s-s-l-i->f to return a string to add to `default-directory'.
 
1674
  (let ((line-info (or (svn-status-get-line-information)
 
1675
                       '(nil nil nil ""))))
 
1676
    (file-name-as-directory
 
1677
     (expand-file-name
 
1678
      (svn-status-line-info->directory-containing-line-info line-info allow-self)))))
 
1679
 
 
1680
(defun svn-status-line-info->directory-containing-line-info (line-info allow-self)
 
1681
  "Find the directory containing for LINE-INFO.
 
1682
 
 
1683
If ALLOW-SELF is t and LINE-INFO refers to a directory then return the
 
1684
directory itself, in all other cases find the parent directory"
 
1685
  (if (and allow-self (svn-status-line-info->directory-p line-info))
 
1686
      (svn-status-line-info->filename line-info)
 
1687
    ;;The next `or' is because (file-name-directory "file") returns nil
 
1688
    (or (file-name-directory (svn-status-line-info->filename line-info))
 
1689
        ".")))
 
1690
 
 
1691
(defun svn-status-set-user-mark (arg)
 
1692
  "Set a user mark on the current file or directory.
 
1693
If the cursor is on a file this file is marked and the cursor advances to the next line.
 
1694
If the cursor is on a directory all files in this directory are marked.
 
1695
 
 
1696
If this function is called with a prefix argument, only the current line is
 
1697
marked, even if it is a directory."
 
1698
  (interactive "P")
 
1699
  (let ((info (svn-status-get-line-information)))
 
1700
    (if info
 
1701
        (progn
 
1702
          (svn-status-apply-usermark t arg)
 
1703
          (svn-status-next-line 1))
 
1704
      (message "No file on this line - cannot set a mark"))))
 
1705
 
 
1706
(defun svn-status-unset-user-mark (arg)
 
1707
  "Remove a user mark on the current file or directory.
 
1708
If the cursor is on a file, this file is unmarked and the cursor advances to the next line.
 
1709
If the cursor is on a directory, all files in this directory are unmarked.
 
1710
 
 
1711
If this function is called with a prefix argument, only the current line is
 
1712
unmarked, even if is a directory."
 
1713
  (interactive "P")
 
1714
  (let ((info (svn-status-get-line-information)))
 
1715
    (if info
 
1716
        (progn
 
1717
          (svn-status-apply-usermark nil arg)
 
1718
          (svn-status-next-line 1))
 
1719
      (message "No file on this line - cannot unset a mark"))))
 
1720
 
 
1721
(defun svn-status-unset-user-mark-backwards ()
 
1722
  "Remove a user mark from the previous file.
 
1723
Then move to that line."
 
1724
  ;; This is consistent with `dired-unmark-backward' and
 
1725
  ;; `cvs-mode-unmark-up'.
 
1726
  (interactive)
 
1727
  (let ((info (save-excursion
 
1728
                (svn-status-next-line -1)
 
1729
                (svn-status-get-line-information))))
 
1730
    (if info
 
1731
        (progn
 
1732
          (svn-status-next-line -1)
 
1733
          (svn-status-apply-usermark nil t))
 
1734
      (message "No file on previous line - cannot unset a mark"))))
 
1735
 
 
1736
(defun svn-status-apply-usermark (set-mark only-this-line)
 
1737
  "Do the work for the various marking/unmarking functions."
 
1738
  (let* ((st-info svn-status-info)
 
1739
         (mark-count 0)
 
1740
         (line-info (svn-status-get-line-information))
 
1741
         (file-name (svn-status-line-info->filename line-info))
 
1742
         (sub-file-regexp (concat "^" (regexp-quote
 
1743
                                       (file-name-as-directory file-name))))
 
1744
         (newcursorpos-fname)
 
1745
         (i-fname)
 
1746
         (current-line svn-start-of-file-list-line-number))
 
1747
    (while st-info
 
1748
      (when (svn-status-line-info->is-visiblep (car st-info))
 
1749
        (setq current-line (1+ current-line)))
 
1750
      (setq i-fname (svn-status-line-info->filename (car st-info)))
 
1751
      (when (or (string= file-name i-fname)
 
1752
                (string-match sub-file-regexp i-fname))
 
1753
        (when (svn-status-line-info->is-visiblep (car st-info))
 
1754
          (when (or (not only-this-line) (string= file-name i-fname))
 
1755
            (setq newcursorpos-fname i-fname)
 
1756
            (unless (eq (car (svn-status-line-info->ui-status (car st-info))) set-mark)
 
1757
              (setcar (svn-status-line-info->ui-status (car st-info)) set-mark)
 
1758
              (setq mark-count (+ 1 mark-count))
 
1759
              (save-excursion
 
1760
                (let ((buffer-read-only nil))
 
1761
                  (goto-line current-line)
 
1762
                  (delete-region (point-at-bol) (point-at-eol))
 
1763
                  (svn-insert-line-in-status-buffer (car st-info))
 
1764
                  (delete-char 1)))
 
1765
              (message "%s %s" (if set-mark "Marked" "Unmarked") i-fname)))))
 
1766
      (setq st-info (cdr st-info)))
 
1767
    ;;(svn-status-update-buffer)
 
1768
    (svn-status-goto-file-name newcursorpos-fname)
 
1769
    (when (> mark-count 1)
 
1770
      (message "%s %d files" (if set-mark "Marked" "Unmarked") mark-count))))
 
1771
 
 
1772
(defun svn-status-apply-usermark-checked (check-function set-mark)
 
1773
  "Mark or unmark files, whether a given function returns t.
 
1774
The function is called with the line information. Therefore the
 
1775
svn-status-line-info->* functions can be used in the check."
 
1776
  (let ((st-info svn-status-info)
 
1777
        (mark-count 0))
 
1778
    (while st-info
 
1779
      (when (apply check-function (list (car st-info)))
 
1780
        (unless (eq (svn-status-line-info->has-usermark (car st-info)) set-mark)
 
1781
          (setq mark-count (+ 1 mark-count))
 
1782
          (message "%s %s"
 
1783
                   (if set-mark "Marked" "Unmarked")
 
1784
                   (svn-status-line-info->filename (car st-info))))
 
1785
        (setcar (svn-status-line-info->ui-status (car st-info)) set-mark))
 
1786
      (setq st-info (cdr st-info)))
 
1787
    (svn-status-update-buffer)
 
1788
    (when (> mark-count 1)
 
1789
      (message "%s %d files" (if set-mark "Marked" "Unmarked") mark-count))))
 
1790
 
 
1791
(defun svn-status-mark-unknown (arg)
 
1792
  "Mark all unknown files.
 
1793
These are the files marked with '?' in the *svn-status* buffer.
 
1794
If the function is called with a prefix arg, unmark all these files."
 
1795
  (interactive "P")
 
1796
  (svn-status-apply-usermark-checked
 
1797
   '(lambda (info) (eq (svn-status-line-info->filemark info) ??)) (not arg)))
 
1798
 
 
1799
(defun svn-status-mark-added (arg)
 
1800
  "Mark all added files.
 
1801
These are the files marked with 'A' in the *svn-status* buffer.
 
1802
If the function is called with a prefix ARG, unmark all these files."
 
1803
  (interactive "P")
 
1804
  (svn-status-apply-usermark-checked
 
1805
   '(lambda (info) (eq (svn-status-line-info->filemark info) ?A)) (not arg)))
 
1806
 
 
1807
(defun svn-status-mark-modified (arg)
 
1808
  "Mark all modified files.
 
1809
These are the files marked with 'M' in the *svn-status* buffer.
 
1810
If the function is called with a prefix ARG, unmark all these files."
 
1811
  (interactive "P")
 
1812
  (svn-status-apply-usermark-checked
 
1813
   '(lambda (info) (or (eq (svn-status-line-info->filemark info) ?M)
 
1814
                       (eq (svn-status-line-info->filemark info)
 
1815
                           svn-status-file-modified-after-save-flag)))
 
1816
   (not arg)))
 
1817
 
 
1818
(defun svn-status-mark-deleted (arg)
 
1819
  "Mark all files scheduled for deletion.
 
1820
These are the files marked with 'D' in the *svn-status* buffer.
 
1821
If the function is called with a prefix ARG, unmark all these files."
 
1822
  (interactive "P")
 
1823
  (svn-status-apply-usermark-checked
 
1824
   '(lambda (info) (eq (svn-status-line-info->filemark info) ?D)) (not arg)))
 
1825
 
 
1826
(defun svn-status-mark-changed (arg)
 
1827
  "Mark all files that could be committed.
 
1828
This means we mark
 
1829
* all modified files
 
1830
* all files scheduled for addition
 
1831
* all files scheduled for deletion
 
1832
 
 
1833
The last two categories include all copied and moved files.
 
1834
If called with a prefix ARG, unmark all such files."
 
1835
  (interactive "P")
 
1836
  (svn-status-mark-added arg)
 
1837
  (svn-status-mark-modified arg)
 
1838
  (svn-status-mark-deleted arg))
 
1839
 
 
1840
(defun svn-status-unset-all-usermarks ()
 
1841
  (interactive)
 
1842
  (svn-status-apply-usermark-checked '(lambda (info) t) nil))
 
1843
 
 
1844
(defun svn-status-toggle-hide-unknown ()
 
1845
  (interactive)
 
1846
  (setq svn-status-hide-unknown (not svn-status-hide-unknown))
 
1847
  (svn-status-update-buffer))
 
1848
 
 
1849
(defun svn-status-toggle-hide-unmodified ()
 
1850
  (interactive)
 
1851
  (setq svn-status-hide-unmodified (not svn-status-hide-unmodified))
 
1852
  (svn-status-update-buffer))
 
1853
 
 
1854
(defun svn-status-get-file-name-buffer-position (name)
 
1855
  "Find the buffer position for a file.
 
1856
If the file is not found, return nil."
 
1857
  (let ((start-pos (point))
 
1858
        (found))
 
1859
        ;; performance optimization: search from point to end of buffer
 
1860
        (while (and (not found) (< (point) (point-max)))
 
1861
          (goto-char (next-overlay-change (point)))
 
1862
          (when (string= name (svn-status-line-info->filename
 
1863
                           (svn-status-get-line-information)))
 
1864
        (setq start-pos (+ (point) svn-status-default-column))
 
1865
        (setq found t)))
 
1866
        ;; search from buffer start to point
 
1867
        (goto-char (point-min))
 
1868
        (while (and (not found) (< (point) start-pos))
 
1869
          (goto-char (next-overlay-change (point)))
 
1870
          (when (string= name (svn-status-line-info->filename
 
1871
                           (svn-status-get-line-information)))
 
1872
        (setq start-pos (+ (point) svn-status-default-column))
 
1873
        (setq found t)))
 
1874
        (and found start-pos)))
 
1875
 
 
1876
(defun svn-status-goto-file-name (name)
 
1877
  "Move the cursor the the line that displays NAME."
 
1878
  (let ((pos (svn-status-get-file-name-buffer-position name)))
 
1879
    (if pos
 
1880
        (goto-char pos)
 
1881
      (message "Warning: svn-status-goto-file-name: %s not found" name))))
 
1882
 
 
1883
(defun svn-status-find-info-for-file-name (name)
 
1884
  (let* ((st-info svn-status-info)
 
1885
         (info))
 
1886
    (while st-info
 
1887
      (when (string= name (svn-status-line-info->filename (car st-info)))
 
1888
        (setq info (car st-info))
 
1889
        (setq st-info nil)) ; terminate loop
 
1890
      (setq st-info (cdr st-info)))
 
1891
    info))
 
1892
 
 
1893
(defun svn-status-marked-files ()
 
1894
  "Return all files marked by `svn-status-set-user-mark',
 
1895
or (if no files were marked) the file under point."
 
1896
  (let* ((st-info svn-status-info)
 
1897
         (file-list))
 
1898
    (while st-info
 
1899
      (when (svn-status-line-info->has-usermark (car st-info))
 
1900
        (setq file-list (append file-list (list (car st-info)))))
 
1901
      (setq st-info (cdr st-info)))
 
1902
    (or file-list
 
1903
        (if (svn-status-get-line-information)
 
1904
            (list (svn-status-get-line-information))
 
1905
          nil))))
 
1906
 
 
1907
(defun svn-status-marked-file-names ()
 
1908
  (mapcar 'svn-status-line-info->filename (svn-status-marked-files)))
 
1909
 
 
1910
(defun svn-status-ui-information-hash-table ()
 
1911
  (let ((st-info svn-status-info)
 
1912
        (svn-status-ui-information (make-hash-table :test 'equal)))
 
1913
    (while st-info
 
1914
      (puthash (svn-status-line-info->filename (car st-info))
 
1915
               (svn-status-line-info->ui-status (car st-info))
 
1916
               svn-status-ui-information)
 
1917
      (setq st-info (cdr st-info)))
 
1918
    svn-status-ui-information))
 
1919
 
 
1920
 
 
1921
(defun svn-status-create-arg-file (file-name prefix file-info-list postfix)
 
1922
  (with-temp-file file-name
 
1923
    (insert prefix)
 
1924
    (let ((st-info file-info-list))
 
1925
      (while st-info
 
1926
        (insert (svn-status-line-info->filename (car st-info)))
 
1927
        (insert "\n")
 
1928
        (setq st-info (cdr st-info)))
 
1929
 
 
1930
    (insert postfix))))
 
1931
 
 
1932
(defun svn-status-show-process-buffer-internal (&optional scroll-to-top)
 
1933
  (when (eq (current-buffer) "*svn-status*")
 
1934
    (delete-other-windows))
 
1935
  (pop-to-buffer "*svn-process*")
 
1936
  (when svn-status-wash-control-M-in-process-buffers
 
1937
    (svn-status-remove-control-M))
 
1938
  (when scroll-to-top
 
1939
    (goto-char (point-min)))
 
1940
  (other-window 1))
 
1941
 
 
1942
(defun svn-status-show-process-output (cmd &optional scroll-to-top)
 
1943
  "Display the result of a svn command.
 
1944
Consider svn-status-window-alist to choose the buffer name."
 
1945
  (let ((window-mode (cadr (assoc cmd svn-status-window-alist))))
 
1946
    (cond ((eq window-mode nil) ;; use *svn-process* buffer
 
1947
           (setq svn-status-last-output-buffer-name "*svn-process*"))
 
1948
          ((eq window-mode t) ;; use *svn-info* buffer
 
1949
           (setq svn-status-last-output-buffer-name "*svn-info*"))
 
1950
          ((eq window-mode 'invisible) ;; don't display the buffer
 
1951
           (setq svn-status-last-output-buffer-name nil))
 
1952
          (t
 
1953
           (setq svn-status-last-output-buffer-name window-mode)))
 
1954
    (when svn-status-last-output-buffer-name
 
1955
      (if window-mode
 
1956
          (progn
 
1957
            (when (eq (current-buffer) "*svn-status*")
 
1958
              (delete-other-windows))
 
1959
            (pop-to-buffer "*svn-process*")
 
1960
            (switch-to-buffer (get-buffer-create svn-status-last-output-buffer-name))
 
1961
            (let ((buffer-read-only nil))
 
1962
              (delete-region (point-min) (point-max))
 
1963
              (insert-buffer-substring "*svn-process*")
 
1964
              (when scroll-to-top
 
1965
                (goto-char (point-min))))
 
1966
            (other-window 1))
 
1967
        (svn-status-show-process-buffer-internal scroll-to-top)))))
 
1968
 
 
1969
 
 
1970
(defun svn-status-show-svn-log (arg)
 
1971
  "Run `svn log' on selected files.
 
1972
The output is put into the *svn-log* buffer
 
1973
The optional prefix argument ARG determines which switches are passed to `svn log':
 
1974
 no prefix               --- use whatever is in the string `svn-status-default-log-arguments'
 
1975
 prefix argument of -1   --- use no arguments
 
1976
 prefix argument of 0:   --- use the -q switch (quiet)
 
1977
 other prefix arguments: --- use the -v switch (verbose)
 
1978
 
 
1979
See `svn-status-marked-files' for what counts as selected."
 
1980
  (interactive "P")
 
1981
  (let ((switch (cond ((eq arg 0) "-q")
 
1982
                      ((eq arg -1) "")
 
1983
                      (arg        "-v")
 
1984
                      (t          svn-status-default-log-arguments))))
 
1985
    (svn-status-create-arg-file svn-status-temp-arg-file "" (svn-status-marked-files) "")
 
1986
    (if (> (length switch) 0)
 
1987
        (svn-run-svn t t 'log "log" "--targets" svn-status-temp-arg-file switch)
 
1988
      (svn-run-svn t t 'log "log" "--targets" svn-status-temp-arg-file))
 
1989
    (save-excursion
 
1990
      (set-buffer "*svn-process*")
 
1991
      (svn-log-view-mode))))
 
1992
 
 
1993
(defun svn-status-info ()
 
1994
  "Run `svn info' on all selected files.
 
1995
See `svn-status-marked-files' for what counts as selected."
 
1996
  (interactive)
 
1997
  (svn-status-create-arg-file svn-status-temp-arg-file "" (svn-status-marked-files) "")
 
1998
  (svn-run-svn t t 'info "info" "--targets" svn-status-temp-arg-file))
 
1999
 
 
2000
;; Todo: add possiblity to specify the revision
 
2001
(defun svn-status-blame ()
 
2002
  "Run `svn blame' on the current file."
 
2003
  (interactive)
 
2004
  ;;(svn-run-svn t t 'blame "blame" "-r" "BASE" (svn-status-line-info->filename (svn-status-get-line-information))))
 
2005
  (svn-run-svn t t 'blame "blame" (svn-status-line-info->filename (svn-status-get-line-information))))
 
2006
 
 
2007
(defun svn-status-show-svn-diff (arg)
 
2008
  "Run `svn diff' on the current file.
 
2009
If there is a newer revision in the repository, the diff is done against HEAD, otherwise
 
2010
compare the working copy with BASE.
 
2011
If ARG then prompt for revision to diff against."
 
2012
  (interactive "P")
 
2013
  (svn-status-ensure-cursor-on-file)
 
2014
  (svn-status-show-svn-diff-internal arg nil))
 
2015
 
 
2016
(defun svn-status-show-svn-diff-for-marked-files (arg)
 
2017
  "Run `svn diff' on all selected files.
 
2018
See `svn-status-marked-files' for what counts as selected.
 
2019
If ARG then prompt for revision to diff against, else compare working copy with BASE."
 
2020
  (interactive "P")
 
2021
  (svn-status-show-svn-diff-internal arg t))
 
2022
 
 
2023
(defun svn-status-show-svn-diff-internal (arg &optional use-all-marked-files)
 
2024
  (let* ((fl (if use-all-marked-files
 
2025
                 (svn-status-marked-files)
 
2026
               (list (svn-status-get-line-information))))
 
2027
         (clear-buf t)
 
2028
         (revision (if arg
 
2029
                       (svn-status-read-revision-string "Diff with files for version: " "PREV")
 
2030
                     (if use-all-marked-files
 
2031
                         "BASE"
 
2032
                       (if (svn-status-line-info->update-available (car fl)) "HEAD" "BASE")))))
 
2033
    (while fl
 
2034
      (svn-run-svn nil clear-buf 'diff "diff" "-r" revision (svn-status-line-info->filename (car fl)))
 
2035
      (setq clear-buf nil)
 
2036
      (setq fl (cdr fl))))
 
2037
  (svn-status-diff-mode))
 
2038
 
 
2039
(defun svn-status-diff-mode ()
 
2040
  "Show the *svn-process* buffer, using the diff-mode."
 
2041
  (svn-status-show-process-output 'diff t)
 
2042
  (save-excursion
 
2043
    (set-buffer svn-status-last-output-buffer-name)
 
2044
    (diff-mode)
 
2045
    (font-lock-fontify-buffer)
 
2046
    (setq buffer-read-only t)))
 
2047
 
 
2048
(defun svn-status-show-process-buffer ()
 
2049
  "Show the content of the *svn-process* buffer"
 
2050
  (interactive)
 
2051
  (svn-status-show-process-output nil))
 
2052
 
 
2053
(defun svn-status-add-file-recursively (arg)
 
2054
  "Run `svn add' on all selected files.
 
2055
When a directory is added, add files recursively.
 
2056
See `svn-status-marked-files' for what counts as selected.
 
2057
When this function is called with a prefix argument, use the actual file instead."
 
2058
  (interactive "P")
 
2059
  (message "adding: %S" (svn-status-get-file-list-names (not arg)))
 
2060
  (svn-status-create-arg-file svn-status-temp-arg-file "" (svn-status-get-file-list (not arg)) "")
 
2061
  (svn-run-svn t t 'add "add" "--targets" svn-status-temp-arg-file))
 
2062
 
 
2063
(defun svn-status-add-file (arg)
 
2064
  "Run `svn add' on all selected files.
 
2065
When a directory is added, don't add the files of the directory
 
2066
 (svn add --non-recursive <file-list> is called).
 
2067
See `svn-status-marked-files' for what counts as selected.
 
2068
When this function is called with a prefix argument, use the actual file instead."
 
2069
  (interactive "P")
 
2070
  (message "adding: %S" (svn-status-get-file-list-names (not arg)))
 
2071
  (svn-status-create-arg-file svn-status-temp-arg-file "" (svn-status-get-file-list (not arg)) "")
 
2072
  (svn-run-svn t t 'add "add" "--non-recursive" "--targets" svn-status-temp-arg-file))
 
2073
 
 
2074
(defun svn-status-make-directory (dir)
 
2075
  "Run `svn mkdir DIR'."
 
2076
  ;; TODO: Allow entering a URI interactively.
 
2077
  ;; Currently, `read-file-name' corrupts it.
 
2078
  (interactive (list (read-file-name "Make directory: "
 
2079
                                     (svn-status-directory-containing-point t))))
 
2080
  (unless (string-match "^[^:/]+://" dir) ; Is it a URI?
 
2081
    (setq dir (file-relative-name dir)))
 
2082
  (svn-run-svn t t 'mkdir "mkdir" "--" dir))
 
2083
 
 
2084
;;TODO: write a svn-status-cp similar to this---maybe a common
 
2085
;;function to do both?
 
2086
(defun svn-status-mv ()
 
2087
  "Prompt for a destination, and `svn mv' selected files there.
 
2088
See `svn-status-marked-files' for what counts as `selected'.
 
2089
 
 
2090
If one file was selected then the destination DEST should be a
 
2091
filename to rename the selected file to, or a directory to move the
 
2092
file into; if multiple files were selected then DEST should be a
 
2093
directory to move the selected files into.
 
2094
 
 
2095
The default DEST is the directory containing point.
 
2096
 
 
2097
BUG: If we've marked some directory containging a file as well as the
 
2098
file itself, then we should just mv the directory, but this implementation
 
2099
doesn't check for that.
 
2100
SOLUTION: for each dir, umark all its contents (but not the dir
 
2101
itself) before running mv."
 
2102
  (interactive)
 
2103
  (let* ((marked-files (svn-status-marked-files))
 
2104
         (num-of-files (length marked-files))
 
2105
         original
 
2106
         dest)
 
2107
    (if (= 1 num-of-files)
 
2108
        ;; one file to rename, prompt for new name, or directory to move the
 
2109
        ;; file into.
 
2110
        (setq dest (read-file-name (format "Rename %s to: "
 
2111
                                           (svn-status-line-info->filename (car marked-files)))
 
2112
                                   (svn-status-directory-containing-point t)))
 
2113
      ;;multiple files selected, so prompt for existing directory to mv them into.
 
2114
      (setq dest (read-directory-name (format "Move %d files to directory: " num-of-files)
 
2115
                                      (svn-status-directory-containing-point t) nil t))
 
2116
      (unless (file-directory-p dest)
 
2117
        (error "%s is not a directory" dest)))
 
2118
    (when (string= dest "")
 
2119
      (error "No destination entered; no files moved"))
 
2120
    (unless (string-match "^[^:/]+://" dest) ; Is it a URI?
 
2121
      (setq dest (file-relative-name dest)))
 
2122
;
 
2123
    ;;do the move: svn mv only lets us move things once at a time, so
 
2124
    ;;we need to run svn mv once for each file (hence second arg to
 
2125
    ;;svn-run-svn is nil.)
 
2126
 
 
2127
    ;;TODO: before doing any moving, For every marked directory,
 
2128
    ;;ensure none of its contents are also marked, since we dont want
 
2129
    ;;to move both file *and* its parent...
 
2130
    ;; what about hidden files?? what if user marks a dir+contents, then presses `_' ??
 
2131
;;   ;one solution:
 
2132
;;      (dolist (original marked-files)
 
2133
;;          (when (svn-status-line-info->directory-p original)
 
2134
;;              ;; run  svn-status-goto-file-name to move point to line of file
 
2135
;;              ;; run  svn-status-unset-user-mark to unmark dir+all contents
 
2136
;;              ;; run  svn-status-set-user-mark   to remark dir
 
2137
;;              ;; maybe check for local mods here, and unmark if user does't say --force?
 
2138
;;              ))
 
2139
        (dolist (original marked-files)
 
2140
      (let ((original-name (svn-status-line-info->filename original))
 
2141
                        (original-filemarks (svn-status-line-info->filemark original))
 
2142
                        (original-propmarks (svn-status-line-info->propmark original)))
 
2143
        (cond
 
2144
         ((or (eq original-filemarks 77)  ;;original has local mods: maybe do `svn mv --force'
 
2145
              (eq original-propmarks 77)) ;;original has local prop mods: maybe do `svn mv --force'
 
2146
          (if (yes-or-no-p (format "%s has local modifications; use `--force' to really move it? "
 
2147
                                   original-name))
 
2148
              (svn-run-svn nil t 'mv "mv" "--force" "--" original-name dest)
 
2149
            (message "Not moving %s" original-name)))
 
2150
         ((eq original-filemarks 63) ;;original is unversioned: maybe do plain `mv'
 
2151
          (if (yes-or-no-p (format "%s is unversioned.  Use plain `mv -i %s %s'? "
 
2152
                                   original-name original-name dest))
 
2153
              (call-process "mv" nil (get-buffer-create "*svn-process*") nil "-i" original-name dest)
 
2154
            (message "Not moving %s" original-name)))
 
2155
 
 
2156
         ((eq original-filemarks 65) ;;original has `A' mark (eg it was `svn add'ed, but not committed)
 
2157
          (message "Not moving %s (try committing it first)" original-name))
 
2158
 
 
2159
         ((eq original-filemarks 32) ;;original is unmodified: can use `svn mv'
 
2160
          (svn-run-svn nil t 'mv "mv" "--" original-name dest))
 
2161
 
 
2162
         ;;file is conflicted in some way?
 
2163
         (t
 
2164
          (if (yes-or-no-p (format "The status of %s looks scary.  Risk moving it anyway? " original-name))
 
2165
              (svn-run-svn nil t 'mv "mv" "--" original-name dest)
 
2166
            (message "Not moving %s" original-name))))))
 
2167
        (svn-status-update)))
 
2168
 
 
2169
(defun svn-status-revert ()
 
2170
  "Run `svn revert' on all selected files.
 
2171
See `svn-status-marked-files' for what counts as selected."
 
2172
  (interactive)
 
2173
  (let* ((marked-files (svn-status-marked-files))
 
2174
         (num-of-files (length marked-files)))
 
2175
    (when (yes-or-no-p
 
2176
           (if (= 1 num-of-files)
 
2177
               (format "Revert %s? " (svn-status-line-info->filename (car marked-files)))
 
2178
             (format "Revert %d files? " num-of-files)))
 
2179
      (message "reverting: %S" (svn-status-marked-file-names))
 
2180
      (svn-status-create-arg-file svn-status-temp-arg-file "" (svn-status-marked-files) "")
 
2181
      (svn-run-svn t t 'revert "revert" "--targets" svn-status-temp-arg-file))))
 
2182
 
 
2183
(defun svn-status-rm (force)
 
2184
  "Run `svn rm' on all selected files.
 
2185
See `svn-status-marked-files' for what counts as selected.
 
2186
When called with a prefix argument add the command line switch --force."
 
2187
  (interactive "P")
 
2188
  (let* ((marked-files (svn-status-marked-files))
 
2189
         (num-of-files (length marked-files)))
 
2190
    (when (yes-or-no-p
 
2191
           (if (= 1 num-of-files)
 
2192
               (format "%sRemove %s? " (if force "Force " "") (svn-status-line-info->filename (car marked-files)))
 
2193
             (format "%sRemove %d files? " (if force "Force " "") num-of-files)))
 
2194
      (message "removing: %S" (svn-status-marked-file-names))
 
2195
      (svn-status-create-arg-file svn-status-temp-arg-file "" (svn-status-marked-files) "")
 
2196
      (if force
 
2197
          (svn-run-svn t t 'rm "rm" "--force" "--targets" svn-status-temp-arg-file)
 
2198
        (svn-run-svn t t 'rm "rm" "--targets" svn-status-temp-arg-file)))))
 
2199
 
 
2200
(defun svn-status-update-cmd ()
 
2201
  (interactive)
 
2202
  ;TODO: use file names also
 
2203
  (svn-run-svn t t 'update "update"))
 
2204
 
 
2205
(defun svn-status-commit-file ()
 
2206
  "Commit selected files.
 
2207
See `svn-status-marked-files' for what counts as selected."
 
2208
  (interactive)
 
2209
  (let* ((marked-files (svn-status-marked-files)))
 
2210
    (setq svn-status-files-to-commit marked-files)
 
2211
    (svn-log-edit-show-files-to-commit)
 
2212
    (svn-status-pop-to-commit-buffer)
 
2213
    (when svn-log-edit-insert-files-to-commit
 
2214
      (svn-log-edit-insert-files-to-commit))))
 
2215
 
 
2216
(defun svn-status-pop-to-commit-buffer ()
 
2217
  (interactive)
 
2218
  (setq svn-status-pre-commit-window-configuration (current-window-configuration))
 
2219
  (let* ((use-existing-buffer (get-buffer "*svn-log-edit*"))
 
2220
         (commit-buffer (get-buffer-create "*svn-log-edit*"))
 
2221
         (dir default-directory))
 
2222
    (pop-to-buffer commit-buffer)
 
2223
    (setq default-directory dir)
 
2224
    (unless use-existing-buffer
 
2225
      (when (and svn-log-edit-file-name (file-readable-p svn-log-edit-file-name))
 
2226
        (insert-file svn-log-edit-file-name)))
 
2227
    (svn-log-edit-mode)))
 
2228
 
 
2229
(defun svn-status-cleanup ()
 
2230
  (interactive)
 
2231
  (let ((file-names (svn-status-marked-file-names)))
 
2232
    (if file-names
 
2233
        (progn
 
2234
          ;(message "svn-status-cleanup %S" file-names))
 
2235
          (svn-run-svn t t 'cleanup (append (list "cleanup") file-names)))
 
2236
      (message "No valid file selected - No status cleanup possible"))))
 
2237
 
 
2238
(defun svn-status-resolved ()
 
2239
  "Run `svn resolved' on all selected files.
 
2240
See `svn-status-marked-files' for what counts as selected."
 
2241
  (interactive)
 
2242
  (let* ((marked-files (svn-status-marked-files))
 
2243
         (num-of-files (length marked-files)))
 
2244
    (when (yes-or-no-p
 
2245
           (if (= 1 num-of-files)
 
2246
               (format "Resolve %s? " (svn-status-line-info->filename (car marked-files)))
 
2247
             (format "Resolve %d files? " num-of-files)))
 
2248
      (message "resolving: %S" (svn-status-marked-file-names))
 
2249
      (svn-status-create-arg-file svn-status-temp-arg-file "" (svn-status-marked-files) "")
 
2250
      (svn-run-svn t t 'resolved "resolved" "--targets" svn-status-temp-arg-file))))
 
2251
 
 
2252
 
 
2253
(defun svn-status-svnversion ()
 
2254
  "Run svnversion on the directory that contains the file at point."
 
2255
  (interactive)
 
2256
  (svn-status-ensure-cursor-on-file)
 
2257
  (let ((simple-path (svn-status-line-info->filename (svn-status-get-line-information)))
 
2258
        (full-path (svn-status-line-info->full-path (svn-status-get-line-information)))
 
2259
        (version))
 
2260
    (unless (file-directory-p simple-path)
 
2261
      (setq simple-path (or (file-name-directory simple-path) "."))
 
2262
      (setq full-path (file-name-directory full-path)))
 
2263
    (setq version (shell-command-to-string (concat "svnversion -n " full-path)))
 
2264
    (message (format "svnversion for '%s': %s" simple-path version))
 
2265
    version))
 
2266
 
 
2267
;; --------------------------------------------------------------------------------
 
2268
;; Update the *svn-status* buffer, when a file is saved
 
2269
;; --------------------------------------------------------------------------------
 
2270
 
 
2271
(defvar svn-status-file-modified-after-save-flag ?m
 
2272
  "Flag shown whenever a file is modified and saved in Emacs.
 
2273
The flag is shown in the *svn-status* buffer.
 
2274
Recommended values are ?m or ?M.")
 
2275
(defun svn-status-after-save-hook ()
 
2276
  "Set a modified indication, when a file is saved from a svn working copy."
 
2277
  (let* ((svn-dir (car-safe svn-status-directory-history))
 
2278
         (svn-dir (when svn-dir (expand-file-name svn-dir)))
 
2279
         (file-dir (file-name-directory (buffer-file-name)))
 
2280
         (svn-dir-len (length (or svn-dir "")))
 
2281
         (file-dir-len (length file-dir))
 
2282
         (file-name))
 
2283
    (when (and (get-buffer "*svn-status*")
 
2284
               svn-dir
 
2285
               (>= file-dir-len svn-dir-len)
 
2286
               (string= (substring file-dir 0 svn-dir-len) svn-dir))
 
2287
      (setq file-name (substring (buffer-file-name) svn-dir-len))
 
2288
      ;;(message (format "In svn-status directory %S" file-name))
 
2289
      (let ((st-info svn-status-info)
 
2290
            (i-fname))
 
2291
        (while st-info
 
2292
          (setq i-fname (svn-status-line-info->filename (car st-info)))
 
2293
          ;;(message (format "i-fname=%S" i-fname))
 
2294
          (when (and (string= file-name i-fname)
 
2295
                     (not (eq (svn-status-line-info->filemark (car st-info)) ??)))
 
2296
            (svn-status-line-info->set-filemark (car st-info)
 
2297
                                                svn-status-file-modified-after-save-flag)
 
2298
            (save-window-excursion
 
2299
              (set-buffer "*svn-status*")
 
2300
              (let ((buffer-read-only nil)
 
2301
                    (pos (svn-status-get-file-name-buffer-position i-fname)))
 
2302
                (if pos
 
2303
                    (progn
 
2304
                      (goto-char pos)
 
2305
                      (delete-region (point-at-bol) (point-at-eol))
 
2306
                      (svn-insert-line-in-status-buffer (car st-info))
 
2307
                      (delete-char 1))
 
2308
                  (message "psvn: file %s not found, updating *svn-status* buffer content..."
 
2309
                           i-fname)
 
2310
                  (svn-status-update-buffer)))))
 
2311
          (setq st-info (cdr st-info))))))
 
2312
  nil)
 
2313
 
 
2314
(add-hook 'after-save-hook 'svn-status-after-save-hook)
 
2315
 
 
2316
;; --------------------------------------------------------------------------------
 
2317
;; Getting older revisions
 
2318
;; --------------------------------------------------------------------------------
 
2319
 
 
2320
(defun svn-status-get-specific-revision (arg)
 
2321
  "Retrieve older revisions.
 
2322
The older revisions are stored in backup files named F.~REVISION~.
 
2323
 
 
2324
When the function is called without a prefix argument: get all marked files.
 
2325
Otherwise get only the actual file."
 
2326
  (interactive "P")
 
2327
  (svn-status-get-specific-revision-internal (not arg) t))
 
2328
 
 
2329
(defun svn-status-get-specific-revision-internal (&optional only-actual-file arg)
 
2330
  (let* ((file-names (if only-actual-file
 
2331
                         (list (svn-status-line-info->filename (svn-status-get-line-information)))
 
2332
                       (svn-status-marked-file-names)))
 
2333
         (revision (if arg (svn-status-read-revision-string "Get files for version: " "PREV") "BASE"))
 
2334
         (file-name)
 
2335
         (file-name-with-revision))
 
2336
    (message "Getting revision %s for %S" revision file-names)
 
2337
    (setq svn-status-get-specific-revision-file-info nil)
 
2338
    (while file-names
 
2339
      (setq file-name (car file-names))
 
2340
      (setq file-name-with-revision (concat file-name ".~" revision "~"))
 
2341
      (add-to-list 'svn-status-get-specific-revision-file-info
 
2342
                   (cons file-name file-name-with-revision))
 
2343
      (save-excursion
 
2344
        (find-file file-name-with-revision)
 
2345
        (setq buffer-read-only nil)
 
2346
        (delete-region (point-min) (point-max))
 
2347
        (svn-run-svn nil t 'cat (append (list "cat" "-r" revision) (list file-name)))
 
2348
        ;;todo: error processing
 
2349
        ;;svn: Filesystem has no item
 
2350
        ;;svn: file not found: revision `15', path `/trunk/file.txt'
 
2351
        (insert-buffer-substring "*svn-process*")
 
2352
        (save-buffer))
 
2353
      (setq file-names (cdr file-names)))
 
2354
    (setq svn-status-get-specific-revision-file-info
 
2355
      (nreverse svn-status-get-specific-revision-file-info))
 
2356
    (message "svn-status-get-specific-revision-file-info: %S"
 
2357
             svn-status-get-specific-revision-file-info)))
 
2358
 
 
2359
 
 
2360
(defun svn-status-ediff-with-revision (arg)
 
2361
  "Run ediff on the current file with a previous revision.
 
2362
If ARG then prompt for revision to diff against."
 
2363
  (interactive "P")
 
2364
  (svn-status-get-specific-revision-internal t arg)
 
2365
  (let* ((ediff-after-quit-destination-buffer (current-buffer))
 
2366
         (my-buffer (find-file-noselect (caar svn-status-get-specific-revision-file-info)))
 
2367
         (base-buff (find-file-noselect (cdar svn-status-get-specific-revision-file-info)))
 
2368
         (svn-transient-buffers (list base-buff ))
 
2369
         (startup-hook '(svn-ediff-startup-hook)))
 
2370
    (ediff-buffers my-buffer base-buff  startup-hook)))
 
2371
 
 
2372
(defun svn-ediff-startup-hook ()
 
2373
  (add-hook 'ediff-after-quit-hook-internal
 
2374
        `(lambda ()
 
2375
           (svn-ediff-exit-hook
 
2376
        ',ediff-after-quit-destination-buffer ',svn-transient-buffers))
 
2377
        nil 'local))
 
2378
 
 
2379
(defun svn-ediff-exit-hook (svn-buf tmp-bufs)
 
2380
  ;; kill the temp buffers (and their associated windows)
 
2381
  (dolist (tb tmp-bufs)
 
2382
    (when (and tb (buffer-live-p tb) (not (buffer-modified-p tb)))
 
2383
      (let ((win (get-buffer-window tb t)))
 
2384
    (when win (delete-window win))
 
2385
    (kill-buffer tb))))
 
2386
  ;; switch back to the *svn* buffer
 
2387
  (when (and svn-buf (buffer-live-p svn-buf)
 
2388
         (not (get-buffer-window svn-buf t)))
 
2389
    (ignore-errors (switch-to-buffer svn-buf))))
 
2390
 
 
2391
 
 
2392
(defun svn-status-read-revision-string (prompt &optional default-value)
 
2393
  "Prompt the user for a svn revision number."
 
2394
  (interactive)
 
2395
  (read-string prompt default-value))
 
2396
 
 
2397
;; --------------------------------------------------------------------------------
 
2398
;; SVN process handling
 
2399
;; --------------------------------------------------------------------------------
 
2400
 
 
2401
(defun svn-process-kill ()
 
2402
  "Kill the current running svn process."
 
2403
  (interactive)
 
2404
  (let ((process (get-process "svn")))
 
2405
    (if process
 
2406
        (delete-process process)
 
2407
      (message "No running svn process"))))
 
2408
 
 
2409
(defun svn-process-send-string (string)
 
2410
  "Send a string to the running svn process.
 
2411
This is useful, if the running svn process asks the user a question.
 
2412
Note: use C-q C-j to send a line termination character."
 
2413
  (interactive "sSend string to svn process: ")
 
2414
  (save-excursion
 
2415
    (set-buffer "*svn-process*")
 
2416
    (let ((buffer-read-only nil))
 
2417
      (insert string))
 
2418
    (set-marker (process-mark (get-process "svn")) (point)))
 
2419
  (process-send-string "svn" string))
 
2420
 
 
2421
;; --------------------------------------------------------------------------------
 
2422
;; Property List stuff
 
2423
;; --------------------------------------------------------------------------------
 
2424
 
 
2425
(defun svn-status-property-list ()
 
2426
  (interactive)
 
2427
  (let ((file-names (svn-status-marked-file-names)))
 
2428
    (if file-names
 
2429
        (progn
 
2430
          (svn-run-svn t t 'proplist (append (list "proplist" "-v") file-names)))
 
2431
      (message "No valid file selected - No property listing possible"))))
 
2432
 
 
2433
(defun svn-status-proplist-start ()
 
2434
  (svn-run-svn t t 'proplist-parse "proplist" (svn-status-line-info->filename
 
2435
                                               (svn-status-get-line-information))))
 
2436
(defun svn-status-property-edit-one-entry (arg)
 
2437
  "Edit a property.
 
2438
When called with a prefix argument, it is possible to enter a new property."
 
2439
  (interactive "P")
 
2440
  (setq svn-status-property-edit-must-match-flag (not arg))
 
2441
  (svn-status-proplist-start))
 
2442
 
 
2443
(defun svn-status-property-set ()
 
2444
  (interactive)
 
2445
  (setq svn-status-property-edit-must-match-flag nil)
 
2446
  (svn-status-proplist-start))
 
2447
 
 
2448
(defun svn-status-property-delete ()
 
2449
  (interactive)
 
2450
  (setq svn-status-property-edit-must-match-flag t)
 
2451
  (svn-status-proplist-start))
 
2452
 
 
2453
(defun svn-status-property-parse-property-names ()
 
2454
  ;(svn-status-show-process-buffer-internal t)
 
2455
  (message "svn-status-property-parse-property-names")
 
2456
  (let ((pl)
 
2457
        (pfl)
 
2458
        (prop-name)
 
2459
        (prop-value))
 
2460
    (save-excursion
 
2461
      (set-buffer "*svn-process*")
 
2462
      (goto-char (point-min))
 
2463
      (forward-line 1)
 
2464
      (while (looking-at "  \\(.+\\)")
 
2465
        (setq pl (append pl (list (match-string 1))))
 
2466
        (forward-line 1)))
 
2467
    ;(cond last-command: svn-status-property-set, svn-status-property-edit-one-entry
 
2468
    (cond ((eq last-command 'svn-status-property-edit-one-entry)
 
2469
           ;;(message "svn-status-property-edit-one-entry")
 
2470
           (setq prop-name
 
2471
                 (completing-read "Set Property - Name: " (mapcar 'list pl)
 
2472
                                  nil svn-status-property-edit-must-match-flag))
 
2473
           (unless (string= prop-name "")
 
2474
             (save-excursion
 
2475
               (set-buffer "*svn-status*")
 
2476
               (svn-status-property-edit (list (svn-status-get-line-information))
 
2477
                                         prop-name))))
 
2478
          ((eq last-command 'svn-status-property-set)
 
2479
           (message "svn-status-property-set")
 
2480
           (setq prop-name
 
2481
                 (completing-read "Set Property - Name: " (mapcar 'list pl) nil nil))
 
2482
           (setq prop-value (read-from-minibuffer "Property value: "))
 
2483
           (unless (string= prop-name "")
 
2484
             (save-excursion
 
2485
               (set-buffer "*svn-status*")
 
2486
               (message "Setting property %s := %s for %S" prop-name prop-value
 
2487
                        (svn-status-marked-file-names))
 
2488
               (let ((file-names (svn-status-marked-file-names)))
 
2489
                 (when file-names
 
2490
                   (svn-run-svn nil t 'propset
 
2491
                                (append (list "propset" prop-name prop-value) file-names))
 
2492
                   )
 
2493
                 )
 
2494
               (message "propset finished.")
 
2495
               )))
 
2496
          ((eq last-command 'svn-status-property-delete)
 
2497
           (setq prop-name
 
2498
                 (completing-read "Delete Property - Name: " (mapcar 'list pl) nil t))
 
2499
           (unless (string= prop-name "")
 
2500
             (save-excursion
 
2501
               (set-buffer "*svn-status*")
 
2502
               (let ((file-names (svn-status-marked-file-names)))
 
2503
                 (when file-names
 
2504
                   (message "Going to delete prop %s for %s" prop-name file-names)
 
2505
                   (svn-run-svn t t 'propdel
 
2506
                                (append (list "propdel" prop-name) file-names))))))))))
 
2507
 
 
2508
(defun svn-status-property-edit (file-info-list prop-name &optional new-prop-value)
 
2509
  (let* ((commit-buffer (get-buffer-create "*svn-property-edit*"))
 
2510
         (dir default-directory)
 
2511
         ;; now only one file is implemented ...
 
2512
         (file-name (svn-status-line-info->filename (car file-info-list)))
 
2513
         (prop-value))
 
2514
    (message "Edit property %s for file %s" prop-name file-name)
 
2515
    (svn-run-svn nil t 'propget-parse "propget" prop-name file-name)
 
2516
    (save-excursion
 
2517
      (set-buffer "*svn-process*")
 
2518
      (setq prop-value (if (> (point-max) 1)
 
2519
                           (buffer-substring (point-min) (- (point-max) 1))
 
2520
                         "")))
 
2521
    (setq svn-status-propedit-property-name prop-name)
 
2522
    (setq svn-status-propedit-file-list file-info-list)
 
2523
    (setq svn-status-pre-propedit-window-configuration (current-window-configuration))
 
2524
    (pop-to-buffer commit-buffer)
 
2525
    (delete-region (point-min) (point-max))
 
2526
    (setq default-directory dir)
 
2527
    (insert prop-value)
 
2528
    (svn-status-remove-control-M)
 
2529
    (when new-prop-value
 
2530
      (when (listp new-prop-value)
 
2531
        (message "Adding new prop values %S " new-prop-value)
 
2532
        (while new-prop-value
 
2533
          (goto-char (point-min))
 
2534
          (unless (re-search-forward
 
2535
                   (concat "^" (regexp-quote (car new-prop-value)) "$") nil t)
 
2536
            (goto-char (point-max))
 
2537
            (when (> (current-column) 0) (insert "\n"))
 
2538
            (insert (car new-prop-value)))
 
2539
          (setq new-prop-value (cdr new-prop-value)))))
 
2540
    (svn-prop-edit-mode)))
 
2541
 
 
2542
(defun svn-status-property-set-property (file-info-list prop-name prop-value)
 
2543
  "Set a property on a given file list."
 
2544
  (save-excursion
 
2545
    (set-buffer (get-buffer-create "*svn-property-edit*"))
 
2546
    (delete-region (point-min) (point-max))
 
2547
    (insert prop-value))
 
2548
  (setq svn-status-propedit-file-list (svn-status-marked-files))
 
2549
  (setq svn-status-propedit-property-name prop-name)
 
2550
  (svn-prop-edit-do-it nil)
 
2551
  (svn-status-update))
 
2552
 
 
2553
 
 
2554
(defun svn-status-get-directory (line-info)
 
2555
  (let* ((file-name (svn-status-line-info->filename line-info))
 
2556
         (file-dir (file-name-directory file-name)))
 
2557
    ;;(message "file-dir: %S" file-dir)
 
2558
    (if file-dir
 
2559
        (substring file-dir 0 (- (length file-dir) 1))
 
2560
      ".")))
 
2561
 
 
2562
(defun svn-status-get-file-list-per-directory (files)
 
2563
  ;;(message "%S" files)
 
2564
  (let ((dir-list nil)
 
2565
        (i files)
 
2566
        (j)
 
2567
        (dir))
 
2568
    (while i
 
2569
      (setq dir (svn-status-get-directory (car i)))
 
2570
      (setq j (assoc dir dir-list))
 
2571
      (if j
 
2572
          (progn
 
2573
            ;;(message "dir already present %S %s" j dir)
 
2574
            (setcdr j (append (cdr j) (list (car i)))))
 
2575
        (setq dir-list (append dir-list (list (list dir (car i))))))
 
2576
      (setq i (cdr i)))
 
2577
    ;;(message "svn-status-get-file-list-per-directory: %S" dir-list)
 
2578
    dir-list))
 
2579
 
 
2580
(defun svn-status-property-ignore-file ()
 
2581
  (interactive)
 
2582
  (let ((d-list (svn-status-get-file-list-per-directory (svn-status-marked-files)))
 
2583
        (dir)
 
2584
        (f-info)
 
2585
        (ext-list))
 
2586
    (while d-list
 
2587
      (setq dir (caar d-list))
 
2588
      (setq f-info (cdar d-list))
 
2589
      (setq ext-list (mapcar '(lambda (i)
 
2590
                                (svn-status-line-info->filename-nondirectory i)) f-info))
 
2591
      ;;(message "ignore in dir %s: %S" dir f-info)
 
2592
      (save-window-excursion
 
2593
        (when (y-or-n-p (format "Ignore %S for %s? " ext-list dir))
 
2594
          (svn-status-property-edit
 
2595
           (list (svn-status-find-info-for-file-name dir)) "svn:ignore" ext-list)
 
2596
          (svn-prop-edit-do-it nil)))   ; synchronous
 
2597
      (setq d-list (cdr d-list)))
 
2598
    (svn-status-update)))
 
2599
 
 
2600
(defun svn-status-property-ignore-file-extension ()
 
2601
  (interactive)
 
2602
  (let ((d-list (svn-status-get-file-list-per-directory (svn-status-marked-files)))
 
2603
        (dir)
 
2604
        (f-info)
 
2605
        (ext-list))
 
2606
    (while d-list
 
2607
      (setq dir (caar d-list))
 
2608
      (setq f-info (cdar d-list))
 
2609
      ;;(message "ignore in dir %s: %S" dir f-info)
 
2610
      (setq ext-list nil)
 
2611
      (while f-info
 
2612
        (add-to-list 'ext-list (concat "*."
 
2613
                                       (file-name-extension
 
2614
                                        (svn-status-line-info->filename (car f-info)))))
 
2615
        (setq f-info (cdr f-info)))
 
2616
      ;;(message "%S" ext-list)
 
2617
      (save-window-excursion
 
2618
        (when (y-or-n-p (format "Ignore %S for %s? " ext-list dir))
 
2619
          (svn-status-property-edit
 
2620
           (list (svn-status-find-info-for-file-name dir)) "svn:ignore"
 
2621
           ext-list)
 
2622
          (svn-prop-edit-do-it nil)))
 
2623
      (setq d-list (cdr d-list)))
 
2624
    (svn-status-update)))
 
2625
 
 
2626
(defun svn-status-property-edit-svn-ignore ()
 
2627
  (interactive)
 
2628
  (let* ((line-info (svn-status-get-line-information))
 
2629
         (dir (if (svn-status-line-info->directory-p line-info)
 
2630
                  (svn-status-line-info->filename line-info)
 
2631
                (svn-status-get-directory line-info))))
 
2632
    (svn-status-property-edit
 
2633
     (list (svn-status-find-info-for-file-name dir)) "svn:ignore")
 
2634
    (message "Edit svn:ignore on %s" dir)))
 
2635
 
 
2636
 
 
2637
(defun svn-status-property-set-keyword-list ()
 
2638
  "Edit the svn:keywords property on the marked files."
 
2639
  (interactive)
 
2640
  ;;(message "Set svn:keywords for %S" (svn-status-marked-file-names))
 
2641
  (svn-status-property-edit (svn-status-marked-files) "svn:keywords"))
 
2642
 
 
2643
(defun svn-status-property-set-eol-style ()
 
2644
  "Edit the svn:eol-style property on the marked files."
 
2645
  (interactive)
 
2646
  (svn-status-property-set-property
 
2647
   (svn-status-marked-files) "svn:eol-style"
 
2648
   (completing-read "Set svn:eol-style for the marked files: "
 
2649
                    (mapcar 'list '("native" "CRLF" "LF" "CR"))
 
2650
                    nil t)))
 
2651
 
 
2652
(defun svn-status-property-set-executable ()
 
2653
  "Set the svn:executable property on the marked files."
 
2654
  (interactive)
 
2655
  (svn-status-property-set-property (svn-status-marked-files) "svn:executable" "*"))
 
2656
 
 
2657
;; --------------------------------------------------------------------------------
 
2658
;; svn-prop-edit-mode:
 
2659
;; --------------------------------------------------------------------------------
 
2660
 
 
2661
(defvar svn-prop-edit-mode-map () "Keymap used in `svn-prop-edit-mode' buffers.")
 
2662
 
 
2663
(when (not svn-prop-edit-mode-map)
 
2664
  (setq svn-prop-edit-mode-map (make-sparse-keymap))
 
2665
  (define-key svn-prop-edit-mode-map [(control ?c) (control ?c)] 'svn-prop-edit-done)
 
2666
  (define-key svn-prop-edit-mode-map [(control ?c) (control ?d)] 'svn-prop-edit-svn-diff)
 
2667
  (define-key svn-prop-edit-mode-map [(control ?c) (control ?s)] 'svn-prop-edit-svn-status)
 
2668
  (define-key svn-prop-edit-mode-map [(control ?c) (control ?l)] 'svn-prop-edit-svn-log)
 
2669
  (define-key svn-prop-edit-mode-map [(control ?c) (control ?q)] 'svn-prop-edit-abort))
 
2670
 
 
2671
(easy-menu-define svn-prop-edit-mode-menu svn-prop-edit-mode-map
 
2672
"'svn-prop-edit-mode' menu"
 
2673
                  '("SVN-PropEdit"
 
2674
                    ["Commit" svn-prop-edit-done t]
 
2675
                    ["Show Diff" svn-prop-edit-svn-diff t]
 
2676
                    ["Show Status" svn-prop-edit-svn-status t]
 
2677
                    ["Show Log" svn-prop-edit-svn-log t]
 
2678
                    ["Abort" svn-prop-edit-abort t]))
 
2679
 
 
2680
(defun svn-prop-edit-mode ()
 
2681
  "Major Mode to edit file properties of files under svn control.
 
2682
Commands:
 
2683
\\{svn-prop-edit-mode-map}"
 
2684
  (interactive)
 
2685
  (kill-all-local-variables)
 
2686
  (use-local-map svn-prop-edit-mode-map)
 
2687
  (easy-menu-add svn-prop-edit-mode-menu)
 
2688
  (setq major-mode 'svn-prop-edit-mode)
 
2689
  (setq mode-name "svn-prop-edit"))
 
2690
 
 
2691
(defun svn-prop-edit-abort ()
 
2692
  (interactive)
 
2693
  (bury-buffer)
 
2694
  (set-window-configuration svn-status-pre-propedit-window-configuration))
 
2695
 
 
2696
(defun svn-prop-edit-done ()
 
2697
  (interactive)
 
2698
  (svn-prop-edit-do-it t))
 
2699
 
 
2700
(defun svn-prop-edit-do-it (async)
 
2701
  (message "svn propset %s on %s"
 
2702
           svn-status-propedit-property-name
 
2703
           (mapcar 'svn-status-line-info->filename svn-status-propedit-file-list))
 
2704
  (save-excursion
 
2705
    (set-buffer (get-buffer "*svn-property-edit*"))
 
2706
    (when (fboundp 'set-buffer-file-coding-system)
 
2707
      (set-buffer-file-coding-system 'undecided-unix nil))
 
2708
    (setq svn-status-temp-file-to-remove
 
2709
          (concat svn-status-temp-dir "svn-prop-edit.txt" svn-temp-suffix))
 
2710
    (write-region (point-min) (point-max) svn-status-temp-file-to-remove nil 1))
 
2711
  (when svn-status-propedit-file-list ; there are files to change properties
 
2712
    (svn-status-create-arg-file svn-status-temp-arg-file ""
 
2713
                                svn-status-propedit-file-list "")
 
2714
    (setq svn-status-propedit-file-list nil)
 
2715
    (svn-run-svn async t 'propset "propset"
 
2716
         svn-status-propedit-property-name
 
2717
                 "--targets" svn-status-temp-arg-file
 
2718
                 "-F" (concat svn-status-temp-dir "svn-prop-edit.txt" svn-temp-suffix))
 
2719
    (unless async (svn-status-remove-temp-file-maybe)))
 
2720
  (when svn-status-pre-propedit-window-configuration
 
2721
    (set-window-configuration svn-status-pre-propedit-window-configuration)))
 
2722
 
 
2723
(defun svn-prop-edit-svn-diff (arg)
 
2724
  (interactive "P")
 
2725
  (set-buffer "*svn-status*")
 
2726
  (svn-status-show-svn-diff-for-marked-files arg))
 
2727
 
 
2728
(defun svn-prop-edit-svn-log (arg)
 
2729
  (interactive "P")
 
2730
  (set-buffer "*svn-status*")
 
2731
  (svn-status-show-svn-log arg))
 
2732
 
 
2733
(defun svn-prop-edit-svn-status ()
 
2734
  (interactive)
 
2735
  (pop-to-buffer "*svn-status*")
 
2736
  (other-window 1))
 
2737
 
 
2738
;; --------------------------------------------------------------------------------
 
2739
;; svn-log-edit-mode:
 
2740
;; --------------------------------------------------------------------------------
 
2741
 
 
2742
(defvar svn-log-edit-mode-map () "Keymap used in `svn-log-edit-mode' buffers.")
 
2743
 
 
2744
(when (not svn-log-edit-mode-map)
 
2745
  (setq svn-log-edit-mode-map (make-sparse-keymap))
 
2746
  (define-key svn-log-edit-mode-map (kbd "C-c C-c") 'svn-log-edit-done)
 
2747
  (define-key svn-log-edit-mode-map (kbd "C-c C-d") 'svn-log-edit-svn-diff)
 
2748
  (define-key svn-log-edit-mode-map (kbd "C-c C-s") 'svn-log-edit-save-message)
 
2749
  (define-key svn-log-edit-mode-map (kbd "C-c C-i") 'svn-log-edit-svn-status)
 
2750
  (define-key svn-log-edit-mode-map (kbd "C-c C-l") 'svn-log-edit-svn-log)
 
2751
  (define-key svn-log-edit-mode-map (kbd "C-c C-?") 'svn-log-edit-show-files-to-commit)
 
2752
  (define-key svn-log-edit-mode-map (kbd "C-c C-z") 'svn-log-edit-erase-edit-buffer)
 
2753
  (define-key svn-log-edit-mode-map (kbd "C-c C-q") 'svn-log-edit-abort))
 
2754
 
 
2755
(easy-menu-define svn-log-edit-mode-menu svn-log-edit-mode-map
 
2756
"'svn-log-edit-mode' menu"
 
2757
                  '("SVN-Log"
 
2758
                    ["Save to disk" svn-log-edit-save-message t]
 
2759
                    ["Commit" svn-log-edit-done t]
 
2760
                    ["Show Diff" svn-log-edit-svn-diff t]
 
2761
                    ["Show Status" svn-log-edit-svn-status t]
 
2762
                    ["Show Log" svn-log-edit-svn-log t]
 
2763
                    ["Show files to commit" svn-log-edit-show-files-to-commit t]
 
2764
                    ["Erase buffer" svn-log-edit-erase-edit-buffer]
 
2765
                    ["Abort" svn-log-edit-abort t]))
 
2766
 
 
2767
(defun svn-log-edit-mode ()
 
2768
  "Major Mode to edit svn log messages.
 
2769
Commands:
 
2770
\\{svn-log-edit-mode-map}"
 
2771
  (interactive)
 
2772
  (kill-all-local-variables)
 
2773
  (use-local-map svn-log-edit-mode-map)
 
2774
  (easy-menu-add svn-log-edit-mode-menu)
 
2775
  (setq major-mode 'svn-log-edit-mode)
 
2776
  (setq mode-name "svn-log-edit")
 
2777
  (setq svn-log-edit-update-log-entry nil)
 
2778
  (run-hooks 'svn-log-edit-mode-hook))
 
2779
 
 
2780
(defun svn-log-edit-abort ()
 
2781
  (interactive)
 
2782
  (bury-buffer)
 
2783
  (set-window-configuration svn-status-pre-commit-window-configuration))
 
2784
 
 
2785
(defun svn-log-edit-done ()
 
2786
  (interactive)
 
2787
  (save-excursion
 
2788
    (set-buffer (get-buffer "*svn-log-edit*"))
 
2789
    (when svn-log-edit-insert-files-to-commit
 
2790
      (svn-log-edit-remove-comment-lines))
 
2791
    (when (fboundp 'set-buffer-file-coding-system)
 
2792
      (set-buffer-file-coding-system 'undecided-unix nil))
 
2793
    (when (or svn-log-edit-update-log-entry svn-status-files-to-commit)
 
2794
      (setq svn-status-temp-file-to-remove
 
2795
            (concat svn-status-temp-dir "svn-log-edit.txt" svn-temp-suffix))
 
2796
      (write-region (point-min) (point-max) svn-status-temp-file-to-remove nil 1)))
 
2797
  (if svn-log-edit-update-log-entry
 
2798
      (when (y-or-n-p "Update the log entry? ")
 
2799
        ;;   svn propset svn:log --revprop -r11672 -F file
 
2800
        (svn-run-svn nil t 'propset "propset" "svn:log" "--revprop"
 
2801
                     (concat "-r" svn-log-edit-update-log-entry)
 
2802
                     "-F" svn-status-temp-file-to-remove)
 
2803
        (save-excursion
 
2804
          (set-buffer "*svn-process*")
 
2805
          (message (buffer-substring (point-min) (- (point-max) 1)))))
 
2806
    (when svn-status-files-to-commit ; there are files to commit
 
2807
      (setq svn-status-operated-on-dot
 
2808
            (and (= 1 (length svn-status-files-to-commit))
 
2809
                 (string= "." (svn-status-line-info->filename (car svn-status-files-to-commit)))))
 
2810
      (svn-status-create-arg-file svn-status-temp-arg-file ""
 
2811
                                  svn-status-files-to-commit "")
 
2812
      (svn-run-svn t t 'commit "commit" "--targets" svn-status-temp-arg-file
 
2813
                   "-F" svn-status-temp-file-to-remove))
 
2814
    (set-window-configuration svn-status-pre-commit-window-configuration)
 
2815
    (message "svn-log editing done")))
 
2816
 
 
2817
(defun svn-log-edit-svn-diff (arg)
 
2818
  "Show the diff we are about to commit.
 
2819
If ARG then show diff between some other version of the selected files."
 
2820
  (interactive "P")
 
2821
  (set-buffer "*svn-status*")
 
2822
  (svn-status-show-svn-diff-for-marked-files arg))
 
2823
 
 
2824
(defun svn-log-edit-svn-log (arg)
 
2825
  (interactive "P")
 
2826
  (set-buffer "*svn-status*")
 
2827
  (svn-status-show-svn-log arg))
 
2828
 
 
2829
(defun svn-log-edit-svn-status ()
 
2830
  (interactive)
 
2831
  (pop-to-buffer "*svn-status*")
 
2832
  (other-window 1))
 
2833
 
 
2834
(defun svn-log-edit-show-files-to-commit ()
 
2835
  (interactive)
 
2836
  (message "Files to commit: %S"
 
2837
           (mapcar 'svn-status-line-info->filename svn-status-files-to-commit)))
 
2838
 
 
2839
(defun svn-log-edit-save-message ()
 
2840
  "Save the current log message to the file `svn-log-edit-file-name'."
 
2841
  (interactive)
 
2842
  (write-region (point-min) (point-max) svn-log-edit-file-name))
 
2843
 
 
2844
(defun svn-log-edit-erase-edit-buffer ()
 
2845
  "Delete everything in the *svn-log-edit* buffer."
 
2846
  (interactive)
 
2847
  (set-buffer "*svn-log-edit*")
 
2848
  (erase-buffer))
 
2849
 
 
2850
(defun svn-log-edit-insert-files-to-commit ()
 
2851
  (interactive)
 
2852
  (svn-log-edit-remove-comment-lines)
 
2853
  (let ((buf-size (- (point-max) (point-min))))
 
2854
    (save-excursion
 
2855
      (goto-char (point-min))
 
2856
      (insert "## Lines starting with '## ' will be removed from the log message.\n")
 
2857
      (insert "## File(s) to commit:\n")
 
2858
      (let ((file-list svn-status-files-to-commit))
 
2859
        (while file-list
 
2860
          (insert (concat "## " (svn-status-line-info->filename (car file-list)) "\n"))
 
2861
          (setq file-list (cdr file-list)))))
 
2862
    (when (= 0 buf-size)
 
2863
      (goto-char (point-max)))))
 
2864
 
 
2865
(defun svn-log-edit-remove-comment-lines ()
 
2866
  (interactive)
 
2867
  (save-excursion
 
2868
    (goto-char (point-min))
 
2869
    (flush-lines "^## .*")))
 
2870
 
 
2871
 
 
2872
;; --------------------------------------------------------------------------------
 
2873
;; svn-log-view-mode:
 
2874
;; --------------------------------------------------------------------------------
 
2875
 
 
2876
(defvar svn-log-view-mode-map () "Keymap used in `svn-log-view-mode' buffers.")
 
2877
 
 
2878
(when (not svn-log-view-mode-map)
 
2879
  (setq svn-log-view-mode-map (make-sparse-keymap))
 
2880
  (suppress-keymap svn-log-view-mode-map)
 
2881
  (define-key svn-log-view-mode-map (kbd "p") 'svn-log-view-prev)
 
2882
  (define-key svn-log-view-mode-map (kbd "n") 'svn-log-view-next)
 
2883
  (define-key svn-log-view-mode-map (kbd "=") 'svn-log-view-diff)
 
2884
  (define-key svn-log-view-mode-map (kbd "e") 'svn-log-edit-log-entry)
 
2885
  (define-key svn-log-view-mode-map (kbd "q") 'bury-buffer))
 
2886
 
 
2887
(easy-menu-define svn-log-view-mode-menu svn-log-view-mode-map
 
2888
"'svn-log-view-mode' menu"
 
2889
                  '("SVN-LogView"
 
2890
                    ["Show Changeset" svn-log-view-diff t]
 
2891
                    ["Edit log message" svn-log-edit-log-entry t]))
 
2892
 
 
2893
(defvar svn-log-view-font-lock-keywords
 
2894
  '(("^r.+" . font-lock-keyword-face)
 
2895
  "Keywords in svn-log-view-mode."))
 
2896
 
 
2897
 
 
2898
(define-derived-mode svn-log-view-mode fundamental-mode "svn-log-view"
 
2899
  "Major Mode to show the output from svn log.
 
2900
Commands:
 
2901
\\{svn-log-view-mode-map}
 
2902
"
 
2903
  (use-local-map svn-log-view-mode-map)
 
2904
  (easy-menu-add svn-log-view-mode-menu)
 
2905
  (set (make-local-variable 'font-lock-defaults) '(svn-log-view-font-lock-keywords t)))
 
2906
 
 
2907
(defun svn-log-view-next ()
 
2908
  (interactive)
 
2909
  (when (re-search-forward "^r[0-9]+" nil t)
 
2910
    (beginning-of-line 3)))
 
2911
 
 
2912
(defun svn-log-view-prev ()
 
2913
  (interactive)
 
2914
  (when (re-search-backward "^r[0-9]+" nil t 2)
 
2915
    (beginning-of-line 3)))
 
2916
 
 
2917
(defun svn-log-revision-at-point ()
 
2918
  (save-excursion
 
2919
    (re-search-backward "^r\\([0-9]+\\)")
 
2920
    (match-string-no-properties 1)))
 
2921
 
 
2922
(defun svn-log-view-diff (arg)
 
2923
  "Show the changeset for a given log entry.
 
2924
When called with a prefix argument, ask the user for the revision."
 
2925
  (interactive "P")
 
2926
  (let* ((upper-rev (svn-log-revision-at-point))
 
2927
        (lower-rev (number-to-string (- (string-to-number upper-rev) 1)))
 
2928
        (rev-arg (concat lower-rev ":" upper-rev)))
 
2929
    (when arg
 
2930
      (setq rev-arg (read-string "Revision for changeset: " rev-arg)))
 
2931
    (svn-run-svn nil t 'diff "diff" (concat "-r" rev-arg))
 
2932
    (svn-status-diff-mode)))
 
2933
 
 
2934
(defun svn-log-edit-log-entry ()
 
2935
  "Edit the given log entry."
 
2936
  (interactive)
 
2937
  (let ((rev (svn-log-revision-at-point))
 
2938
        (log-message))
 
2939
    (svn-run-svn nil t 'propget-parse "propget" "--revprop" (concat "-r" rev) "svn:log")
 
2940
    (save-excursion
 
2941
      (set-buffer "*svn-process*")
 
2942
      (setq log-message (if (> (point-max) 1)
 
2943
                            (buffer-substring (point-min) (- (point-max) 1))
 
2944
                          "")))
 
2945
    (svn-status-pop-to-commit-buffer)
 
2946
    (delete-region (point-min) (point-max))
 
2947
    (insert log-message)
 
2948
    (goto-char (point-min))
 
2949
    (setq svn-log-edit-update-log-entry rev)))
 
2950
 
 
2951
;; --------------------------------------------------------------------------------
 
2952
;; svn status persistent options
 
2953
;; --------------------------------------------------------------------------------
 
2954
 
 
2955
(defun svn-status-base-dir ()
 
2956
  (let ((base-dir (expand-file-name default-directory))
 
2957
        (dot-svn-dir)
 
2958
        (dir-below (expand-file-name default-directory)))
 
2959
    (setq dot-svn-dir (concat base-dir ".svn"))
 
2960
    (while (when (and dir-below (file-exists-p dot-svn-dir))
 
2961
             (setq base-dir (file-name-directory dot-svn-dir))
 
2962
             (string-match "\\(.+/\\).+/" dir-below)
 
2963
             (setq dir-below (match-string 1 dir-below))
 
2964
             (setq dot-svn-dir (concat dir-below ".svn"))))
 
2965
    base-dir))
 
2966
 
 
2967
(defun svn-status-save-state ()
 
2968
  (interactive)
 
2969
  (let ((buf (find-file (concat (svn-status-base-dir) "++psvn.state"))))
 
2970
    (delete-region (point-min) (point-max))
 
2971
    (setq svn-status-options
 
2972
          (list
 
2973
           (list "svn-trac-project-root" svn-trac-project-root)
 
2974
           (list "sort-status-buffer" svn-status-sort-status-buffer)
 
2975
           (list "elide-list" svn-status-elided-list)
 
2976
           (list "module-name" svn-status-module-name)))
 
2977
    (insert (pp-to-string svn-status-options))
 
2978
    (save-buffer)
 
2979
    (kill-buffer buf)))
 
2980
 
 
2981
(defun svn-status-load-state (&optional no-error)
 
2982
  (interactive)
 
2983
  (let ((file (concat (svn-status-base-dir) "++psvn.state")))
 
2984
    (if (file-readable-p file)
 
2985
        (with-temp-buffer
 
2986
          (insert-file-contents file)
 
2987
          (setq svn-status-options (read (current-buffer)))
 
2988
          (setq svn-status-sort-status-buffer
 
2989
                (nth 1 (assoc "sort-status-buffer" svn-status-options)))
 
2990
          (setq svn-trac-project-root
 
2991
                (nth 1 (assoc "svn-trac-project-root" svn-status-options)))
 
2992
          (setq svn-status-elided-list
 
2993
                (nth 1 (assoc "elide-list" svn-status-options)))
 
2994
          (setq svn-status-module-name
 
2995
                (nth 1 (assoc "module-name" svn-status-options)))
 
2996
          (when (and (interactive-p) svn-status-elided-list (svn-status-apply-elide-list)))
 
2997
          (message "psvn.el: loaded %s" file))
 
2998
      (unless no-error (error "psvn.el: %s is not readable." file)))))
 
2999
 
 
3000
(defun svn-status-toggle-sort-status-buffer ()
 
3001
  "If you turn off sorting, you can speed up M-x svn-status.
 
3002
However, the buffer is not correct sorted then.
 
3003
This function will be removed again, when a faster parsing and
 
3004
display routine for svn-status is available."
 
3005
  (interactive)
 
3006
  (setq svn-status-sort-status-buffer (not svn-status-sort-status-buffer))
 
3007
  (message (concat "The *svn-status* buffer will be"
 
3008
                   (if svn-status-sort-status-buffer "" " not")
 
3009
                   " sorted.")))
 
3010
 
 
3011
(defun svn-status-set-trac-project-root ()
 
3012
  (interactive)
 
3013
  (setq svn-trac-project-root
 
3014
        (read-string "Trac project root (e.g.: http://projects.edgewall.com/trac/): "
 
3015
                     svn-trac-project-root))
 
3016
  (when (yes-or-no-p "Save the new setting for svn-trac-project-root to disk? ")
 
3017
    (svn-status-save-state)))
 
3018
 
 
3019
(defun svn-status-set-module-name ()
 
3020
  "Interactively set svn-status-module-name."
 
3021
  (interactive)
 
3022
  (setq svn-status-module-name
 
3023
        (read-string "Short Unit Name (e.g.: MyProject): "
 
3024
                     svn-status-module-name))
 
3025
  (when (yes-or-no-p "Save the new setting for svn-status-module-name to disk? ")
 
3026
    (svn-status-save-state)))
 
3027
 
 
3028
;; --------------------------------------------------------------------------------
 
3029
;; svn status trac integration
 
3030
;; --------------------------------------------------------------------------------
 
3031
(defun svn-trac-browse-timeline ()
 
3032
  "Open the trac timeline view for the current svn repository."
 
3033
  (interactive)
 
3034
  (unless svn-trac-project-root
 
3035
    (svn-status-set-trac-project-root))
 
3036
  (browse-url (concat svn-trac-project-root "timeline")))
 
3037
 
 
3038
 
 
3039
;;;------------------------------------------------------------
 
3040
;;; resolve conflicts using ediff
 
3041
;;;------------------------------------------------------------
 
3042
(defun svn-resolve-conflicts-ediff (&optional name-A name-B)
 
3043
  "Invoke ediff to resolve conflicts in the current buffer.
 
3044
The conflicts must be marked with rcsmerge conflict markers."
 
3045
  (interactive)
 
3046
  (let* ((found nil)
 
3047
         (file-name (file-name-nondirectory buffer-file-name))
 
3048
         (your-buffer (generate-new-buffer
 
3049
                       (concat "*" file-name
 
3050
                               " " (or name-A "WORKFILE") "*")))
 
3051
         (other-buffer (generate-new-buffer
 
3052
                        (concat "*" file-name
 
3053
                                " " (or name-B "CHECKED-IN") "*")))
 
3054
         (result-buffer (current-buffer)))
 
3055
    (save-excursion
 
3056
      (set-buffer your-buffer)
 
3057
      (erase-buffer)
 
3058
      (insert-buffer result-buffer)
 
3059
      (goto-char (point-min))
 
3060
      (while (re-search-forward "^<<<<<<< .mine\n" nil t)
 
3061
        (setq found t)
 
3062
        (replace-match "")
 
3063
        (if (not (re-search-forward "^=======\n" nil t))
 
3064
            (error "Malformed conflict marker"))
 
3065
        (replace-match "")
 
3066
        (let ((start (point)))
 
3067
          (if (not (re-search-forward "^>>>>>>> .r[0-9]+\n" nil t))
 
3068
              (error "Malformed conflict marker"))
 
3069
          (delete-region start (point))))
 
3070
      (if (not found)
 
3071
          (progn
 
3072
            (kill-buffer your-buffer)
 
3073
            (kill-buffer other-buffer)
 
3074
            (error "No conflict markers found")))
 
3075
      (set-buffer other-buffer)
 
3076
      (erase-buffer)
 
3077
      (insert-buffer result-buffer)
 
3078
      (goto-char (point-min))
 
3079
      (while (re-search-forward "^<<<<<<< .mine\n" nil t)
 
3080
        (let ((start (match-beginning 0)))
 
3081
          (if (not (re-search-forward "^=======\n" nil t))
 
3082
              (error "Malformed conflict marker"))
 
3083
          (delete-region start (point))
 
3084
          (if (not (re-search-forward "^>>>>>>> .r[0-9]+\n" nil t))
 
3085
              (error "Malformed conflict marker"))
 
3086
          (replace-match "")))
 
3087
      (let ((config (current-window-configuration))
 
3088
            (ediff-default-variant 'default-B))
 
3089
 
 
3090
        ;; Fire up ediff.
 
3091
 
 
3092
        (set-buffer (ediff-merge-buffers your-buffer other-buffer))
 
3093
 
 
3094
        ;; Ediff is now set up, and we are in the control buffer.
 
3095
        ;; Do a few further adjustments and take precautions for exit.
 
3096
 
 
3097
        (make-local-variable 'svn-ediff-windows)
 
3098
        (setq svn-ediff-windows config)
 
3099
        (make-local-variable 'svn-ediff-result)
 
3100
        (setq svn-ediff-result result-buffer)
 
3101
        (make-local-variable 'ediff-quit-hook)
 
3102
        (setq ediff-quit-hook
 
3103
              (lambda ()
 
3104
                (let ((buffer-A ediff-buffer-A)
 
3105
                      (buffer-B ediff-buffer-B)
 
3106
                      (buffer-C ediff-buffer-C)
 
3107
                      (result svn-ediff-result)
 
3108
                      (windows svn-ediff-windows))
 
3109
                  (ediff-cleanup-mess)
 
3110
                  (set-buffer result)
 
3111
                  (erase-buffer)
 
3112
                  (insert-buffer buffer-C)
 
3113
                  (kill-buffer buffer-A)
 
3114
                  (kill-buffer buffer-B)
 
3115
                  (kill-buffer buffer-C)
 
3116
                  (set-window-configuration windows)
 
3117
                  (message "Conflict resolution finished; you may save the buffer"))))
 
3118
        (message "Please resolve conflicts now; exit ediff when done")
 
3119
        nil))))
 
3120
 
 
3121
(defun svn-resolve-conflicts (filename)
 
3122
  (let ((buff (find-file-noselect filename)))
 
3123
    (if buff
 
3124
        (progn (switch-to-buffer buff)
 
3125
               (svn-resolve-conflicts-ediff))
 
3126
      (error "can not open file %s" filename))))
 
3127
 
 
3128
(defun svn-status-resolve-conflicts ()
 
3129
  "Resolve conflict in the selected file"
 
3130
  (interactive)
 
3131
  (let ((file-info (svn-status-get-line-information)))
 
3132
    (or (and file-info
 
3133
             (= ?C (svn-status-line-info->filemark file-info))
 
3134
             (svn-resolve-conflicts
 
3135
              (svn-status-line-info->full-path file-info)))
 
3136
        (error "can not resolve conflicts at this point"))))
 
3137
 
 
3138
;; --------------------------------------------------------------------------------
 
3139
;; svn status profiling
 
3140
;; --------------------------------------------------------------------------------
 
3141
;;; Note about profiling psvn:
 
3142
;;  (load-library "elp")
 
3143
;;  M-x elp-reset-all
 
3144
;;  (elp-instrument-package "svn-")
 
3145
;;  M-x svn-status
 
3146
;;  M-x elp-results
 
3147
 
 
3148
(defun svn-status-elp-init ()
 
3149
  (interactive)
 
3150
  (require 'elp)
 
3151
  (elp-reset-all)
 
3152
  (elp-instrument-package "svn-")
 
3153
  (message "Run the desired svn command (e.g. M-x svn-status), then use M-x elp-results."))
 
3154
 
 
3155
 
 
3156
(provide 'psvn)
 
3157
 
 
3158
;;; psvn.el ends here