Last active
October 1, 2019 12:00
-
-
Save QiangF/c85dd8aacc1fea815f44ccccbc0381e6 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;;; an collection of functions that I developed to execute some commands | |
;;; asynchronously | |
;;; only run ob unix-based systems | |
;;; TODO: stick the output window with the result buffer | |
;;; using dedicated window | |
;;; TODO: shortcut keys for close the result window | |
;;; TODO: check process exit status, if not success, not close the result window | |
;;; TODO: undo function | |
;;; ---------------------------------------------- | |
;;; ---------------------------------------------- | |
(require 'dash) | |
(require 'notify) | |
(defvar my-dired-mutex (make-mutex "my-dired mutex")) | |
(defvar tda/rsync-multiple-file-list | |
() "The list of the files to be copied") | |
(defvar tda/rsync-multiple-failed-list | |
() "The alist (dir . failed-file-list) of the files to be manualled processed") | |
(defvar tda/rsync-modified-buffer-list | |
()) | |
(defun my-dired-utils-goto-line (filename) | |
"Go to line describing FILENAME in listing. | |
Should be absolute file name matched against | |
`dired-get-filename'." | |
(goto-char (point-min)) | |
(let (stop) | |
(while (and (not stop) | |
(= (forward-line) 0)) | |
(when (equal filename (dired-get-filename nil t)) | |
(setq stop t) | |
(dired-move-to-filename))) | |
stop)) | |
;;;###autoload | |
(defun dired-ranger--revert-target (char target-directory files) | |
"Revert the target buffer and mark the new files. | |
CHAR is the temporary value for `dired-marker-char'. | |
TARGET-DIRECTORY is the current dired directory. | |
FILES is the list of files (from the `dired-ranger-copy-ring') we | |
operated on." | |
(let ((current-file (dired-get-filename nil t))) | |
(revert-buffer) | |
(let ((dired-marker-char char)) | |
(--each (-map 'file-name-nondirectory files) | |
(my-dired-utils-goto-line (concat target-directory it)) | |
(dired-mark 1))) | |
(my-dired-utils-goto-line current-file))) | |
;; use thread | |
;;;###autoload | |
(defun my-clean-failed-tasks () | |
"Create a dired buffer for tda/rsync-multiple-failed-list and process it manually" | |
(interactive) | |
(let* ((item (pop tda/rsync-multiple-failed-list))) | |
(when item | |
(dired (car item)) | |
(delete-other-windows) | |
(split-window-right) | |
(dired (cons "failed file list" (cdr item))) | |
))) | |
(defun my-dired-move (arg) | |
"(git mv rsyn) move tda/rsync-multiple-file-list to current directory." | |
(interactive "P") | |
(async-start | |
`(lambda () | |
,(async-inject-variables "^tda/rsync-multiple-file-list$") | |
,(async-inject-variables "^tda/rsync-modified-buffer-list$") | |
,(let* ((file-list tda/rsync-multiple-file-list) | |
(modified-buffer-list tda/rsync-modified-buffer-list) | |
(target-directory (dired-current-directory)) | |
moved-list failed-list) | |
(dolist (it file-list (list target-directory modified-buffer-list moved-list failed-list)) | |
(let (exit-status) | |
(setq exit-status | |
(call-process-shell-command | |
(concat "git mv " (shell-quote-argument it) " " (shell-quote-argument target-directory)) | |
nil " *git mv*")) | |
(unless (= 0 exit-status) | |
;; https://gnu.huihoo.org/emacs/24.4/emacs-lisp/Standard-Errors.html | |
(if (string= (shell-command-to-string | |
(concat "df --output=target " (shell-quote-argument it))) | |
(shell-command-to-string | |
(concat "df --output=target " (shell-quote-argument target-directory)))) | |
;; on the same file system | |
(ignore-errors | |
(progn | |
(rename-file it target-directory nil) | |
(setq exit-status 0))) | |
;; on different file systems | |
(setq exit-status | |
(call-process-shell-command | |
(concat "rsync -avz --progress --delete " (shell-quote-argument it) " " (shell-quote-argument target-directory)))) | |
;; (with-current-buffer (dired-find-buffer-nocreate target-directory) | |
;; (setq mode-line-process | |
;; (format "%d/%d " | |
;; (length moved-list) | |
;; (length file-list)))) | |
(notify-via-dbus "Moving files:" (format "%d/%d to %s" | |
(length moved-list) | |
(length file-list) | |
target-directory)))) | |
(if (= exit-status 0) | |
(let ((buf (get-file-buffer it))) | |
(add-to-list 'moved-list it) | |
(when buf | |
;; redirect opened buffer to new file path see vc-rename-file | |
(with-current-buffer buf | |
(set-visited-file-name (concat target-directory (file-name-nondirectory it)) nil t)))) | |
(add-to-list 'failed-list it)))))) | |
(lambda (result) | |
(let ((target-directory (nth 0 result)) | |
(modified-buffer-list (nth 1 result)) | |
(moved-list (nth 2 result)) | |
(failed-list (nth 3 result))) | |
(with-mutex my-dired-mutex | |
;; mark moved files in target dir | |
(when moved-list | |
(dired-ranger--revert-target ?M target-directory moved-list)) | |
;; add failed-list to tda/rsync-multiple-failed-list | |
(if failed-list | |
(progn | |
(setf (alist-get target-directory tda/rsync-multiple-failed-list nil t 'equal) | |
(delete-dups (append failed-list (alist-get target-directory tda/rsync-multiple-failed-list nil nil 'equal)))) | |
(message "Warning: some tasks failed, please run my-clean-failed-tasks!")) | |
(message (format "Moved %d/%d to %s" | |
(length moved-list) | |
(length file-list) | |
target-directory))) | |
;; revert affected dired buffers | |
(dolist (it modified-buffer-list nil) | |
(when (buffer-live-p it) | |
(with-current-buffer it (revert-buffer)))))))) | |
(tda/rsync-multiple-empty-list)) | |
;;; get file size | |
(defvar tda/get-files-size-command "du" | |
"The name of \"du\" command (or the path to the \"du\" command)") | |
(defvar tda/get-files-size-arguments "-hc" | |
"The arguments for passing into the \"du\" command") | |
;;; get file size | |
(defun tda/get-files-size () | |
"Calculate files size for all the marked files" | |
(interactive) | |
(let ((files (dired-get-marked-files)) command) | |
;; the get files size command | |
(setq command tda/get-files-size-command) | |
(setq command (concat command " " tda/get-files-size-arguments " ")) | |
;; add selected file names as arguments to the command | |
(dolist (file files) | |
(setq command (concat command (shell-quote-argument file) " "))) | |
;; execute the command | |
(tat/execute-async command "file size"))) | |
;;; ---------------------------------------------- | |
;;; ---------------------------------------------- | |
;;; Async Rsync | |
(defvar tda/rsync-command-name "rsync" | |
"The name of rsync command (or the path to the rsync command).") | |
(defvar tda/rsync-arguments "-avz --progress" | |
"The arguments for passing into the rsync command") | |
(defun tda/rsync (dest) | |
"Asynchronously copy file using Rsync for dired. | |
This function runs only on Unix-based system. | |
Usage: same as normal dired copy function." | |
(interactive ;; offer dwim target as the suggestion | |
(list (expand-file-name (read-file-name "Rsync to:" (dired-dwim-target-directory))))) | |
(let ((files (dired-get-marked-files nil current-prefix-arg)) | |
command) | |
;; the rsync command | |
(setq command | |
(concat tda/rsync-command-name " " tda/rsync-arguments " ")) | |
;; add all selected file names as arguments to the rsync command | |
(dolist (file files) | |
(setq command (concat command (shell-quote-argument file) " "))) | |
;; append the destination to the rsync command | |
(setq command (concat command (shell-quote-argument dest))) | |
;; execute the command asynchronously | |
(tat/execute-async command "rsync"))) | |
(defun tda/rsync-sudo (dest) | |
"Asynchronously copy file using Rsync for dired. | |
This function runs only on Unix-based system. | |
Usage: same as normal dired copy function." | |
(interactive ;; offer dwim target as the suggestion | |
(list (expand-file-name (read-file-name "Rsync to:" (dired-dwim-target-directory))))) | |
(let ((files (dired-get-marked-files nil current-prefix-arg)) | |
command) | |
;; the rsync command | |
(setq command | |
(concat "sudo " tda/rsync-command-name " " tda/rsync-arguments " ")) | |
;; add all selected file names as arguments to the rsync command | |
(dolist (file files) | |
(setq command (concat command (shell-quote-argument file) " "))) | |
;; append the destination to the rsync command | |
(setq command (concat command (shell-quote-argument dest))) | |
;; execute the command asynchronously | |
(tat/execute-async command "rsync"))) | |
(defun tda/rsync-delete (dest) | |
"Asynchronously copy file using Rsync for dired include the delete option | |
This function runs only on Unix-based system. | |
Usage: same as normal dired copy function." | |
(interactive ;; offer dwim target as the suggestion | |
(list (expand-file-name (read-file-name "Rsync delete to:" (dired-dwim-target-directory))))) | |
(let ((files (dired-get-marked-files nil current-prefix-arg)) | |
command) | |
;; the rsync command | |
(setq command | |
(concat tda/rsync-command-name " " tda/rsync-arguments " --delete ")) | |
;; add all selected file names as arguments to the rsync command | |
(dolist (file files) | |
(setq command (concat command (shell-quote-argument file) " "))) | |
;; append the destination to the rsync command | |
(setq command (concat command (shell-quote-argument dest))) | |
;; execute the command asynchronously | |
(tat/execute-async command "rsync"))) | |
(defun tda/rsync-delete-sudo (dest) | |
"Asynchronously copy file using Rsync for dired include the delete option | |
This function runs only on Unix-based system. | |
Usage: same as normal dired copy function." | |
(interactive ;; offer dwim target as the suggestion | |
(list (expand-file-name (read-file-name "Rsync delete to:" (dired-dwim-target-directory))))) | |
(let ((files (dired-get-marked-files nil current-prefix-arg)) | |
command) | |
;; the rsync command | |
(setq command | |
(concat "sudo " tda/rsync-command-name " " tda/rsync-arguments " --delete ")) | |
;; add all selected file names as arguments to the rsync command | |
(dolist (file files) | |
(setq command (concat command (shell-quote-argument file) " "))) | |
;; append the destination to the rsync command | |
(setq command (concat command (shell-quote-argument dest))) | |
;; execute the command asynchronously | |
(tat/execute-async command "rsync"))) | |
;;; ---------------------------------------------- | |
;;; ---------------------------------------------- | |
;;; async zip files | |
(defvar tda/zip-command "zip" | |
"The command name (or the path to the zip command") | |
(defvar tda/zip-arguments | |
"-ru9" "The compression level for dired async zip command, from 0-9. This variable is a string, so if you change this value, please set it as a string.") | |
(defun tda/zip (output) | |
"Asynchronously compress marked files to the output file" | |
(interactive | |
(list (expand-file-name (read-file-name "Add to file: ")))) | |
(let (command | |
(files (dired-get-marked-files nil current-prefix-arg))) | |
;; the zip command | |
(setq command | |
(concat tda/zip-command " " tda/zip-arguments " ")) | |
;; append the output file | |
(setq command | |
(concat command (shell-quote-argument output) " ")) | |
;; add all selected files as argument | |
(dolist (file files) | |
(setq command | |
(concat command | |
(shell-quote-argument | |
(file-name-nondirectory file)) " "))) | |
(message command) | |
;; execute the command asynchronously | |
(tat/execute-async command "zip"))) | |
;;; ---------------------------------------------- | |
;;; ---------------------------------------------- | |
;;; Uncompress function | |
(defvar tda/unzip-command "unzip" | |
"The command name (or path to the unzip command)") | |
(defvar tda/unzip-arguments "" | |
"The arguments for passing into the unzip command") | |
(defun tda/unzip () | |
"Asynchronously decompress the zip file at point" | |
(interactive) | |
(let (command | |
output-directory | |
(file (dired-get-filename 'verbatim))) | |
;; new directory name for the output files | |
(setq output-directory | |
(file-name-sans-extension | |
(dired-get-filename 'verbatim))) | |
;; the unzip command | |
(setq command (concat tda/unzip-command " " tda/unzip-arguments " ")) | |
;; append the file name | |
(setq command | |
(concat command | |
(shell-quote-argument file) " ")) | |
;; append the output directory name | |
(setq command | |
(concat command "-d " | |
(shell-quote-argument output-directory))) | |
;; execute the command asynchronously | |
(tat/execute-async command "unzip"))) | |
;;; ---------------------------------------------- | |
;;; ---------------------------------------------- | |
;;; Rsync from multiple directories | |
(defun tda/rsync-multiple-mark-file () | |
"Add file to waiting list for copying" | |
(interactive) | |
;; Add file to the list | |
(let ((files (dired-get-marked-files nil current-prefix-arg))) | |
(mapc (lambda (item) (add-to-list 'tda/rsync-multiple-file-list item)) files) | |
;; Message for user | |
(add-to-list 'tda/rsync-modified-buffer-list (current-buffer)) | |
(message "Marked file added to waiting list."))) | |
(defun tda/rsync-multiple-empty-list () | |
"Empty the waiting list" | |
(interactive) | |
;; Empty the list | |
(setq tda/rsync-multiple-file-list '()) | |
(setq tda/rsync-modified-buffer-list '()) | |
;; message for the user | |
(message "Waiting list empty.")) | |
(defun tda/rsync-multiple-remove-item () | |
"Remove the file at point from the waiting list if it is in" | |
(interactive) | |
(let ((files (dired-get-filename))) | |
;; remove the item from the list | |
(setq tda/rsync-multiple-file-list | |
(dolist (file files) | |
(remove file tda/rsync-multiple-file-list))) | |
;; message for the use | |
(message | |
(concat "Marked file(s) removed from the list.")))) | |
;; Copy file from multiple directories | |
(defun tda/rsync-multiple () | |
"Mark file in multiple places and then paste in 1 directory" | |
(interactive) | |
(let (command) | |
(if (equal tda/rsync-multiple-file-list ()) | |
(progn | |
(message "Please add file to the waiting list.")) | |
(progn | |
;; the rsync command | |
(setq command (concat tda/rsync-command-name " " tda/rsync-arguments " ")) | |
;; add all selected file names as arguments to the rsync command | |
(dolist (file tda/rsync-multiple-file-list) | |
(setq command | |
(concat command (shell-quote-argument file) " "))) | |
;; append the destination to the rsync command | |
(setq command | |
(concat command | |
(shell-quote-argument (expand-file-name default-directory)))) | |
;; execute the command asynchronously | |
(tat/execute-async command "rsync") | |
;; empty the waiting list | |
(tda/rsync-multiple-empty-list))))) | |
;; tda/rsync-multiple replacement | |
(defun my-dired-paste () | |
(interactive) | |
(make-thread | |
(lambda () | |
(let* ((file-list tda/rsync-multiple-file-list) | |
(modified-buffer-list tda/rsync-modified-buffer-list) | |
(target-directory (dired-current-directory)) | |
moved-list failed-list) | |
(tda/rsync-multiple-empty-list) | |
(--each file-list | |
(let (exit-status) | |
(setq exit-status | |
(call-process-shell-command | |
(concat "rsync -avz --progress --delete " (shell-quote-argument it) " " (shell-quote-argument target-directory)))) | |
(if (= exit-status 0) | |
(let ((buf (get-file-buffer it))) | |
(add-to-list 'moved-list it) | |
(with-current-buffer (dired-find-buffer-nocreate target-directory) | |
(setq mode-line-process | |
(format "%d/%d " | |
(length moved-list) | |
(length file-list)))) | |
(message (format "Moved %d/%d to %s" | |
(length moved-list) | |
(length file-list) | |
target-directory)) | |
(when buf | |
;; redirect opened buffer to new file path see vc-rename-file | |
(with-current-buffer buf | |
(set-visited-file-name (concat target-directory (file-name-nondirectory it)) nil t)))) | |
(add-to-list 'failed-list it)))) | |
(with-mutex my-dired-mutex | |
;; add failed-list to tda/rsync-multiple-failed-list | |
(if failed-list | |
(progn | |
(setf (alist-get target-directory tda/rsync-multiple-failed-list nil t 'equal) | |
(delete-dups (append failed-list (alist-get target-directory tda/rsync-multiple-failed-list nil nil 'equal)))) | |
(message "Warning: some tasks failed, please run my-clean-failed-tasks!")) | |
(message (format "Copied %d/%d to %s" | |
(length moved-list) | |
(length file-list) | |
target-directory))) | |
;; add failed-list to tda/rsync-multiple-failed-list | |
;; mark moved files in target dir | |
(when moved-list | |
(dired-ranger--revert-target ?M target-directory moved-list)) | |
;; revert affected dired buffers | |
(--each modified-buffer-list | |
(when (buffer-live-p it) | |
(with-current-buffer it (revert-buffer))))))))) | |
;;; ---------------------------------------------- | |
;;; ---------------------------------------------- | |
;;; download file to current dir | |
(defvar tda/download-command "wget" | |
"The download program to download to current dir. The default is wget, ou can replace it to curl, aria2c,...") | |
(defun tda/download-to-current-dir (src) | |
"Read the link and download the file to current directory" | |
(interactive (list (read-from-minibuffer "Link: "))) | |
(let ((command "")) | |
;; create the command | |
(setq command (concat command tda/download-command " ")) | |
;; append the link | |
(setq command (concat command (shell-quote-argument src))) | |
;; execute | |
(tat/execute-async command "download"))) | |
(defun tda/download-clipboard-link-to-current-dir () | |
"Read the clipboard link and download it into the current dir" | |
(interactive) | |
(tda/download-to-current-dir (x-get-clipboard))) | |
(provide 'tmtxt-dired-async) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment