;;; syd-file.el -*- lexical-binding: t; -*- (require 'syd-prelude) (require 'syd-buffers) (syd-define-stub syd/copy-this-file :desc "Copy current file. See `doom/copy-this-file'." :interactive t) (defun syd-files--update-refs (&rest files) "Ensure FILES are updated in `recentf', `magit' and `save-place'." (let (toplevels) (dolist (file files) (when (featurep 'vc) (vc-file-clearprops file) (when-let (buffer (get-file-buffer file)) (with-current-buffer buffer (vc-refresh-state)))) (when (featurep 'magit) (when-let (default-directory (magit-toplevel (file-name-directory file))) (cl-pushnew default-directory toplevels))) (unless (file-readable-p file) (when (bound-and-true-p recentf-mode) (recentf-remove-if-non-kept file)) (dolist (default-directory toplevels) (magit-refresh)) (when (bound-and-true-p save-place-mode) (save-place-forget-unreadable-files)))))) (defun syd/delete-this-file (&optional path force-p) "Delete PATH, kill its buffers and expunge it from vc/magit cache. If PATH is not specified, default to the current buffer's file. If FORCE-P, delete without confirmation." (interactive (list (buffer-file-name (buffer-base-buffer)) current-prefix-arg)) (let* ((path (or path (buffer-file-name (buffer-base-buffer)))) (short-path (and path (abbreviate-file-name path)))) (unless path (user-error "Buffer is not visiting any file")) (unless (file-exists-p path) (error "File doesn't exist: %s" path)) (unless (or force-p (y-or-n-p (format "Really delete %S?" short-path))) (user-error "Aborted")) (let ((buf (current-buffer))) (unwind-protect (progn (delete-file path t) t)) (if (file-exists-p path) (error "Failed to delete %S" short-path) ;; Ensures that windows displaying this buffer will be switched to ;; real buffers (`doom-real-buffer-p') (syd/kill-this-buffer-in-all-windows buf t) (syd-files--update-refs path) (message "Deleted %S" short-path))))) (syd-define-stub syd/move-this-file :desc "Move current file. See `doom/move-this-file'." :interactive t) (syd-define-stub syd/find-file-under-emacs-user-directory :desc "Find under `emacs-user-directory'. See `doom/find-file-in-private-config'." :interactive t) (syd-define-stub syd/find-file-under-here :desc "Find under CWD. See `+default/find-file-under-here'." :interactive t) (syd-define-stub syd/yank-buffer-path :desc "Yank buffer path." :interactive t) (defun syd/find-file-in-emacs-user-directory () (interactive) (unless (file-directory-p user-emacs-directory) (user-error "`emacs-user-directory' doesn't exist! (%s)" (abbreviate-file-name emacs-user-directory))) (let ((default-directory user-emacs-directory)) (call-interactively #'find-file))) (syd-define-stub syd/open-this-file-as-root :desc "Open current file as root. See `doom/sudo-this-file'." :interactive t) (syd-define-stub syd/find-file-as-root :desc "Open current file as root. See `doom/sudo-this-file'." :interactive t) (defun syd--make-syncthing-merge-finalise-hook (file-name conflict-file-name) (lambda () (let ((merge-result-file (read-file-name (format "Write merge to (default: %s):" file-name) nil file-name)) (delete-conflict-p (yes-or-no-p (format "Delete conflict file? (%s)" conflict-file-name))))) (when merge-result-file (with-current-buffer ediff-buffer-C (set-visited-file-name merge-result-file) (save-buffer)) (kill-buffer ediff-buffer-C)) (when delete-conflict-p (kill-buffer (find-buffer-visiting conflict-file-name)) (delete-file conflict-file-name)))) (defun syd--read-syncthing-conflict-file (&optional directory) (let ((conflict-files (directory-files-recursively (or directory default-directory) (rx ".sync-conflict-")))) (completing-read "Conflict file: " conflict-files nil t))) (defun syd--syncthing-conflict-file-base-name (conflict-file) (replace-regexp-in-string (rx ".sync-conflict-" (* (not ?.))) "" conflict-file)) (defun syd-resolve-syncthing-conflict (conflict-file) (interactive (list (syd--read-syncthing-conflict-file))) (require 'ediff) (let* ((base-file (syd--syncthing-conflict-file-base-name conflict-file)) (ediff-after-quit-hook-internal ;; Override Ediff's "save and quit" hook with our own. (cons (syd--make-syncthing-merge-finalise-hook base-file conflict-file) (remq #'ediff-write-merge-buffer-and-maybe-kill (ensure-list ediff-quit-merge-hook))))) (ediff-merge-files base-file conflict-file))) (defun syd-split-tramp-file-name (file-name) "Split FILE-NAME into (TRAMP-PREFIX . LOCAL-NAME). Returns (nil . FILE-NAME) if FILE-NAME has no TRAMP prefix." (if (tramp-tramp-file-p file-name) (let* ((dissected (tramp-dissect-file-name file-name t)) (localname (tramp-file-name-localname dissected))) (setf (tramp-file-name-localname dissected) nil) (cons (tramp-make-tramp-file-name dissected) localname)) (cons nil file-name))) (provide 'syd-file)