emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[nongnu] elpa/nix-mode f968cb98ef 097/500: Add nix-buffer.


From: ELPA Syncer
Subject: [nongnu] elpa/nix-mode f968cb98ef 097/500: Add nix-buffer.
Date: Sat, 29 Jan 2022 08:26:40 -0500 (EST)

branch: elpa/nix-mode
commit f968cb98ef7efa4b844a85ccea273ab14cbf1b71
Author: Matthew Bauer <mjbauer95@gmail.com>
Commit: Matthew Bauer <mjbauer95@gmail.com>

    Add nix-buffer.
---
 nix-buffer.el | 227 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 227 insertions(+)

diff --git a/nix-buffer.el b/nix-buffer.el
new file mode 100644
index 0000000000..539009427c
--- /dev/null
+++ b/nix-buffer.el
@@ -0,0 +1,227 @@
+;;; nix-buffer.el --- Set up buffer environments with nix
+
+;; Copyright (C) 2016, 2017 Shea Levy
+
+;; Author: Shea Levy
+;; URL: https://github.com/shlevy/nix-buffer/tree/master/
+;; Version: 3.0.0
+;; Package-Requires: ((f "0.17.3") (emacs "24.4"))
+
+;;; Commentary:
+
+;; This package provides 'nix-buffer', to modify your buffer
+;; according to a directory-local nix expression.  Think of it as
+;; nix-shell for Emacs.  See the documentation for 'nix-buffer' for
+;; more details.
+
+;; It may be desirable to run 'nix-buffer' before 'normal-mode' is
+;; called so it affects all modes.
+
+;;; Code:
+
+(require 'f)
+(require 'subr-x)
+
+(defgroup nix-buffer nil "Customization for nix-buffer."
+  :prefix "nix-buffer-"
+  :group 'environment
+  :package-version '('nix-buffer . "2.3.0"))
+
+(defun nix-buffer--directory-name-setter (opt val)
+  "Defcustom setter for nix-buffer-directory-name.
+OPT The option we're setting.
+
+VAL The value it's being set to."
+  (nix-buffer-update-directory-name val))
+
+(defcustom nix-buffer-directory-name
+  (locate-user-emacs-file "nix-buffer")
+  "Path where nix-buffer keeps its data.
+To update this variable outside of Customize, please use
+'nix-buffer-update-directory-name'."
+  :group 'nix-buffer
+  :type '(directory)
+  :set 'nix-buffer--directory-name-setter
+  :initialize 'custom-initialize-default
+  :risky t)
+
+(defvar nix-buffer--trust-exprs-file
+  (f-join nix-buffer-directory-name "trusted-exprs"))
+
+(defun nix-buffer--load-trusted-exprs ()
+  "Load the trusted nix-buffer exprs."
+  (let ((tbl (ignore-errors
+               (with-temp-buffer
+                 (insert-file-contents-literally
+                  nix-buffer--trust-exprs-file)
+                 (read (current-buffer))))))
+    (if (hash-table-p tbl)
+        tbl
+      (make-hash-table :test 'equal))))
+
+(defvar nix-buffer--trusted-exprs (nix-buffer--load-trusted-exprs))
+
+(defun nix-buffer-update-directory-name (path)
+  "Update the nix-buffer state directory.
+PATH The path to store the nix-buffer state."
+  (setq nix-buffer-directory-name path)
+  (setq nix-buffer--trust-exprs-file
+        (f-join nix-buffer-directory-name "trusted-exprs"))
+  (setq nix-buffer--trusted-exprs (nix-buffer--load-trusted-exprs)))
+
+(defun nix-buffer-unload-function ()
+  "Save state on unload."
+  (ignore-errors (make-directory nix-buffer-directory-name t))
+  (with-temp-buffer
+    (prin1 nix-buffer--trusted-exprs (current-buffer))
+    (write-region nil nil nix-buffer--trust-exprs-file))
+  nil)
+
+(defun nix-buffer--unique-filename (path)
+  "Create a unix-safe filename from an entire path.
+PATH the path to generate the name from."
+  (replace-regexp-in-string "[|\\/]"
+                            (lambda (str)
+                              (if (equal str "/")
+                                  "|"
+                                (concat "\\\\" str)))
+                            path))
+
+(defun nix-buffer--query-safety (expr-file lisp-file)
+  "Ask the user whether to trust a Lisp file.
+EXPR-FILE The nix expression leading to this file.
+
+LISP-FILE The file in question."
+  (let ((res (yes-or-no-p (concat expr-file
+                                  " resulted in unknown Lisp file "
+                                  lisp-file
+                                  "; trust it? "))))
+    (puthash lisp-file res nix-buffer--trusted-exprs)
+    res))
+
+(defvar nix-buffer-after-load-hook nil
+  "Hook run after nix-buffer loads an expression.")
+
+(defun nix-buffer--load-result (expr-file out)
+  "Load the result of a nix-buffer build, checking for safety.
+EXPR-FILE The nix expression being built.
+
+OUT The build result."
+  (when (or (gethash out nix-buffer--trusted-exprs)
+            (nix-buffer--query-safety expr-file out))
+    (load-file out)
+    (run-hooks 'nix-buffer-after-load-hook)))
+
+(defun nix-buffer--sentinel
+    (out-link last-out expr-file user-buf err-buf process event)
+  "Handle the results of the nix build.
+OUT-LINK The path to the output symlink.
+
+LAST-OUT The previous build result, if any.
+
+EXPR-FILE The nix expression being built.
+
+USER-BUF The buffer to apply the results to.
+
+ERR-BUF The standard error buffer of the nix-build
+
+PROCESS The process whose status changed.
+
+EVENT The process status change event string."
+  (unless (process-live-p process)
+    (let ((out-buf (process-buffer process)))
+      (progn
+        (if (= (process-exit-status process) 0)
+            (let ((cur-out (with-current-buffer out-buf
+                             (string-trim-right (buffer-string)))))
+              (if (string= "" cur-out)
+                  (ignore-errors (delete-file out-link))
+                (unless (string= last-out cur-out)
+                  (with-current-buffer user-buf
+                    (nix-buffer--load-result expr-file cur-out)))))
+          (with-current-buffer
+              (get-buffer-create "*nix-buffer errors*")
+            (insert "nix-build for nix-buffer for "
+                    (buffer-name user-buf)
+                    " "
+                    (string-trim-right event)
+                    " with error output: \n")
+            (insert-buffer-substring err-buf)
+            (pop-to-buffer (current-buffer))))
+        (kill-buffer out-buf)
+        (kill-buffer err-buf)))))
+
+(defun nix-buffer--nix-build (root expr-file)
+  "Start the nix build.
+ROOT The path we started from.
+
+EXPR-FILE The file containing the nix expression to build."
+  (let* ((state-dir (f-join nix-buffer-directory-name
+                            (nix-buffer--unique-filename root)))
+         (out-link (f-join state-dir "result"))
+         (current-out (file-symlink-p out-link))
+         (err (generate-new-buffer " nix-buffer-nix-build-stderr")))
+    (progn
+      (ignore-errors (make-directory state-dir t))
+      (make-process
+       :name "nix-buffer-nix-build"
+       :buffer (generate-new-buffer " nix-buffer-nix-build-stdout")
+       :command (list
+                 "nix-build"
+                 "--arg" "root" root
+                 "--out-link" out-link
+                 expr-file
+                 )
+       :noquery t
+       :sentinel (apply-partially 'nix-buffer--sentinel
+                                  out-link
+                                  current-out
+                                  expr-file
+                                  (current-buffer)
+                                  err)
+       :stderr err)
+      (when current-out
+        (nix-buffer--load-result expr-file current-out)))))
+
+;;;###autoload
+(defun nix-buffer ()
+  "Set up the buffer according to the directory-local nix expression.
+Looks for dir-locals.nix upward from the current directory.  If found,
+asynchronously builds the derivation defined there with the 'root' arg
+set to the current buffer file name or directory and evaluates the
+resulting elisp if safe to do so.  'nix-buffer-after-load-hook' can be
+used to detect when the elisp load occurs.
+
+If we have previously built dir-locals.nix for the current file or
+directory, the elisp corresponding to the last build is evaluated
+synchronously and the new elisp is evaluated when the build completes,
+unless the newly-built file is identical.  As such, the elisp
+generated by dir-locals.nix should be written with multiple
+evaluations in mind.
+
+Because in practice dir-locals.nix will always want to do things that
+are unsafe in dir-locals.el (e.g. append to 'exec-path'), we don't
+reuse that mechanism and instead just load the file as elisp.  Because
+this allows arbitrary code execution, the first time we're asked to
+load a particular store path we query the user to verify if it's safe
+to load beforehand.
+
+The Lisp code generated by dir-locals.nix should limit itself to
+modifying buffer-local variables, but there is no actual enforcement
+of this.  'setq-local' is your friend.
+
+If dir-locals.nix does not evaluate to any derivations (e.g. it
+evaluates to {}), then nothing is loaded and the cached result, if any,
+is removed."
+  (interactive)
+  (let* ((root (or (buffer-file-name) default-directory))
+         (expr-dir (locate-dominating-file root "dir-locals.nix")))
+    (when expr-dir
+      (let ((expr-file (f-expand "dir-locals.nix" expr-dir)))
+        (nix-buffer--nix-build root expr-file)))))
+
+(add-hook 'kill-emacs-hook 'nix-buffer-unload-function)
+
+(provide 'nix-buffer)
+
+;;; nix-buffer.el ends here



reply via email to

[Prev in Thread] Current Thread [Next in Thread]