Hopefully finish trusted-files.el
This commit is contained in:
		@ -1,869 +0,0 @@
 | 
			
		||||
;;; trusted-dirs.el --- Simplistic security for Eglot and auto-complete. -*- lexical-binding: t -*-
 | 
			
		||||
;;; Commentary:
 | 
			
		||||
;;; Code:
 | 
			
		||||
(require 'cl-lib)
 | 
			
		||||
(eval-and-compile
 | 
			
		||||
  (defconst trusted-dirs-generated-function-name-prefix "trusted-dirs--"
 | 
			
		||||
    "Prefix to append to generated functions.
 | 
			
		||||
This is used by `trusted-dirs-add-hook-if-safe' and
 | 
			
		||||
`trusted-dirs-mark-function-unsafe'.  Note that these two functions are actually
 | 
			
		||||
macros, so if you change this (which you probably shouldn't do), code that uses
 | 
			
		||||
these will need to be recompiled.")
 | 
			
		||||
 | 
			
		||||
  (defconst trusted-dirs-hook-function-name-suffix "@trusted-dirs-hook-if-safe"
 | 
			
		||||
    "Suffix to append to function names in `trusted-dirs-add-hook-if-safe'.
 | 
			
		||||
Note that `trusted-dirs-add-hook-if-safe' is a macro, so if this value is
 | 
			
		||||
changed (which you probably shouldn't do), code that calls
 | 
			
		||||
`trusted-dirs-add-hook-if-safe' will need to be recompiled.")
 | 
			
		||||
 | 
			
		||||
  (defconst trusted-dirs-advice-function-name-suffix
 | 
			
		||||
    "@trusted-dirs-advice-if-safe"
 | 
			
		||||
    "Suffix to append to function names in `trusted-dirs-mark-function-unsafe'.
 | 
			
		||||
Note that `trusted-dirs-mark-function-unsafe' is a macro, so if this value is
 | 
			
		||||
changed (which you probably shouldn't do), code that calls
 | 
			
		||||
`trusted-dirs-mark-function-unsafe' will need to be recompiled. "))
 | 
			
		||||
 | 
			
		||||
(defgroup trusted-dirs nil
 | 
			
		||||
  "Simplistic security for Eglot, auto-complete, etc."
 | 
			
		||||
  :group 'files
 | 
			
		||||
  :prefix "trusted-dirs-")
 | 
			
		||||
 | 
			
		||||
(defun trusted-dirs--validate-only-allow-absolute-paths (widget)
 | 
			
		||||
  "Custom validation function to only allow WIDGET to contain absolute paths."
 | 
			
		||||
  (let ((path (widget-value widget)))
 | 
			
		||||
    (unless (and (stringp path) (file-name-absolute-p path))
 | 
			
		||||
      (widget-put widget :error "Path must be absolute")
 | 
			
		||||
      widget)))
 | 
			
		||||
 | 
			
		||||
(defcustom trusted-dirs-list ()
 | 
			
		||||
  "List of directories that should be considered safe."
 | 
			
		||||
  :group 'trusted-dirs
 | 
			
		||||
  :tag "Trusted Directories"
 | 
			
		||||
  :type '(repeat
 | 
			
		||||
          (cons :tag "Entry"
 | 
			
		||||
                (directory
 | 
			
		||||
                 :tag "Directory"
 | 
			
		||||
                 :validate trusted-dirs--validate-only-allow-absolute-paths)
 | 
			
		||||
                (boolean :tag "Also trust subdirectories" :value t)))
 | 
			
		||||
  :risky t)
 | 
			
		||||
 | 
			
		||||
(defcustom trusted-dirs-truename-trusted-directories t
 | 
			
		||||
  "If non-nil, use the `file-truename' of for entries in `trusted-dirs-list'.
 | 
			
		||||
Note that this does not affect `default-directory', see
 | 
			
		||||
`trusted-dirs-truename-current-directory' for that."
 | 
			
		||||
  :group 'trusted-dirs
 | 
			
		||||
  :tag "Resolve Symbolic Links for Trusted Directories"
 | 
			
		||||
  :type 'boolean
 | 
			
		||||
  :risky t)
 | 
			
		||||
 | 
			
		||||
(defcustom trusted-dirs-truename-current-directory t
 | 
			
		||||
  "If non-nil, use the `file-truename' of `default-directory' when checking safety.
 | 
			
		||||
If this is nil, each link to a directory must individually be in
 | 
			
		||||
`trusted-dirs-list' to be considered safe.  Note that this does _NOT_ effect the
 | 
			
		||||
entries in `trusted-dirs-list', only `default-directory'."
 | 
			
		||||
  :group 'trusted-dirs
 | 
			
		||||
  :tag "Resolve Symbolic Links for the Current Directory"
 | 
			
		||||
  :type 'boolean
 | 
			
		||||
  :risky t)
 | 
			
		||||
 | 
			
		||||
(defcustom trusted-dirs-show-in-modeline 'dynamic-temporary-untrusted
 | 
			
		||||
  "How to show the current buffer's trusted status in the mode line.
 | 
			
		||||
There are three possible values:
 | 
			
		||||
  - t: always show the status
 | 
			
		||||
  - \\='untrusted: show the status if `default-directory' is untrusted
 | 
			
		||||
  - \\='dynamic: as above, but only if a protected function tried to run
 | 
			
		||||
  - \\='dynamic-untrusted: as above, but only if the function failed
 | 
			
		||||
  - \\='dynamic-temporary: save as \\='dynamic, but also show when the buffer is
 | 
			
		||||
                        temporarily trusted
 | 
			
		||||
  - \\='dynamic-temporary-untrusted: as above, but only if a function failed
 | 
			
		||||
 | 
			
		||||
To completely disabled display of the trusted status, disable
 | 
			
		||||
`trusted-dirs-modeline-mode'."
 | 
			
		||||
  :group 'tusted-dirs
 | 
			
		||||
  :tag "Show Trusted State in Modeline"
 | 
			
		||||
  :type '(choice (const :tag "Always" t)
 | 
			
		||||
                 (const :tag "If Untrusted" untrusted)
 | 
			
		||||
                 (const :tag "Dynamic" dynamic)
 | 
			
		||||
                 (const :tag "Dynamic if Untrusted" dynamic-untrusted)
 | 
			
		||||
                 (const :tag "Dynamic (or Temporary)" dynamic-temporary)
 | 
			
		||||
                 (const :tag "Dynamic if Untrusted (or Temporary)"
 | 
			
		||||
                        dynamic-temporary-untrusted))
 | 
			
		||||
  :set (lambda (sym val)
 | 
			
		||||
         (set-default-toplevel-value sym val)
 | 
			
		||||
         (force-mode-line-update t)))
 | 
			
		||||
 | 
			
		||||
(defface trusted-dirs-trusted-modeline-face
 | 
			
		||||
  '((t))
 | 
			
		||||
  "Face for the trusted notification string in the mode line."
 | 
			
		||||
  :group 'trusted-dirs
 | 
			
		||||
  :tag "Mode Line Trusted Notification Face")
 | 
			
		||||
 | 
			
		||||
(defface trusted-dirs-temporary-modeline-face
 | 
			
		||||
  '((t . (:inherit warning)))
 | 
			
		||||
  "Face for the temporarily trusted notification string in the mode line."
 | 
			
		||||
  :group 'trusted-dirs
 | 
			
		||||
  :tag "Mode Line Temporarily Trusted Notification Face")
 | 
			
		||||
 | 
			
		||||
(defface trusted-dirs-untrusted-modeline-face
 | 
			
		||||
  '((t . (:inherit error)))
 | 
			
		||||
  "Face for the untrusted notification string in the mode line."
 | 
			
		||||
  :group 'trusted-dirs
 | 
			
		||||
  :tag "Mode Line Untrusted Notification Face")
 | 
			
		||||
 | 
			
		||||
(defvar-local trusted-dirs--did-protected-function-run nil
 | 
			
		||||
  "Non-nil if a protected function tried to run in the current buffer.")
 | 
			
		||||
;;;###autoload (put 'trusted-dirs--did-protected-function-run 'risky-local-variable t)
 | 
			
		||||
 | 
			
		||||
(defvar-local trusted-dirs--did-protected-function-fail nil
 | 
			
		||||
  "Non-nil if a protected function failed to run in the current buffer.")
 | 
			
		||||
;;;###autoload (put 'trusted-dirs--did-protected-function-fail 'risky-local-variable t)
 | 
			
		||||
 | 
			
		||||
(defvar trusted-dirs--temporarily-trusted-cache (make-hash-table :test 'equal)
 | 
			
		||||
  "Hash table of temporarily trusted directories and buffers.
 | 
			
		||||
Each key is a directory or buffer.  In the case of a buffer, any non-nil values
 | 
			
		||||
means that the buffer is trusted.  In the case of a directory, the key is one of
 | 
			
		||||
the following:
 | 
			
		||||
  - t: this directory is trusted
 | 
			
		||||
  - \\='subdir: this directory and its subdirectories are trusted
 | 
			
		||||
 | 
			
		||||
Entries are removed from this list by
 | 
			
		||||
`trusted-dirs--cleanup-temporary-trusted-cache', which is called from
 | 
			
		||||
`kill-buffer-hook'.")
 | 
			
		||||
;;;###autoload (put 'trusted-dirs--temporarily-trusted-cache 'risky-local-variable t)
 | 
			
		||||
 | 
			
		||||
(defun trusted-dirs--modeline-string ()
 | 
			
		||||
  "Return the trusted-dirs mode line string for the current buffer.
 | 
			
		||||
To change when this is shown, customize `trusted-dirs-show-in-modeline'."
 | 
			
		||||
  (let* ((safe (car (trusted-dirs-safe-p nil t)))
 | 
			
		||||
         (temporary (car (memq safe
 | 
			
		||||
                               '(temp-buffer temp-dir temp-subdir)))))
 | 
			
		||||
    (and (or (eq trusted-dirs-show-in-modeline t)
 | 
			
		||||
             (and temporary
 | 
			
		||||
                  (memq trusted-dirs-show-in-modeline
 | 
			
		||||
                        '(dynamic-temporary dynamic-temporary-untrusted)))
 | 
			
		||||
             (and (not safe) (eq trusted-dirs-show-in-modeline 'untrusted))
 | 
			
		||||
             (and trusted-dirs--did-protected-function-run
 | 
			
		||||
                  (memq trusted-dirs-show-in-modeline
 | 
			
		||||
                        '(dynamic dynamic-temporary)))
 | 
			
		||||
             (and trusted-dirs--did-protected-function-fail
 | 
			
		||||
                  (memq trusted-dirs-show-in-modeline
 | 
			
		||||
                        '(dynamic-untrusted dynamic-temporary-untrusted))))
 | 
			
		||||
         (list
 | 
			
		||||
          (cond
 | 
			
		||||
           (temporary
 | 
			
		||||
            `(:propertize ,(format "Temp. Trusted %s"
 | 
			
		||||
                                   (cl-case temporary
 | 
			
		||||
                                     (temp-buffer "(B)")
 | 
			
		||||
                                     (temp-dir "(D)")
 | 
			
		||||
                                     (temp-subdir "(S)")))
 | 
			
		||||
              face trusted-dirs-temporary-modeline-face
 | 
			
		||||
              mouse-face mode-line-highlight
 | 
			
		||||
              help-echo
 | 
			
		||||
              ,(cl-case temporary
 | 
			
		||||
                 (temp-buffer
 | 
			
		||||
                  "This buffer is temp. trusted. Click to untrust.")
 | 
			
		||||
                 (temp-dir
 | 
			
		||||
                  "This directory is temp. trusted. Click to untrust it.")
 | 
			
		||||
                 (temp-subdir
 | 
			
		||||
                  "A parent directory is temp. trusted. Click to untrust it."))
 | 
			
		||||
              keymap
 | 
			
		||||
              (mode-line keymap
 | 
			
		||||
                         (mouse-1 . trusted-dirs-remove-temporary-current-buffer))))
 | 
			
		||||
           (safe '(:propertize "Trusted"
 | 
			
		||||
                   face trusted-dirs-trusted-modeline-face
 | 
			
		||||
                   help-echo
 | 
			
		||||
                   (cl-case safe
 | 
			
		||||
                     (dir "This buffer's directory (not a parent) is trusted.")
 | 
			
		||||
                     (subdir "A parent directory of this buffer is trusted."))))
 | 
			
		||||
           (t '(:propertize "Untrusted"
 | 
			
		||||
                face trusted-dirs-untrusted-modeline-face
 | 
			
		||||
                help-echo "This buffer is untrusted.")))
 | 
			
		||||
          " "))))
 | 
			
		||||
 | 
			
		||||
;;;###autoload
 | 
			
		||||
(define-minor-mode trusted-dirs-modeline-mode
 | 
			
		||||
  "Minor mode for showing current buffer's trusted status in the mode line."
 | 
			
		||||
  :group 'trusted-dirs
 | 
			
		||||
  :global t
 | 
			
		||||
  :lighter nil
 | 
			
		||||
  (let ((item '(:eval (trusted-dirs--modeline-string))))
 | 
			
		||||
    (if trusted-dirs-modeline-mode
 | 
			
		||||
        (add-to-list 'global-mode-string item)
 | 
			
		||||
      (setq global-mode-string (remove item global-mode-string))))
 | 
			
		||||
  (force-mode-line-update))
 | 
			
		||||
 | 
			
		||||
(defun trusted-dirs--remove-extra-path-parts (path)
 | 
			
		||||
  "Remove extra path parts from PATH.
 | 
			
		||||
This removes \".\" and \"..\" components.  This always returns a path without a
 | 
			
		||||
trailing slash.  The difference between this and `expand-file-name' is that this
 | 
			
		||||
will not return things like \"/..\"."
 | 
			
		||||
  (let ((expanded (expand-file-name path)))
 | 
			
		||||
    (while (string-prefix-p "/.." expanded)
 | 
			
		||||
      (setq expanded (substring expanded 3))
 | 
			
		||||
      (unless (string-prefix-p "/" expanded)
 | 
			
		||||
        (setq expanded (concat "/" expanded))))
 | 
			
		||||
    (directory-file-name expanded)))
 | 
			
		||||
 | 
			
		||||
(defun trusted-dirs--resolve-trusted-directory (path)
 | 
			
		||||
  "Resolve PATH, which is resolved according to user settings.
 | 
			
		||||
If `trusted-dirs-truename-trusted-directories' is set, return the
 | 
			
		||||
`file-truename' of PATH.  In any case, remove \".\" and \"..\" components from
 | 
			
		||||
PATH.  Also make PATH absolute and remove any trailing slash."
 | 
			
		||||
  (if trusted-dirs-truename-trusted-directories
 | 
			
		||||
      (directory-file-name (file-truename path))
 | 
			
		||||
    (trusted-dirs--remove-extra-path-parts path)))
 | 
			
		||||
 | 
			
		||||
(defun trusted-dirs-resolved-list ()
 | 
			
		||||
  "Return `trusted-dirs-list' as a hash table, removing invalid entries.
 | 
			
		||||
Also, take the `file-truename' of each entry if
 | 
			
		||||
`trusted-dirs-truename-trusted-directories' is set."
 | 
			
		||||
  (let ((ht (make-hash-table :test 'equal)))
 | 
			
		||||
    (dolist (entry trusted-dirs-list ht)
 | 
			
		||||
      (when (file-name-absolute-p (car entry))
 | 
			
		||||
        (let* ((resolved (trusted-dirs--resolve-trusted-directory (car entry)))
 | 
			
		||||
               (cur-val (gethash resolved ht)))
 | 
			
		||||
          ;; Prioritize the most specific rule.  If the output is already set to
 | 
			
		||||
          ;; t (more specific than 'subdir), don't touch it.  Otherwise, set the
 | 
			
		||||
          ;; output to whatever the current entry is
 | 
			
		||||
          (unless (eq cur-val t)
 | 
			
		||||
            (puthash resolved (cdr entry) ht)))))))
 | 
			
		||||
 | 
			
		||||
(defsubst trusted-dirs--resolve-current-directory (path)
 | 
			
		||||
  "Resolve PATH, which is resolved according to user settings.
 | 
			
		||||
If `trusted-dirs-truename-current-directory' is set, return the `file-truename'
 | 
			
		||||
of PATH.  In any case, remove \".\" and \"..\" components from PATH.  Also make
 | 
			
		||||
PATH absolute and remove any trailing slash."
 | 
			
		||||
  (if trusted-dirs-truename-current-directory
 | 
			
		||||
      (directory-file-name (file-truename path))
 | 
			
		||||
    (trusted-dirs--remove-extra-path-parts path)))
 | 
			
		||||
 | 
			
		||||
(defun trusted-dirs--path-equal-p (path1 path2)
 | 
			
		||||
  "Return non-nil if PATH1 and PATH2 are the same path.
 | 
			
		||||
This is the same as `equal' except that it calls `directory-file-name' on PATH1
 | 
			
		||||
and PATH2 before comparing them."
 | 
			
		||||
  (and (equal (directory-file-name path1)
 | 
			
		||||
              (directory-file-name path2))))
 | 
			
		||||
 | 
			
		||||
(defun trusted-dirs--normal-buffer-p (&optional buffer)
 | 
			
		||||
  "Return non-nil if BUFFER (or the current buffer) is a normal buffer.
 | 
			
		||||
A buffer is normal if it visits a file, is not hidden, and it's name does not
 | 
			
		||||
start and end with asterisks."
 | 
			
		||||
  (unless buffer (setq buffer (current-buffer)))
 | 
			
		||||
  (and (buffer-file-name buffer)
 | 
			
		||||
       (not (string-prefix-p " " (buffer-name buffer)))
 | 
			
		||||
       (not (string-match-p "\\`\\*.*\\*\\'" (buffer-name buffer)))))
 | 
			
		||||
 | 
			
		||||
(defun trusted-dirs--subdirectory-p (parent child &optional resolved)
 | 
			
		||||
  "Return non-nil if CHILD is a subdirectory of PARENT.
 | 
			
		||||
This will resolve both PARENT and CHILD with
 | 
			
		||||
`trusted-dirs--resolve-current-directory', unless RESOLVED is non-nil."
 | 
			
		||||
  (unless resolved
 | 
			
		||||
    (setq parent (trusted-dirs--resolve-current-directory parent)
 | 
			
		||||
          child (trusted-dirs--resolve-current-directory child)))
 | 
			
		||||
  (or (equal parent "/")
 | 
			
		||||
      (string-match-p (concat "\\`" (regexp-quote parent) "\\(\\'\\|/\\)") child)))
 | 
			
		||||
 | 
			
		||||
(defun trusted-dirs--find-buffers-in-dir
 | 
			
		||||
    (dir &optional subdir-too resolved special-too)
 | 
			
		||||
  "Return a list of buffers that have a `default-directory' of DIR.
 | 
			
		||||
If SUBDIR-TOO is set, also search for subdirectories of DIR.  If SPECIAL-TOO is
 | 
			
		||||
set, also consider buffers that are special.  Otherwise, only consider regular,
 | 
			
		||||
visible, file-visiting buffers.
 | 
			
		||||
 | 
			
		||||
Unless RESOLVED is set, resolve DIR with
 | 
			
		||||
`trusted-dirs--resolve-current-directory'."
 | 
			
		||||
  (unless resolved (setq dir (trusted-dirs--resolve-current-directory dir)))
 | 
			
		||||
  (let (out)
 | 
			
		||||
    (dolist (buffer (buffer-list) out)
 | 
			
		||||
      (when (or special-too (trusted-dirs--normal-buffer-p buffer))
 | 
			
		||||
        (let ((target-dir (trusted-dirs--resolve-current-directory
 | 
			
		||||
                           (buffer-local-value 'default-directory
 | 
			
		||||
                                               buffer))))
 | 
			
		||||
          (when (or (and subdir-too (trusted-dirs--subdirectory-p
 | 
			
		||||
                                     dir target-dir))
 | 
			
		||||
                    (equal dir target-dir))
 | 
			
		||||
            (push buffer out)))))))
 | 
			
		||||
 | 
			
		||||
(defun trusted-dirs--cleanup-temporary-trusted-cache ()
 | 
			
		||||
  "Cleanup `trusted-dirs--temporarily-trusted-cache'."
 | 
			
		||||
  (remhash (current-buffer) trusted-dirs--temporarily-trusted-cache)
 | 
			
		||||
  (cl-loop with resolved = (trusted-dirs--resolve-current-directory
 | 
			
		||||
                            default-directory)
 | 
			
		||||
           for prev = nil then cur
 | 
			
		||||
           for cur = resolved then (directory-file-name
 | 
			
		||||
                                    (file-name-directory cur))
 | 
			
		||||
           until (equal prev cur)
 | 
			
		||||
           for rule = (gethash cur trusted-dirs--temporarily-trusted-cache)
 | 
			
		||||
           when (and rule (null (delq (current-buffer)
 | 
			
		||||
                                      (trusted-dirs--find-buffers-in-dir
 | 
			
		||||
                                       cur (eq rule 'subdir)))))
 | 
			
		||||
           collect cur into steps
 | 
			
		||||
           and do (remhash cur trusted-dirs--temporarily-trusted-cache)
 | 
			
		||||
           finally do
 | 
			
		||||
           (message "Untrusted %s" (trusted-dirs--pprint-list steps))))
 | 
			
		||||
 | 
			
		||||
(add-hook 'kill-buffer-hook #'trusted-dirs--cleanup-temporary-trusted-cache)
 | 
			
		||||
 | 
			
		||||
(defun trusted-dirs--buffer-temporarily-trusted-p (buffer)
 | 
			
		||||
  "Return non-nil if BUFFER is temprarily trusted.
 | 
			
		||||
This checks both BUFFER and BUFFER's `default-directory'.
 | 
			
		||||
 | 
			
		||||
Return a cons.  For the car if the BUFFER is trusted, return \\='temp-buffer.
 | 
			
		||||
If `default-directory' is exactly trusted, return \\='temp-dir.  If a parent
 | 
			
		||||
directory of it is trusted, return \\='temp-subdir.  For the cdr, return the
 | 
			
		||||
directory that matched, or the BUFFER it itself matched."
 | 
			
		||||
  (or
 | 
			
		||||
   (and (gethash buffer trusted-dirs--temporarily-trusted-cache)
 | 
			
		||||
        (cons 'temp-buffer buffer))
 | 
			
		||||
   (cl-loop with dir = (trusted-dirs--resolve-current-directory
 | 
			
		||||
                        (buffer-local-value 'default-directory buffer))
 | 
			
		||||
            for prev = nil then cur
 | 
			
		||||
            for cur = dir then (directory-file-name
 | 
			
		||||
                                (file-name-directory cur))
 | 
			
		||||
            until (equal prev cur)
 | 
			
		||||
            for result = (gethash cur trusted-dirs--temporarily-trusted-cache)
 | 
			
		||||
            ;; if we find an exact match, return t
 | 
			
		||||
            when (and result (equal dir cur)) return (cons 'temp-dir cur)
 | 
			
		||||
            ;; otherwise, return t if the entry allows subdirectories
 | 
			
		||||
            when (eq result 'subdir) return (cons 'temp-subdir cur))))
 | 
			
		||||
 | 
			
		||||
(defun trusted-dirs--permanently-trusted-p (path &optional resolved)
 | 
			
		||||
  "Return non-nil if PATH is in `trusted-dirs-list'.
 | 
			
		||||
This will resolve PATH with `trusted-dirs--resolve-trusted-directory' unless
 | 
			
		||||
RESOLVED is non-nil.
 | 
			
		||||
 | 
			
		||||
Return a cons.  For the car if PATH (not a parent directory) is trusted, return
 | 
			
		||||
\\='dir.  If a parent directory of PATH is trusted, return \\='subdir.  For the
 | 
			
		||||
cdr, return the directory that matched."
 | 
			
		||||
  (unless resolved (setq path (trusted-dirs--resolve-current-directory path)))
 | 
			
		||||
  (cl-loop with trusted-list = (trusted-dirs-resolved-list)
 | 
			
		||||
           for prev = nil then cur
 | 
			
		||||
           for cur = path then (directory-file-name
 | 
			
		||||
                                (file-name-directory cur))
 | 
			
		||||
           until (equal prev cur)
 | 
			
		||||
           for result = (gethash cur trusted-list)
 | 
			
		||||
           ;; if we find an exact match, return t
 | 
			
		||||
           when (and result (equal path cur)) return (cons 'dir cur)
 | 
			
		||||
           ;; otherwise, return t if the entry allows subdirectories
 | 
			
		||||
           when (eq result 'subdir) return (cons 'subdir cur)))
 | 
			
		||||
 | 
			
		||||
(defun trusted-dirs-safe-p (&optional dir no-modify)
 | 
			
		||||
  "Return non-nil if DIR is considered safe.
 | 
			
		||||
DIR defaults to `default-directory' if it is nil.  Also, if DIR is unsafe, set
 | 
			
		||||
`trusted-dirs--did-protected-function-fail' to t unless NO-MODIFY is non-nil.
 | 
			
		||||
In any case, set `trusted-dirs--did-protected-function-run' to t unless
 | 
			
		||||
NO-MODIFY is non-nil.
 | 
			
		||||
 | 
			
		||||
This can return a few different things depending on how DIR is trusted.  See
 | 
			
		||||
`trusted-dirs--permanently-trusted-p' and
 | 
			
		||||
`trusted-dirs--buffer-temporarily-trusted-p' for a list of possible return
 | 
			
		||||
values."
 | 
			
		||||
  (unless dir (setq dir default-directory))
 | 
			
		||||
  (setq dir (trusted-dirs--resolve-current-directory dir))
 | 
			
		||||
  (unless no-modify
 | 
			
		||||
    (setq trusted-dirs--did-protected-function-run t))
 | 
			
		||||
  (let ((result (or (trusted-dirs--permanently-trusted-p dir)
 | 
			
		||||
                    (trusted-dirs--buffer-temporarily-trusted-p
 | 
			
		||||
                     (current-buffer)))))
 | 
			
		||||
    (unless (or no-modify result)
 | 
			
		||||
      (setq trusted-dirs--did-protected-function-fail t))
 | 
			
		||||
    (unless no-modify
 | 
			
		||||
      (force-mode-line-update))
 | 
			
		||||
    result))
 | 
			
		||||
 | 
			
		||||
(defun trusted-dirs--visible-buffer-list ()
 | 
			
		||||
  "Return a list of all visible buffers.
 | 
			
		||||
A buffer is coincided visible if it's name does not start with a space."
 | 
			
		||||
  (cl-delete-if (lambda (buf)
 | 
			
		||||
                  (string-prefix-p " " (buffer-name buf)))
 | 
			
		||||
                (buffer-list)))
 | 
			
		||||
 | 
			
		||||
(defun trusted-dirs--pprint-buffer-name (buffer)
 | 
			
		||||
  "Return a string which can represent BUFFER when prompting the user."
 | 
			
		||||
  (if-let ((path (buffer-file-name buffer))
 | 
			
		||||
           (file (file-name-nondirectory path)))
 | 
			
		||||
      (if (equal file (buffer-name buffer))
 | 
			
		||||
          file
 | 
			
		||||
        (format "%s (buffer %s)" file (buffer-name buffer)))
 | 
			
		||||
    (buffer-name buffer)))
 | 
			
		||||
 | 
			
		||||
(defun trusted-dirs-outdated-trust-information-p (&optional buffer)
 | 
			
		||||
  "Return non-nil if BUFFER has outdated trust information.
 | 
			
		||||
See `trusted-dirs-reload-newly-trusted-buffers' for an explanation of when a
 | 
			
		||||
buffer might have outdated trust information.
 | 
			
		||||
 | 
			
		||||
If BUFFER is nil, default to the current buffer;"
 | 
			
		||||
  (with-current-buffer (or buffer (current-buffer))
 | 
			
		||||
    (let ((safe (trusted-dirs-safe-p nil t)))
 | 
			
		||||
      (or (and safe trusted-dirs--did-protected-function-fail)
 | 
			
		||||
          (and (not safe) trusted-dirs--did-protected-function-run
 | 
			
		||||
               (not trusted-dirs--did-protected-function-fail))))))
 | 
			
		||||
 | 
			
		||||
(cl-defun trusted-dirs--outdated-buffer-list (&optional (buffers (buffer-list)))
 | 
			
		||||
  "Return a list of buffers that have outdated trust information.
 | 
			
		||||
See `trusted-dirs-reload-newly-trusted-buffers' for an explanation of when a
 | 
			
		||||
buffer might have outdated trust information.
 | 
			
		||||
 | 
			
		||||
If BUFFERS is passed, only consider buffers in that list.  Otherwise, consider
 | 
			
		||||
all live buffers (even special and hidden ones)."
 | 
			
		||||
  (cl-remove-if-not #'trusted-dirs-outdated-trust-information-p buffers))
 | 
			
		||||
 | 
			
		||||
(defun trusted-dirs--princ-to-string (object)
 | 
			
		||||
  "Return the output resulting from calling `princ' on OBJECT."
 | 
			
		||||
  (with-output-to-string
 | 
			
		||||
    (princ object standard-output)))
 | 
			
		||||
 | 
			
		||||
(defun trusted-dirs--pprint-list (items &optional formatter no-oxford-comma)
 | 
			
		||||
  "Pretty print ITEMS, a list of things.
 | 
			
		||||
Each item will be converted to a string, using FORMATTER, before being printed.
 | 
			
		||||
If FORMATTER is nil, use `trusted-dirs--princ-to-string'.  The FORMATTER must
 | 
			
		||||
take a single argument, the item to format, and return a string.
 | 
			
		||||
 | 
			
		||||
With NO-OXFORD-COMMA, don't insert an Oxford comma."
 | 
			
		||||
  (unless formatter (setq formatter #'trusted-dirs--princ-to-string))
 | 
			
		||||
  (let ((len (length items)))
 | 
			
		||||
    (cl-case len
 | 
			
		||||
      (0 "")
 | 
			
		||||
      (1 (funcall formatter (car items)))
 | 
			
		||||
      (2 (concat (funcall formatter (cl-first items))
 | 
			
		||||
                 " and "
 | 
			
		||||
                 (funcall formatter (cl-second items))))
 | 
			
		||||
      (t (cl-loop for i upfrom 1
 | 
			
		||||
                  for item in items
 | 
			
		||||
                  when (/= len i)
 | 
			
		||||
                  concat (funcall formatter item)
 | 
			
		||||
                  and concat (if (and no-oxford-comma
 | 
			
		||||
                                      (= i (1- len)))
 | 
			
		||||
                                 " "
 | 
			
		||||
                               ", ")
 | 
			
		||||
                  else
 | 
			
		||||
                  concat "and "
 | 
			
		||||
                  and concat (funcall formatter item))))))
 | 
			
		||||
 | 
			
		||||
;;;###autoload
 | 
			
		||||
(cl-defun trusted-dirs-revert-newly-trusted-buffers
 | 
			
		||||
    (&optional force silent (buffers (trusted-dirs--visible-buffer-list)))
 | 
			
		||||
  "Revert all buffers that have outdated trust information.
 | 
			
		||||
A buffer is considered to have outdated trust information if:
 | 
			
		||||
 - it is marked as having a had a function fail, even though it is trusted
 | 
			
		||||
 - it is marked as having had no function fail, even though it is untrusted
 | 
			
		||||
 | 
			
		||||
By default this prompts the user to save any buffers before reverting them.  If
 | 
			
		||||
the user says no to saving a buffer, skip it.  With FORCE, don't ask the user
 | 
			
		||||
anything and (possibly destructively) revert all buffers.
 | 
			
		||||
 | 
			
		||||
Unless SILENT is non-nil, `message' the user with a list of each revered buffer.
 | 
			
		||||
 | 
			
		||||
By default, revert all live buffers.  To only check some buffers, pass a list of
 | 
			
		||||
buffers in BUFFERS."
 | 
			
		||||
  (interactive)
 | 
			
		||||
  (let (reverted)
 | 
			
		||||
    (dolist (buffer (trusted-dirs--outdated-buffer-list buffers))
 | 
			
		||||
      (with-current-buffer buffer
 | 
			
		||||
        (when (and (not force)
 | 
			
		||||
                   (buffer-modified-p)
 | 
			
		||||
                   (y-or-n-p (format "Save and revert %s?"
 | 
			
		||||
                                     (trusted-dirs--pprint-buffer-name buffer))))
 | 
			
		||||
          (save-buffer))
 | 
			
		||||
        (when (or force (not (buffer-modified-p)))
 | 
			
		||||
          (revert-buffer nil t)
 | 
			
		||||
          (push buffer reverted))))
 | 
			
		||||
    (when (and (not silent) reverted)
 | 
			
		||||
      (message
 | 
			
		||||
       "Reverted buffer%s %s"
 | 
			
		||||
       (if (length= reverted 1) "" "s")
 | 
			
		||||
       (trusted-dirs--pprint-list reverted
 | 
			
		||||
                                  #'trusted-dirs--pprint-buffer-name)))))
 | 
			
		||||
 | 
			
		||||
(cl-defun trusted-dirs--maybe-prompt-revert-newly-trusted-buffers
 | 
			
		||||
    (&optional (buffers (trusted-dirs--outdated-buffer-list
 | 
			
		||||
                         (trusted-dirs--visible-buffer-list))))
 | 
			
		||||
  "If there are buffers with outdated trust, prompt the user to revert them.
 | 
			
		||||
For a definition of what qualifies as a buffer with outdated trust, see
 | 
			
		||||
`trusted-dirs-revert-newly-trusted-buffers'.
 | 
			
		||||
 | 
			
		||||
With BUFFERS, only consider those buffers."
 | 
			
		||||
  (and buffers (y-or-n-p "Buffers with outdated trust detected! Revert?")
 | 
			
		||||
       (trusted-dirs-revert-newly-trusted-buffers nil nil buffers)))
 | 
			
		||||
 | 
			
		||||
;;;###autoload
 | 
			
		||||
(defun trusted-dirs-add (path &optional no-recursive no-revert)
 | 
			
		||||
  "Mark PATH as a trusted directory.
 | 
			
		||||
If NO-RECURSIVE is non-nil, don't trust any subdirectories of PATH.
 | 
			
		||||
Interactively, prompt for PATH.  With a prefix argument, set NO-RECURSIVE.
 | 
			
		||||
 | 
			
		||||
By default, this calls asks the user if they want to run
 | 
			
		||||
`trusted-dirs-revert-newly-trusted-buffers'.  If NO-REVERT is set, don't ask or
 | 
			
		||||
call it.
 | 
			
		||||
 | 
			
		||||
PATH is processed according to `trusted-dirs-truename-trusted-directories'."
 | 
			
		||||
  (interactive "DTrust Directory: \nP")
 | 
			
		||||
  (cl-loop with resolved = (trusted-dirs--resolve-trusted-directory path)
 | 
			
		||||
           with set-val = (if no-recursive t 'subdir)
 | 
			
		||||
           with did-set = nil
 | 
			
		||||
           for entry in trusted-dirs-list
 | 
			
		||||
           for entry-dir = (trusted-dirs--resolve-trusted-directory
 | 
			
		||||
                            (car entry))
 | 
			
		||||
           when (trusted-dirs--path-equal-p entry-dir resolved) do
 | 
			
		||||
           (setcdr entry set-val)
 | 
			
		||||
           (setq did-set t)
 | 
			
		||||
           finally
 | 
			
		||||
           (unless did-set
 | 
			
		||||
             (push (cons resolved set-val) trusted-dirs-list))
 | 
			
		||||
           ;; Now that resolved is permanently trusted, we can remove it from
 | 
			
		||||
           ;; the temporary cache
 | 
			
		||||
           (remhash resolved trusted-dirs--temporarily-trusted-cache)
 | 
			
		||||
           (unless no-revert
 | 
			
		||||
             (message "Added %s to the list of trusted directories"
 | 
			
		||||
                      resolved)
 | 
			
		||||
             (trusted-dirs--maybe-prompt-revert-newly-trusted-buffers))))
 | 
			
		||||
 | 
			
		||||
;;;###autoload
 | 
			
		||||
(defun trusted-dirs-add-current (&optional no-recursive no-revert)
 | 
			
		||||
  "Mark `default-directory' as a trusted directory.
 | 
			
		||||
NO-RECURSIVE and NO-REVERT are the same as for `trusted-dirs-add' (which see).
 | 
			
		||||
 | 
			
		||||
`default-directory' is processed according to
 | 
			
		||||
`trusted-dirs-truename-trusted-directories'."
 | 
			
		||||
  (interactive "P")
 | 
			
		||||
  (trusted-dirs-add default-directory no-recursive no-revert))
 | 
			
		||||
 | 
			
		||||
(defun trusted-dirs--read-trusted-dir (&optional prompt)
 | 
			
		||||
  "Read a trusted directory from the minibuffer with completion.
 | 
			
		||||
PROMPT is the prompt to use, defaulting to \"Trusted Directory: \"."
 | 
			
		||||
  (let ((ht (trusted-dirs-resolved-list))
 | 
			
		||||
        dirs)
 | 
			
		||||
    (maphash (lambda (dir _v) (push dir dirs)) ht)
 | 
			
		||||
    (completing-read (or prompt "Trusted Directory") dirs nil t)))
 | 
			
		||||
 | 
			
		||||
;;;###autoload
 | 
			
		||||
(defun trusted-dirs-remove (path &optional no-revert)
 | 
			
		||||
  "Mark PATH as an untrusted directory.
 | 
			
		||||
Interactively, prompt for PATH.  Return the number of entries removed, or nil if
 | 
			
		||||
none where removed.
 | 
			
		||||
 | 
			
		||||
By default, this asks the user if they want to run
 | 
			
		||||
`trusted-dirs-revert-newly-trusted-buffers'.  If NO-REVERT is set, don't ask or
 | 
			
		||||
call it.
 | 
			
		||||
 | 
			
		||||
PATH is processed according to `trusted-dirs-truename-trusted-directories'."
 | 
			
		||||
  (interactive (list (trusted-dirs--read-trusted-dir "Untrust Directory: ")))
 | 
			
		||||
  (cl-loop with resolved = (trusted-dirs--resolve-trusted-directory path)
 | 
			
		||||
           for entry in trusted-dirs-list
 | 
			
		||||
           for entry-dir = (trusted-dirs--resolve-trusted-directory
 | 
			
		||||
                            (car entry))
 | 
			
		||||
           when (trusted-dirs--path-equal-p entry-dir resolved)
 | 
			
		||||
           count t into removed-count
 | 
			
		||||
           else
 | 
			
		||||
           collect entry into new-list
 | 
			
		||||
           finally
 | 
			
		||||
           (setq trusted-dirs-list new-list)
 | 
			
		||||
           (unless no-revert
 | 
			
		||||
             (if (zerop removed-count)
 | 
			
		||||
                 (message "%s is not trusted" resolved)
 | 
			
		||||
               (message "Removed %s from the list of trusted directories"
 | 
			
		||||
                        resolved))
 | 
			
		||||
             (trusted-dirs--maybe-prompt-revert-newly-trusted-buffers))
 | 
			
		||||
           finally return (unless (zerop removed-count)
 | 
			
		||||
                            removed-count)))
 | 
			
		||||
 | 
			
		||||
(defun trusted-dirs-remove-current (&optional no-revert)
 | 
			
		||||
  "Mark `default-directory' as an untrusted directory.
 | 
			
		||||
NO-REVERT is the same as for `trusted-dirs-remove' (which see).  The return
 | 
			
		||||
value is also the same.
 | 
			
		||||
 | 
			
		||||
`default-directory' is processed according to
 | 
			
		||||
`trusted-dirs-truename-trusted-directories'."
 | 
			
		||||
  (interactive)
 | 
			
		||||
  (trusted-dirs-remove default-directory no-revert))
 | 
			
		||||
 | 
			
		||||
;;;###autoload
 | 
			
		||||
(defun trusted-dirs-add-temporary-directory
 | 
			
		||||
    (path &optional no-recursive no-revert)
 | 
			
		||||
  "Temporarily trust PATH.
 | 
			
		||||
PATH will be trusted until _ALL_ buffers that have it as their
 | 
			
		||||
`default-directory' are closed.  Unless NO-RECURSIVE is set, also trust
 | 
			
		||||
subdirectories of `default-directory'.  In this case buffers in all
 | 
			
		||||
subdirectories of `default-directory' will also be trusted, and PATH will not be
 | 
			
		||||
untrusted until _ALL_ of these buffers are closed as well.
 | 
			
		||||
 | 
			
		||||
Unless NO-REVERT is set, prompt the user to call
 | 
			
		||||
`trusted-dirs-revert-newly-trusted-buffers'.
 | 
			
		||||
 | 
			
		||||
Note that only non-special, visible, file-visiting buffers are considered."
 | 
			
		||||
  (interactive "DTemporarily Trust: \nP")
 | 
			
		||||
  (let ((resolved (trusted-dirs--resolve-trusted-directory path)))
 | 
			
		||||
    (when (trusted-dirs--permanently-trusted-p resolved t)
 | 
			
		||||
      (user-error "%s is already permanently trusted" resolved))
 | 
			
		||||
    (unless (trusted-dirs--find-buffers-in-dir resolved (not no-recursive) t)
 | 
			
		||||
      (user-error "There are no buffers in %s" resolved))
 | 
			
		||||
    (puthash resolved (if no-recursive t 'subdir)
 | 
			
		||||
             trusted-dirs--temporarily-trusted-cache)
 | 
			
		||||
    (unless no-revert
 | 
			
		||||
      (message "Temporarily trusted %s" resolved)
 | 
			
		||||
      (trusted-dirs--maybe-prompt-revert-newly-trusted-buffers))))
 | 
			
		||||
 | 
			
		||||
;;;###autoload
 | 
			
		||||
(defun trusted-dirs-add-temporary-buffer (&optional buffer-or-name no-revert)
 | 
			
		||||
  "Temporarily trust BUFFER-OR-NAME, defaulting to the current buffer.
 | 
			
		||||
The buffer will be trusted until it is closed.  If a new buffer visiting the
 | 
			
		||||
same file were to be created at a later time, that buffer would not be trusted.
 | 
			
		||||
Interactively, prompt for the buffer.
 | 
			
		||||
 | 
			
		||||
Unless NO-REVERT is set, prompt the user to revert the buffer if it is deemed to
 | 
			
		||||
have outdated trust information.  For an explanation of what this means, see
 | 
			
		||||
`trusted-dirs-revert-newly-trusted-buffers'."
 | 
			
		||||
  (interactive "bTemporarily Trust:")
 | 
			
		||||
  (unless buffer-or-name (setq buffer-or-name (current-buffer)))
 | 
			
		||||
  (unless (bufferp buffer-or-name)
 | 
			
		||||
    (setq buffer-or-name (get-buffer buffer-or-name)))
 | 
			
		||||
  (puthash buffer-or-name t trusted-dirs--temporarily-trusted-cache)
 | 
			
		||||
  (unless no-revert
 | 
			
		||||
    (message "Temporarily trusted %s"
 | 
			
		||||
             (trusted-dirs--pprint-buffer-name buffer-or-name))
 | 
			
		||||
    (when (trusted-dirs-outdated-trust-information-p buffer-or-name)
 | 
			
		||||
      (trusted-dirs--maybe-prompt-revert-newly-trusted-buffers
 | 
			
		||||
       (list buffer-or-name)))))
 | 
			
		||||
 | 
			
		||||
(defun trusted-dirs--filter-temporary-cache (predicate)
 | 
			
		||||
  "Return anything in the temporary trust cache that matches PREDICATE.
 | 
			
		||||
PREDICATE should be a function of one argument.  If will be passed each key in
 | 
			
		||||
`trusted-dirs--temporarily-trusted-cache'.  It should return non-nil if that
 | 
			
		||||
item should be included in the returned set."
 | 
			
		||||
  (cl-delete-if-not predicate
 | 
			
		||||
                    (hash-table-keys trusted-dirs--temporarily-trusted-cache)))
 | 
			
		||||
 | 
			
		||||
(defun trusted-dirs--read-temporary-directory (&optional prompt)
 | 
			
		||||
  "Prompt for and return the path of a temporarily trusted directory.
 | 
			
		||||
PROMPT defaults to \"Temporarily Trusted Directory: \"."
 | 
			
		||||
  (completing-read (or prompt "Temporarily Trusted Directory: ")
 | 
			
		||||
                   (trusted-dirs--filter-temporary-cache 'stringp)
 | 
			
		||||
                   nil t))
 | 
			
		||||
 | 
			
		||||
;;;###autoload
 | 
			
		||||
(defun trusted-dirs-remove-temporary-directory (path &optional no-revert)
 | 
			
		||||
  "Untrust the temporarily trusted directory PATH.
 | 
			
		||||
 | 
			
		||||
Unless NO-REVERT is set, prompt the user to revert the buffer if it is deemed to
 | 
			
		||||
have outdated trust information.  For an explanation of what this means, see
 | 
			
		||||
`trusted-dirs-revert-newly-trusted-buffers'."
 | 
			
		||||
  (interactive (list (trusted-dirs--read-temporary-directory
 | 
			
		||||
                      "Untrust Directory: ")))
 | 
			
		||||
  (let ((resolved (trusted-dirs--resolve-trusted-directory path)))
 | 
			
		||||
    (remhash resolved trusted-dirs--temporarily-trusted-cache)
 | 
			
		||||
    (unless no-revert
 | 
			
		||||
      (message "Untrusted %s" resolved)
 | 
			
		||||
      (trusted-dirs--maybe-prompt-revert-newly-trusted-buffers))))
 | 
			
		||||
 | 
			
		||||
(defun trusted-dirs--read-temporary-buffer (&optional prompt)
 | 
			
		||||
  "Prompt the user for a temporarily trusted buffer and it (not its name).
 | 
			
		||||
PROMPT defaults to \"Temporarily Trusted Buffer: \"."
 | 
			
		||||
  (let ((names (mapcar 'buffer-name
 | 
			
		||||
                       (trusted-dirs--filter-temporary-cache 'bufferp))))
 | 
			
		||||
    (get-buffer (read-buffer (or prompt "Temporarily Trusted Buffer: ")
 | 
			
		||||
                             nil t (lambda (buf-name)
 | 
			
		||||
                                     (unless (stringp buf-name)
 | 
			
		||||
                                       (setq buf-name (car buf-name)))
 | 
			
		||||
                                     (member buf-name names))))))
 | 
			
		||||
 | 
			
		||||
;;;###autoload
 | 
			
		||||
(defun trusted-dirs-remove-temporary-buffer (&optional buffer-or-name no-revert)
 | 
			
		||||
  "Untust BUFFER-OR-NAME if it is a temporarily trusted buffer.
 | 
			
		||||
If it was trusted, return non-nil, otherwise, return nil.  Note that this only
 | 
			
		||||
untrusts BUFFER-OR-NAME, and not its `default-directory'.  For that, see
 | 
			
		||||
`trusted-dirs-remove-temporary-directory'.
 | 
			
		||||
 | 
			
		||||
Unless NO-REVERT is set, prompt the user to revert the buffer if it is deemed to
 | 
			
		||||
have outdated trust information.  For an explanation of what this means, see
 | 
			
		||||
`trusted-dirs-revert-newly-trusted-buffers'."
 | 
			
		||||
  (interactive (list (trusted-dirs--read-temporary-buffer "Untrust Buffer: ")))
 | 
			
		||||
  (unless buffer-or-name (setq buffer-or-name (current-buffer)))
 | 
			
		||||
  (unless (bufferp buffer-or-name)
 | 
			
		||||
    (setq buffer-or-name (get-buffer buffer-or-name)))
 | 
			
		||||
  (remhash buffer-or-name trusted-dirs--temporarily-trusted-cache)
 | 
			
		||||
  (unless no-revert
 | 
			
		||||
    (message "Untrusted %s"
 | 
			
		||||
             (trusted-dirs--pprint-buffer-name buffer-or-name))
 | 
			
		||||
    (when (trusted-dirs-outdated-trust-information-p buffer-or-name)
 | 
			
		||||
      (trusted-dirs--maybe-prompt-revert-newly-trusted-buffers
 | 
			
		||||
       (list buffer-or-name)))))
 | 
			
		||||
 | 
			
		||||
;;;###autoload
 | 
			
		||||
(defun trusted-dirs-remove-temporary-current-buffer (&optional no-revert)
 | 
			
		||||
  "Untrust the current buffer, however it's temporarily trusted.
 | 
			
		||||
This will either untrust the current buffer directly, untrust its
 | 
			
		||||
`default-directory', or untrust a parent of its `default-directory'.  If need
 | 
			
		||||
be, it may do multiple of these.
 | 
			
		||||
 | 
			
		||||
Unless NO-REVERT is set, prompt the user to revert the buffer if it is deemed to
 | 
			
		||||
have outdated trust information.  For an explanation of what this means, see
 | 
			
		||||
`trusted-dirs-revert-newly-trusted-buffers'."
 | 
			
		||||
  (interactive)
 | 
			
		||||
  (let (steps)
 | 
			
		||||
    (while-let ((how (cdr (trusted-dirs--buffer-temporarily-trusted-p
 | 
			
		||||
                           (current-buffer)))))
 | 
			
		||||
      (push how steps)
 | 
			
		||||
      (if (stringp how)
 | 
			
		||||
          (trusted-dirs-remove-temporary-directory how t)
 | 
			
		||||
        (trusted-dirs-remove-temporary-buffer how t)))
 | 
			
		||||
    (unless no-revert
 | 
			
		||||
      (message "Untrusted %s"
 | 
			
		||||
               (trusted-dirs--pprint-list
 | 
			
		||||
                (nreverse steps)
 | 
			
		||||
                (lambda (elt)
 | 
			
		||||
                  (if (stringp elt)
 | 
			
		||||
                      elt
 | 
			
		||||
                    (concat "buffer " (buffer-name elt))))))
 | 
			
		||||
      (trusted-dirs--maybe-prompt-revert-newly-trusted-buffers))))
 | 
			
		||||
 | 
			
		||||
(eval-and-compile
 | 
			
		||||
  (defun trusted-dirs--quoted-symbol-p (form)
 | 
			
		||||
    "Return non-nil if FORM is a quoted symbol.
 | 
			
		||||
This returns non-nil if FORM is a proper list of two elements, the first being
 | 
			
		||||
the symbol \\='quote or \\='function and the second being a symbol."
 | 
			
		||||
    (and (memq (car-safe form) '(function quote))
 | 
			
		||||
         (consp (cdr form))
 | 
			
		||||
         (symbolp (cadr form))
 | 
			
		||||
         (null (cddr form)))))
 | 
			
		||||
 | 
			
		||||
(defmacro trusted-dirs-only-if-safe (function &optional replacement prefix suffix)
 | 
			
		||||
  "Return a function that will call FUNCTION if `default-directory' is safe.
 | 
			
		||||
If REPLACEMENT is non-nil, call it instead of FUNCTION if `default-directory' is
 | 
			
		||||
unsafe.  REPLACEMENT is called with the same arguments that FUNCTION would have
 | 
			
		||||
been called with.
 | 
			
		||||
 | 
			
		||||
If either PREFIX or SUFFIX is a string and FUNCTION is a symbol (this is a
 | 
			
		||||
macro, so these must be true at compile time), define a new function named by
 | 
			
		||||
concatenating PREFIX, the name of FUNCTION, and SUFFIX."
 | 
			
		||||
  (let* ((args (make-symbol "args"))
 | 
			
		||||
         (evaled-prefix (eval prefix t))
 | 
			
		||||
         (evaled-suffix (eval suffix t))
 | 
			
		||||
         (do-defun (and (or (stringp evaled-prefix) (stringp evaled-suffix))
 | 
			
		||||
                        (trusted-dirs--quoted-symbol-p function))))
 | 
			
		||||
    `(,@(if do-defun
 | 
			
		||||
            (list 'defun (intern (concat (when (stringp evaled-prefix)
 | 
			
		||||
                                           evaled-prefix)
 | 
			
		||||
                                         (symbol-name (cl-second function))
 | 
			
		||||
                                         (when (stringp evaled-suffix)
 | 
			
		||||
                                           evaled-suffix))))
 | 
			
		||||
          '(lambda))
 | 
			
		||||
      (&rest ,args)
 | 
			
		||||
      ,@(when do-defun
 | 
			
		||||
          (list (format "Execute `%s' when `default-directory' is safe.
 | 
			
		||||
The safety check is done with `trusted-dirs-safe-p'."
 | 
			
		||||
                        (cl-second function))))
 | 
			
		||||
      (require 'trusted-dirs)
 | 
			
		||||
      (if (trusted-dirs-safe-p)
 | 
			
		||||
          (apply ,function ,args)
 | 
			
		||||
        ,@(when replacement
 | 
			
		||||
            (list `(apply ,replacement ,args)))))))
 | 
			
		||||
 | 
			
		||||
(cl-defmacro trusted-dirs-add-hook-if-safe
 | 
			
		||||
    (hook function &optional (depth nil depthp) (local nil localp))
 | 
			
		||||
  "Like `add-hook', but only when `default-directory' is trusted.
 | 
			
		||||
This will add FUNCTION to HOOK, initializing it if necessary.  DEPTH and LOCAL
 | 
			
		||||
are the same as `add-hook' (which see).  If FUNCTION is a symbol, it is wrapped
 | 
			
		||||
in a new function who's name is formed by concatenating the name of FUNCTION and
 | 
			
		||||
`trusted-dirs-hook-function-name-suffix'."
 | 
			
		||||
  `(add-hook ,hook (trusted-dirs-only-if-safe
 | 
			
		||||
                    ,function nil ,trusted-dirs-generated-function-name-prefix
 | 
			
		||||
                    ,trusted-dirs-hook-function-name-suffix)
 | 
			
		||||
             ,@(when depthp
 | 
			
		||||
                 (list depth))
 | 
			
		||||
             ,@(when localp
 | 
			
		||||
                 (list local))))
 | 
			
		||||
 | 
			
		||||
(defun trusted-dirs-remove-hook (hook function &optional local)
 | 
			
		||||
  "Remove FUNCTION from HOOK if it was added by trusted-dirs.
 | 
			
		||||
This undoes `trusted-dirs-add-hook-if-safe'.  LOCAL is the same for this as for
 | 
			
		||||
`add-hook'.  This only works if FUNCTION is a symbol."
 | 
			
		||||
  (cl-check-type function symbol)
 | 
			
		||||
  (when-let ((wrapped (intern-soft
 | 
			
		||||
                       (format "%s%s%s"
 | 
			
		||||
                               trusted-dirs-generated-function-name-prefix
 | 
			
		||||
                               (symbol-name function)
 | 
			
		||||
                               trusted-dirs-hook-function-name-suffix))))
 | 
			
		||||
    (remove-hook hook wrapped local)))
 | 
			
		||||
 | 
			
		||||
(eval-and-compile
 | 
			
		||||
  (defun trusted-dirs--format-doc-string (format &rest args)
 | 
			
		||||
    "Call `format' fill the output as a documentation string.
 | 
			
		||||
This will call `format' using FORMAT and ARGS.  Every paragraph in the output
 | 
			
		||||
except the first line will then be filled to a `fill-column' of 80 using
 | 
			
		||||
`fill-region'."
 | 
			
		||||
    (let ((raw-string (apply 'format format args)))
 | 
			
		||||
      (with-temp-buffer
 | 
			
		||||
        (insert raw-string)
 | 
			
		||||
        (goto-char (point-min))
 | 
			
		||||
        (forward-line)
 | 
			
		||||
        (fill-individual-paragraphs (point) (point-max))
 | 
			
		||||
        (buffer-string))))
 | 
			
		||||
 | 
			
		||||
  (defun trusted-dirs--make-advice-function (target replacement)
 | 
			
		||||
    "Make `:around' advice for TARGET to only call it in safe directories.
 | 
			
		||||
If REPLACEMENT is non-nil, it will be called instead in unsafe directories."
 | 
			
		||||
    (let ((oldfun (make-symbol "oldfun"))
 | 
			
		||||
          (args (make-symbol "args"))
 | 
			
		||||
          (do-defun (trusted-dirs--quoted-symbol-p target)))
 | 
			
		||||
      `(,@(if do-defun
 | 
			
		||||
              `(defun ,(intern
 | 
			
		||||
                        (concat trusted-dirs-generated-function-name-prefix
 | 
			
		||||
                                (symbol-name (cl-second target))
 | 
			
		||||
                                trusted-dirs-advice-function-name-suffix)))
 | 
			
		||||
            '(lambda))
 | 
			
		||||
        (,oldfun &rest ,args)
 | 
			
		||||
        ,@(when do-defun
 | 
			
		||||
            (list
 | 
			
		||||
             (trusted-dirs--format-doc-string
 | 
			
		||||
              "Advice `:around' `%s' to only call it in safe directories.
 | 
			
		||||
The safety check is done with `trusted-dirs-safe-p'.  If this check fails, %s."
 | 
			
		||||
              (symbol-name (cl-second target))
 | 
			
		||||
              (cond
 | 
			
		||||
               ((trusted-dirs--quoted-symbol-p replacement)
 | 
			
		||||
                (concat (symbol-name (cl-second replacement))
 | 
			
		||||
                        " is called instead"))
 | 
			
		||||
               (replacement
 | 
			
		||||
                "an anonymous function is called instead.")
 | 
			
		||||
               (t "nil is returned instead.")))))
 | 
			
		||||
        (require 'trusted-dirs)
 | 
			
		||||
        (if (trusted-dirs-safe-p)
 | 
			
		||||
            (apply ,oldfun ,args)
 | 
			
		||||
          ,@(when replacement
 | 
			
		||||
              (list `(apply ,replacement ,args))))))))
 | 
			
		||||
 | 
			
		||||
(defmacro trusted-dirs-mark-function-unsafe (function &optional replacement)
 | 
			
		||||
  "Mark FUNCTION as only being runnable in safe directories.
 | 
			
		||||
This will add advice to FUNCTION such that it will simply return nil unless the
 | 
			
		||||
current directory is safe.  If REPLACEMENT is non-nil, it will be run instead of
 | 
			
		||||
FUNCTION in unsafe directories.  If FUNCTION is a symbol, it is wrapped
 | 
			
		||||
in a new function who's name is formed by concatenating the name of FUNCTION and
 | 
			
		||||
`trusted-dirs-advice-function-name-suffix'.
 | 
			
		||||
 | 
			
		||||
This will attempt to make the advice run before any other advice by giving it a
 | 
			
		||||
depth of -100 (see `add-function' for what this means), however, there is
 | 
			
		||||
nothing stopping other functions from doing this as well, so care must be taken
 | 
			
		||||
that these other pieces of advice do not call potentially unsafe functions."
 | 
			
		||||
  (let ((advice (trusted-dirs--make-advice-function function replacement)))
 | 
			
		||||
    (if (trusted-dirs--quoted-symbol-p function)
 | 
			
		||||
        `(advice-add ,function :around ,advice '(:depth -100))
 | 
			
		||||
      `(add-function :around ,function ,advice '(:depth -100)))))
 | 
			
		||||
 | 
			
		||||
(defun trusted-dirs-unmark-function (function)
 | 
			
		||||
  "Mark FUNCTION as safe for execution in unsafe directories.
 | 
			
		||||
This undoes the effects of `trusted-dirs-mark-function-unsafe'.  This only works
 | 
			
		||||
if FUNCTION is a symbol.
 | 
			
		||||
 | 
			
		||||
Note that this is a function and that is a macro.  Thus, this will only work if
 | 
			
		||||
the values of `trusted-dirs-generated-function-name-prefix' and
 | 
			
		||||
`trusted-dirs-advice-function-name-suffix' are the same as when
 | 
			
		||||
`trusted-dirs-mark-function-unsafe' was compiled."
 | 
			
		||||
  (cl-check-type function symbol)
 | 
			
		||||
  (when-let ((advice (intern-soft
 | 
			
		||||
                      (format "%s%s%s"
 | 
			
		||||
                              trusted-dirs-generated-function-name-prefix
 | 
			
		||||
                              (symbol-name function)
 | 
			
		||||
                              trusted-dirs-advice-function-name-suffix))))
 | 
			
		||||
    (advice-remove function advice)))
 | 
			
		||||
 | 
			
		||||
(provide 'trusted-dirs)
 | 
			
		||||
;;; trusted-dirs.el ends here
 | 
			
		||||
							
								
								
									
										1068
									
								
								elisp/trusted-files.el
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1068
									
								
								elisp/trusted-files.el
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										51
									
								
								init.el
									
									
									
									
									
								
							
							
						
						
									
										51
									
								
								init.el
									
									
									
									
									
								
							@ -38,6 +38,9 @@
 | 
			
		||||
  (no-littering-theme-backups)
 | 
			
		||||
  (setq custom-file (no-littering-expand-etc-file-name "custom.el")))
 | 
			
		||||
 | 
			
		||||
;; load things saved with custom
 | 
			
		||||
(load custom-file t t)
 | 
			
		||||
 | 
			
		||||
;; diminish
 | 
			
		||||
(use-package diminish
 | 
			
		||||
  :config
 | 
			
		||||
@ -624,6 +627,11 @@ With NO-EDGE, return nil if beg or end fall on the edge of the range."
 | 
			
		||||
  (advice-add 'sp-region-ok-p :around 'my/-evil-cp-region-ok-p-no-string)
 | 
			
		||||
  (advice-add 'evil-cp--balanced-block-p :around 'my/-evil-cp-block-ok-p-no-string))
 | 
			
		||||
 | 
			
		||||
;; be (hopefully) safer
 | 
			
		||||
(require 'trusted-files)
 | 
			
		||||
(keymap-global-set "C-c t" 'trusted-files-map)
 | 
			
		||||
(trusted-files-modeline-mode)
 | 
			
		||||
 | 
			
		||||
;; better lisp editing
 | 
			
		||||
(use-package adjust-parens
 | 
			
		||||
  :hook (prog-mode . adjust-parens-mode)
 | 
			
		||||
@ -1053,7 +1061,7 @@ to `posframe-show' if the display is graphical."
 | 
			
		||||
 | 
			
		||||
;; flycheck
 | 
			
		||||
(use-package flycheck
 | 
			
		||||
  :hook ((sh-mode emacs-lisp-mode) . flycheck-mode)
 | 
			
		||||
  :hook ((sh-mode emacs-lisp-mode) . trusted-files-flycheck-mode-if-safe)
 | 
			
		||||
  :custom
 | 
			
		||||
  (flycheck-indication-mode 'left-margin)
 | 
			
		||||
  :init
 | 
			
		||||
@ -1199,7 +1207,7 @@ With PROJECT, give diagnostics for all buffers in the current project."
 | 
			
		||||
  :init
 | 
			
		||||
  ;; (defun my/eglot-in-text-mode-only ()
 | 
			
		||||
  ;;   (when (eq major-mode 'text-mode)
 | 
			
		||||
  ;;     (eglot-ensure)))
 | 
			
		||||
  ;;     (trusted-files-eglot-ensure-if-safe)))
 | 
			
		||||
  (defvar my/-eglot-documentation-buffer nil
 | 
			
		||||
    "Buffer for showing documentation for `my/eglot-documentation-at-point'.")
 | 
			
		||||
  (defun my/eglot-documentation-at-point ()
 | 
			
		||||
@ -1577,7 +1585,7 @@ otherwise, call `bibtex-find-text'."
 | 
			
		||||
  :hook ((LaTeX-mode . turn-on-reftex)
 | 
			
		||||
         (LaTeX-mode . LaTeX-math-mode)
 | 
			
		||||
         (LaTeX-mode . my/-setup-LaTeX-mode)
 | 
			
		||||
         (LaTeX-mode . flycheck-mode))
 | 
			
		||||
         (LaTeX-mode . trusted-files-flycheck-mode-if-safe))
 | 
			
		||||
  :bind (:map TeX-mode-map
 | 
			
		||||
         ("C-c ?" . latex-help))
 | 
			
		||||
  :init
 | 
			
		||||
@ -1630,17 +1638,17 @@ otherwise, call `bibtex-find-text'."
 | 
			
		||||
 | 
			
		||||
;; blueprint
 | 
			
		||||
(use-package blueprint-ts-mode
 | 
			
		||||
  :hook (blueprint-ts-mode . eglot-ensure)
 | 
			
		||||
  :hook (blueprint-ts-mode . trusted-files-eglot-ensure-if-safe)
 | 
			
		||||
  :after eglot)
 | 
			
		||||
 | 
			
		||||
;; python-ts-mode
 | 
			
		||||
(use-package python-ts-mode
 | 
			
		||||
  :ensure nil
 | 
			
		||||
  :hook (python-ts-mode . eglot-ensure))
 | 
			
		||||
  :hook (python-ts-mode . trusted-files-eglot-ensure-if-safe))
 | 
			
		||||
 | 
			
		||||
;; java-ts-mode
 | 
			
		||||
(use-package java-ts-mode
 | 
			
		||||
  :hook ((java-ts-mode . eglot-ensure)
 | 
			
		||||
  :hook ((java-ts-mode . trusted-files-eglot-ensure-if-safe)
 | 
			
		||||
         (java-ts-mode . my/-setup-java-ts-mode))
 | 
			
		||||
  :config
 | 
			
		||||
  (defun my/-setup-java-ts-mode ()
 | 
			
		||||
@ -1654,7 +1662,7 @@ otherwise, call `bibtex-find-text'."
 | 
			
		||||
;; c-ts-mode
 | 
			
		||||
(use-package c-ts-mode
 | 
			
		||||
  :after evil
 | 
			
		||||
  :hook ((c-ts-mode c++-ts-mode) . eglot-ensure)
 | 
			
		||||
  :hook ((c-ts-mode c++-ts-mode) . trusted-files-eglot-ensure-if-safe)
 | 
			
		||||
  :init
 | 
			
		||||
  (setq-default c-ts-mode-indent-offset 4)
 | 
			
		||||
  :config
 | 
			
		||||
@ -1673,11 +1681,11 @@ otherwise, call `bibtex-find-text'."
 | 
			
		||||
 | 
			
		||||
;; php-mode
 | 
			
		||||
(use-package php-mode
 | 
			
		||||
  :hook (php-mode . eglot-ensure))
 | 
			
		||||
  :hook (php-mode . trusted-files-eglot-ensure-if-safe))
 | 
			
		||||
 | 
			
		||||
;; web-mode
 | 
			
		||||
(use-package web-mode
 | 
			
		||||
  :hook (web-mode . eglot-ensure)
 | 
			
		||||
  :hook (web-mode . trusted-files-eglot-ensure-if-safe)
 | 
			
		||||
  :init
 | 
			
		||||
  (add-to-list 'eglot-server-programs
 | 
			
		||||
               '(web-mode . ("vscode-html-language-server" "--stdio"))))
 | 
			
		||||
@ -1685,12 +1693,12 @@ otherwise, call `bibtex-find-text'."
 | 
			
		||||
;; JavaScript
 | 
			
		||||
(use-package js
 | 
			
		||||
  :ensure nil
 | 
			
		||||
  :hook (js-ts-mode . eglot-ensure))
 | 
			
		||||
  :hook (js-ts-mode . trusted-files-eglot-ensure-if-safe))
 | 
			
		||||
 | 
			
		||||
;; TypeScript
 | 
			
		||||
(use-package typescript-ts-mode
 | 
			
		||||
  :ensure nil
 | 
			
		||||
  :hook (typescript-ts-mode . eglot-ensure)
 | 
			
		||||
  :hook (typescript-ts-mode . trusted-files-eglot-ensure-if-safe)
 | 
			
		||||
  :init
 | 
			
		||||
  (add-to-list 'auto-mode-alist `(,(rx ".ts" eos) . typescript-ts-mode)))
 | 
			
		||||
 | 
			
		||||
@ -1721,24 +1729,24 @@ otherwise, call `bibtex-find-text'."
 | 
			
		||||
;; go mode
 | 
			
		||||
(use-package go-mode
 | 
			
		||||
  :defer nil
 | 
			
		||||
  :hook (go-mode . eglot-ensure))
 | 
			
		||||
  :hook (go-mode . trusted-files-eglot-ensure-if-safe))
 | 
			
		||||
(use-package go-ts-mode
 | 
			
		||||
  :ensure nil
 | 
			
		||||
  :hook (go-ts-mode . eglot-ensure))
 | 
			
		||||
  :hook (go-ts-mode . trusted-files-eglot-ensure-if-safe))
 | 
			
		||||
 | 
			
		||||
;; rust
 | 
			
		||||
(use-package rust-mode)
 | 
			
		||||
(use-package rust-ts-mode
 | 
			
		||||
  :ensure nil
 | 
			
		||||
  :hook (rust-ts-mode . eglot-ensure))
 | 
			
		||||
  :hook (rust-ts-mode . trusted-files-eglot-ensure-if-safe))
 | 
			
		||||
 | 
			
		||||
;; zig
 | 
			
		||||
(use-package zig-mode
 | 
			
		||||
  :hook (zig-mode . eglot-ensure))
 | 
			
		||||
  :hook (zig-mode . trusted-files-eglot-ensure-if-safe))
 | 
			
		||||
 | 
			
		||||
;; lua
 | 
			
		||||
(use-package lua-mode
 | 
			
		||||
  :hook (lua-mode . eglot-ensure))
 | 
			
		||||
  :hook (lua-mode . trusted-files-eglot-ensure-if-safe))
 | 
			
		||||
 | 
			
		||||
;; markdown
 | 
			
		||||
(use-package markdown-mode
 | 
			
		||||
@ -1758,7 +1766,7 @@ otherwise, call `bibtex-find-text'."
 | 
			
		||||
 | 
			
		||||
;; json
 | 
			
		||||
(use-package json-ts-mode
 | 
			
		||||
  :hook (json-ts-mode . eglot-ensure))
 | 
			
		||||
  :hook (json-ts-mode . trusted-files-eglot-ensure-if-safe))
 | 
			
		||||
(use-package json-mode)
 | 
			
		||||
 | 
			
		||||
;; csv
 | 
			
		||||
@ -1769,7 +1777,7 @@ otherwise, call `bibtex-find-text'."
 | 
			
		||||
 | 
			
		||||
;; yaml
 | 
			
		||||
(use-package yaml-ts-mode
 | 
			
		||||
  :hook ((yaml-ts-mode . eglot-ensure)
 | 
			
		||||
  :hook ((yaml-ts-mode . trusted-files-eglot-ensure-if-safe)
 | 
			
		||||
         (yaml-ts-mode . my/-setup-yaml-ts-mode))
 | 
			
		||||
  :init
 | 
			
		||||
  (defun my/-setup-yaml-ts-mode ()
 | 
			
		||||
@ -2382,7 +2390,7 @@ R is rest of the arguments to OLDFUN."
 | 
			
		||||
;; ledger
 | 
			
		||||
(use-package ledger-mode)
 | 
			
		||||
(use-package flycheck-ledger
 | 
			
		||||
  :hook (ledger-mode . flycheck-mode))
 | 
			
		||||
  :hook (ledger-mode . trusted-files-flycheck-mode-if-safe))
 | 
			
		||||
 | 
			
		||||
;; khard contacts
 | 
			
		||||
(require 'khard)
 | 
			
		||||
@ -2533,7 +2541,7 @@ The name is compared with the field name using TESTFN (defaults to `equal')."
 | 
			
		||||
  "Setup up stuff in `org-mu4e-compose' buffers."
 | 
			
		||||
  (setq-local ltex-eglot-variable-save-method 'file)
 | 
			
		||||
  ;; this should come last so it can pick up the above
 | 
			
		||||
  ;; (eglot-ensure)
 | 
			
		||||
  ;; (trusted-files-eglot-ensure-if-safe)
 | 
			
		||||
  )
 | 
			
		||||
(add-hook 'org-mu4e-compose-mode-hook #'my/-setup-org-mu4e-compose-mode)
 | 
			
		||||
 | 
			
		||||
@ -2780,7 +2788,8 @@ one of the normal rainbow-delimiters-depth-N-face faces."
 | 
			
		||||
      (dashboard-refresh-buffer)
 | 
			
		||||
      (setq my/-dashboard-did-fix-image t)))
 | 
			
		||||
  (defun my/-dashboard-setup-function ()
 | 
			
		||||
    (add-hook 'window-configuration-change-hook 'my/-dashboard-fix-image nil t))
 | 
			
		||||
    (add-hook 'window-configuration-change-hook 'my/-dashboard-fix-image nil t)
 | 
			
		||||
    (setq-local display-line-numbers nil))
 | 
			
		||||
  (add-hook 'dashboard-mode-hook 'my/-dashboard-setup-function)
 | 
			
		||||
  (set-face-background 'dashboard-banner-logo-title nil)
 | 
			
		||||
  (dashboard-setup-startup-hook)
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user