--- psvn.el	(revision 15394 variant kon.10.ensure-file)
+++ psvn.el	(revision 15394 variant kon.11.keywords-form)
@@ -83,6 +83,7 @@
 ;; P I   - svn-status-property-ignore-file-extension
 ;; P C-i - svn-status-property-edit-svn-ignore
 ;; P k   - svn-status-property-set-keyword-list
+;; P K   - svn-status-property-form-svn:keywords
 ;; P y   - svn-status-property-set-eol-style
 ;; P x   - svn-status-property-set-executable
 ;; h     - svn-status-use-history
@@ -112,7 +113,6 @@
 ;;   svn co http://svn.collab.net/repos/svn/trunk/contrib/client-side/psvn psvn
 
 ;; TODO:
-;; * shortcut for svn propset svn:keywords "Date" psvn.el
 ;; * docstrings for the functions
 ;; * perhaps shortcuts for ranges, dates
 ;; * when editing the command line - offer help from the svn client
@@ -415,6 +415,11 @@
 (defvar svn-status-elided-list nil)
 (defvar svn-status-custom-hide-function nil)
 
+;; These are only defined in the *svn-property-form* buffer.
+(defvar svn-status-property-form-file-info)
+(defvar svn-status-property-form-checkboxes) ;reverse order
+(defvar svn-status-property-form-extra)
+
 ;; That is an example for the svn-status-custom-hide-function:
 ;; (setq svn-status-custom-hide-function 'svn-status-hide-pyc-files)
 ;; (defun svn-status-hide-pyc-files (info)
@@ -1096,6 +1101,7 @@
   (define-key svn-status-mode-property-map [(control ?i)] 'svn-status-property-edit-svn-ignore)
   (define-key svn-status-mode-property-map (kbd "TAB") 'svn-status-property-edit-svn-ignore)
   (define-key svn-status-mode-property-map (kbd "k") 'svn-status-property-set-keyword-list)
+  (define-key svn-status-mode-property-map (kbd "K") 'svn-status-property-form-svn:keywords)
   (define-key svn-status-mode-property-map (kbd "y") 'svn-status-property-set-eol-style)
   (define-key svn-status-mode-property-map (kbd "x") 'svn-status-property-set-executable)
   ;; TODO: Why is `svn-status-select-line' in `svn-status-mode-property-map'?
@@ -1192,6 +1198,13 @@
      :style toggle :selected svn-status-hide-unmodified]
     ))
 
+(defvar svn-status-property-form-keymap nil
+  "Keymap used in property-editing forms.")
+(unless svn-status-property-form-keymap
+  (setq svn-status-property-form-keymap (make-sparse-keymap))
+  (set-keymap-parent svn-status-property-form-keymap widget-keymap)
+  (define-key svn-status-property-form-keymap (kbd "C-c C-c")
+              'svn-status-property-form-done))
 
 (defun svn-status-popup-menu (event)
   (interactive "e")
@@ -2925,6 +2938,98 @@
   ;;(message "Set svn:keywords for %S" (svn-status-marked-file-names))
   (svn-status-property-edit (svn-status-marked-files) "svn:keywords"))
 
+(defun svn-status-split-svn:keywords (keywords)
+  "Convert the svn:keywords string KEYWORDS to a list.
+Each element of the list is one keyword as a string.
+This does not remove duplicate keywords."
+  ;; `split-string' in Emacs 21.4 doesn't support an OMIT-NULLS
+  ;; argument and behaves as if it were t.  Filter empty strings
+  ;; with a separate loop (not requiring `cl-seq' at run time).
+  ;;
+  ;; The svnbook says svn:keywords is a space-separated list.  However,
+  ;; svn_subst_build_keywords in trunk/subversion/libsvn_subr/subst.c
+  ;; (r15394) recognizes all of these characters as separators.
+  (loop for keyword in (split-string keywords "[ \t\v\n\b\r\f]+")
+        unless (equal keyword "")
+          collect keyword))
+
+(defun svn-status-property-form-svn:keywords ()
+  "Edit the svn:keywords property of the current file with a form."
+  (interactive)
+  (svn-status-ensure-cursor-on-file)
+  (let* ((file-info (svn-status-get-line-information))
+         (file-name (svn-status-line-info->filename file-info))
+         keywords
+         (window-configuration (current-window-configuration)))
+    (svn-run-svn nil t 'propget-parse "propget" "svn:keywords" file-name)
+    (with-current-buffer "*svn-process*"
+      (setq keywords (svn-status-split-svn:keywords (buffer-string))))
+
+    (pop-to-buffer "*svn-property-form*")
+    (kill-all-local-variables)
+    (let ((inhibit-read-only t))
+      (erase-buffer))
+    (remove-overlays)
+    (set (make-local-variable 'svn-status-pre-propedit-window-configuration)
+         window-configuration)
+    (set (make-local-variable 'svn-status-property-form-file-info) file-info)
+    (set (make-local-variable 'svn-status-property-form-checkboxes) '())
+    (make-local-variable 'svn-status-property-form-extra)
+    (use-local-map svn-status-property-form-keymap)
+
+    (widget-insert "Editing the svn:keywords property for " file-name
+                   "\n\nSelect which keywords should be expanded, then press ")
+    (widget-create 'push-button
+                   :notify #'(lambda (widget nested-widget &optional event)
+                               (svn-status-property-form-done))
+                   "Done")
+    (widget-insert (substitute-command-keys
+                    " or \\[svn-status-property-form-done].
+All keywords listed on the same line are aliases for each other.\n"))
+    ;; TODO: Display the expansions too, once the svn command-line
+    ;; client provides a clean way to get them.
+    (loop for (column keyword)
+            in '((0 "URL")                 (26 "HeadURL")
+                 (0 "Author")              (26 "LastChangedBy")
+                 (0 "Date")                (26 "LastChangedDate")
+                 (0 "Rev") (10 "Revision") (26 "LastChangedRevision")
+                 (0 "Id"))
+          do (if (zerop column) (insert "\n") (indent-to column))
+             (push (widget-create 'checkbox
+                                  :svn-status-keyword keyword
+                                  (not (null (member keyword keywords))))
+                   svn-status-property-form-checkboxes)
+             (widget-insert " " keyword)
+             (setq keywords (delete keyword keywords)))
+    (setq svn-status-property-form-extra
+          (widget-create 'string :tag "\nOther keywords"
+                         (mapconcat #'identity keywords " ")))
+    (widget-setup)))
+
+(defun svn-status-property-form-done ()
+  "Set the properties that were edited in the form.
+This should be used in the *svn-property-form* buffer only." 
+  (interactive)
+  ;; For now, only the svn:keywords property can be edited with a form.
+  ;; In the future, we might have another buffer-local variable that
+  ;; indicates which property was being edited.
+  (let ((file-name (svn-status-line-info->filename
+                    svn-status-property-form-file-info))
+        (keywords (svn-status-split-svn:keywords
+                   (widget-value svn-status-property-form-extra))))
+    (dolist (checkbox svn-status-property-form-checkboxes)
+      (when (widget-value checkbox)
+        (push (widget-get checkbox :svn-status-keyword) keywords)))
+    (if keywords
+        (svn-run-svn t t 'propset "propset" "--"
+                     "svn:keywords" (mapconcat #'identity keywords " ")
+                     file-name)
+      (svn-run-svn t t 'propdel "propdel" "--"
+                   "svn:keywords" file-name)))
+  (if svn-status-pre-propedit-window-configuration
+      (set-window-configuration svn-status-pre-propedit-window-configuration)
+    (bury-buffer)))
+
 (defun svn-status-property-set-eol-style ()
   "Edit the svn:eol-style property on the marked files."
   (interactive)

