branch: elpa/nix-mode commit 55b0ea2d962d2403f2c1e0d80d083856f991341f Merge: 414a40fe54 ae17e75812 Author: Matthew Justin Bauer <mjbaue...@gmail.com> Commit: GitHub <nore...@github.com>
Merge pull request #18 from matthewbauer/more-modes More modes --- company-nixos-options.el | 75 ++++++++++++++++ helm-nixos-options.el | 49 ++++++++++ nix-buffer.el | 227 +++++++++++++++++++++++++++++++++++++++++++++++ nix-mode-mmm.el | 23 +++++ nix-sandbox.el | 127 ++++++++++++++++++++++++++ nix-smie.el | 121 +++++++++++++++++++++++++ nixos-options.el | 128 ++++++++++++++++++++++++++ nixos-packages.el | 115 ++++++++++++++++++++++++ 8 files changed, 865 insertions(+) diff --git a/company-nixos-options.el b/company-nixos-options.el new file mode 100644 index 0000000000..d42cb30bb3 --- /dev/null +++ b/company-nixos-options.el @@ -0,0 +1,75 @@ +;;; company-nixos-options.el --- Company Backend for nixos-options + +;; Copyright (C) 2015 Diego Berrocal and Travis B. Hartwell + +;; Author: Diego Berrocal <cestdi...@gmail.com> +;; Travis B. Hartwell <na...@travishartwell.net> +;; Created: 18 July 2015 + +;; Keywords: unix +;; Homepage: http://www.github.com/travisbhartwell/nix-emacs/ +;; Version: 0.1.0 +;; Package-Requires: ((company "0.8.0") (nixos-options "0.0.1") (cl-lib "0.5.0")) + +;; This file is not part of GNU Emacs. + +;;; License: GPLv3 + +;;; Commentary: + +;; Useful functions for exploring the NixOS options. Inspired by +;; https://nixos.org/nixos/options.html. + +;;; Code: +(require 'nixos-options) +(require 'company) +(require 'cl-lib) + +(defun company-nixos-options--doc-buffer (candidate) + "Return documentation buffer for chosen CANDIDATE." + (let ((doc (nixos-options-get-documentation-for-option + (nixos-options-get-option-by-name candidate)))) + (and doc (nixos-options-doc-buffer doc)))) + +(defun company-nixos-options--candidates (prefix) + (let ((res)) + (dolist (option nixos-options) + (let ((name (nixos-options-get-name option))) + (when (string-prefix-p prefix name) + (push name res)))) + res)) + +(defun company-nixos-options--annotation (candidate) + (let ((type (nixos-options-get-type + (nixos-options-get-option-by-name + candidate)))) + (format " <%s>" type))) + +(defun company-nixos--grab-symbol () + (buffer-substring (point) (save-excursion (skip-syntax-backward "w_.") + (point)))) + +(defun company-nixos--in-nix-context-p () + (or (eq major-mode 'nix-mode) + (equal "nix" (file-name-extension + (buffer-file-name (current-buffer)))))) + +(defun company-nixos-options--prefix () + "Grab prefix at point." + (and (company-nixos--in-nix-context-p) + (or (company-nixos--grab-symbol) + 'stop))) + +;;;###autoload +(defun company-nixos-options (command &optional arg &rest ignored) + (interactive (list 'interactive)) + (cl-case command + (interactive (company-begin-backend 'company-nixos-options)) + (prefix (company-nixos-options--prefix)) + (candidates (company-nixos-options--candidates arg)) + (doc-buffer (company-nixos-options--doc-buffer arg)) + (annotation (company-nixos-options--annotation arg)))) + +(provide 'company-nixos-options) +;;; company-nixos-options.el ends here + diff --git a/helm-nixos-options.el b/helm-nixos-options.el new file mode 100644 index 0000000000..974274d425 --- /dev/null +++ b/helm-nixos-options.el @@ -0,0 +1,49 @@ +;;; helm-nixos-options.el --- Helm Interface for nixos-options + +;; Copyright (C) 2015 Diego Berrocal and Travis B. Hartwell + +;; Author: Diego Berrocal <cestdi...@gmail.com> +;; Travis B. Hartwell <na...@travishartwell.net> +;; Created: 18 July 2015 + +;; Keywords: unix +;; Homepage: http://www.github.com/travisbhartwell/nix-emacs/ +;; Version: 0.1.0 +;; Package-Requires: ((nixos-options "0.0.1") (helm "1.5.6")) + +;; This file is not part of GNU Emacs. + +;;; License: GPLv3 + +;;; Commentary: + +;; Useful functions for exploring the NixOS options. Inspired by +;; https://nixos.org/nixos/options.html. + +;;; Code: +(require 'nixos-options) +(require 'helm) + +(defun helm-source-nixos-options-search () + `((name . "NixOS Options") + (requires-pattern . 2) + (candidates . nixos-options) + (follow . 1) + (persistent-action . (lambda (f) (message (format "%s" (nixos-options-get-description f))))) + (action . (("View documentation" . (lambda (f) + (switch-to-buffer-other-window + (nixos-options-doc-buffer + (nixos-options-get-documentation-for-option f))))) + ("Insert into buffer" . (lambda (f) (insert (nixos-options-get-name f)))) + ("Pretty print" . (lambda (f) (message "Pretty Printed: %s" (pp f)))) + ("Display name" . (lambda (f) (message "Name: %s" (nixos-options-get-name f)))))))) + +;;;###autoload +(defun helm-nixos-options () + (interactive) + (helm :sources `(,(helm-source-nixos-options-search)) + :buffer "*helm-nixos-options*")) + +(provide 'helm-nixos-options) +;;; helm-nixos-options.el ends here + 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 diff --git a/nix-mode-mmm.el b/nix-mode-mmm.el new file mode 100644 index 0000000000..acfc8fef15 --- /dev/null +++ b/nix-mode-mmm.el @@ -0,0 +1,23 @@ +;;; nix-mode-mmm --- Summary + +;;; Commentary: + +;;; Code: + +(require 'mmm-mode) + +(mmm-add-group 'nix-sh + '((sh-command + :submode sh-mode + :face mmm-output-submode-face + :front "''" + :back "''" + :include-front t + :front-offset 2 + ))) + +(setq mmm-global-mode 'maybe) +(mmm-add-mode-ext-class 'nix-mode "\\.nix\\'" 'nix-sh) + +(provide 'nix-mode-mmm) +;;; nix-mode-mmm.el ends here diff --git a/nix-sandbox.el b/nix-sandbox.el new file mode 100644 index 0000000000..492a68d422 --- /dev/null +++ b/nix-sandbox.el @@ -0,0 +1,127 @@ +;;; nix-sandbox.el --- Utility functions to work with nix-shell sandboxes + +;; Copyright (C) 2015 Sven Keidel + +;; Author: Sven Keidel <svenkei...@gmail.com> +;; Package-Version: 0.1 +;; Package-Requires: ((dash "2.12.1") (s "1.10.0")) +;; Homepage: https://github.com/travisbhartwell/nix-emacs + +;; This file is not part of GNU Emacs. + +;;; License: GPLv3 + +;;; Commentary: + +;; Useful functions for working with nix-shell sandboxes + +;;; Code: + +(require 'dash) +(require 's) + +(defgroup nix nil + "customizations for nix" + :prefix "nix-" + :group 'external) + +(defcustom nix-nixpkgs-path nil + "Absolute path to a nixpkgs directory. + +Can be customized to select a nix-channel +e.g. /home/user/.nix-defexpr/channels/unstable/nixpkgs" + :group 'nix + :type '(choice (const :tag "No channel" nil) + (directory "Custom path to a nixpkgs distribution"))) + +(defun nix-create-sandbox-rc (sandbox) + "Create a new rc file containing the environment for the given SANDBOX." + (let ((env-str (shell-command-to-string + (concat "nix-shell " + (or (and nix-nixpkgs-path (concat "-I nixpkgs=" nix-nixpkgs-path)) + "") + " --run 'declare +x shellHook; declare -x; declare -xf' " + (shell-quote-argument sandbox) + " 2> /dev/null"))) + (tmp-file (make-temp-file "nix-sandbox-rc-"))) + (write-region env-str nil tmp-file 'append) + tmp-file)) + +(defvar nix-sandbox-rc-map (make-hash-table :test 'equal + :size 4)) + +(defun nix-sandbox-rc (sandbox) + "Return the rc file for the given SANDBOX or create one." + (or (gethash sandbox nix-sandbox-rc-map) + (puthash sandbox (nix-create-sandbox-rc sandbox) nix-sandbox-rc-map))) + +;;;###autoload +(defun nix-shell-command (sandbox &rest args) + "Assemble a command from ARGS that can be executed in the specified SANDBOX." + (list "bash" "-c" (format "source %s; %s" (nix-sandbox-rc sandbox) (s-join " " args)))) + +(defun nix-shell-string (sandbox &rest args) + "Assemble a command string from ARGS that can be executed in the specifed SANDBOX." + (combine-and-quote-strings + (apply 'nix-shell-command sandbox args))) + +;;;###autoload +(defun nix-compile (sandbox &rest command) + "Compile a program using the given COMMAND in SANDBOX." + (interactive "Dsandbox: \nMcommand: ") + (compile (apply 'nix-shell-string sandbox command))) + +;;;###autoload +(defun nix-shell (sandbox &rest command) + "Run a COMMAND in the given SANDBOX and return the output." + (shell-command-to-string (apply 'nix-shell-string sandbox command))) + +(defvar nix-exec-path-map (make-hash-table :test 'equal + :size 4)) + +;;;###autoload +(defun nix-exec-path (sandbox) + "Return the `exec-path' of the given SANDBOX." + + (or (gethash sandbox nix-exec-path-map) + (puthash sandbox + (split-string (nix-shell sandbox "printenv" "PATH") ":") + nix-exec-path-map))) + +;;;###autoload +(defun nix-executable-find (sandbox executable) + "Search for an EXECUTABLE in the given SANDBOX." + (let ((exec-path (nix-exec-path sandbox))) + (and exec-path (executable-find executable)))) + +;;;###autoload +(defun nix-find-sandbox (path) + "Search for a sandbox starting at PATH traversing upwards the directory tree. +If the directory contains a `shell.nix' file, the path to this +file is returned. Otherwise if the directory contains a +`default.nix' file, the parent directory is returned." + (and (file-exists-p path) + (let* ((map-nil (lambda (f x) (if x (funcall f x) nil))) + (sandbox-directory + (funcall map-nil 'expand-file-name + (locate-dominating-file path + '(lambda (dir) (directory-files dir t ".*\.nix$"))))) + (shell-nix (and sandbox-directory (concat sandbox-directory "shell.nix")))) + (if (and sandbox-directory (file-exists-p shell-nix)) + shell-nix + sandbox-directory)))) + +;;;###autoload +(defun nix-current-sandbox () + "Return the path of the sandbox that is closest to the current working directory." + (nix-find-sandbox default-directory)) + +(defun nix-clear-caches () + "Clear cached information for all sandboxes." + (interactive) + (clrhash nix-sandbox-rc-map) + (clrhash nix-exec-path-map)) + +(provide 'nix-sandbox) + +;;; nix-sandbox.el ends here diff --git a/nix-smie.el b/nix-smie.el new file mode 100644 index 0000000000..cad6eb7077 --- /dev/null +++ b/nix-smie.el @@ -0,0 +1,121 @@ +;;; nix-mode+.el --- Major mode to edit Nix files + +;; Copyright (C) 2014 Jenny Cassou + +;; Author: Jenny Cassou <damien.cas...@gmail.com> +;; Version: 0.1 + +;; This file is not part of GNU Emacs. + +;; This program is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see <http://www.gnu.org/licenses/>. + +;;; Commentary: + +;; Major mode to edit Nix files + +;;; Code: +(require 'smie) + +(defvar nix+-smie-grammar + (smie-prec2->grammar + (smie-bnf->prec2 + '((id) + (expr (exprfunction)) + (exprfunction ("{" formals "}" ":" exprfunction) + (exprassert)) + (exprassert ("assert" expr ";" exprassert) + (exprif)) + (exprif ("if" expr "then" expr "else" expr) + (exprop)) + (exprop ("!" exprop) + (exprop "==" exprop) + (exprop "!=" exprop) + (exprop "&&" exprop) + (exprop "||" exprop) + (exprop "->" exprop) + (exprop "//" exprop) + (exprop "~" exprop) + (exprop "?" id) + (exprapp)) + (exprapp (exprapp "." exprselect) + (exprselect)) + (exprselect (exprsimple)) + (exprsimple (id) + ("true") + ("false") + ("null") + ("(" expr ")") + ("{" binds "}") + ("let" "{" binds "}") + ("rec" "{" binds "}") + ("[" exprselects "]")) + (binds (binds ";" binds) + (bind)) + (exprselects (expreselect)) + (bind (id "=" expr) + ("inherit" exprp)) + (exprp ("(" expr ")")) + (formals (formals "," formals) + (formal)) + (formal (id) + (id "?" expr))) + '((assoc ";")) + '((assoc ",")) + '((assoc "==") + (assoc "!=") + (assoc "&&") + (assoc "||") + (assoc "->") + (assoc "//") + (assoc "~") + (assoc "?") + (assoc "!"))))) + +(defvar nix+-syntax-table + (let ((table (make-syntax-table))) + (modify-syntax-entry ?/ ". 14" table) + (modify-syntax-entry ?* ". 23" table) + (modify-syntax-entry ?# "< b" table) + (modify-syntax-entry ?\n "> b" table) + table) + "Syntax table for Nix mode.") + +;;;###autoload +(define-derived-mode nix-mode+ prog-mode "Nix" + "Major mode for editing Nix expressions. + +The following commands may be useful: + + '\\[newline-and-indent]' + Insert a newline and move the cursor to align with the previous + non-empty line. + + '\\[fill-paragraph]' + Refill a paragraph so that all lines are at most `fill-column' + lines long. This should do the right thing for comments beginning + with `#'. However, this command doesn't work properly yet if the + comment is adjacent to code (i.e., no intervening empty lines). + In that case, select the text to be refilled and use + `\\[fill-region]' instead. + +The hook `nix-mode-hook' is run when Nix mode is started. + +\\{nix-mode-map} +" + (set-syntax-table nix+-syntax-table) + (smie-setup nix+-smie-grammar #'ignore)) + +(provide 'nix-mode+) + +;;; nix-mode+.el ends here diff --git a/nixos-options.el b/nixos-options.el new file mode 100644 index 0000000000..89ace9bbc4 --- /dev/null +++ b/nixos-options.el @@ -0,0 +1,128 @@ +;;; nixos-options.el --- Interface for browsing and completing NixOS options. + +;; Copyright (C) 2015 Diego Berrocal and Travis B. Hartwell + +;; Author: Diego Berrocal <cestdi...@gmail.com> +;; Travis B. Hartwell <na...@travishartwell.net> +;; Created: 18 July 2015 + +;; Keywords: unix +;; Homepage: http://www.github.com/travisbhartwell/nix-emacs/ +;; Version: 0.0.1 +;; Package-Requires: ((emacs "24")) + +;; This file is not part of GNU Emacs. + +;;; License: GPLv3 + +;;; Commentary: + +;; Useful functions for exploring the NixOS options. Inspired by +;; https://nixos.org/nixos/options.html. + +;;; Code: + +(require 'json) + +(defvar nixos-options-name-indent-amount 0 + "Indent by the maximum length, plus a colon, plus two spaces.") + +;; Macros for defining constants and functions for working with options +(defmacro define-nixos-options-item (item long-name) + (let* ((name-const (intern (concat "nixos-options-" item))) + (long-name-const (intern (concat "nixos-options-" item "-long-name"))) + (long-name-length-plus-padding (+ 3 (length long-name))) + (long-name-docstring (format "The long description for %s." item)) + (item-getter (intern (concat "nixos-options-get-" item))) + (item-getter-docstring + (format "Get the value of %s from OPTION." item)) + (item-display (intern (concat "nixos-options-display-" item))) + (item-display-docstring + (format "Display the value for %s from OPTION." item))) + `(progn + (defconst ,name-const ,item) + (defconst ,long-name-const ,long-name ,long-name-docstring) + (if (> ,long-name-length-plus-padding nixos-options-name-indent-amount) + (setq nixos-options-name-indent-amount + ,long-name-length-plus-padding)) + (defun ,item-getter (option) + ,item-getter-docstring + (cdr (assoc ,name-const option))) + (defun ,item-display (option) + ,item-display-docstring + (let ((item (,item-getter option)) + (format-string + (format "%%-%ds %%s\n" nixos-options-name-indent-amount))) + (if (not (null item)) + (format format-string (concat ,long-name-const ":") item) + "")))))) + +(define-nixos-options-item "name" "Name") +(define-nixos-options-item "type" "Type") +(define-nixos-options-item "description" "Description") +(define-nixos-options-item "default" "Default value") +(define-nixos-options-item "example" "Example value") +(define-nixos-options-item "declarations" "Declared in") + +(defvar nixos-options-json-file + (let* ((cmd + "export NIXPKGS_ALLOW_UNFREE=1; nix-build -Q --no-out-link '<nixpkgs/nixos/release.nix>' -A options 2>/dev/null") + (dir (replace-regexp-in-string "\n\\'" "" + (shell-command-to-string cmd)))) + (expand-file-name "share/doc/nixos/options.json" dir)) + "Location of the options file.") + +(defun nixos-options--boolean-string (value) + "Return the string representation of the boolean VALUE. +Returns VALUE unchanged if not a boolean." + (cond ((eq value 't) "true") + ((eq value :json-false) "false") + (t value))) + +(defun nixos-options--make-alist (option) + (let ((name (car option)) + (data (cdr option)) + (default (nixos-options-get-default option)) + (example (nixos-options-get-example option))) + (progn + (if (not (null default)) + (setcdr (assoc nixos-options-default option) + (nixos-options--boolean-string default))) + (if (not (null example)) + (setcdr (assoc nixos-options-example option) + (nixos-options--boolean-string example))) + (add-to-list 'data `(,nixos-options-name . ,name)) + `(,name . ,data)))) + +(defvar nixos-options + (if (file-exists-p nixos-options-json-file) + (let* ((json-key-type 'string) + (raw-options (json-read-file nixos-options-json-file))) + (mapcar 'nixos-options--make-alist raw-options)) + (message "Warning: Cannot find nixos option file."))) + +(defun nixos-options-get-documentation-for-option (option) + (concat (nixos-options-display-name option) + (nixos-options-display-type option) + (nixos-options-display-description option) + (nixos-options-display-default option) + (nixos-options-display-example option) + (nixos-options-display-declarations option))) + +;; Borrowed from anaconda-mode +(defun nixos-options-doc-buffer (doc) + "Display documentation buffer with contents DOC." + (let ((buf (get-buffer-create "*nixos-options-doc*"))) + (with-current-buffer buf + (view-mode -1) + (erase-buffer) + (insert doc) + (goto-char (point-min)) + (view-mode 1) + buf))) + +(defun nixos-options-get-option-by-name (name) + (assoc name nixos-options)) + +(provide 'nixos-options) +;;; nixos-options.el ends here diff --git a/nixos-packages.el b/nixos-packages.el new file mode 100644 index 0000000000..bc21c4e3e4 --- /dev/null +++ b/nixos-packages.el @@ -0,0 +1,115 @@ +;;; nixos-packages.el --- Interface for browsing and completing NixOS packages. + +;; Copyright (C) 2015 Diego Berrocal and Travis B. Hartwell + +;; Author: Diego Berrocal <cestdi...@gmail.com> +;; Travis B. Hartwell <na...@travishartwell.net> +;; Created: 18 July 2015 + +;; Keywords: unix +;; Homepage: http://www.github.com/travisbhartwell/nix-emacs/ +;; Version: 0.0.1 +;; Package-Requires: ((emacs "24")) + +;; This file is not part of GNU Emacs. + +;;; License: GPLv3 + +;;; Commentary: + +;; Useful functions for exploring the NixOS packages. Inspired by +;; https://nixos.org/nixos/packages.html. + +;;; Code: + +(require 'json) + +(defvar nixos-packages-json-file + (expand-file-name "nixos-packages.json" spacemacs-cache-directory) + "Where to store the nixos-packages in JSON format") + +(defvar nixos-packages-name-indent-amount 0 + "Indent by the maximum length, plus a colon, plus two spaces.") + +(setq nixos-packages + (if (file-exists-p nixos-packages-json-file) + (let ((json-key-type 'string)) + (json-read-file nixos-packages-json-file)) + (let* ((cmd "nix-env -qaP hello --json") + (data (replace-regexp-in-string "\n\\'" "" + (shell-command-to-string cmd))) + (json-key-type 'string)) + (append-to-file data nil nixos-packages-json-file) + (json-read-from-string data)))) + + +(assoc "name" (car nixos-packages)) +(assoc "system" (car nixos-packages)) +(assoc "description" (assoc "meta" (car nixos-packages))) +(assoc "homepage" (assoc "meta" (car nixos-packages))) +(assoc "license" (assoc "meta" (car nixos-packages))) +(assoc "longDescription" (assoc "meta" (car nixos-packages))) +(assoc "platforms" (assoc "meta" (car nixos-packages))) +(assoc "position" (assoc "meta" (car nixos-packages))) +(assoc "maintainers" (assoc "meta" (car nixos-packages))) +(assoc "maintainers" (assoc "meta" (car nixos-packages))) + +;; Macros for defining constants and functions for working with options +(defmacro define-nixos-packages-item (item long-name &optional isMeta?) + (let* ((name-const (intern (concat "nixos-packages-" item))) + (long-name-const (intern (concat "nixos-packages-" item "-long-name"))) + (long-name-length-plus-padding (+ 3 (length long-name))) + (long-name-docstring (format "The long description for %s." item)) + (item-getter (intern (concat "nixos-packages-get-" item))) + (item-getter-docstring + (format "Get the value of %s from PACKAGE" item)) + (item-display (intern (concat "nixos-packages-display-" item))) + (item-display-docstring + (format "Display the value for %s from PACKAGE" item))) + `(progn + (defconst ,name-const ,item) + (defconst ,long-name-const ,long-name ,long-name-docstring) + (if (> ,long-name-length-plus-padding nixos-packages-name-indent-amount) + (setq nixos-packages-name-indent-amount + ,long-name-length-plus-padding)) + (if ,isMeta? + (defun ,item-getter (option) + ,item-getter-docstring + (cdr (assoc ,name-const (assoc "meta" option)))) + (defun ,item-getter (option) + ,item-getter-docstring + (cdr (assoc ,name-const option)))) + + (defun ,item-display (option) + ,item-display-docstring + (let ((item (,item-getter option)) + (format-string + (format "%%-%ds %%s\n" nixos-packages-name-indent-amount))) + (if (not (null item)) + (format format-string (concat ,long-name-const ":") item) + "")))))) + +(define-nixos-packages-item "description" "Description" t) +(define-nixos-packages-item "homepage" "Home Page URL" t) +(define-nixos-packages-item "license" "License" t) +(define-nixos-packages-item "longDescription" "Long Description" t) +(define-nixos-packages-item "platforms" "Supported Platforms" t) +(define-nixos-packages-item "maintainers" "List of Maintainers" t) +(define-nixos-packages-item "position" "Path to the nix-expression with line" t) +(define-nixos-packages-item "name" "Name") +(define-nixos-packages-item "system" "System") + +(defun nixos-packages--make-alist (package) + (let ((name (car package)) + (data (cdr package)) + (default (nixos-options-get-default package)) + (example (nixos-options-get-example package))) + (progn + (if (not (null default)) + (setcdr (assoc nixos-options-default package) + (nixos-options--boolean-string default))) + (if (not (null example)) + (setcdr (assoc nixos-options-example package) + (nixos-options--boolean-string example))) + (add-to-list 'data `(,nixos-options-name . ,name)) + `(,name . ,data))))