This is a template haskell project to quickly get started using nix.
- The app:
app/
contains the main file that will generate the binary.src/
contains the library used byapp/
.test/
contains the tests testing the library insrc/
.
- Build the app:
- Editor files:
hie.yaml
is the config file forghcide
.
- Nix files:
default.nix
used when runningnix-build
.shell.nix
used when runningnix-shell
.release.nix
is imported bydefault.nix
andshell.nix
.
nix-shell
includes ghcide
,
an LSP server
$ nix-shell --pure
[nix-shell] $ ghcide
ghcide version: 0.3.0 (GHC: 8.6) (PATH: /nix/store/i0xbrrhzz5bavwmij7a4rix6fzyh4jhm-ghcide-0.3.0/bin/ghcide)
Ghcide setup tester in /home/timi/projects/hs-template-nix.
Report bugs at https://github.com/digital-asset/ghcide/issues
Step 1/4: Finding files to test in /home/timi/projects/hs-template-nix
Found 4 files
Step 2/4: Looking for hie.yaml files that control setup
Found 1 cradle
Step 3/4: Initializing the IDE
[DEBUG] Warning: Client does not support watched files. Falling back to OS polling
Step 4/4: Type checking the files
[...]
[INFO] finish: User TypeCheck (took 0.06s)
Completed (4 files worked, 0 files failed
brittany
, a
code formatter:
[nix-shell] $ brittany --check-mode */*.hs; echo $?
0
and hlint
, a
code improvement suggester:
[nix-shell] $ hlint **/*.hs
No hints
$ nix-build --pure
$ ./result/bin/hs-template-nix-exe
Hello World
This runs the tests too. Running this will always recompile everything, you probably want to use the next section instead.
$ nix-shell --pure --run 'stack build'
$ nix-shell --pure --run 'stack run
Hello World
Or, using the makefile:
$ make build
$ make run
Hello World
$ nix-shell --pure --run 'stack build --haddock --haddock-deps'
$ nix-shell --pure --run 'stack hoogle -- generate --quiet --local'
$ nix-shell --pure --run 'stack hoogle -- server --local --port=65000 --no-security-headers'
Or, using the makefile:
$ make hoogle-build hoogle-generate hoogle-serve
Server starting on port 65000 and host/IP Host "127.0.0.1"
Then go to http://localhost:65000/?scope=package%3Ahs-template-nix to
see the documentation of this project. You will see the documentation
for the Utils
package:
$ modd
This starts a hoogle server with make hoogle-serve
as well as runs
in parallel make hoogle-build hoogle-generate
and make test
anytime a file has changed. See modd.conf.
Cachix integration is provided by the ibizaman
binary cache:
cachix use ibizaman
Or, with the Makefile:
make cachix-enable
To push to it, run:
$ nix-build | cachix push ibizaman
$ nix-build shell.nix | cachix push ibizaman
Or, with the Makefile:
make cachix-push
A Github Action builds, tests and uploads the binary of the project as an artifact. Thanks to cachix, this is pretty quick, under 2 minutes.
To download it, follow these steps:
- Go to the latest github action run.
- Download the artifact.
- Unzip it with
unzip hs-template-nix.zip
- set the executable permission bit with
chmod a+x hs-template-nix
- run it with
./hs-template-nix-exe
and you will see printed"Hello World"
.
This is mostly about integrating your editor with the ghcide
,
brittany
and hlint
executables found inside the nix-shell
.
About ghcide
, the hie.nix file helps it find the files in
the app/
, src/
and test/
folders.
I saw recommended to run your editor directly in nix-shell so it can access all needed binaries but I don't really like that. Also, I run an emacs daemon so there's no way this can scale.
Anyway, here is a working config that gracefully loads binary from inside nix-shell whenever the project is using nix.
(setq-default tab-width 4)
(defun my/disable-tabs ()
"Disable tabs and set them to 4 spaces."
(setq-default tab-width 4)
(setq tab-width 4)
(setq indent-tabs-mode nil))
; Tabs are used to format buffer with `lsp-format-buffer'.
(add-hook 'haskell-mode-hook 'my/disable-tabs)
(defun my/lsp-format-buffer-silent ()
"Silence errors from `lsp-format-buffer'."
(ignore-errors (lsp-format-buffer)))
(use-package lsp-mode
:straight t
:commands lsp
:hook ((sh-mode . lsp-deferred)
(javascript-mode . lsp-deferred)
(html-mode . lsp-deferred)
(before-save . my/lsp-format-buffer-silent))
:config
(setq lsp-signature-auto-activate t)
(lsp-lens-mode t))
(use-package lsp-ui
:straight t
:hook (lsp-mode-hook . lsp-ui-mode)
:commands lsp-ui-mode
:config
(setq lsp-ui-flycheck-enable t
lsp-ui-flycheck-live-reporting nil))
(use-package company-lsp
:straight t
:commands company-lsp
:config
(push 'company-lsp company-backends))
(use-package nix-sandbox
:straight t)
(use-package haskell-mode
:straight t
:after nix-sandbox
:init
(defun my/haskell-set-stylish ()
(if-let* ((sandbox (nix-current-sandbox))
(fullcmd (nix-shell-command sandbox "brittany"))
(path (car fullcmd))
(args (cdr fullcmd)))
(setq-local haskell-mode-stylish-haskell-path path
haskell-mode-stylish-haskell-args args)))
(defun my/haskell-set-hoogle ()
(if-let* ((sandbox (nix-current-sandbox)))
(setq-local haskell-hoogle-command (nix-shell-string sandbox "hoogle"))))
:hook ((haskell-mode . capitalized-words-mode)
(haskell-mode . haskell-decl-scan-mode)
(haskell-mode . haskell-indent-mode)
(haskell-mode . haskell-indentation-mode)
(haskell-mode . my/haskell-set-stylish)
(haskell-mode . my/haskell-set-hoogle)
(haskell-mode . lsp-deferred)
(haskell-mode . haskell-auto-insert-module-template))
:config
(defun my/haskell-hoogle--server-command (port)
(if-let* ((hooglecmd `("hoogle" "serve" "--local" "-p" ,(number-to-string port)))
(sandbox (nix-current-sandbox)))
(apply 'nix-shell-command sandbox hooglecmd)
hooglecmd))
(setq haskell-hoogle-server-command 'my/haskell-hoogle--server-command
haskell-stylish-on-save t))
(use-package lsp-haskell
:straight t
:after nix-sandbox
:init
(setq lsp-prefer-flymake nil)
(require 'lsp-haskell)
:config
;; from https://github.com/travisbhartwell/nix-emacs#haskell-mode
(defun my/nix--lsp-haskell-wrapper (args)
(if-let ((sandbox (nix-current-sandbox)))
(apply 'nix-shell-command sandbox args)
args))
(setq lsp-haskell-process-path-hie "ghcide"
lsp-haskell-process-args-hie '()
lsp-haskell-process-wrapper-function 'my/nix--lsp-haskell-wrapper))
(use-package nix-mode
:straight t
:mode "\\.nix\\'"
:init
(require 'nix-build))