diff options
| author | Juri Linkov | 2020-03-30 01:34:47 +0300 |
|---|---|---|
| committer | Juri Linkov | 2020-03-30 01:34:47 +0300 |
| commit | 7a6f5a5167037cdc3a0e9e312393781daedec085 (patch) | |
| tree | 4ecad790cfa5a3d82be76d7b672a22cce1153b02 | |
| parent | 1276c8e10b000b571a12227ebe9216cc6305ef7f (diff) | |
| download | emacs-7a6f5a5167037cdc3a0e9e312393781daedec085.tar.gz emacs-7a6f5a5167037cdc3a0e9e312393781daedec085.zip | |
Support state changing VC operations on directories in Dired (bug#34949)
* lisp/dired-aux.el (dired-vc-next-action): New command.
(dired-vc-deduce-fileset): Rename from vc-dired-deduce-fileset in vc.el.
* lisp/dired.el (dired-mode-map): Remap vc-next-action to
dired-vc-next-action.
* lisp/vc/vc-dir.el (vc-dir-mark-files): New function.
(vc-dir-refresh): Run hook vc-dir-refresh-hook.
* lisp/vc/vc.el (vc-deduce-fileset): Rename arg 'observer' to
'not-state-changing' and document it in docstring.
(vc-dired-deduce-fileset): Rename to dired-vc-deduce-fileset in dired-aux.el.
* lisp/cedet/ede.el (ede-turn-on-hook, ede-minor-mode):
* lisp/desktop.el (desktop-minor-mode-table): Rename the long ago
obsolete vc-dired-mode to vc-dir-mode.
| -rw-r--r-- | etc/NEWS | 4 | ||||
| -rw-r--r-- | lisp/cedet/ede.el | 4 | ||||
| -rw-r--r-- | lisp/desktop.el | 2 | ||||
| -rw-r--r-- | lisp/dired-aux.el | 62 | ||||
| -rw-r--r-- | lisp/dired.el | 1 | ||||
| -rw-r--r-- | lisp/vc/vc-dir.el | 14 | ||||
| -rw-r--r-- | lisp/vc/vc.el | 40 |
7 files changed, 90 insertions, 37 deletions
| @@ -108,8 +108,8 @@ Mark mode, then Dired commands operate only on files in the active | |||
| 108 | region. The values 'file' and 'line' of this user option define the | 108 | region. The values 'file' and 'line' of this user option define the |
| 109 | details of marking the file at the end of the region. | 109 | details of marking the file at the end of the region. |
| 110 | 110 | ||
| 111 | *** State changing VC operations are supported in 'dired-mode' on files | 111 | *** State changing VC operations are supported in Dired on files and |
| 112 | (but still not on directories). | 112 | directories with the help of new command 'dired-vc-next-action'. |
| 113 | 113 | ||
| 114 | ** Change Logs and VC | 114 | ** Change Logs and VC |
| 115 | 115 | ||
diff --git a/lisp/cedet/ede.el b/lisp/cedet/ede.el index c2036878288..8c336117c92 100644 --- a/lisp/cedet/ede.el +++ b/lisp/cedet/ede.el | |||
| @@ -470,7 +470,7 @@ To be used in hook functions." | |||
| 470 | ;; Emacs 21 has no buffer file name for directory edits. | 470 | ;; Emacs 21 has no buffer file name for directory edits. |
| 471 | ;; so we need to add these hacks in. | 471 | ;; so we need to add these hacks in. |
| 472 | (eq major-mode 'dired-mode) | 472 | (eq major-mode 'dired-mode) |
| 473 | (eq major-mode 'vc-dired-mode)) | 473 | (eq major-mode 'vc-dir-mode)) |
| 474 | (ede-minor-mode 1))) | 474 | (ede-minor-mode 1))) |
| 475 | 475 | ||
| 476 | (define-minor-mode ede-minor-mode | 476 | (define-minor-mode ede-minor-mode |
| @@ -481,7 +481,7 @@ controlled project, then this mode is activated automatically | |||
| 481 | provided `global-ede-mode' is enabled." | 481 | provided `global-ede-mode' is enabled." |
| 482 | :group 'ede | 482 | :group 'ede |
| 483 | (cond ((or (eq major-mode 'dired-mode) | 483 | (cond ((or (eq major-mode 'dired-mode) |
| 484 | (eq major-mode 'vc-dired-mode)) | 484 | (eq major-mode 'vc-dir-mode)) |
| 485 | (ede-dired-minor-mode (if ede-minor-mode 1 -1))) | 485 | (ede-dired-minor-mode (if ede-minor-mode 1 -1))) |
| 486 | (ede-minor-mode | 486 | (ede-minor-mode |
| 487 | (if (not ede-constructing) | 487 | (if (not ede-constructing) |
diff --git a/lisp/desktop.el b/lisp/desktop.el index de601a4de8c..9d117c6f0d6 100644 --- a/lisp/desktop.el +++ b/lisp/desktop.el | |||
| @@ -534,7 +534,7 @@ can guess how to load the mode's definition.") | |||
| 534 | '((defining-kbd-macro nil) | 534 | '((defining-kbd-macro nil) |
| 535 | (isearch-mode nil) | 535 | (isearch-mode nil) |
| 536 | (vc-mode nil) | 536 | (vc-mode nil) |
| 537 | (vc-dired-mode nil) | 537 | (vc-dir-mode nil) |
| 538 | (erc-track-minor-mode nil) | 538 | (erc-track-minor-mode nil) |
| 539 | (savehist-mode nil)) | 539 | (savehist-mode nil)) |
| 540 | "Table mapping minor mode variables to minor mode functions. | 540 | "Table mapping minor mode variables to minor mode functions. |
diff --git a/lisp/dired-aux.el b/lisp/dired-aux.el index 6f50a3da6ca..60a352d78e0 100644 --- a/lisp/dired-aux.el +++ b/lisp/dired-aux.el | |||
| @@ -3050,6 +3050,68 @@ instead." | |||
| 3050 | (backward-delete-char 1)) | 3050 | (backward-delete-char 1)) |
| 3051 | (message "%s" (buffer-string))))) | 3051 | (message "%s" (buffer-string))))) |
| 3052 | 3052 | ||
| 3053 | |||
| 3054 | ;;; Version control from dired | ||
| 3055 | |||
| 3056 | (declare-function vc-dir-unmark-all-files "vc-dir") | ||
| 3057 | (declare-function vc-dir-mark-files "vc-dir") | ||
| 3058 | |||
| 3059 | ;;;###autoload | ||
| 3060 | (defun dired-vc-next-action (verbose) | ||
| 3061 | "Do the next version control operation on marked files/directories. | ||
| 3062 | When only files are marked then call `vc-next-action' with the | ||
| 3063 | same value of the VERBOSE argument. | ||
| 3064 | When also directories are marked then call `vc-dir' and mark | ||
| 3065 | the same files/directories in the VC-Dir buffer that were marked | ||
| 3066 | in the Dired buffer." | ||
| 3067 | (interactive "P") | ||
| 3068 | (let* ((marked-files | ||
| 3069 | (dired-get-marked-files nil nil nil nil t)) | ||
| 3070 | (mark-files | ||
| 3071 | (when (cl-some #'file-directory-p marked-files) | ||
| 3072 | ;; Fix deficiency of Dired by adding slash to dirs | ||
| 3073 | (mapcar (lambda (file) | ||
| 3074 | (if (file-directory-p file) | ||
| 3075 | (file-name-as-directory file) | ||
| 3076 | file)) | ||
| 3077 | marked-files)))) | ||
| 3078 | (if mark-files | ||
| 3079 | (let ((transient-hook (make-symbol "vc-dir-mark-files"))) | ||
| 3080 | (fset transient-hook | ||
| 3081 | (lambda () | ||
| 3082 | (remove-hook 'vc-dir-refresh-hook transient-hook t) | ||
| 3083 | (vc-dir-unmark-all-files t) | ||
| 3084 | (vc-dir-mark-files mark-files))) | ||
| 3085 | (vc-dir-root) | ||
| 3086 | (add-hook 'vc-dir-refresh-hook transient-hook nil t)) | ||
| 3087 | (vc-next-action verbose)))) | ||
| 3088 | |||
| 3089 | (declare-function vc-compatible-state "vc") | ||
| 3090 | |||
| 3091 | (defun dired-vc-deduce-fileset (&optional state-model-only-files not-state-changing) | ||
| 3092 | (let ((backend (vc-responsible-backend default-directory)) | ||
| 3093 | (files (dired-get-marked-files nil nil nil nil t)) | ||
| 3094 | only-files-list | ||
| 3095 | state | ||
| 3096 | model) | ||
| 3097 | (when (and (not not-state-changing) (cl-some #'file-directory-p files)) | ||
| 3098 | (user-error "State changing VC operations on directories supported only in `vc-dir'")) | ||
| 3099 | |||
| 3100 | (when state-model-only-files | ||
| 3101 | (setq only-files-list (mapcar (lambda (file) (cons file (vc-state file))) files)) | ||
| 3102 | (setq state (cdar only-files-list)) | ||
| 3103 | ;; Check that all files are in a consistent state, since we use that | ||
| 3104 | ;; state to decide which operation to perform. | ||
| 3105 | (dolist (crt (cdr only-files-list)) | ||
| 3106 | (unless (vc-compatible-state (cdr crt) state) | ||
| 3107 | (error "When applying VC operations to multiple files, the files are required\nto be in similar VC states.\n%s in state %s clashes with %s in state %s" | ||
| 3108 | (car crt) (cdr crt) (caar only-files-list) state))) | ||
| 3109 | (setq only-files-list (mapcar 'car only-files-list)) | ||
| 3110 | (when (and state (not (eq state 'unregistered))) | ||
| 3111 | (setq model (vc-checkout-model backend only-files-list)))) | ||
| 3112 | (list backend files only-files-list state model))) | ||
| 3113 | |||
| 3114 | |||
| 3053 | (provide 'dired-aux) | 3115 | (provide 'dired-aux) |
| 3054 | 3116 | ||
| 3055 | ;; Local Variables: | 3117 | ;; Local Variables: |
diff --git a/lisp/dired.el b/lisp/dired.el index 41bbf9f56a2..72d1cc250a3 100644 --- a/lisp/dired.el +++ b/lisp/dired.el | |||
| @@ -1870,6 +1870,7 @@ Do so according to the former subdir alist OLD-SUBDIR-ALIST." | |||
| 1870 | (define-key map "\177" 'dired-unmark-backward) | 1870 | (define-key map "\177" 'dired-unmark-backward) |
| 1871 | (define-key map [remap undo] 'dired-undo) | 1871 | (define-key map [remap undo] 'dired-undo) |
| 1872 | (define-key map [remap advertised-undo] 'dired-undo) | 1872 | (define-key map [remap advertised-undo] 'dired-undo) |
| 1873 | (define-key map [remap vc-next-action] 'dired-vc-next-action) | ||
| 1873 | ;; thumbnail manipulation (image-dired) | 1874 | ;; thumbnail manipulation (image-dired) |
| 1874 | (define-key map "\C-td" 'image-dired-display-thumbs) | 1875 | (define-key map "\C-td" 'image-dired-display-thumbs) |
| 1875 | (define-key map "\C-tt" 'image-dired-tag-files) | 1876 | (define-key map "\C-tt" 'image-dired-tag-files) |
diff --git a/lisp/vc/vc-dir.el b/lisp/vc/vc-dir.el index b760e170676..ab5943917b8 100644 --- a/lisp/vc/vc-dir.el +++ b/lisp/vc/vc-dir.el | |||
| @@ -696,6 +696,17 @@ share the same state." | |||
| 696 | (vc-dir-mark-file crt))) | 696 | (vc-dir-mark-file crt))) |
| 697 | (setq crt (ewoc-next vc-ewoc crt)))))))) | 697 | (setq crt (ewoc-next vc-ewoc crt)))))))) |
| 698 | 698 | ||
| 699 | (defun vc-dir-mark-files (mark-files) | ||
| 700 | "Mark files specified by file names in the argument MARK-FILES. | ||
| 701 | MARK-FILES should be a list of absolute filenames." | ||
| 702 | (ewoc-map | ||
| 703 | (lambda (filearg) | ||
| 704 | (when (member (expand-file-name (vc-dir-fileinfo->name filearg)) | ||
| 705 | mark-files) | ||
| 706 | (setf (vc-dir-fileinfo->marked filearg) t) | ||
| 707 | t)) | ||
| 708 | vc-ewoc)) | ||
| 709 | |||
| 699 | (defun vc-dir-unmark-file () | 710 | (defun vc-dir-unmark-file () |
| 700 | ;; Unmark the current file and move to the next line. | 711 | ;; Unmark the current file and move to the next line. |
| 701 | (let* ((crt (ewoc-locate vc-ewoc)) | 712 | (let* ((crt (ewoc-locate vc-ewoc)) |
| @@ -1193,7 +1204,8 @@ Throw an error if another update process is in progress." | |||
| 1193 | (if remaining | 1204 | (if remaining |
| 1194 | (vc-dir-refresh-files | 1205 | (vc-dir-refresh-files |
| 1195 | (mapcar 'vc-dir-fileinfo->name remaining)) | 1206 | (mapcar 'vc-dir-fileinfo->name remaining)) |
| 1196 | (setq mode-line-process nil)))))))))))) | 1207 | (setq mode-line-process nil) |
| 1208 | (run-hooks 'vc-dir-refresh-hook)))))))))))) | ||
| 1197 | 1209 | ||
| 1198 | (defun vc-dir-show-fileentry (file) | 1210 | (defun vc-dir-show-fileentry (file) |
| 1199 | "Insert an entry for a specific file into the current *VC-dir* listing. | 1211 | "Insert an entry for a specific file into the current *VC-dir* listing. |
diff --git a/lisp/vc/vc.el b/lisp/vc/vc.el index 607fb37807c..d4323d59eb3 100644 --- a/lisp/vc/vc.el +++ b/lisp/vc/vc.el | |||
| @@ -1006,12 +1006,18 @@ Within directories, only files already under version control are noticed." | |||
| 1006 | 1006 | ||
| 1007 | (declare-function vc-dir-current-file "vc-dir" ()) | 1007 | (declare-function vc-dir-current-file "vc-dir" ()) |
| 1008 | (declare-function vc-dir-deduce-fileset "vc-dir" (&optional state-model-only-files)) | 1008 | (declare-function vc-dir-deduce-fileset "vc-dir" (&optional state-model-only-files)) |
| 1009 | (declare-function dired-vc-deduce-fileset "dired-aux" (&optional state-model-only-files not-state-changing)) | ||
| 1009 | 1010 | ||
| 1010 | (defun vc-deduce-fileset (&optional observer allow-unregistered | 1011 | (defun vc-deduce-fileset (&optional not-state-changing |
| 1012 | allow-unregistered | ||
| 1011 | state-model-only-files) | 1013 | state-model-only-files) |
| 1012 | "Deduce a set of files and a backend to which to apply an operation. | 1014 | "Deduce a set of files and a backend to which to apply an operation. |
| 1013 | Return (BACKEND FILESET FILESET-ONLY-FILES STATE CHECKOUT-MODEL). | 1015 | Return (BACKEND FILESET FILESET-ONLY-FILES STATE CHECKOUT-MODEL). |
| 1014 | 1016 | ||
| 1017 | NOT-STATE-CHANGING if non-nil, means that the operation | ||
| 1018 | requesting the fileset doesn't intend to change VC state, | ||
| 1019 | such as printing the log or showing the diff. | ||
| 1020 | |||
| 1015 | If we're in VC-dir mode, FILESET is the list of marked files, | 1021 | If we're in VC-dir mode, FILESET is the list of marked files, |
| 1016 | or the directory if no files are marked. | 1022 | or the directory if no files are marked. |
| 1017 | Otherwise, if in a buffer visiting a version-controlled file, | 1023 | Otherwise, if in a buffer visiting a version-controlled file, |
| @@ -1025,14 +1031,12 @@ the FILESET-ONLY-FILES STATE and MODEL info. Otherwise, that | |||
| 1025 | part may be skipped. | 1031 | part may be skipped. |
| 1026 | 1032 | ||
| 1027 | BEWARE: this function may change the current buffer." | 1033 | BEWARE: this function may change the current buffer." |
| 1028 | ;; FIXME: OBSERVER is unused. The name is not intuitive and is not | ||
| 1029 | ;; documented. It's set to t when called from diff and print-log. | ||
| 1030 | (let (backend) | 1034 | (let (backend) |
| 1031 | (cond | 1035 | (cond |
| 1032 | ((derived-mode-p 'vc-dir-mode) | 1036 | ((derived-mode-p 'vc-dir-mode) |
| 1033 | (vc-dir-deduce-fileset state-model-only-files)) | 1037 | (vc-dir-deduce-fileset state-model-only-files)) |
| 1034 | ((derived-mode-p 'dired-mode) | 1038 | ((derived-mode-p 'dired-mode) |
| 1035 | (vc-dired-deduce-fileset state-model-only-files observer)) | 1039 | (dired-vc-deduce-fileset state-model-only-files not-state-changing)) |
| 1036 | ((setq backend (vc-backend buffer-file-name)) | 1040 | ((setq backend (vc-backend buffer-file-name)) |
| 1037 | (if state-model-only-files | 1041 | (if state-model-only-files |
| 1038 | (list backend (list buffer-file-name) | 1042 | (list backend (list buffer-file-name) |
| @@ -1048,7 +1052,7 @@ BEWARE: this function may change the current buffer." | |||
| 1048 | (derived-mode-p 'dired-mode))))) | 1052 | (derived-mode-p 'dired-mode))))) |
| 1049 | (progn ;FIXME: Why not `with-current-buffer'? --Stef. | 1053 | (progn ;FIXME: Why not `with-current-buffer'? --Stef. |
| 1050 | (set-buffer vc-parent-buffer) | 1054 | (set-buffer vc-parent-buffer) |
| 1051 | (vc-deduce-fileset observer allow-unregistered state-model-only-files))) | 1055 | (vc-deduce-fileset not-state-changing allow-unregistered state-model-only-files))) |
| 1052 | ((and (derived-mode-p 'log-view-mode) | 1056 | ((and (derived-mode-p 'log-view-mode) |
| 1053 | (setq backend (vc-responsible-backend default-directory))) | 1057 | (setq backend (vc-responsible-backend default-directory))) |
| 1054 | (list backend nil)) | 1058 | (list backend nil)) |
| @@ -1065,32 +1069,6 @@ BEWARE: this function may change the current buffer." | |||
| 1065 | (list buffer-file-name)))) | 1069 | (list buffer-file-name)))) |
| 1066 | (t (error "File is not under version control"))))) | 1070 | (t (error "File is not under version control"))))) |
| 1067 | 1071 | ||
| 1068 | (declare-function dired-get-marked-files "dired" | ||
| 1069 | (&optional localp arg filter distinguish-one-marked error)) | ||
| 1070 | |||
| 1071 | (defun vc-dired-deduce-fileset (&optional state-model-only-files observer) | ||
| 1072 | (let ((backend (vc-responsible-backend default-directory)) | ||
| 1073 | (files (dired-get-marked-files nil nil nil nil t)) | ||
| 1074 | only-files-list | ||
| 1075 | state | ||
| 1076 | model) | ||
| 1077 | (when (and (not observer) (cl-some #'file-directory-p files)) | ||
| 1078 | (error "State changing VC operations on directories not supported in `dired-mode'")) | ||
| 1079 | |||
| 1080 | (when state-model-only-files | ||
| 1081 | (setq only-files-list (mapcar (lambda (file) (cons file (vc-state file))) files)) | ||
| 1082 | (setq state (cdar only-files-list)) | ||
| 1083 | ;; Check that all files are in a consistent state, since we use that | ||
| 1084 | ;; state to decide which operation to perform. | ||
| 1085 | (dolist (crt (cdr only-files-list)) | ||
| 1086 | (unless (vc-compatible-state (cdr crt) state) | ||
| 1087 | (error "When applying VC operations to multiple files, the files are required\nto be in similar VC states.\n%s in state %s clashes with %s in state %s" | ||
| 1088 | (car crt) (cdr crt) (caar only-files-list) state))) | ||
| 1089 | (setq only-files-list (mapcar 'car only-files-list)) | ||
| 1090 | (when (and state (not (eq state 'unregistered))) | ||
| 1091 | (setq model (vc-checkout-model backend only-files-list)))) | ||
| 1092 | (list backend files only-files-list state model))) | ||
| 1093 | |||
| 1094 | (defun vc-ensure-vc-buffer () | 1072 | (defun vc-ensure-vc-buffer () |
| 1095 | "Make sure that the current buffer visits a version-controlled file." | 1073 | "Make sure that the current buffer visits a version-controlled file." |
| 1096 | (cond | 1074 | (cond |