ADD dependency management and conditional loading

This commit is contained in:
Nathan Dwarshuis 2020-08-15 13:00:04 -04:00
parent 4122f4784e
commit 213c8fea95
1 changed files with 165 additions and 116 deletions

View File

@ -162,10 +162,16 @@ OS is one of those in `system-type'."
`(when (not (eq system-type ,os)) (progn ,@body) `(when (not (eq system-type ,os)) (progn ,@body)
(print "Skipping OS-restricted code"))) (print "Skipping OS-restricted code")))
(defvar nd/required-exes '()
"Running list of executables required to run various configuations.")
(defmacro nd/when-bin (bin &rest body) (defmacro nd/when-bin (bin &rest body)
"Execute BODY if the program BIN exists." "Execute BODY if the program BIN exists."
(declare (indent 1)) (declare (indent 1))
`(if (executable-find ,bin) (progn ,@body) `(if (executable-find ,bin)
(progn
(setq nd/required-exes (-union '(,bin) nd/required-exes))
,@body)
(print (format "Executable %s not found. Skipping." ,bin)))) (print (format "Executable %s not found. Skipping." ,bin))))
(defmacro nd/time-exec (&rest body) (defmacro nd/time-exec (&rest body)
@ -262,6 +268,27 @@ If FRONT is t, do to the front of current values instead of the back."
(--each (--each
(where-is-internal f keymap nil nil) (where-is-internal f keymap nil nil)
(define-key keymap it nil))) (define-key keymap it nil)))
(defun nd/detect-package-manager ()
"Return the package manager being used on this OS."
(cond
;; for now only pacman...because arch is the best (TM)
((file-exists-p "/usr/bin/pacman")
'pacman)))
(defun nd/pacman-find-owner (file)
"Return the pacman packages that owns FILE.
Assumes pacman is installed and FILE is an absolute path."
(-some->> (format "pacman -Fq %s" file)
(shell-command-to-string)
(s-trim)
(s-split "\n")
(--map (replace-regexp-in-string ".*/" "" it t))
(cons file)))
(defun nd/detect-dependencies ()
"Return a list of required packages for this configuration."
(--map (nd/pacman-find-owner (format "/usr/bin/%s" it)) nd/required-exes))
#+END_SRC #+END_SRC
** interactive ** interactive
:PROPERTIES: :PROPERTIES:
@ -807,60 +834,64 @@ For me this means R but ess also supports S-plus, SAS, Stata, and other statisti
Flycheck syntax checkers Flycheck syntax checkers
- r-lintr (install from CRAN) - r-lintr (install from CRAN)
#+begin_src emacs-lisp #+begin_src emacs-lisp
(defun nd/init-ess-company () (nd/when-bin "R"
"Set the company backends for ess modes." (defun nd/init-ess-company ()
(setq-local company-backends '((company-R-objects company-R-args)))) "Set the company backends for ess modes."
(setq-local company-backends '((company-R-objects company-R-args))))
(use-package ess (use-package ess
:straight t :straight t
:init :init
(require 'ess-r-mode) (require 'ess-r-mode)
:hook :hook
((ess-mode . flycheck-mode) ((ess-mode . flycheck-mode)
(ess-mode . company-mode) (ess-mode . company-mode)
(ess-mode . origami-mode) (ess-mode . origami-mode)
(ess-mode . nd/init-ess-company) (ess-mode . nd/init-ess-company)
(ess-mode . prettify-symbols-mode) (ess-mode . prettify-symbols-mode)
(ess-mode . fci-mode) (ess-mode . fci-mode)
(inferior-ess-mode . company-mode) (inferior-ess-mode . company-mode)
(inferior-ess-mode . nd/init-ess-company) (inferior-ess-mode . nd/init-ess-company)
(inferior-ess-mode . prettify-symbols-mode)) (inferior-ess-mode . prettify-symbols-mode))
:config :config
(setq inferior-R-program "R" (setq inferior-R-program "R"
inferior-R-args "--quiet --no-save" inferior-R-args "--quiet --no-save"
ess-history-file "session.Rhistory" ess-history-file "session.Rhistory"
ess-history-directory (substitute-in-file-name "${XDG_CONFIG_HOME}/r/"))) ess-history-directory (substitute-in-file-name "${XDG_CONFIG_HOME}/r/")))
;; fast compile ;; fast compile
(defun nd/ess-r-add-env (orig-fun inf-buf proc-name start-args) (defun nd/ess-r-add-env (orig-fun inf-buf proc-name start-args)
(let ((process-environment (cons "MAKEFLAGS=-j8" process-environment))) (let ((process-environment (cons "MAKEFLAGS=-j8" process-environment)))
(funcall orig-fun inf-buf proc-name start-args))) (funcall orig-fun inf-buf proc-name start-args)))
(defun nd/ess-r-start-env (orig-fun &rest args) (defun nd/ess-r-start-env (orig-fun &rest args)
(nd/with-advice (nd/with-advice
((#'inferior-ess--start-process :around #'nd/ess-r-add-env)) ((#'inferior-ess--start-process :around #'nd/ess-r-add-env))
(apply orig-fun args))) (apply orig-fun args)))
(defun nd/ess-r-setwd-maybe (orig-fun &rest args) (advice-add #'run-ess-r :around #'nd/ess-r-start-env)
(nd/with-advice
((#'ess-set-working-directory :override #'ignore))
(apply orig-fun args)))
(advice-add #'run-ess-r :around #'nd/ess-r-start-env) (nd/when-bin "docker"
(defun nd/ess-r-setwd-maybe (orig-fun &rest args)
(nd/with-advice
((#'ess-set-working-directory :override #'ignore))
(apply orig-fun args)))
(advice-add #'run-ess-r :around #'nd/ess-r-setwd-maybe) (advice-add #'run-ess-r :around #'nd/ess-r-setwd-maybe)
;; force flycheck to use the system-wide R install instead of whatever ;; force flycheck to use system R instead of whatever is in docker
;; is in docker (defun nd/flycheck-find-exe-no-docker (orig-fun exe)
(defun nd/flycheck-find-exe-no-docker (orig-fun exe) (if (or (equal exe "R") (s-starts-with? "R " exe))
(if (or (equal exe "R") (s-starts-with? "R " exe)) "/bin/R" (funcall orig-fun exe)))
"/bin/R" (funcall orig-fun exe)))
(advice-add #'flycheck-default-executable-find :around (advice-add #'flycheck-default-executable-find :around
#'nd/flycheck-find-exe-no-docker) #'nd/flycheck-find-exe-no-docker)))
#+END_SRC #+END_SRC
*** C *** C
:PROPERTIES:
:ID: 0ee09480-e722-4a06-af8f-52f7dbf3f906
:END:
#+BEGIN_SRC emacs-lisp #+BEGIN_SRC emacs-lisp
(defun nd/init-c-company () (defun nd/init-c-company ()
"Set the company backends for anaconda mode." "Set the company backends for anaconda mode."
@ -869,19 +900,21 @@ Flycheck syntax checkers
company-irony))) company-irony)))
;; requires clang (duh) ;; requires clang (duh)
(use-package flycheck-clang-analyzer (nd/when-bin "clang"
:straight t (use-package flycheck-clang-analyzer
:after flycheck :straight t
:config :after flycheck
(flycheck-clang-analyzer-setup)) :config
(flycheck-clang-analyzer-setup)))
;; requires cmake/llvm ;; requires cmake/llvm
(use-package irony (nd/when-bin "cmake"
:straight t (use-package irony
:hook ((irony-mode . irony-cdb-autosetup-compile-options))) :straight t
:hook ((irony-mode . irony-cdb-autosetup-compile-options)))
(use-package company-irony (use-package company-irony
:straight t) :straight t))
(use-package company-c-headers (use-package company-c-headers
:straight t) :straight t)
@ -948,8 +981,9 @@ Anaconda (not related to the Python/R distribution?) is much lighter and easier
:END: :END:
[[https://github.com/python/black][Black]] is a really nice syntax formatter. It must be externally installed to work. [[https://github.com/python/black][Black]] is a really nice syntax formatter. It must be externally installed to work.
#+BEGIN_SRC emacs-lisp #+BEGIN_SRC emacs-lisp
(use-package blacken (nd/when-bin "black"
:straight t) (use-package blacken
:straight t))
#+END_SRC #+END_SRC
**** pyenv **** pyenv
:PROPERTIES: :PROPERTIES:
@ -959,16 +993,17 @@ For isolation I use [[https://github.com/pyenv/pyenv][pyenv]] and [[https://gith
Note this also requires all external packages to be installed in each environement (eg ipython, black, flake8, and pylint). Note this also requires all external packages to be installed in each environement (eg ipython, black, flake8, and pylint).
#+BEGIN_SRC emacs-lisp #+BEGIN_SRC emacs-lisp
(use-package pyenv-mode (nd/when-bin "pyenv"
:straight t (use-package pyenv-mode
:after python :straight t
:init (-some--> (getenv "PYENV_ROOT") :after python
(f-join it "versions") :init (-some--> (getenv "PYENV_ROOT")
(add-to-list 'exec-path it))) (f-join it "versions")
(add-to-list 'exec-path it)))
;; resolve symlinks when setting the pyenv, otherwise we get some ;; resolve symlinks when setting the pyenv, otherwise we get some
;; strange errors when activating a symlinked env ;; strange errors when activating a symlinked env
(advice-add #'pyenv-mode-full-path :filter-return #'file-truename) (advice-add #'pyenv-mode-full-path :filter-return #'file-truename))
#+END_SRC #+END_SRC
*** Ruby *** Ruby
:PROPERTIES: :PROPERTIES:
@ -1007,49 +1042,50 @@ Since most of these need GHCi to run properly, I added a hook to load haskell so
I have also found this to be much simpler and conflicting with other packages such as =dante= and =intero= (and probably =haskell-ide-engine= and friends). I have also found this to be much simpler and conflicting with other packages such as =dante= and =intero= (and probably =haskell-ide-engine= and friends).
#+BEGIN_SRC emacs-lisp #+BEGIN_SRC emacs-lisp
(defun nd/init-haskell-company () (nd/when-bin "stack"
"Set the company backends for haskell mode." (defun nd/init-haskell-company ()
(setq-local company-backends "Set the company backends for haskell mode."
;; capf is standard completion and dabbrev provides (setq-local company-backends
;; local completions in 'where' and 'let' clauses ;; capf is standard completion and dabbrev provides
'((company-capf company-dabbrev)))) ;; local completions in 'where' and 'let' clauses
'((company-capf company-dabbrev))))
(defun nd/haskell-load-maybe () (defun nd/haskell-load-maybe ()
"Prompts user to load haskell source to GHCi." "Prompts user to load haskell source to GHCi."
(when (y-or-n-p "Load Haskell source into GHCi? ") (when (y-or-n-p "Load Haskell source into GHCi? ")
(haskell-process-load-or-reload))) (haskell-process-load-or-reload)))
(use-package haskell-mode (use-package haskell-mode
:straight t :straight t
:hook ((haskell-mode . origami-mode) :hook ((haskell-mode . origami-mode)
(haskell-mode . company-mode) (haskell-mode . company-mode)
(haskell-mode . haskell-indentation-mode) (haskell-mode . haskell-indentation-mode)
;; this enables better integration with the running GHCi process ;; this enables better integration with the running GHCi process
;; NOTE this is NOT the same is haskell-interactive-mode which is used ;; NOTE this is NOT the same is haskell-interactive-mode which is used
;; in the repl that is launched within projects when loading files ;; in the repl that is launched within projects when loading files
(haskell-mode . interactive-haskell-mode) (haskell-mode . interactive-haskell-mode)
(haskell-mode . nd/init-haskell-company) (haskell-mode . nd/init-haskell-company)
(haskell-mode . nd/haskell-load-maybe) (haskell-mode . nd/haskell-load-maybe)
;; camelcase is defacto for haskell ;; camelcase is defacto for haskell
(haskell-mode . subword-mode)) (haskell-mode . subword-mode))
:config :config
(setq haskell-interactive-popup-errors nil (setq haskell-interactive-popup-errors nil
;; we use stack...which counterintuitively means we set the ;; we use stack...which counterintuitively means we set the
;; cabal build command to be stack ;; cabal build command to be stack
haskell-compile-cabal-build-command "stack build" haskell-compile-cabal-build-command "stack build"
;; use stylish (requires the stylish binary somewhere in $PATH) ;; use stylish (requires the stylish binary somewhere in $PATH)
haskell-stylish-on-save t haskell-stylish-on-save t
;; use some handy suggestions ;; use some handy suggestions
haskell-process-suggest-remove-import-lines t haskell-process-suggest-remove-import-lines t
haskell-process-auto-import-loaded-modules t haskell-process-auto-import-loaded-modules t
;; use TAGS file (requires hasktags binary to be in $PATH) ;; use TAGS file (requires hasktags binary to be in $PATH)
haskell-tags-on-save t)) haskell-tags-on-save t))
;; this minor mode name is long and unnecessary ;; this minor mode name is long and unnecessary
(delight 'interactive-haskell-mode nil "haskell") (delight 'interactive-haskell-mode nil "haskell")
;; unnecessary to see on the modeline ;; unnecessary to see on the modeline
(delight 'subword-mode nil "subword") (delight 'subword-mode nil "subword"))
#+END_SRC #+END_SRC
**** hlint **** hlint
:PROPERTIES: :PROPERTIES:
@ -1057,8 +1093,9 @@ I have also found this to be much simpler and conflicting with other packages su
:END: :END:
This is an additional syntax checker and requires the =hlint= binary (install through stack). This is an additional syntax checker and requires the =hlint= binary (install through stack).
#+BEGIN_SRC emacs-lisp #+BEGIN_SRC emacs-lisp
(with-eval-after-load 'haskell (nd/when-bin "hlint"
(flycheck-add-next-checker 'haskell-stack-ghc '(t . haskell-hlint))) (with-eval-after-load 'haskell
(flycheck-add-next-checker 'haskell-stack-ghc '(t . haskell-hlint))))
#+END_SRC #+END_SRC
**** helper functions **** helper functions
:PROPERTIES: :PROPERTIES:
@ -1081,8 +1118,9 @@ Other helper functions that make haskell even more fun.
:END: :END:
For flycheck, install =luacheck= (from AUR on Arch). For flycheck, install =luacheck= (from AUR on Arch).
#+BEGIN_SRC emacs-lisp #+BEGIN_SRC emacs-lisp
(use-package lua-mode (nd/when-bin "luacheck"
:straight t) (use-package lua-mode
:straight t))
#+END_SRC #+END_SRC
*** TeX *** TeX
**** AUCTeX **** AUCTeX
@ -1245,6 +1283,9 @@ Overlays hex color codes with matching colors in certain modes like css and html
:straight t) :straight t)
#+END_SRC #+END_SRC
*** Jinja2 *** Jinja2
:PROPERTIES:
:ID: a38b0792-46fe-43cc-b57a-d8e3a189fdc5
:END:
#+BEGIN_SRC emacs-lisp #+BEGIN_SRC emacs-lisp
(use-package jinja2-mode (use-package jinja2-mode
:straight t :straight t
@ -1365,11 +1406,15 @@ No custom code here, but flycheck needs =sqlint= (on Arch available through the
:ID: ce24b075-ede6-4d6c-81db-4c6aa40e4fd0 :ID: ce24b075-ede6-4d6c-81db-4c6aa40e4fd0
:END: :END:
#+BEGIN_SRC emacs-lisp #+BEGIN_SRC emacs-lisp
(use-package dockerfile-mode (nd/when-bin "docker"
:straight t) (use-package dockerfile-mode
:straight t))
#+END_SRC #+END_SRC
** testing ** testing
*** buttercup *** buttercup
:PROPERTIES:
:ID: 9539395e-98aa-4e47-b2ff-4233b63d40b1
:END:
Include this so I can have the docs and indentation specs handy when writing test suites Include this so I can have the docs and indentation specs handy when writing test suites
#+BEGIN_SRC emacs-lisp #+BEGIN_SRC emacs-lisp
(use-package buttercup (use-package buttercup
@ -3380,12 +3425,13 @@ For some reason there is no default way to get a "print prompt." Instead one nee
:ID: 67e11402-a9e5-4aae-8644-0e2c4f9ad2bc :ID: 67e11402-a9e5-4aae-8644-0e2c4f9ad2bc
:END: :END:
#+BEGIN_SRC emacs-lisp #+BEGIN_SRC emacs-lisp
(use-package magit (nd/when-bin "git"
:straight t (use-package magit
:config :straight t
:delight auto-revert-mode :config
(setq magit-push-always-verify nil :delight auto-revert-mode
git-commit-summary-max-length 50)) (setq magit-push-always-verify nil
git-commit-summary-max-length 50)))
#+END_SRC #+END_SRC
** dired ** dired
*** no confirm *** no confirm
@ -4615,6 +4661,9 @@ The function keys are nice because they are almost (not always) free in every mo
("=" #'balance-windows :exit t)) ("=" #'balance-windows :exit t))
#+END_SRC #+END_SRC
*** other *** other
:PROPERTIES:
:ID: dff1f586-7231-4394-8f4c-2730dbe8a901
:END:
#+BEGIN_SRC emacs-lisp #+BEGIN_SRC emacs-lisp
;; exchange point and marker (I never saw the use for this) ;; exchange point and marker (I never saw the use for this)
(global-unset-key (kbd "C-x C-x")) (global-unset-key (kbd "C-x C-x"))