581 lines
24 KiB
EmacsLisp
581 lines
24 KiB
EmacsLisp
;;; treemacs.el --- A tree style file viewer package -*- lexical-binding: t -*-
|
|
|
|
;; Copyright (C) 2024 Alexander Miller
|
|
|
|
;; This program is free software; you can redistribute it and/or modify
|
|
;; it under the terms of the GNU General Public License as published by
|
|
;; the Free Software Foundation, either version 3 of the License, or
|
|
;; (at your option) any later version.
|
|
|
|
;; This program is distributed in the hope that it will be useful,
|
|
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
;; GNU General Public License for more details.
|
|
|
|
;; You should have received a copy of the GNU General Public License
|
|
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
;;; Commentary:
|
|
|
|
;; Everything related to file management.
|
|
|
|
;; NOTE: This module is lazy-loaded.
|
|
|
|
|
|
;;; Code:
|
|
|
|
(require 'dash)
|
|
(require 'hydra)
|
|
(require 'treemacs-core-utils)
|
|
(require 'treemacs-visuals)
|
|
(require 'treemacs-filewatch-mode)
|
|
(require 'treemacs-logging)
|
|
(require 'treemacs-rendering)
|
|
(require 'treemacs-annotations)
|
|
(require 'treemacs-async)
|
|
|
|
(eval-when-compile
|
|
(require 'inline)
|
|
(require 'treemacs-macros))
|
|
|
|
(declare-function string-join "subr-x.el")
|
|
|
|
(defconst treemacs--mark-annotation-source "treemacs-marked-paths")
|
|
|
|
(defvar-local treemacs--marked-paths nil)
|
|
|
|
(with-eval-after-load 'recentf
|
|
|
|
(declare-function recentf-remove-if-non-kept "recentf")
|
|
(declare-function treemacs--remove-from-recentf-after-move/rename "treemacs-file-management")
|
|
|
|
(defun treemacs--remove-from-recentf-after-move/rename (path _)
|
|
"Remove PATH from recentf after the file was moved or renamed."
|
|
(recentf-remove-if-non-kept path))
|
|
|
|
(add-hook 'treemacs-rename-file-functions #'treemacs--remove-from-recentf-after-move/rename)
|
|
(add-hook 'treemacs-move-file-functions #'treemacs--remove-from-recentf-after-move/rename)
|
|
(add-hook 'treemacs-delete-file-functions #'recentf-remove-if-non-kept))
|
|
|
|
(defconst treemacs--file-node-states
|
|
'(file-node-open file-node-closed dir-node-open dir-node-closed)
|
|
"List of node states treemacs is able to rename/delete etc.")
|
|
|
|
(define-inline treemacs--is-node-file-manageable? (btn)
|
|
"Determines whether BTN is a file node treemacs can rename/delete."
|
|
(declare (side-effect-free t))
|
|
(inline-letevals (btn)
|
|
(inline-quote
|
|
(memq (treemacs-button-get ,btn :state)
|
|
treemacs--file-node-states))))
|
|
|
|
;;;###autoload
|
|
(defun treemacs-delete-file (&optional arg)
|
|
"Delete node at point.
|
|
A delete action must always be confirmed. Directories are deleted recursively.
|
|
By default files are deleted by moving them to the trash. With a prefix ARG
|
|
they will instead be wiped irreversibly."
|
|
(interactive "P")
|
|
(treemacs-block
|
|
(treemacs-unless-let (btn (treemacs-current-button))
|
|
(treemacs-pulse-on-failure "Nothing to delete here.")
|
|
(treemacs-error-return-if (not (memq (treemacs-button-get btn :state)
|
|
'(file-node-open file-node-closed dir-node-open dir-node-closed)))
|
|
"Only files and directories can be deleted.")
|
|
(treemacs--without-filewatch
|
|
(let* ((delete-by-moving-to-trash (not arg))
|
|
(path (treemacs--select-file-from-btn btn "Delete: "))
|
|
(file-name (propertize (treemacs--filename path) 'face 'font-lock-string-face)))
|
|
(cond
|
|
((file-symlink-p path)
|
|
(if (yes-or-no-p (format "Remove link '%s -> %s' ? "
|
|
file-name
|
|
(propertize (file-symlink-p path) 'face 'font-lock-face)))
|
|
(delete-file path delete-by-moving-to-trash)
|
|
(treemacs-return (treemacs-log "Cancelled."))))
|
|
((file-regular-p path)
|
|
(if (yes-or-no-p (format "Delete '%s' ? " file-name))
|
|
(delete-file path delete-by-moving-to-trash)
|
|
(treemacs-return (treemacs-log "Cancelled."))))
|
|
((file-directory-p path)
|
|
(if (yes-or-no-p (format "Recursively delete '%s' ? " file-name))
|
|
(delete-directory path t delete-by-moving-to-trash)
|
|
(treemacs-return (treemacs-log "Cancelled."))))
|
|
(t
|
|
(treemacs-error-return
|
|
(treemacs-pulse-on-failure
|
|
"Item is neither a file, a link or a directory - treemacs does not know how to delete it. (Maybe it no longer exists?)"))))
|
|
(treemacs--on-file-deletion path)
|
|
(treemacs-without-messages
|
|
(treemacs-run-in-every-buffer
|
|
(treemacs-delete-single-node path)))
|
|
(run-hook-with-args 'treemacs-delete-file-functions path)
|
|
(treemacs-log "Deleted %s."
|
|
(propertize path 'face 'font-lock-string-face))))
|
|
(treemacs--evade-image))))
|
|
(defalias 'treemacs-delete #'treemacs-delete-file)
|
|
(make-obsolete #'treemacs-delete #'treemacs-delete-file "v2.9.3")
|
|
|
|
;;;###autoload
|
|
(defun treemacs-delete-marked-files (&optional arg)
|
|
"Delete all marked files.
|
|
|
|
A delete action must always be confirmed. Directories are deleted recursively.
|
|
By default files are deleted by moving them to the trash. With a prefix ARG
|
|
they will instead be wiped irreversibly.
|
|
|
|
For marking files see `treemacs-bulk-file-actions'."
|
|
(interactive "P")
|
|
(treemacs-block
|
|
(let ((delete-by-moving-to-trash (not arg))
|
|
(to-delete (-filter #'file-exists-p treemacs--marked-paths)))
|
|
|
|
(treemacs-error-return-if (null treemacs--marked-paths)
|
|
"There are no marked files")
|
|
|
|
(unless (yes-or-no-p (format "Really delete %s marked files?"
|
|
(length to-delete)))
|
|
(treemacs-return (treemacs-log "Cancelled.")))
|
|
|
|
(treemacs--without-filewatch
|
|
(dolist (path to-delete)
|
|
;; 2nd check in case of recursive deletes
|
|
(when (file-exists-p path)
|
|
(cond
|
|
((or (file-symlink-p path) (file-regular-p path))
|
|
(delete-file path delete-by-moving-to-trash))
|
|
((file-directory-p path)
|
|
(delete-directory path t delete-by-moving-to-trash))))
|
|
(treemacs--on-file-deletion path)
|
|
(treemacs-without-messages
|
|
(treemacs-run-in-every-buffer
|
|
(treemacs-delete-single-node path)))
|
|
(run-hook-with-args 'treemacs-delete-file-functions path))
|
|
(treemacs--evade-image)
|
|
(setf treemacs--marked-paths (-difference treemacs--marked-paths to-delete))
|
|
(treemacs-log "Deleted %s files." (length to-delete))))))
|
|
|
|
;;;###autoload
|
|
(defun treemacs-move-file ()
|
|
"Move file (or directory) at point.
|
|
|
|
If the selected target is an existing directory the source file will be directly
|
|
moved into this directory. If the given target instead does not exist then it
|
|
will be treated as the moved file's new name, meaning the original source file
|
|
will be both moved and renamed."
|
|
(interactive)
|
|
(treemacs--copy-or-move
|
|
:action 'move
|
|
:no-node-msg "There is nothing to move here."
|
|
:wrong-type-msg "Only files and directories can be moved."
|
|
:action-fn #'rename-file
|
|
:prompt "Move to: "
|
|
:flat-prompt "File to copy: "
|
|
:finish-verb "Moved"))
|
|
|
|
;;;###autoload
|
|
(defun treemacs-copy-file ()
|
|
"Copy file (or directory) at point.
|
|
|
|
If the selected target is an existing directory the source file will be directly
|
|
copied into this directory. If the given target instead does not exist then it
|
|
will be treated as the copied file's new name, meaning the original source file
|
|
will be both copied and renamed."
|
|
(interactive)
|
|
(treemacs--copy-or-move
|
|
:action 'copy
|
|
:no-node-msg "There is nothing to move here."
|
|
:wrong-type-msg "Only files and directories can be copied."
|
|
:action-fn (lambda (from to)
|
|
(if (file-directory-p from)
|
|
(copy-directory from to)
|
|
(copy-file from to)))
|
|
:prompt "Copy to: "
|
|
:flat-prompt "File to copy: "
|
|
:finish-verb "Copied"))
|
|
|
|
(cl-defun treemacs--copy-or-move
|
|
(&key
|
|
action
|
|
no-node-msg
|
|
wrong-type-msg
|
|
action-fn
|
|
prompt
|
|
flat-prompt
|
|
finish-verb)
|
|
"Internal implementation for copying and moving files.
|
|
|
|
ACTION: either `copy' or `move'
|
|
NO-NODE-MSG: error message in case there is no node in the current line
|
|
WRONG-TYPE-MSG: error message in case current node is not a file
|
|
ACTION-FN: function to actually copy or move a file
|
|
PROMPT: prompt to read the target directory
|
|
FLAT-PROMPT: prompt to select source file when node is flattened
|
|
FINISH-VERB: finisher for the success message."
|
|
(treemacs-block
|
|
(let ((btn (treemacs-current-button)))
|
|
(treemacs-error-return-if (null btn)
|
|
no-node-msg)
|
|
(treemacs-error-return-if
|
|
(not (memq (treemacs-button-get btn :state)
|
|
'(file-node-open file-node-closed dir-node-open dir-node-closed)))
|
|
wrong-type-msg)
|
|
(let* ((source (treemacs--select-file-from-btn btn flat-prompt))
|
|
(destination (treemacs--canonical-path
|
|
(read-directory-name prompt nil default-directory)))
|
|
(destination-dir (if (file-directory-p destination)
|
|
destination
|
|
(treemacs--parent-dir destination)))
|
|
(target-name (treemacs--filename
|
|
(if (file-directory-p destination)
|
|
source
|
|
destination)))
|
|
(target (->> target-name
|
|
(treemacs-join-path destination-dir)
|
|
(treemacs--find-repeated-file-name))))
|
|
(unless (file-exists-p destination-dir)
|
|
(make-directory destination-dir :parents))
|
|
(when (eq action 'move)
|
|
;; do the deletion *before* moving the file, otherwise it will
|
|
;; no longer exist and treemacs will not recognize it as a file path
|
|
(treemacs-do-delete-single-node source))
|
|
(treemacs--without-filewatch
|
|
(funcall action-fn source target))
|
|
(pcase action
|
|
('move
|
|
(run-hook-with-args 'treemacs-copy-file-functions source target)
|
|
(treemacs--on-file-deletion source))
|
|
('copy
|
|
(run-hook-with-args 'treemacs-move-file-functions source target)
|
|
(treemacs-remove-annotation-face source "treemacs-marked-paths")))
|
|
|
|
(treemacs-update-node destination-dir)
|
|
|
|
(when (treemacs-is-path target :in-workspace)
|
|
(treemacs-goto-file-node target))
|
|
|
|
(treemacs-pulse-on-success "%s %s to %s"
|
|
finish-verb
|
|
(propertize (treemacs--filename target) 'face 'font-lock-string-face)
|
|
(propertize destination-dir 'face 'font-lock-string-face))))))
|
|
|
|
;;;###autoload
|
|
(defun treemacs-move-marked-files ()
|
|
"Move all marked files.
|
|
|
|
For marking files see `treemacs-bulk-file-actions'."
|
|
(interactive)
|
|
(treemacs--bulk-copy-or-move
|
|
:action 'move
|
|
:action-fn #'rename-file
|
|
:prompt "Move to: "
|
|
:finish-verb "Moved"))
|
|
|
|
;;;###autoload
|
|
(defun treemacs-copy-marked-files ()
|
|
"Copy all marked files.
|
|
|
|
For marking files see `treemacs-bulk-file-actions'."
|
|
(interactive)
|
|
(treemacs--bulk-copy-or-move
|
|
:action 'copy
|
|
:action-fn (lambda (from to)
|
|
(if (file-directory-p from)
|
|
(copy-directory from to)
|
|
(copy-file from to)))
|
|
:prompt "Copy to: "
|
|
:finish-verb "Copied"))
|
|
|
|
(cl-defun treemacs--bulk-copy-or-move
|
|
(&key
|
|
action
|
|
action-fn
|
|
prompt
|
|
finish-verb)
|
|
"Internal implementation for bulk-copying and -moving files.
|
|
ACTION: either `copy' or `move'
|
|
ACTION-FN: function to actually copy or move a file
|
|
PROMPT: prompt to read the target directory
|
|
FINISH-VERB: finisher for the success message."
|
|
(treemacs-block
|
|
(let* ((to-move (-filter #'file-exists-p treemacs--marked-paths))
|
|
(destination-dir (treemacs--canonical-path
|
|
(read-directory-name prompt nil default-directory)))
|
|
(projects (->> to-move
|
|
(-map #'treemacs--find-project-for-path)
|
|
(cl-remove-duplicates)
|
|
(-filter #'identity))))
|
|
(treemacs-save-position
|
|
(dolist (source to-move)
|
|
(let ((target (->> source
|
|
(treemacs--filename)
|
|
(treemacs-join-path destination-dir)
|
|
(treemacs--find-repeated-file-name))))
|
|
(unless (string= source target)
|
|
(unless (file-exists-p destination-dir)
|
|
(make-directory destination-dir :parents))
|
|
(when (eq action 'move)
|
|
;; do the deletion *before* moving the file, otherwise it will
|
|
;; no longer exist and treemacs will not recognize it as a file path
|
|
(treemacs-do-delete-single-node source))
|
|
(treemacs--without-filewatch
|
|
(funcall action-fn source target))
|
|
(pcase action
|
|
('move
|
|
(run-hook-with-args 'treemacs-copy-file-functions source target)
|
|
(treemacs--on-file-deletion source))
|
|
('copy
|
|
(run-hook-with-args 'treemacs-move-file-functions source target)
|
|
(treemacs-remove-annotation-face source "treemacs-marked-paths"))))))
|
|
|
|
(dolist (project projects)
|
|
(treemacs-project->refresh! project)))
|
|
|
|
(when (treemacs-is-path destination-dir :in-workspace)
|
|
(treemacs-goto-file-node destination-dir))
|
|
|
|
(setf treemacs--marked-paths (-difference treemacs--marked-paths to-move))
|
|
|
|
(treemacs-pulse-on-success "%s %s files to %s"
|
|
finish-verb
|
|
(propertize (number-to-string (length to-move)) 'face 'font-lock-constant-face)
|
|
(propertize destination-dir 'face 'font-lock-string-face)))))
|
|
|
|
;;;###autoload
|
|
(cl-defun treemacs-rename-file ()
|
|
"Rename the file/directory at point.
|
|
|
|
Buffers visiting the renamed file or visiting a file inside the renamed
|
|
directory and windows showing them will be reloaded. The list of recent files
|
|
will likewise be updated."
|
|
(interactive)
|
|
(treemacs-block
|
|
(treemacs-unless-let (btn (treemacs-current-button))
|
|
(treemacs-pulse-on-failure "Nothing to rename here.")
|
|
(-let [old-path (treemacs--select-file-from-btn btn "Rename: ")]
|
|
(treemacs-error-return-if (null old-path)
|
|
"Found nothing to rename here.")
|
|
(treemacs-error-return-if (not (treemacs--is-node-file-manageable? btn))
|
|
"Only files and directories can be deleted.")
|
|
(treemacs-error-return-if (not (file-exists-p old-path))
|
|
"The file to be renamed does not exist.")
|
|
(let* ((old-name (treemacs--filename old-path))
|
|
(new-name (treemacs--read-string
|
|
"New name: " (file-name-nondirectory old-path)))
|
|
(dir (treemacs--parent-dir old-path))
|
|
(new-path (treemacs-join-path dir new-name))
|
|
(parent (treemacs-button-get btn :parent)))
|
|
(treemacs-error-return-if
|
|
(and (file-exists-p new-path)
|
|
(or (not (eq 'darwin system-type))
|
|
(not (string= old-name new-name))))
|
|
"A file named %s already exists."
|
|
(propertize new-name 'face font-lock-string-face))
|
|
(treemacs--without-filewatch
|
|
(rename-file old-path new-path)
|
|
(treemacs--replace-recentf-entry old-path new-path)
|
|
(-let [treemacs-silent-refresh t]
|
|
(treemacs-run-in-every-buffer
|
|
(treemacs--on-rename old-path new-path treemacs-filewatch-mode)
|
|
;; save-excursion does not work for whatever reason
|
|
(-let [p (point)]
|
|
(treemacs-do-update-node (treemacs-button-get parent :path))
|
|
(goto-char p)))))
|
|
(treemacs--reload-buffers-after-rename old-path new-path)
|
|
(run-hook-with-args
|
|
'treemacs-rename-file-functions
|
|
old-path new-path)
|
|
(treemacs-pulse-on-success "Renamed %s to %s."
|
|
(propertize (treemacs--filename old-path) 'face font-lock-string-face)
|
|
(propertize new-name 'face font-lock-string-face)))))))
|
|
|
|
(defalias 'treemacs-rename #'treemacs-rename-file)
|
|
(make-obsolete #'treemacs-rename #'treemacs-rename-file "v2.9.3")
|
|
|
|
;;; Bulk Actions
|
|
|
|
;;;###autoload
|
|
(defun treemacs-show-marked-files ()
|
|
"Print a list of all files marked by treemacs."
|
|
(interactive)
|
|
(let* ((len (length treemacs--marked-paths))
|
|
(message
|
|
(pcase len
|
|
(0 "There are currently no marked files.")
|
|
(1 (format "There is currently 1 marked file:\n%s"
|
|
(car treemacs--marked-paths)))
|
|
(_ (format "There are currently %s marked files:\n%s"
|
|
len
|
|
(string-join treemacs--marked-paths "\n"))))))
|
|
(treemacs-log message)))
|
|
|
|
;;;###autoload
|
|
(defun treemacs-mark-or-unmark-path-at-point ()
|
|
"Mark or unmark the absolute path of the node at point."
|
|
(interactive)
|
|
(treemacs-block
|
|
(-let [path (treemacs--prop-at-point :path)]
|
|
(treemacs-error-return-if (null path)
|
|
"There is nothing to mark here")
|
|
(treemacs-error-return-if
|
|
(or (not (stringp path)) (not (file-exists-p path)))
|
|
"Path at point is not a file or directory.")
|
|
(if (member path treemacs--marked-paths)
|
|
(progn
|
|
(setq treemacs--marked-paths
|
|
(remove path treemacs--marked-paths))
|
|
(treemacs-log "Unmarked path: %s" (propertize path 'face 'font-lock-string-face))
|
|
(treemacs-remove-annotation-face path "treemacs-marked-paths"))
|
|
(progn
|
|
(setq treemacs--marked-paths
|
|
(append treemacs--marked-paths (list path)))
|
|
(treemacs-log "Marked path: %s" (propertize path 'face 'font-lock-string-face))
|
|
(treemacs-set-annotation-face path 'treemacs-marked-file-face "treemacs-marked-paths")))
|
|
(treemacs-apply-annotations (treemacs--parent-dir path)))))
|
|
|
|
;;;###autoload
|
|
(defun treemacs-reset-marks ()
|
|
"Unmark all previously marked files in the current buffer."
|
|
(interactive)
|
|
(let ((count (length treemacs--marked-paths))
|
|
(projects))
|
|
(dolist (path treemacs--marked-paths)
|
|
(treemacs-remove-annotation-face path treemacs--mark-annotation-source)
|
|
(push (treemacs--find-project-for-path path) projects))
|
|
(setf treemacs--marked-paths nil)
|
|
(dolist (project (-uniq projects))
|
|
(treemacs-apply-annotations (treemacs-project->path project)))
|
|
(treemacs-pulse-on-success "Unmarked %s file(s)." count)))
|
|
|
|
;;;###autoload
|
|
(defun treemacs-delete-marked-paths ()
|
|
"Delete all previously marked files."
|
|
(interactive)
|
|
(treemacs-save-position
|
|
(when (yes-or-no-p
|
|
(format "Really delete %s marked file(s)?"
|
|
(length treemacs--marked-paths)))
|
|
(-let [count (length treemacs--marked-paths)]
|
|
(dolist (path treemacs--marked-paths)
|
|
(if (file-directory-p path)
|
|
(delete-directory path t)
|
|
(delete-file path))
|
|
(treemacs-do-delete-single-node path)
|
|
(treemacs-remove-annotation-face path treemacs--mark-annotation-source))
|
|
(setf treemacs--marked-paths nil)
|
|
(hl-line-highlight)
|
|
(treemacs-log "Deleted %s files." count)))))
|
|
|
|
;; shut down docstring width warnings
|
|
(with-no-warnings
|
|
(defhydra treemacs-bulk-file-actions-hydra (:exit t :hint nil)
|
|
("m" #'treemacs-mark-or-unmark-path-at-point "(un)mark")
|
|
("u" #'treemacs-reset-marks "unmark all")
|
|
("s" #'treemacs-show-marked-files "show")
|
|
("d" #'treemacs-delete-marked-files "delete")
|
|
("c" #'treemacs-copy-marked-files "copy")
|
|
("o" #'treemacs-move-marked-files "move")
|
|
("q" nil "cancel")))
|
|
|
|
;;;###autoload
|
|
(defun treemacs-bulk-file-actions ()
|
|
"Activate the bulk file actions hydra.
|
|
This interface allows to quickly (unmark) files, so as to copy, move or delete
|
|
them in bulk.
|
|
|
|
Note that marking files is *permanent*, files will stay marked until they are
|
|
either manually unmarked or deleted. You can show a list of all currently
|
|
marked files with `treemacs-show-marked-files' or `s' in the hydra."
|
|
(interactive)
|
|
(treemacs-bulk-file-actions-hydra/body))
|
|
|
|
;;;###autoload
|
|
(defun treemacs-create-file ()
|
|
"Create a new file.
|
|
Enter first the directory to create the new file in, then the new file's name.
|
|
The pre-selection for what directory to create in is based on the \"nearest\"
|
|
path to point - the containing directory for tags and files or the directory
|
|
itself, using $HOME when there is no path at or near point to grab."
|
|
(interactive)
|
|
(treemacs--create-file/dir t))
|
|
|
|
;;;###autoload
|
|
(defun treemacs-create-dir ()
|
|
"Create a new directory.
|
|
Enter first the directory to create the new dir in, then the new dir's name.
|
|
The pre-selection for what directory to create in is based on the \"nearest\"
|
|
path to point - the containing directory for tags and files or the directory
|
|
itself, using $HOME when there is no path at or near point to grab."
|
|
(interactive)
|
|
(treemacs--create-file/dir nil))
|
|
|
|
(defun treemacs--create-file/dir (is-file?)
|
|
"Interactively create either a file or directory, depending on IS-FILE.
|
|
|
|
IS-FILE?: Bool"
|
|
(interactive)
|
|
(let* ((curr-path (treemacs--select-file-from-btn
|
|
(treemacs-current-button)
|
|
"Create in: " :dir-only))
|
|
(path-to-create (treemacs-canonical-path
|
|
(read-file-name
|
|
(if is-file? "Create File: " "Create Directory: ")
|
|
(treemacs--add-trailing-slash
|
|
(if (file-directory-p curr-path)
|
|
curr-path
|
|
(treemacs--parent-dir curr-path)))))))
|
|
(treemacs-block
|
|
(treemacs-error-return-if (file-exists-p path-to-create)
|
|
"%s already exists." (propertize path-to-create 'face 'font-lock-string-face))
|
|
(treemacs--without-filewatch
|
|
(if is-file?
|
|
(-let [dir (treemacs--parent-dir path-to-create)]
|
|
(unless (file-exists-p dir)
|
|
(make-directory dir t))
|
|
(write-region "" nil path-to-create nil 0))
|
|
(make-directory path-to-create t))
|
|
(run-hook-with-args 'treemacs-create-file-functions path-to-create))
|
|
(-when-let (project (treemacs--find-project-for-path path-to-create))
|
|
(-when-let* ((created-under (treemacs--parent path-to-create))
|
|
(created-under-btn (treemacs-find-visible-node created-under)))
|
|
;; update only the part that changed to keep things smooth
|
|
;; for files that's just their parent, for directories we have to take
|
|
;; flattening into account
|
|
(-let [path-to-update
|
|
(if (treemacs-button-get created-under-btn :collapsed)
|
|
(treemacs-button-get (treemacs-button-get created-under-btn :parent) :path)
|
|
(treemacs-button-get created-under-btn :path))]
|
|
(treemacs-update-node path-to-update)
|
|
(when (treemacs--non-simple-git-mode-enabled)
|
|
(treemacs-update-single-file-git-state path-to-update))))
|
|
(treemacs-goto-file-node path-to-create project)
|
|
(recenter))
|
|
(treemacs-pulse-on-success
|
|
"Created %s." (propertize path-to-create 'face 'font-lock-string-face)))))
|
|
|
|
(defun treemacs--select-file-from-btn (btn prompt &optional dir-only)
|
|
"Select the file at BTN for file management.
|
|
Offer a specifying dialogue with PROMPT when the button is flattened.
|
|
Pick only directories when DIR-ONLY is non-nil."
|
|
(declare (side-effect-free t))
|
|
(let* ((path (and btn (treemacs-button-get btn :path)))
|
|
(collapse-info (and btn (treemacs-button-get btn :collapsed)))
|
|
(is-str (and path (stringp path)))
|
|
(is-dir (and is-str (file-directory-p path)))
|
|
(is-file (and is-str (file-regular-p path))))
|
|
(cond
|
|
(collapse-info
|
|
(completing-read prompt collapse-info nil :require-match))
|
|
(is-dir
|
|
path)
|
|
((and is-file dir-only)
|
|
(treemacs--parent-dir path))
|
|
(is-file
|
|
path)
|
|
(t
|
|
(expand-file-name "~")))))
|
|
|
|
(provide 'treemacs-file-management)
|
|
|
|
;;; treemacs-file-management.el ends here
|