File Management¶
Alternative to shell¶
For the file management tasks like rename and delete, I’d like to wrapper it as a Lisp function and call it directly in Emacs.
Rename the buffer-visiting file, and also rename the buffer. Similar to the save as idea but will remove the older file.
;; rename current buffer-visiting file
(defun yt/rename-current-buffer-file ()
"Renames current buffer and file it is visiting."
(interactive)
(let ((name (buffer-name))
(filename (buffer-file-name)))
(if (not (and filename (file-exists-p filename)))
(error "Buffer '%s' is not visiting a file!" name)
(let ((new-name (read-file-name "New name: " filename)))
(if (get-buffer new-name)
(error "A buffer named '%s' already exists!" new-name)
(rename-file filename new-name 1)
(rename-buffer new-name)
(set-visited-file-name new-name)
(set-buffer-modified-p nil)
(message "File '%s' successfully renamed to '%s'"
name (file-name-nondirectory new-name)))))))
Another useful Lisp function is to copy the file path to clipboard for cross reference.
;; full path of current buffer
(defun yt/copy-full-path-to-kill-ring ()
"copy buffer's full path to kill ring"
(interactive)
(when buffer-file-name
(let* ((file-truename buffer-file-name))
;;(rel-name (file-relative-name file-truename "~/"))) ; BUG: if filename is not relative to home directory.
;; (kill-new (concat "~/" rel-name)))))
(kill-new file-truename))))
Open a file as a root user in Emacs, very handy.
(defun yt/sudo-find-file (file-name)
"Like find file, but opens the file as root."
(interactive "FSudo Find File: ")
(let ((tramp-file-name (concat "/sudo::" (expand-file-name file-name))))
(find-file tramp-file-name)))
Find out the last modified date for current buffer, I need this often when updating a blog post or documents.
(defun yt/last-updated-date ()
"return modification time of current file-visitng buffer"
(interactive)
(let* ((mtime (visited-file-modtime)))
(unless (integerp mtime)
(concat "/Last UPdated/: "
(format-time-string "%d %b %Y" mtime)))))
Remove current buffer-visiting file, and kill the buffer. I use this function often in testing and trying out.
(defun yt/delete-this-buffer-and-file ()
"Removes file connected to current buffer and kills buffer."
(interactive)
(let ((filename (buffer-file-name))
(buffer (current-buffer))
(name (buffer-name)))
(if (not (and filename (file-exists-p filename)))
(error "Buffer '%s' is not visiting a file!" name)
(when (yes-or-no-p "Are you sure you want to remove this file? ")
(delete-file filename)
(kill-buffer buffer)
(message "File '%s' successfully removed" filename)))))
It is a good practise to group all the file management related commands together using hydra.
(defhydra hydra-file-management (:color red
:hint nil)
"
_o_pen file
_O_pen file as Sudo user
copy file _P_ath to kill ring
_r_ename buffer-visiting file
_d_elete buffer-visiting file
open with _e_xternal application
_g_it sync"
("o" find-file)
("O" yt/sudo-find-file)
("P" yt/copy-full-path-to-kill-ring)
("r" yt/rename-current-buffer-file)
("c" yt/copy-file-to)
("d" yt/delete-this-buffer-and-file)
("e" prelude-open-with)
("g" yt/git-up))
(global-set-key [f3] 'hydra-file-management/body)
Open the file manager at the default directory.
;; http://ergoemacs.org/emacs/emacs_dired_open_file_in_ext_apps.html
(defun yt/open-file-manager ()
"Show current file in desktop (OS's file manager)."
(interactive)
(cond
((string-equal system-type "windows-nt")
(w32-shell-execute "explore" (replace-regexp-in-string "/" "\\" default-directory t t)))
((string-equal system-type "darwin") (shell-command "open ."))
((string-equal system-type "gnu/linux")
(let ((process-connection-type nil)) (start-process "" nil "xdg-open" "."))
;; (shell-command "xdg-open .") ;; 2013-02-10 this sometimes froze emacs till the folder is closed. ⁖ with nautilus
)))
Projectile - Directory Access¶
Projectile is an powerful Emacs package but I only use projectile
to jump between different git folders, so there isn’t much
configuration except using helm
for selection.
(require 'projectile)
(projectile-mode +1)
(define-key projectile-mode-map (kbd "C-c p") 'projectile-command-map)
(helm-projectile-on)
(require 'helm-projectile)
(projectile-global-mode)
(setq projectile-enable-caching t)
(setq projectile-switch-project-action 'projectile-dired)
(setq projectile-remember-window-configs t )
(setq projectile-completion-system 'helm)
(setq projectile-switch-project-action 'helm-projectile)
(setq projectile-project-root-files-bottom-up '(".git" ".projectile")) ;; .projectile comes first
There are many things work out of box. For example, use C-p p
to
choose which project to jump to, but I can type M-g
to invoke Magit
or M-e
to invoke Eshell for that project.
Remote (SSH)¶
I can work on the remote files in Emacs via ssh or tramp, both are build-in packages.
(require 'tramp)
(require 'ssh)
I’d like catch the password so that I don’t need to type it every time to open a file.
(setq password-cache-expiry nil)
I mainly run R on a remote machine. Sometimes I want to copy the charts I created to local to include them in my report. This workfow is suspended because it fails when the file size is large.
;; (defun yt/sync-local-remote ()
;; (interactive)
;; "copy all files in remote:~/LR_share to local:~/LR_share,
;; does not support the ther way"
;; (find-file "/ssh:remote_host:/remote_directory")
;; ;; (mark-whole-buffer)
;; (dired-mark-subdir-files)
;; ;; (find-file "~/LR_share")
;; ;; (setq-local dirqed-dwim-target t)
;; (dired-do-copy))
Git Sync¶
I use git and Github a lot, and usually in shell-mode
, but I just
can’t remember all the commands. Magit provides an interface to Git,
and it is really pleasant to use. So I don’t need to remmeber all the
commands, also it comes with excellent manual and cheatsheet.
(require 'magit)
(setq magit-last-seen-setup-instructions "1.4.0")
(setq magit-auto-revert-mode nil)
(global-set-key (kbd "<f9> g") 'magit-status)
(global-auto-revert-mode t)
Occasionally my office machine goes down because I run R with big data, and it consumes all the memory. If that happens, I potentially lose the newsiest version of scripts, which is bit annoy. The following snippets will save all buffers in every hours.
(defun yt/save-all-buffers ()
"save all files-visiting buffers without user confirmation"
(interactive)
(save-some-buffers t nil)
(message "save all buffers... done"))
(run-at-time "05:59" 3600 'yt/save-all-buffers)
Sometimes I have to leave at the last minutes, then what I do is call a functions that commits and upload to the repo so that I can continue work at home.
The yt/git-up
function will do
- pull from the remote repo, and make sure the local repo is always up-to-date.
- add everything and commit with a timesamp.
- push local changes to the remote repo.
Here is the snippets.
(defun yt/git-backup ()
(let ((git-sh-scripts "
echo Start to Sync: $(date)
REPOS=\"org\"
for REPO in $REPOS
do
echo
echo \"Repository: $REPO\"
cd ~/git/$REPO
# update
git pull
# Remove deleted files
git ls-files --deleted -z | xargs -0 git rm >/dev/null 2>&1
# Add new files
git add -A . >/dev/null 2>&1
git commit -m \"$(date)\"
git push origin master
done
echo Finished Sync: $(date)
"))
(async-shell-command git-sh-scripts))
(message "all git sync... done"))
(defun yt/git-up ()
(interactive)
(yt/save-all-buffers)
(yt/git-backup))
Few times I did some important work over the weenend, but once I arrived office I realised I forgot uploading, These situations are quick frustrating. The following snippets will start to uploads once every three hours on my MacbookPro, but I don’t use it anymore, since I can get most of my work done in the office.
Note this workflow is suspended for it’s unsafe.
;; (cond ((eq system-type 'darwin)
;; (run-at-time "05:59" 10800 'yt/git-up)))
Testing Buffers¶
scratch buffer is usually used for testing Emacs lisp functions. I
also need temporary buffers for testing R code and org-mode. In the
following settings, I can use F9-f
to select temporal buffers.
(defvar yt/temp-dir "~/.tmp"
"temporay folders")
(defun yt/open-tmp-R ()
(interactive)
(find-file (expand-file-name "tmp.R" yt/temp-dir)))
(defun yt/open-tmp-el ()
(interactive)
(find-file (expand-file-name "tmp.el" yt/temp-dir)))
(defun yt/open-tmp-org ()
(interactive)
(find-file (expand-file-name "tmp.org" yt/temp-dir)))
(global-set-key (kbd "<f9> f r") 'yt/open-tmp-R)
(global-set-key (kbd "<f9> f e") 'yt/open-tmp-el)
(global-set-key (kbd "<f9> f o") 'yt/open-tmp-org)
Frequently visiting buffers¶
(defun yt/org-find-file (filepath)
(interactive)
(find-file (expand-file-name filepath "~/git/org") nil))
(defhydra hydra/open-common-files (:color blue)
"Open file:
"
("R" (find-file "~/git/career/Profession/R.org") "R.org")
("p" (find-file "~/git/career/Profession/Python.org") "Python.org")
("E" (find-file "~/git/career/Profession/Emacs.org") "Emacs.org")
("l" (find-file "~/git/org/life/life.org") "life.org")
("i" (find-file "~/git/.emacs.d/init.org" t) "init.org")
("e" (find-file "~/.emacs" t) ".emacs")
("d" (yt/org-find-file "dournal/diary.org") "diary.org")
("r" (yt/org-find-file "life/review.org") "review.org")
("f" (yt/org-find-file "finance/ledger_transaction_2019.org") "ledger.org")
)
(global-set-key (kbd "<f6>") 'hydra/open-common-files/body)