 ;;;/.doom.d/config.el -*- lexical-binding: t; -*-
 
-;;; Doom settings
-(setq
+;;; Doom config
+(setq!
     doom-leader-key "SPC"
     doom-localleader-key "SPC SPC"
     doom-theme (my/remember-theme-read 'doom-one)
     doom-font (font-spec :family "Fira Code")
-    doom-variable-pitch-font (font-spec :family "Noto Sans")
-    +pretty-code-enabled-modes '(haskell-mode fsharp-mode emacs-lisp-mode markdown-mode)
+    doom-variable-pitch-font (font-spec :family "Noto Sans"))
+
+;;; Evil mode
+(setq! evil-move-cursor-back nil)
+(add-hook 'evil-normal-state-entry-hook #'my/save-if-named)
+
+;;; Performance
+(setq!
     ;; Smooth(er) scrolling
     scroll-step 1
     maximum-scroll-margin 0.2
-    scroll-margin 20)
+    scroll-margin 20
+
+    ;; Make flycheck faster
+    flycheck-highlighting-mode 'lines
+
+    ;; Reduce strain from company completion
+    company-idle-delay 0.5)
+(setq! +pretty-code-enabled-modes '(haskell-mode fsharp-mode emacs-lisp-mode markdown-mode))
+
+;;; File management
+(setq! dired-listing-switches "-ABhl -X --group-directories-first")
+(map!
+    :leader
+    :desc "File drawer" "/" #'+treemacs/toggle
+
+    (:when (featurep! :ui neotree-mode)
+        :mode neotree-mode
+
+        :desc "Go to project root"   "P" #'my/neotree-project-root
+        :desc "Go to parent"         "U" #'neotree-select-up-node
+        :desc "Set as current node"  "l" #'neotree-change-root
+        :desc "Open next to current" "h" #'neotree-enter-vertical-split
+        :desc "Open below current"   "v" #'neotree-enter-horizontal-split
+        :desc "Open externally"      "<C-return>" #'neotree-open-file-in-system-application))
+
+;;; Popup rules
+(set-popup-rule! "^\\*doom:\\(?:v?term\\|eshell\\)-popup"
+    :vslot -5
+    :size 0.4
+    :select t
+    :modeline t
+    :quit 'current
+    :ttl nil
+    :side 'right)
+
+(set-popup-rule! "^\\*compilation\\*"
+    :vslot -4
+    :size 0.4
+    :select t
+    :modeline t
+    :quit 'current
+    :ttl nil
+    :side 'right)
 
 ;;; Save theme
 (advice-add 'load-theme :after
     (lambda (&rest args) (my/remember-theme-save)))
 
-;;; Options
-(setq!
-    evil-move-cursor-back nil
-
-    +latex-viewers '(pdf-tools zathura)
-    LaTeX-item-indent 2
-
-    vterm-shell "/bin/fish"
-
-    org-agenda-files (list "~/org/")
-    org-todo-keywords
-    '((sequence "TODO(t)" "MAYBE(m)" "WIP(p)" "SCHEDULED(s)" "|" "WAIT(w)" "DONE(d)" "CANCEL(c)"))
-
-    magit-blame-echo-style 'margin
-
-    lsp-ui-sideline-enable nil
-    lsp-eldoc-enable-hover nil
-    lsp-eldoc-hook '(lsp-hover)
-    lsp-auto-guess-root 't
-
-    alloy-mode-map (make-sparse-keymap)
-    alloy-basic-offset 2
-
-    web-mode-markup-indent-offset 2
-
-    dired-listing-switches "-ABhl -X --group-directories-first"
-
-    ;; Make flycheck faster
-    flycheck-highlighting-mode 'lines
-
-    ;; Reduce strain from company completion
-    company-idle-delay 0.5)
-
-;; Enable vimish fold globally
-(vimish-fold-global-mode 1)
-
-;;; Mode customizations
-(add-to-list '+lookup-provider-url-alist '("Mozilla Developer Network" "https://developer.mozilla.org/en-US/search?q=%s"))
-
-(add-hook 'LaTeX-mode-hook (lambda () (auto-fill-mode -1)))
-
-;; This is slow and useless so just disable
-(add-hook 'haskell-mode-hook (lambda () (smartparens-mode -1)))
-
-(add-hook 'vimish-fold-mode-hook #'vimish-fold-from-marks)
-
+;;; General map
+(map!
+    (:desc "Universal Argument" :leader "u" #'universal-argument)
+
+    (:map override
+        :n ";" 'evil-ex
+        :n ":" 'execute-extended-command
+        :nv "s" 'evil-substitute)
+
+
+    :n "M-u" 'previous-buffer
+    :n "M-i" 'next-buffer
+    :g "<mouse-8>" 'previous-buffer
+    :g "<mouse-9>" 'next-buffer
+
+    :i "C-SPC"   #'company-complete
+    :n "gc" 'evilnc-comment-operator
+    :textobj "c" #'evilnc-inner-comment #'evilnc-outer-commenter
+
+    ;; Make newline behave
+    :i [remap newline] #'newline-and-indent
+    :i "C-j"           #'+default/newline
+
+    ;; don't leave visual mode after shifting
+    :v  "<"     #'+evil/visual-dedent
+    :v  ">"     #'+evil/visual-indent
+
+    ;; evil-surround
+    :v  "S"     #'evil-surround-region
+    :o  "s"     #'evil-surround-edit
+
+    ;; Multiple cursors
+    :v  "gi"    #'+multiple-cursors/evil-mc-toggle-cursor-here
+
+    ;; Next/Previous
+    :desc "Previous Error"  :n "[e" 'flycheck-previous-error
+    :desc "Next Error"      :n "]e" 'flycheck-next-error
+    :desc "Previous spelling error" :n "[s" #'evil-prev-flyspell-error
+    :desc "Next spelling error"     :n "]s" #'evil-next-flyspell-error)
+
+;;; Searching
+(map!
+    :leader
+    :prefix ("f" . "Search")
+
+    :desc "Org files"         "o" (λ! () (counsel-file-jump "" "~/org"))
+    :desc "Buffers"           "b" #'ivy-switch-buffer
+    :desc "Themes"            "t" #'load-theme
+    :desc "Shells"            "s" #'counsel-switch-to-shell-buffer
+    :desc "Dotfiles"          "d" (λ! () (counsel-file-jump "" "~/.config/dotfiles"))
+    ;; This needs updating every year
+    :desc "College Files"     "c" (λ! () (counsel-file-jump "" "~/Documents/Imperial/Year_4"))
+    :desc "Online"            "o" #'+lookup/online
+    :desc "Recent files"      "r" #'counsel-recentf)
+
+;;; Treemacs
 (after! treemacs
     (add-to-list 'treemacs-pre-file-insert-predicates #'treemacs-is-file-git-ignored?))
 

(add-to-list 'auto-mode-alist
    '("\\.mod\\'" . gmpl-mode))

(after! alloy-mode
    (add-hook 'alloy-mode-hook
        (lambda () (setq indent-tabs-mode nil))))

(after! evil
    (add-hook 'evil-normal-state-entry-hook #'my/save-if-named))

(after! tramp
    (add-to-list 'tramp-remote-path 'tramp-own-remote-path))

(after! tex
    (setq TeX-master "shared"))

(after! forge
    (add-to-list 'forge-alist '("gitlab.haskell.org" "gitlab.haskell.org/api/v4" "gitlab.haskell.org" forge-gitlab-repository)))

(after! boogie-friends
    (setq flycheck-dafny-executable "~/.local/share/dafny/dafny")
    (setq flycheck-boogie-executable "~/.local/share/dafny/dafny-server")
    (setq flycheck-z3-smt2-executable "~/.local/share/dafny/z3/bin/z3")
    (setq flycheck-inferior-dafny-executable "~/.local/share/dafny/dafny-server"))

;; Hugo mode
(after! hugo (evil-set-initial-state 'hugo-mode 'emacs))
(autoload 'hugo-minor-mode "hugo" "Hugo minor mode")
(add-hook 'markdown-mode-hook #'hugo-minor-mode)

;; Set up ccls for TRAMP
(after! (lsp tramp)
    (lsp-register-client

            :initialization-options (lambda () ccls-initialization-options)
            :library-folders-fn ccls-library-folders-fn
            :remote? t)))
(setq!
    lsp-ui-sideline-enable nil
    lsp-eldoc-enable-hover nil
    lsp-eldoc-hook '(lsp-hover)
    lsp-auto-guess-root 't)
(map!
    :mode lsp-mode
    (:localleader
        :desc "Rename symbol" "r" #'lsp-rename
        :desc "Code action"   "a" #'lsp-execute-code-action
        :desc "Format buffer" "f" #'lsp-format-buffer
        :desc "Find symbol"   "s" #'lsp-ui-find-workspace-symbol)

    :desc "Glance documentation"  :n "gh" #'lsp-ui-doc-glance
    :desc "Go to type definition" :n "gt" #'lsp-goto-type-definition)

;;; Languages
;;; Web mode
(setq! web-mode-markup-indent-offset 2)
;;;; Coq
(map!
    :mode coq-mode
    :localleader
    :desc "Proof go to point" "SPC" #'company-coq-proof-goto-point
    :desc "Show definition overlay" "d" #'company-coq-toggle-definition-overlay)

;;;; C(++)
(map!
    :mode cpp-mode
    :localleader
    :desc "Toggle header/source" "t" #'ff-find-other-file)

;;;; Make
(map!
    :leader
    :prefix ("m" . "Make")
    :desc "Execute last recipe" "m" #'+make/run-last
    :desc "Execute recipe"      "r" #'+make/run)

;;;; TeX Mode
(setq!
    +latex-viewers '(pdf-tools zathura)
    LaTeX-item-indent 2)
(after! tex
    (setq TeX-master "shared"))
(add-hook 'LaTeX-mode-hook (lambda () (auto-fill-mode -1)))
(map!
    :mode tex-mode
    :localleader
    :desc "To environment" "e" #'LaTeX-narrow-to-environment
    :desc "To group"       "g" #'TeX-narrow-to-group)

;;;; Dafny/boogie
(after! boogie-friends
    (setq flycheck-dafny-executable "~/.local/share/dafny/dafny")
    (setq flycheck-boogie-executable "~/.local/share/dafny/dafny-server")
    (setq flycheck-z3-smt2-executable "~/.local/share/dafny/z3/bin/z3")
    (setq flycheck-inferior-dafny-executable "~/.local/share/dafny/dafny-server"))

;;;; Alloy
(after! alloy-mode
    (setq!
        alloy-mode-map (make-sparse-keymap)
        alloy-basic-offset 2)
    (add-hook 'alloy-mode-hook
        (lambda () (setq indent-tabs-mode nil))))

;;;; Hugo
(autoload 'hugo-minor-mode "hugo" "Hugo minor mode")
(after! hugo
    (evil-set-initial-state 'hugo-mode 'emacs)
    (add-hook 'markdown-mode-hook #'hugo-minor-mode))
(map!
    :mode hugo-minor-mode
    :localleader
    :prefix ("h" . "Hugo")

    :desc "Hugo status"        "h" #'hugo-status
    :desc "Start/Stop server"  "s" #'hugo-start-stop-server
    :desc "Browse to website"  "b" #'hugo-browse)

;;;; Verilog
(after! verilog-mode
    (setq verilog-indent-level 2)
    (setq verilog-indent-level-module 0)
    (setq verilog-indent-level-declaration 0))

;;;; Ivy
(map!
    (:after ivy
        :map ivy-minibuffer-map
        "C-SPC" #'ivy-call-and-recenter  ; preview file
        "C-l"   #'ivy-alt-done
        "C-v"   #'yank)
    (:desc "Search in file" :leader "j" #'swiper)
    (:after counsel
        :map counsel-ag-map
        "C-SPC"    #'ivy-call-and-recenter ; preview
        "C-l"      #'ivy-done
        [C-return] #'+ivy/git-grep-other-window-action))
;;;
;;;; Org mode
(setq!
    org-agenda-files (list "~/org/")
    org-todo-keywords
    '((sequence "TODO(t)" "MAYBE(m)" "WIP(p)" "SCHEDULED(s)" "|" "WAIT(w)" "DONE(d)" "CANCEL(c)")))
(map!
    :when (featurep! :lang org)
    :after org
    :mode org-mode

    ;; Unmap these because they interfere with window switching
    :n "M-h" nil
    :n "M-l" nil

    :localleader
    :desc "TODO"         "t" #'org-todo
    :desc "Export"       "e" #'org-export-dispatch
    :desc "Schedule"     "s" #'org-schedule
    :desc "Add deadline" "d" #'org-deadline
    :desc "Cycle list"   "b" #'org-cycle-list-bullet

    (:prefix-map ("i" . "insert")
        :desc "Table""t" #'org-table-create)

    (:prefix "l"
        :desc "Toggle link display" "v" #'org-toggle-link-display)

    (:prefix-map ("n" . "Narrow")
        :desc "To block"   "b" #'org-narrow-to-block
        :desc "To element" "e" #'org-narrow-to-element
        :desc "To subtree" "s" #'org-narrow-to-subtree))

;;;; Haskell
;; This is slow and useless so just disable
(add-hook 'haskell-mode-hook (lambda () (smartparens-mode -1)))

(use-package lsp-haskell
    :defer t
    :ensure t
    :config
    (setq lsp-haskell-process-path-hie "haskell-language-server-wrapper"))

(map!
    :map haskell-mode-map
    :localleader
    :desc "Hoogle query" "h" #'haskell-hoogle)

;;;; Kima
(define-generic-mode 'kima-mode
    '("#")
    '("fun" "data" "True""False" "let""var" "while""if" "else" "effect" "handle" "with" "IO" "Unit")
    nil
    '(".k\\'")
    "Major mode for the kima programming language")
(quickrun-add-command "kima"
    '((:command . "kima")
         (:exec . "%c run %s"))
    :mode 'kima-mode)

;;; Local Variables
;; Local Variables:
