(defun wdired-change-to-partial-wdired-mode () "Put a Dired buffer in Writable Dired (WDired) mode. \\ In WDired mode, you can edit the names of the files in the buffer, the target of the links, and the permission bits of the files. After typing \\[wdired-finish-edit], Emacs modifies the files and directories to reflect your edits. See `wdired-mode'." (interactive) (unless (derived-mode-p 'dired-mode) (error "Not a Dired buffer")) (setq-local wdired-old-content (buffer-substring (line-beginning-position) (line-end-position))) (setq-local wdired-old-marks (dired-remember-marks (line-beginning-position) (line-end-position))) (setq-local wdired-old-point (point)) (setq-local query-replace-skip-read-only t) (add-function :after-while (local 'isearch-filter-predicate) #'wdired-isearch-filter-read-only) (use-local-map wdired-mode-map) (force-mode-line-update) (setq buffer-read-only nil) (dired-unadvertise default-directory) (add-hook 'kill-buffer-hook 'wdired-check-kill-buffer nil t) (add-hook 'after-change-functions 'wdired--restore-properties nil t) (setq major-mode 'wdired-mode) (setq mode-name "Partially Editable Dired") (setq revert-buffer-function 'wdired-revert) ;; I temp disable undo for performance: since I'm going to clear the ;; undo list, it can save more than a 9% of time with big ;; directories because setting properties modify the undo-list. (buffer-disable-undo) (wdired-partial-preprocess-files) (if wdired-allow-to-change-permissions (wdired-partial-preprocess-perms)) (if (fboundp 'make-symbolic-link) (wdired-partial-preprocess-symlinks)) (buffer-enable-undo) ; Performance hack. See above. (set-buffer-modified-p nil) (setq buffer-undo-list nil) (run-mode-hooks 'wdired-mode-hook) (message "%s" (substitute-command-keys "Press \\[wdired-finish-edit] when finished \ or \\[wdired-abort-changes] to abort changes"))) (defun wdired-abort-changes () "Abort changes and return to dired mode." (interactive) (let ((inhibit-read-only t)) (if (equal mode-name "Partially Editable Dired") (delete-region (line-beginning-position) (line-end-position)) (erase-buffer)) (insert wdired-old-content) (goto-char wdired-old-point)) (wdired-change-to-dired-mode) (set-buffer-modified-p nil) (setq buffer-undo-list nil) (message "Changes aborted")) ;; Protect the buffer so only the filename can be changed, and put ;; properties so filename (old and new) can be easily found. (defun wdired-partial-preprocess-files () (put-text-property (line-beginning-position) (1+ (line-beginning-position))'front-sticky t) (save-excursion (goto-char (line-beginning-position)) (let ((b-protection (point)) (used-F (dired-check-switches dired-actual-switches "F" "classify")) filename) (setq filename (dired-get-filename nil t)) (when (and filename (not (member (file-name-nondirectory filename) '("." "..")))) (dired-move-to-filename) ;; The rear-nonsticky property below shall ensure that text preceding ;; the filename can't be modified. (add-text-properties (1- (point)) (point) `(old-name ,filename rear-nonsticky (read-only))) (put-text-property b-protection (point) 'read-only t) (dired-move-to-end-of-filename t) (put-text-property (point) (1+ (point)) 'end-name t)) (when (and used-F (looking-at "[*/@|=>]$")) (forward-char)) (when (save-excursion (and (re-search-backward dired-permission-flags-regexp nil t) (looking-at "l") (search-forward " -> " (line-end-position) t))) (goto-char (line-end-position))) (setq b-protection (point)) (put-text-property b-protection (line-end-position) 'read-only t)))) (defun wdired-partial-preprocess-perms () (let ((inhibit-read-only t)) (setq-local wdired-col-perm nil) (save-excursion (goto-char (line-beginning-position)) (when (and (not (looking-at dired-re-sym)) (wdired-get-filename) (re-search-forward dired-re-perms (line-end-position) 'eol)) (let ((begin (match-beginning 0)) (end (match-end 0))) (unless wdired-col-perm (setq wdired-col-perm (- (current-column) 9))) (if (eq wdired-allow-to-change-permissions 'advanced) (progn (put-text-property begin end 'read-only nil) ;; make first permission bit writable (put-text-property (1- begin) begin 'rear-nonsticky '(read-only))) ;; avoid that keymap applies to text following permissions (add-text-properties (1+ begin) end `(keymap ,wdired-perm-mode-map rear-nonsticky (keymap)))) (put-text-property end (1+ end) 'end-perm t) (put-text-property begin (1+ begin) 'old-perm (match-string-no-properties 0))))))) (defun wdired-partial-preprocess-symlinks () (let ((inhibit-read-only t)) (save-excursion (goto-char (line-beginning-position)) (when (looking-at dired-re-sym) (re-search-forward " -> \\(.*\\)$") (put-text-property (1- (match-beginning 1)) (match-beginning 1) 'old-link (match-string-no-properties 1)) (put-text-property (match-end 1) (1+ (match-end 1)) 'end-link t) (unless wdired-allow-to-redirect-links (put-text-property (match-beginning 0) (match-end 1) 'read-only t))))))