pkg update and first config fix
org-brain not working, add org-roam
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
;;; treemacs.el --- A tree style file viewer package -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2021 Alexander Miller
|
||||
;; Copyright (C) 2022 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
|
||||
@@ -25,16 +25,24 @@
|
||||
;;; 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)
|
||||
|
||||
(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")
|
||||
@@ -107,81 +115,230 @@ they will instead be wiped irreversibly."
|
||||
(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.
|
||||
Destination may also be a filename, in which case the moved file will also
|
||||
be renamed."
|
||||
|
||||
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 :move))
|
||||
(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.
|
||||
Destination may also be a filename, in which case the copied file will also
|
||||
be renamed."
|
||||
(interactive)
|
||||
(treemacs--copy-or-move :copy))
|
||||
|
||||
(defun treemacs--copy-or-move (action)
|
||||
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 will be either `:copy' or `:move', depending on whether we are calling
|
||||
from `treemacs-copy-file' or `treemacs-move-file'."
|
||||
(let ((no-node-msg)
|
||||
(wrong-type-msg)
|
||||
(prompt)
|
||||
(action-function)
|
||||
(finish-msg))
|
||||
(pcase action
|
||||
(:copy
|
||||
(setf no-node-msg "There is nothing to copy here."
|
||||
wrong-type-msg "Only files and directories can be copied."
|
||||
prompt "Copy to: "
|
||||
action-function (lambda (from to)
|
||||
(if (file-directory-p from)
|
||||
(copy-directory from to)
|
||||
(copy-file from to)))
|
||||
finish-msg "Copied %s to %s"))
|
||||
(:move
|
||||
(setf no-node-msg "There is nothing to move here."
|
||||
wrong-type-msg "Only files and directories can be moved."
|
||||
prompt "Move to: "
|
||||
action-function #'rename-file
|
||||
finish-msg "Moved %s to %s")))
|
||||
(treemacs-block
|
||||
(treemacs-unless-let (node (treemacs-node-at-point))
|
||||
(treemacs-error-return no-node-msg)
|
||||
(treemacs-error-return-if (not (treemacs-is-node-file-or-dir? node))
|
||||
wrong-type-msg)
|
||||
(let* ((source (treemacs--select-file-from-btn
|
||||
node (if (eq action :copy "File to copy: " "File to move: "))))
|
||||
(source-name (treemacs--filename source))
|
||||
(destination (treemacs--unslash (read-file-name prompt nil default-directory)))
|
||||
(target-is-dir? (file-directory-p destination))
|
||||
(target-name (if target-is-dir? (treemacs--filename source) (treemacs--filename destination)))
|
||||
(destination-dir (if target-is-dir? destination (treemacs--parent-dir destination)))
|
||||
(target (treemacs--find-repeated-file-name (treemacs-join-path destination-dir target-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-function source target))
|
||||
;; no waiting for filewatch, if we copied to an expanded directory refresh it immediately
|
||||
(-let [parent (treemacs--parent target)]
|
||||
(when (treemacs-is-path-visible? parent)
|
||||
(treemacs-do-update-node parent)))
|
||||
(treemacs-goto-file-node target)
|
||||
(run-hook-with-args
|
||||
(pcase action
|
||||
(:copy 'treemacs-copy-file-functions)
|
||||
(:move 'treemacs-move-file-functions))
|
||||
source target)
|
||||
(treemacs-pulse-on-success finish-msg
|
||||
(propertize source-name 'face 'font-lock-string-face)
|
||||
(propertize destination 'face 'font-lock-string-face)))))))
|
||||
|
||||
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 ()
|
||||
@@ -219,7 +376,10 @@ will likewise be updated."
|
||||
(-let [treemacs-silent-refresh t]
|
||||
(treemacs-run-in-every-buffer
|
||||
(treemacs--on-rename old-path new-path treemacs-filewatch-mode)
|
||||
(treemacs-update-node (treemacs-button-get parent :path)))))
|
||||
;; 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
|
||||
@@ -231,6 +391,103 @@ will likewise be updated."
|
||||
(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.
|
||||
@@ -256,9 +513,9 @@ itself, using $HOME when there is no path at or near point to grab."
|
||||
|
||||
IS-FILE?: Bool"
|
||||
(interactive)
|
||||
(let* ((curr-path (--if-let (treemacs-current-button)
|
||||
(treemacs--select-file-from-btn it "Create in: ")
|
||||
(expand-file-name "~")))
|
||||
(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: ")
|
||||
@@ -291,13 +548,27 @@ IS-FILE?: Bool"
|
||||
(treemacs-pulse-on-success
|
||||
"Created %s." (propertize path-to-create 'face 'font-lock-string-face)))))
|
||||
|
||||
(defun treemacs--select-file-from-btn (btn prompt)
|
||||
"Select the file represented by BTN for file management.
|
||||
Offer a specifying dialogue with PROMPT when BTN is flattened."
|
||||
(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))
|
||||
(-if-let (collapse-info (treemacs-button-get btn :collapsed))
|
||||
(completing-read prompt collapse-info nil :require-match)
|
||||
(treemacs-button-get btn :key)))
|
||||
(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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user