Browse Source

Haskell: Greatly simplify my haskell-mode configuration

And a few other changes:

  * Go back to using a company delay

  * Remove unused company backends

  * Don't override RET for all modes

  * Use evil-leader overrides for Haskell
Peter J. Jones 4 months ago
Signed by: Peter Jones <> GPG Key ID: 9DAFAA8D01941E49
8 changed files with 47 additions and 142 deletions
  1. 0
  2. 0
  3. 0
  4. 2
  5. 0
  6. 1
  7. 44
  8. 0

+ 0
- 26
hydras/haskell-mode.el View File

@@ -1,26 +0,0 @@
;;; haskell-mode.el ;; Hydras for haskell-mode

;;; Commentary:

;;; Code:
(require 'hydra)

(defhydra pjones:hydras:haskell-mode (:hint nil)
^Imports^ ^GHCi^ ^Insert/Edit^ ^Run
_C-c C-i_: jump _C-c C-;_: info _C-c C-0_: cost center _C-c c_: compile
_C-c C-l_: return _C-c C-r_: reload _C-c C-n_: kill module
_C-c C-s_: sort _C-c C-t_: type _C-c C-e_: edit cabal
("C-c C-;" dante-info)
("C-c C-0" haskell-mode-toggle-scc-at-point :color blue)
("C-c C-e" pjones:haskell-edit-cabal-file :color blue)
("C-c C-i" haskell-navigate-imports)
("C-c C-l" haskell-navigate-imports-return :color blue)
("C-c C-n" pjones:haskell-module-name-to-kill-ring :color blue)
("C-c C-r" dante-restart)
("C-c C-s" pjones:haskell-sort-imports)
("C-c C-t" dante-type-at :color blue))

;;; haskell-mode ends here

+ 0
- 1
lisp/code.el View File

@@ -81,7 +81,6 @@ already been cached."
(setq comment-empty-lines t)
(set (make-local-variable 'comment-auto-fill-only-comments) t)
(local-set-key (kbd "C-c <tab>") 'pjones:comment-bar)
(local-set-key (kbd "RET") 'reindent-then-newline-and-indent)

+ 0
- 1
lisp/keys.el View File

@@ -110,7 +110,6 @@
(require 'evil-leader)

"SPC" #'pjones:switch-to-previous-buffer
"A" #'align
"a" #'ialign
"b" #'ivy-switch-buffer

+ 2
- 2
modes/company-conf.el View File

@@ -11,7 +11,7 @@

;; Settings for company-mode:
'(company-idle-delay nil)
'(company-idle-delay 1)
'(company-show-numbers nil)
'(company-selection-wrap-around t)
'(company-lighter-base "")
@@ -50,7 +50,7 @@
(define-key map (kbd "M-j") #'company-select-next)
(define-key map (kbd "M-k") #'company-select-previous))

(company-quickhelp-mode +1)

(add-hook 'company-mode-hook 'pjones:company-mode-hook)

+ 0
- 6
modes/company-ghc-conf.el View File

@@ -1,6 +0,0 @@
;;; company-ghc-conf.el --- company-mode ghc-mod backend.
(require 'company-ghc))

(setq company-ghc-show-info t
ghc-command "nix-hs-ghc-mod")

+ 1
- 1
modes/flycheck-conf.el View File

@@ -6,7 +6,7 @@
(require 'flycheck)

'(flycheck-disabled-checkers '(javascript-eslint javascript-gjslint))
'(flycheck-disabled-checkers '(javascript-gjslint))
'(flycheck-standard-error-navigation nil)
'(flymake-no-changes-timeout nil)
'(flymake-start-syntax-check-on-newline nil)

+ 44
- 103
modes/haskell-mode-conf.el View File

@@ -2,135 +2,76 @@
;;; Commentary:
;; My goal with this configuration is to reduce the write->compile
;; cycle with Haskell code, without requiring a ton of dependencies or
;; complicated set up. I'm also not interested in fancy completion or
;; refactoring tools.
;;; Code:
(require 'cl))

(require 'company-ghc)
(require 'dante)
(require 'direnv)
(require 'evil-leader)
(require 'flycheck)
(require 'ghc)
(require 'haskell)
(require 'haskell-interactive-mode)
(require 'haskell-mode)
(require 'haskell-process)

(declare-function pjones:prog-mode-hook "../lisp/code.el")
(declare-function pjones:define-keys-from-hydra "../lisp/functions.el")

;; Settings for haskell-mode and friends:
'(haskell-stylish-on-save nil)
'(haskell-tags-on-save nil)
'(haskell-completing-read-function 'ido-completing-read)
'(shm-auto-insert-skeletons nil)
'(dante-repl-command-line '("nix-hs" "repl")))

(defun pjones:haskell-find-cabal-file ()
"Return the full path to the *.cabal file for the current project."
(let* ((dir default-directory)
(default (concat dir "/" (file-name-base dir) ".cabal"))
(while (and (not (string= dir "/"))
(not (setq name (file-expand-wildcards (concat dir "?*.cabal")))))
(setq dir (file-name-directory (substring dir 0 -1))))
(if (string= dir "/") default (car name))))

(defun pjones:haskell-edit-cabal-file ()
"Open the project's cabal file for editing."
(find-file (pjones:haskell-find-cabal-file)))

(defun pjones:haskell-beginning-of-defun (&optional arg)
"Move to the beginning of the current function ARG times."
(dotimes (i (or arg 1))
(while (and (not (bobp)) (or (eolp) (looking-at "^\\s-")))
(forward-line -1))
(if (save-excursion (forward-line -1) (looking-at "^\\w"))
(forward-line -1))) t)

(defun pjones:haskell-end-of-defun (&optional arg)
"Move to the end of the current function ARG times."
(dotimes (i (or arg 1))
(while (and (not (eobp)) (looking-at "^\\w"))
(forward-line)) ;; Move past the function name.
(while (and (not (eobp)) (or (eolp) (looking-at "^\\s-")))
(forward-line))) t)

(defun pjones:haskell-module-name ()
"Return the module name for the current buffer."
(goto-char (point-min))
(let ((start (search-forward-regexp "module "))
(end (search-forward-regexp "[[:space:]]")))
(buffer-substring start (- end 2)))))

(defun pjones:haskell-module-name-to-kill-ring ()
"Save the module name of the current buffer to the kill ring."
(kill-new (pjones:haskell-module-name)))

(defun pjones:haskell-sort-imports ()
"If point is in a block of import statements then sort them.
Otherwise go totally crazy."
(let ((b (save-excursion
(move-beginning-of-line nil)
(while (looking-at "^import") (forward-line -1))
(forward-line 1)
(e (save-excursion
(move-beginning-of-line nil)
(while (looking-at "^import") (forward-line 1))
(forward-line -1)
(move-end-of-line nil)
nil "^import +\\(qualified \\)?\\(.+\\)$" "\\2" b e)))

(defun pjones-haskell-process-wrapper-function (argv)
"Run Haskell tools through nix-shell by modifying ARGV.
See `haskell-process-wrapper-function' for details."
(append (list "nix-hs")
(list (mapconcat 'identity argv " "))))
'(haskell-tags-on-save t)
'(haskell-completing-read-function 'ivy-completing-read)
'(haskell-process-type 'cabal-new-repl)
'(haskell-process-suggest-hoogle-imports t)
'(dante-repl-command-line '("cabal" "new-repl")))

;; A few extra key bindings:
(evil-leader/set-key-for-mode 'haskell-mode
"SPC e" #'haskell-cabal-visit-file
"SPC i" #'haskell-navigate-imports
"SPC s" #'haskell-sort-imports
"SPC t" #'dante-info)

;; This overwrite fixes a bug where imports are not sorted because I
;; put a comment line above them.
(defun haskell-sort-imports-goto-group-start ()
"Overwrite the version from haskell-sort-imports.el."
(while (looking-at "^import") (forward-line -1))
(forward-line 1))

(defun pjones:haskell-mode-hook ()
"Hook run on new Haskell buffers."
(make-local-variable 'tab-always-indent)

;; These need to be set before calling `pjones:prog-mode-hook'.
(setq tab-always-indent t
beginning-of-defun-function 'pjones:haskell-beginning-of-defun
end-of-defun-function 'pjones:haskell-end-of-defun)
;; Update environment variables (i.e. PATH) first!

;; Load helper packages:
;; Boot `haskell-mode':
(flycheck-add-next-checker 'haskell-dante '(warning . haskell-hlint))

;; Configure completion:
(make-local-variable 'company-backends)
(add-to-list 'company-backends '(company-ghc company-dabbrev company-abbrev))
(add-to-list 'company-backends '(company-capf company-dabbrev company-abbrev))

;; A few extra key bindings:
(let ((map haskell-mode-map))
(define-key map (kbd "C-c C-e") #'pjones:haskell-edit-cabal-file)
(define-key map (kbd "C-c C-s") #'pjones:haskell-sort-imports)))
;; Load helper packages:

(defun pjones:dante-mode-hook ()
"Peter's hook for Dante."
(let ((map dante-mode-map))
(define-key map (kbd "C-c ,") nil)))
(flycheck-add-next-checker 'haskell-dante '(warning . haskell-hlint)))

(add-hook 'haskell-mode-hook #'pjones:haskell-mode-hook)
(add-hook 'haskell-mode-hook #'pjones:haskell-mode-hook)
(add-hook 'haskell-cabal-mode-hook #'pjones:prog-mode-hook)
(add-hook 'dante-mode-hook #'pjones:dante-mode-hook)
(add-hook 'dante-mode-hook #'pjones:dante-mode-hook)

;; Local Variables:
;; byte-compile-warnings: (not noruntime)
;; End:
;;; haskell-mode-conf.el ends here

+ 0
- 2
nix/packages.nix View File

@@ -28,8 +28,6 @@ overrides.emacsWithPackages (epkgs: with epkgs; [
beginend # Redefine M-< and M-> for some modes
captain # CAPiTalization is Automatic IN emacs
company # Modular text completion framework
company-ghc # company-mode ghc-mod backend
company-ghci # company backend which uses the current ghci process
company-quickhelp # Popup documentation for completion candidates
company-statistics # Sort candidates using completion history
counsel # Various completion functions using Ivy