;;; init --- user init file
(setq inhibit-startup-echo-area-message "alan")
(setq inhibit-startup-screen t
      initial-scratch-message ""
      initial-major-mode 'text-mode
      package-enable-at-startup nil)

(package-initialize)

(defun reload-user-init-file ()
  "Reload init file."
  (interactive)
  (load-file user-init-file))

(eval-when-compile
  (require 'use-package)
  (setq use-package-expand-minimally t))
(setq use-package-always-demand (daemonp))

(defvar ap/path-configured nil)
(when (and (eq system-type 'darwin)
           (not ap/path-configured))
  (setq exec-path
        (delete-dups
         (append
          (parse-colon-path
           (elt (split-string-and-unquote
                 (with-output-to-string
                   (with-current-buffer standard-output
                     (call-process "/usr/libexec/path_helper" nil t nil "-s")))
                 "[=;]")
                1))
          exec-path))
        ap/path-configured t))

;;; Customize

(setq custom-file "~/.emacs.d/custom.el")
(load custom-file :noerror :nomessage)

(use-package crux
  :defer 1
  :config (crux-reopen-as-root-mode -1))

;;; Styles

 ;; I prefer an always-visible cursor.  Feels less distracting.
(blink-cursor-mode -1)
;; Disable all the bars, unless on OSX, in which case, keep the menu bar.
(unless (eq system-type 'darwin)
  (menu-bar-mode -1))
(scroll-bar-mode -1)
(tool-bar-mode -1)
(set-fringe-mode '(4 . 4))

;; Ring the bell sometimes, but not so often
(setq ring-bell-function
      (lambda ()
        (unless (memq this-command
                      '(isearch-abort abort-recursive-edit exit-minibuffer keyboard-quit minibuffer-keyboard-quit undo-tree-undo))
          (ding))))

(when (or (daemonp)
           window-system)
  (load-theme 'almost-mono-white t)
  (if (eq window-system 'x)
      (setq-default line-spacing 0.2))
  (setq frame-background-mode 'light)
  (mapc 'frame-set-background-mode (frame-list)))

;;; Chrome
(use-package minions
  :config (progn
            (setq minions-mode-line-lighter "  "
                  minions-mode-line-delimiters nil)
            (minions-mode +1)))

(column-number-mode +1)
(use-package doom-modeline
  :hook (after-init . doom-modeline-mode)
  :config (progn
            (setq doom-modeline-buffer-file-name-style 'relative-from-project
                  doom-modeline-buffer-icon nil
                  doom-modeline-buffer-modification-icon nil
                  doom-modeline-vcs-max-length 24
                  doom-modeline-env-version nil)
            (let ((foreground (face-attribute 'font-lock-comment-face :foreground)))
              (set-face-attribute 'doom-modeline-buffer-modified nil :foreground foreground))))

(when (eq system-type 'darwin)
  (add-to-list 'default-frame-alist '(ns-transparent-titlebar . t))
  (add-to-list 'default-frame-alist '(ns-appearance . 'light)))

(add-to-list 'default-frame-alist '(width . 100))
(add-to-list 'default-frame-alist '(height . 40))

;;; Dates & Times

(defvar calendar-week-start-day 1)
(defvar calendar-date-style 'iso)

(defun insert-date (prefix)
  "Insert the current date.
With PREFIX, use British format.
With two prefix arguments, write out the day and month name."
  (interactive "P")
  (let ((format (cond
                 ((not prefix) "%Y-%m-%d")
                 ((equal prefix '(4)) "%d/%m/%Y")
                 ((equal prefix '(16)) "%A, %d %B %Y"))))
    (insert (format-time-string format))))

(defun insert-datetime (prefix)
  "Insert current date and time.  With PREFIX, use ISO8601 format."
  (interactive "P")
  (let ((format (cond
                 ((not prefix) "%Y-%m-%d %H:%M:%S")
                 ((equal prefix '(4)) "%Y-%m-%dT%H:%M:%SZ"))))
    (insert (format-time-string format))))

;;; Keybindings

(when (eq system-type 'darwin)
  (setq mac-option-modifier 'meta
        mac-right-option-modifier 'none
        mac-control-modifier 'control
        mac-right-control-modifier 'left
        mac-command-modifier 'super
        mac-right-command-modifier 'left
        mac-function-modifier 'hyper))

(use-package general
  :functions (general-unbind general-define-key)
  :config (progn
	    (general-override-mode +1)
            (when (eq system-type 'darwin)
              (general-unbind "s-x"))))

(use-package avy
  :config (setq avy-all-windows nil))
(use-package ace-link
  :after avy
  :commands (ace-link-setup-default)
  :config (ace-link-setup-default))

;; Popup keybindings following a prefix automatically.

(use-package which-key
  :defer 5
  :config (progn
            (which-key-mode +1)
            (which-key-setup-side-window-right-bottom)))

;;; Modeline

(use-package relative-buffers
  :defer 2
  :config (progn
            (setq relative-buffers-project-prefix t)
            (global-relative-buffers-mode +1)))

;;; Minibuffer

(setq enable-recursive-minibuffers t
      save-silently t)
(minibuffer-depth-indicate-mode t)

(use-package hydra
  :defer 2)
(use-package ivy
  :config (progn
            (ivy-mode +1)))
(use-package ivy-hydra
  :defer 2)

(use-package smerge-mode
  :after magit
  :config
  (defhydra unpackaged/smerge-hydra
    (:color pink :hint nil :post (smerge-auto-leave))
    "
^Move^       ^Keep^               ^Diff^                 ^Other^
^^-----------^^-------------------^^---------------------^^-------
_n_ext       _b_ase               _<_: upper/base        _C_ombine
_p_rev       _u_pper              _=_: upper/lower       _r_esolve
^^           _l_ower              _>_: base/lower        _k_ill current
^^           _a_ll                _R_efine
^^           _RET_: current       _E_diff
"
    ("n" smerge-next)
    ("p" smerge-prev)
    ("b" smerge-keep-base)
    ("u" smerge-keep-upper)
    ("l" smerge-keep-lower)
    ("a" smerge-keep-all)
    ("RET" smerge-keep-current)
    ("\C-m" smerge-keep-current)
    ("<" smerge-diff-base-upper)
    ("=" smerge-diff-upper-lower)
    (">" smerge-diff-base-lower)
    ("R" smerge-refine)
    ("E" smerge-ediff)
    ("C" smerge-combine-with-next)
    ("r" smerge-resolve)
    ("k" smerge-kill-current)
    ("w" (lambda ()
            (interactive)
            (save-buffer)
            (bury-buffer))
     "Save and bury buffer" :color blue)
    ("q" nil "cancel" :color blue))
  :hook (magit-diff-visit-file . (lambda ()
                                   (when smerge-mode
                                     (unpackaged/smerge-hydra/body)))))

(use-package swiper
  :general ([remap isearch-forward] #'swiper-isearch)
  :config (progn
            (setq ivy-count-format "%d/%d ")))

;; transition smex history to amx
(let ((smex-save-file (concat user-emacs-directory "smex-items")))
  (use-package amx
    :config (setq amx-history-length 100)))

(use-package counsel
  :commands (counsel-unicode-char)
  :general ("M-x" #'counsel-M-x))

;;; Windows

(defun split-window-properly (&optional window)
    (let ((window (or window (selected-window))))
    (or (and (window-splittable-p window)
	     ;; Split window vertically.
	     (with-selected-window window
	       (split-window-below)))
	(and (window-splittable-p window t)
	     ;; Split window horizontally.
	     (with-selected-window window
	       (split-window-right))))))

(setq split-window-preferred-function #'split-window-properly
      split-height-threshold nil
      split-width-threshold 200)

(use-package eyebrowse
  :after (evil)
  :config (progn
            (setq eyebrowse-new-workspace t
                  eyebrowse-mode-line-left-delimiter ""
                  eyebrowse-mode-line-right-delimiter ""
                  eyebrowse-mode-line-separator " "
                  eyebrowse-mode-line-style 'always)
            (eyebrowse-mode +1))
  :general (:keymaps 'evil-window-map
                     "0" #'eyebrowse-switch-to-window-config-0
                     "1" #'eyebrowse-switch-to-window-config-1
                     "2" #'eyebrowse-switch-to-window-config-2
                     "3" #'eyebrowse-switch-to-window-config-3
                     "4" #'eyebrowse-switch-to-window-config-4
                     "5" #'eyebrowse-switch-to-window-config-5
                     "6" #'eyebrowse-switch-to-window-config-6
                     "7" #'eyebrowse-switch-to-window-config-7
                     "8" #'eyebrowse-switch-to-window-config-8
                     "9" #'eyebrowse-switch-to-window-config-9)
  :ghook ('evil-after-load-hook #'eyebrowse-setup-evil-keys))

(use-package winner
  :after evil
  :defer 8
  :config (progn
            (setq winner-boring-buffers '("*Completions*" "*Help*" "*Apropos*" "*Buffer List*" "*info*" "*Compile-Log*"))
            (winner-mode +1))
  :general (:keymaps 'evil-window-map
                    "u" #'winner-undo
                    "r" #'winner-redo
                    "C-r" #'winner-redo))
;;; Evil

(defvar evil-want-integration nil)
(defvar evil-want-keybinding nil)
(eval-and-compile
  (setq evil-want-integration t
        evil-want-keybinding nil))

(use-package evil
  :demand t
  :commands (evil-mode evil-delete-buffer evil-ex-define-cmd)
  :config (progn
            (setq-default evil-shift-width 2)
            (setq evil-mode-line-format nil)
            (add-to-list 'evil-emacs-state-modes 'eshell-mode)
            (add-to-list 'evil-emacs-state-modes 'ledger-reconcile-mode)
            (evil-mode +1))
  :general
  (:states 'motion
	   "C-;" #'evil-avy-goto-line)
  (:states 'normal
	   ";" #'evil-ex)
  (:states '(normal motion)
           "g s" #'evil-avy-goto-symbol-1))

(use-package evil-anzu
  :after evil)

(defvar evil-collection-company-use-tng t)
(use-package evil-collection
  :after (evil)
  :defer 3
  :demand t
  :commands (evil-collection-init)
  :config (progn
	    (evil-collection-init)))

(general-create-definer my-leader-def
  :keymaps 'override
  :states '(normal motion)
  :prefix ",")

(use-package evil-space
  :defer 1
  :after evil
  :config (evil-space-mode +1))

;; this macro was copied from here: https://stackoverflow.com/a/22418983/4921402
(defmacro define-and-bind-quoted-text-object (name key start-regex end-regex)
  (let ((inner-name (make-symbol (concat "evil-inner-" name)))
        (outer-name (make-symbol (concat "evil-a-" name))))
    `(progn
       (evil-define-text-object ,inner-name (count &optional beg end type)
         (evil-select-paren ,start-regex ,end-regex beg end type count nil))
       (evil-define-text-object ,outer-name (count &optional beg end type)
         (evil-select-paren ,start-regex ,end-regex beg end type count t))
       (define-key evil-inner-text-objects-map ,key #',inner-name)
       (define-key evil-outer-text-objects-map ,key #',outer-name))))

(use-package evil-surround
  :after evil
  :defer 2
  :config (progn
            (define-and-bind-quoted-text-object "slash" "/" "\\/" "\\/")
            (push '(?\/ . ("/" . "/")) evil-surround-pairs-alist)
            (global-evil-surround-mode +1)))


(use-package evil-embrace
  :after evil-surround
  :config (progn
            (setq evil-embrace-show-help-p nil)
            (push ?\/ evil-embrace-evil-surround-keys)
            (evil-embrace-enable-evil-surround-integration)
            (add-hook 'LaTeX-mode-hook #'embrace-LaTeX-mode-hook)
            (add-hook 'org-mode-hook #'embrace-org-mode-hook)
            (add-hook 'ruby-mode-hook #'embrace-ruby-mode-hook)
            (add-hook 'emacs-lisp-mode-hook #'embrace-emacs-lisp-mode-hook)))

(use-package evil-commentary
  :after evil
  :defer 2
  :config (evil-commentary-mode +1))

(use-package evil-magit
  :after magit
  :config (setq evil-magit-use-y-for-yank nil))

(use-package evil-matchit
  :after evil
  :config (progn
            (global-evil-matchit-mode +1)))

(use-package evil-quickscope
  :after evil
  :commands (evil-quickscope-mode)
  :ghook ('(magit-mode-hook git-rebase-mode-hook) #'turn-off-evil-quickscope-mode)
  :config (global-evil-quickscope-mode +1))

(use-package evil-numbers
  :after evil
  :general (:states '(normal visual)
                    "C-a" #'evil-numbers/inc-at-pt
                    "C-q" #'evil-numbers/dec-at-pt))

(use-package evil-org
  :after org
  :commands (evil-org-set-key-theme)
  :ghook ('org-mode-hook #'evil-org-mode)
  :gfhook #'evil-org-set-key-theme)

(use-package evil-org-agenda
  :after org
  :ghook ('org-agenda-mode-hook #'evil-org-agenda-set-keys))

;;; Projects

(use-package projectile
  :defer 1
  :defines projectile-command-map
  :config (progn
            (add-to-list 'projectile-globally-ignored-files "package-lock.json")
            (add-to-list 'projectile-globally-ignored-files "pnpm-lock.yaml")
            (add-to-list 'projectile-project-root-files "package.json")
            (add-to-list 'projectile-project-root-files-bottom-up "pnpm-workspace.yaml")
            (setq projectile-completion-system 'ivy
                  projectile-project-root-files-functions '(projectile-root-local
                                                            projectile-root-top-down
                                                            projectile-root-bottom-up
                                                            projectile-root-top-down-recurring))
            (projectile-mode +1)
	    (with-eval-after-load 'evil-ex
              (evil-ex-define-cmd "rg" #'ripgrep-regexp)
	      (evil-ex-define-cmd "prg" #'projectile-ripgrep)
              (evil-ex-define-cmd "pesh[ell]" #'projectile-run-eshell))))

(use-package counsel-projectile
  :defer 1
  :commands (counsel-projectile-switch-project
	     counsel-projectile-rg
	     counsel-projectile-switch-to-buffer
             counsel-projectile-mode)
  :general (:keymaps 'projectile-command-map
                     "s s" #'counsel-projectile-rg
                     "s r" #'counsel-projectile-rg)
  :config (progn
            (assq-delete-all #'projectile-ripgrep counsel-projectile-key-bindings)
            (counsel-projectile-mode +1)
	    (with-eval-after-load 'evil-ex
	      (evil-ex-define-cmd "cprg" #'counsel-projectile-rg)
	      (evil-ex-define-cmd "pb" #'counsel-projectile-switch-to-buffer)
	      (evil-ex-define-cmd "psw[itch]" #'counsel-projectile-switch-project))))

(use-package magit
  :defer 10
  :commands (magit-status magit-dispatch)
  :config (progn
            (setq magit-auto-revert-mode nil magit-section-visibility-indicator nil
                  magit-diff-refine-hunk 'all
                  magit-display-buffer-function #'display-buffer
                  magit-completing-read-function #'ivy-completing-read)
            (global-magit-file-mode +1)
            (remove-hook 'magit-section-highlight-hook 'magit-section-highlight)
            (remove-hook 'magit-section-highlight-hook 'magit-section-highlight-selection)
            (remove-hook 'magit-section-highlight-hook 'magit-diff-highlight)))

(eval-when-compile (require 'fringe-helper))
(use-package git-gutter
  :defer t)
(use-package git-gutter-fringe
  :defer 5
  :config (progn
            (global-git-gutter-mode 1)
            (add-hook 'magit-post-refresh-hook
                      #'git-gutter:update-all-windows)
            ;; places the git gutter outside the margins.
            (setq-default fringes-outside-margins nil)
            ;; thin fringe bitmaps
            (fringe-helper-define 'git-gutter-fr:added '(center repeated)
              ".XXX....")
            (fringe-helper-define 'git-gutter-fr:modified '(center repeated)
              ".XXX....")
            (fringe-helper-define 'git-gutter-fr:deleted '(center repeated)
              ".XXX....")
            (setq git-gutter-fr:side 'right-fringe)))

(use-package git-messenger
  :commands (git-messenger:popup-message)
  :defer 10
  :config (setq git-messenger:use-magit-popup t))

(use-package git-timemachine
  :commands (git-timemachine))

(use-package editorconfig
  :defer 2
  :init (progn
          (unless (executable-find "editorconfig")
            (warn "Missing `editorconfig' executable.")))
  :config (editorconfig-mode +1))

;;; Completion

(use-package company
  :defer 2
  :commands (company-explicit-action-p)
  :config (progn
            (setq company-idle-delay 0.2
                  company-tooltip-align-annotations t
                  company-show-numbers t
                  company-dabbrev-downcase nil
                  company-dabbrev-ignore-case nil
                  company-begin-commands '(self-insert-command)
                  company-auto-complete #'company-explicit-action-p
                  company-auto-complete-chars '(?\ ?\( ?\) ?.))
            (global-company-mode +1)
            (general-unbind company-active-map
              "RET"
              [return]))
  :general (:states 'insert
                    "TAB" #'company-indent-or-complete-common))

(use-package all-the-icons
  :config (setq all-the-icons-color-icons nil))

(use-package company-lsp
  :config (progn
            (add-to-list 'company-backends #'company-lsp)))

(eval-when-compile (require 'subr-x))
(eval-and-compile
  (defun company-tabnine-load-path ()
    (string-trim-right (shell-command-to-string "ghq list -p company-tabnine"))))

(use-package company-tabnine
  :commands (company-tabnine)
  :after (company-lsp)
  :load-path (lambda () (list (company-tabnine-load-path)))
  :config (setq company-tabnine-binaries-folder "~/.local/tabnine")
  :general ("<M-tab>" #'company-tabnine-call-other-backends
            "<C-tab>" #'company-tabnine-call-other-backends)
  :init (progn
          (add-to-list 'company-backends #'company-tabnine)))

;;; Documentation

(use-package eldoc
  :defer 5
  :config (progn
            (setq eldoc-idle-delay 0.5)
            (global-eldoc-mode +1)))

(use-package eldoc-box
  :after (eldoc eglot)
  :config (progn
            (eldoc-box-hover-mode +1)
            (eldoc-box-hover-at-point-mode +1)))

(use-package ehelp
  :defer 15)

(use-package helpful
  :after ehelp
  :general (ehelp-map
            "k" #'helpful-key
            "v" #'helpful-variable
            "f" #'helpful-callable))

;;; Files

;;;; Auto-saving

;; Auto-save everything to a temporary directory, instead of cluttering
;; the filesystem.  I don’t want emacs-specific lockfiles, either.

(setq auto-save-file-name-transforms `((".*" ,temporary-file-directory t))
      create-lockfiles nil)

;;;; Auto-reloading

(use-package autorevert
  :config (progn
            (setq auto-revert-verbose t
                  auto-revert-use-notify t)
            (global-auto-revert-mode t)))

;;;; Backups

;; I like to keep my backups out of regular folders.  I tell emacs to use
;; a subfolder of its configuration directory for that.  Also, use the
;; trash for deleting on OS X.
(let ((backup-dir (expand-file-name "~/.emacs.d/backups/")))
  (unless (file-directory-p backup-dir)
    (make-directory backup-dir))
  (setq backup-directory-alist `((".*" . ,backup-dir))
        backup-by-copying-when-linked t
        backup-by-copying-when-mismatch t))

(setq delete-by-moving-to-trash t)

(use-package goto-chg
  :defer 1)

;;; Directories

(setq dired-dwim-target t
      dired-recursive-copies 'top
      dired-listing-switches "-alh"
      dired-recursive-deleted (if delete-by-moving-to-trash
                                  'always
                                'top))

(add-hook 'dired-mode-hook
          (lambda ()
            (dired-hide-details-mode)))

(use-package dired-git-info
  :general (:keymaps 'dired-mode-map
                     :states 'normal
                     ")" #'dired-git-info-mode))

;;; Shells

(use-package eshell
  :defer 5
  :commands (eshell)
  :functions (eshell/pwd)
  :init (progn
          (with-eval-after-load 'evil-ex
            (evil-ex-define-cmd "esh[ell]" #'eshell)))
  :config (setq eshell-prompt-function (lambda ()
                                         (concat (eshell/pwd) "\n$ "))
                eshell-prompt-regexp "^[$][[:blank:]]"
                eshell-cmpl-cycle-completions nil))

(use-package eshell-toggle
  :after projectile
  :commands (eshell-toggle)
  :general ("C-`" . eshell-toggle)
  :config (setq eshell-toggle-use-projectile-root t))

(use-package esh-autosuggest
  :after eshell
  :ghook ('eshell-mode-hook))

(declare-function eshell-push-command "esh-buf-stack" (CMD))
(defun my-bind-esh-push ()
  (general-define-key
   :states '(normal insert)
   :keymaps 'local
   "M-q" #'eshell-push-command))

(use-package esh-buf-stack
  :after (eshell)
  :ghook ('eshell-mode-hook #'my-bind-esh-push)
  :config (setup-eshell-buf-stack))

(use-package bash-completion
  :after (eshell))

(use-package fish-completion
  :when (executable-find "fish")
  :after (bash-completion)
  :commands (global-fish-completion-mode)
  :config (progn
            (setq fish-completion-fallback-on-bash-p t)
            (global-fish-completion-mode)))

(use-package esh-help
  :after (eshell)
  :config (setup-esh-help-eldoc))

(use-package eshell-fringe-status
  :after eshell
  :ghook '(eshell-mode-hook))

(use-package eshell-up
  :after (eshell))

(use-package shell
  :defer t
  :general (:keymaps 'shell-mode-map
                     "C-d" #'comint-delchar-or-maybe-eof))

(use-package comint
  :defer t
  :general (:keymaps 'comint-mode-map
                     "C-c C-l" #'counsel-shell-history))

;;; Editing

(setq-default indent-tabs-mode nil
              tab-always-indent 'complete)

(use-package ws-butler
  :ghook ('prog-mode-hook))

;;; Major modes

;;;; js
(setq js-indent-level 2
      js-enabled-frameworks '(javascript))

;;;; typescript
(setq typescript-indent-level 2)
(autoload 'ansi-color-apply-on-region "ansi-color")
(defun colorise-compilation-buffer ()
  (ansi-color-apply-on-region compilation-filter-start (point-max)))
(add-hook 'compilation-filter-hook #'colorise-compilation-buffer)

;;;; shell
(general-add-hook 'sh-mode-hook
                  (lambda ()
                    (general-add-hook 'after-save-hook
                                      #'executable-make-buffer-file-executable-if-script-p :append :local)))

(add-to-list 'auto-mode-alist '("\\.env\\'" . conf-unix-mode))
(use-package sh-script
  :mode (("\\.zsh\\'" . shell-script-mode)
         ("zshenv\\'" . shell-script-mode)
         ("zshrc\\'"  . shell-script-mode))
  :config (setq sh-shell-file "/usr/bin/env zsh"
                sh-basic-offset 2))

(add-hook 'shell-mode-hook 'ansi-color-for-comint-mode-on)

;;;; make
(general-add-hook 'makefile-mode-hook
                  (lambda ()
                    (setq-local indent-tabs-mode t)))

;;;; nix
(setq nix-indent-function #'nix-indent-line)

(use-package nix-update
  :commands (nix-update-fetch))

;;;; gitlab-ci.yml
(with-eval-after-load 'git-gutter-fringe
              (fringe-helper-define 'flycheck-fringe-bitmap-double-arrow '(center repeated)
                "XXX....."))

(setq gitlab-ci-url "https://gitlab.satoshipay.tech")
(use-package gitlab-ci-mode-flycheck
  :ghook ('gitlab-ci-mode-hook (list #'gitlab-ci-mode-flycheck-enable
                                     #'flycheck-mode)))

;;;; *ignore
(use-package gitignore-mode
  :mode ((".dockerignore\\'" . gitignore-mode)))

;;;; kubernetes
(setq k8s-site-docs-version "v1.13")

;;;; beancount

(use-package beancount
  :load-path "~/projects/bitbucket.org/blais/beancount/editors/emacs")

;;;; ledger

(use-package ledger-mode
  :gfhook '(ledger-flymake-enable ws-butler-mode)
  :config (progn
            (setq ledger-reconcile-default-commodity "€"
                  ledger-narrow-on-reconcile t
                  ledger-clear-whole-transactions t
                  ledger-reports `(("Monthly Expenses" "ledger -f %(ledger-file) reg -M \\^Expenses --real -X EUR -l \"payee != 'Opening Balances'\"")
                                   ("Total Expenses:This Month" "ledger -f %(ledger-file) bal \\^Expenses -p \"this month\"")
                                   ("Total Expenses:Past 90 Days" ,(concat "ledger -f %(ledger-file) balance ^Expenses -b "
                                                                           (format-time-string "%Y-%m-%d" (time-add (current-time) (days-to-time -90)))))
                                   ("Total Expenses:Past 180 Days" ,(concat "ledger -f %(ledger-file) balance ^Expenses -b "
                                                                            (format-time-string "%Y-%m-%d" (time-add (current-time) (days-to-time -180)))))
                                   ("Expenses:This Month" "ledger -f %(ledger-file) register \\^Expenses -p \"this month\"")
                                   ("Expenses:Past 90 Days (monthly)" ,(concat "ledger -f %(ledger-file) register --monthly ^Expenses -b "
                                                                     (format-time-string "%Y-%m-%d" (time-add (current-time) (days-to-time -90)))))
                                   ("Expenses:Past 180 Days (monthly)" ,(concat "ledger -f %(ledger-file) register --monthly ^Expenses -b "
                                                                      (format-time-string "%Y-%m-%d" (time-add (current-time) (days-to-time -180)))))
                                   ("All Account Balances" "ledger -f %(ledger-file) bal --current -R \\^Assets \\^Liabilities")))))


(use-package evil-ledger
  :after ledger-mode
  :ghook '(ledger-mode-hook))

;;;; org

(setq org-ellipsis "…"
      org-directory "~/Documents/org")

(use-package org-journal
  :commands (org-journal-new-date-entry
	     org-journal-new-entry
	     org-journal-new-scheduled-entry)
  :gfhook (#'variable-pitch-mode)
  :config (setq org-journal-date-format "%A, %d %B %Y"
                org-journal-dir "~/Documents/journal"))

;;;; web modes (tsx, html)

(use-package web-mode
  :mode (("\\.tsx" . web-mode))
  :config (setq web-mode-enable-auto-pairing nil
                web-mode-code-indent-offset 2
                web-mode-markup-indent-offset 2
                web-mode-css-indent-offset 2
                web-mode-style-padding 0
                web-mode-script-padding 0))

;;; IDE features

(fringe-helper-define 'left-vertical-bar '(center repeated)
  "XXX.....")
(use-package flymake
  :config (setq flymake-error-bitmap '(left-vertical-bar compilation-error)
                flymake-warning-bitmap '(left-vertical-bar compilation-warning)))
(use-package flycheck
  :defer 5
  :ghook ('(typescript-mode-hook
            dockerfile-mode-hook
            yaml-mode-hook
            js-mode-hook
            css-mode-hook
            scss-mode-hook
            html-mode-hook
            haskell-mode-hook))
  :config (progn
            (setq flycheck-check-syntax-automatically '(save idle-change idle-buffer-switch mode-enabled)
                  flycheck-highlighting-mode 'sexps)
            (flycheck-define-error-level 'error
              :severity 100
              :compilation-level 2
              :overlay-category 'flycheck-error-overlay
              :fringe-bitmap 'left-vertical-bar
              :fringe-face 'flycheck-fringe-error
              :error-list-face 'flycheck-error-list-error)
            (flycheck-define-error-level 'warning
              :severity 10
              :compilation-level 1
              :overlay-category 'flycheck-warning-overlay
              :fringe-bitmap 'left-vertical-bar
              :fringe-face 'flycheck-fringe-warning
              :warning-list-face 'flycheck-error-list-warning)
            (flycheck-define-error-level 'info
              :severity -10
              :compilation-level 0
              :overlay-category 'flycheck-info-overlay
              :fringe-bitmap 'left-vertical-bar
              :fringe-face 'flycheck-fringe-info
              :info-list-face 'flycheck-error-list-info)))


(use-package treemacs
  :defer t
  :config (progn
            (setq treemacs-no-png-images t)))

(use-package lsp-mode
  :defer 2
  :general (:states 'normal :keymaps 'lsp-mode-map
                    "gd" #'xref-find-definitions
                    "gr" #'xref-find-references
                    "C-t" #'xref-pop-marker-stack)
  :ghook ('(typescript-mode-hook
            dockerfile-mode-hook
            yaml-mode-hook
            js-mode-hook
            css-mode-hook
            scss-mode-hook
            html-mode-hook
            haskell-mode-hook)
          #'lsp)
  :config (progn
            (setq lsp-auto-guess-root t
                  lsp-auto-configure nil
                  lsp-prefer-flymake nil
                  lsp-enable-symbol-highlighting nil)
            (add-to-list 'lsp-language-id-configuration '(js-mode . "javascript"))))

(use-package lsp-clients
  :after (lsp-mode))

(use-package lsp-ui
  :after lsp-mode
  :ghook ('lsp-mode-hook)
  :general (:states 'normal :keymaps 'lsp-ui-mode-map
                    "gd" #'lsp-ui-peek-find-definitions
                    "gr" #'lsp-ui-peek-find-references
                    "C-t" #'lsp-ui-peek-jump-backward)
  :config (progn
            (setq lsp-enable-snippet nil
                  lsp-ui-sideline-show-code-actions nil
                  lsp-ui-sideline-enable t
                  lsp-ui-doc-enable nil)))

(use-package lsp-haskell
  :after lsp-mode)

;; Inside a javascript project, it's common to install tools locally to
;; the project.  This will allows emacs to find their executables.

(use-package add-node-modules-path
  :config (setq add-node-modules-max-depth 6)
  :ghook ('(js2-mode-hook typescript-mode-hook) #'add-node-modules-path))

;;;; Reformat on save

(use-package prettier-js
  :ghook ('(javascript-mode-hook typescript-mode-hook) #'prettier-js-mode t)
  :config (progn
            (setq prettier-js-show-errors 'echo)
            (if-let ((prettier_d (executable-find "prettier_d")))
                (setq prettier-js-command prettier_d
                      prettier-js-args '("--pkg-conf" "--parser" "typescript"))
              (message "prettier_d is not available"))))

;;; E-mail

(declare-function sendmail-send-it "sendmail")
(setq send-mail-function #'sendmail-send-it)

(use-package mu4e
  :if (executable-find "mu")
  :commands (mu4e)
  :defines (mu4e-contexts)
  :init (progn
          (with-eval-after-load 'evil-ex
            (evil-ex-define-cmd "mu[4e]" #'mu4e)))
  :config (progn
            (setq mail-user-agent #'mu4e-user-agent
                  mu4e-completing-read-function #'ivy-completing-read
                  mu4e-maildir "~/mail"
                  mu4e-context-policy 'pick-first
                  mu4e-update-interval 600
                  mu4e-change-filenames-when-moving t
                  mu4e-index-lazy-check t
                  mu4e-hide-index-messages t
                  mu4e-compose-format-flowed t
                  mu4e-contexts (list
                                 (make-mu4e-context
                                  :name "SatoshiPay"
                                  :match-func (lambda (msg)
                                                (if msg
                                                    (mu4e-message-contact-field-matches
                                                     msg :to ".*@satoshipay.io")
                                                  (string-equal (system-name) "satoshipad")))
                                  :vars '((user-mail-address . "alan@satoshipay.io")
                                          (mu4e-sent-messages-behavior . delete)
                                          (mu4e-drafts-folder . "/satoshipay/[Gmail]/Drafts")
                                          (mu4e-sent-folder . "/satoshipay/[Gmail]/Sent Mail")
                                          (mu4e-refile-folder . "/satoshipay/[Gmail]/All Mail")
                                          (mu4e-trash-folder . "/satoshipay/[Gmail]/Bin")
                                          (mu4e-maildir-shortcuts . (("/satoshipay/INBOX" . ?i)
                                                                     ("/satoshipay/[Gmail]/All Mail" . ?a)
                                                                     ("/satoshipay/[Gmail]/Sent Mail" . ?s)
                                                                     ("/satoshipay/[Gmail]/Spam" . ?p)
                                                                     ("/satoshipay/[Gmail]/Spam" . ?j)))))))))

;;; Take me to my leader

(my-leader-def
 "" nil
 "`" #'eshell-toggle
 "h" '(:keymap ehelp-map :package ehelp)
 "w" '(:keymap evil-window-map :package evil)
 "x" '(:keymap ctl-x-map)
 "c" (general-simulate-key "C-c")
 "q" #'evil-delete-buffer
 "p" projectile-command-map
 "v" #'split-window-right
 "o" #'other-window
 "u" #'universal-argument
 ";" #'counsel-M-x
 "bb" #'ivy-switch-buffer
 "bx" #'kill-this-buffer
 "br" #'revert-buffer
 "bk" #'kill-buffer
 "dd" #'dired
 "fs" #'save-buffer
 "ff" #'find-file
 "fw" #'write-file
 "fd" #'crux-delete-file-and-buffer
 "fr" #'crux-rename-file-and-buffer
 "gs" #'magit-status
 "gm" #'git-messenger:popup-message
 "gg" #'magit-dispatch
 "gn" #'git-gutter:next-hunk
 "gp" #'git-gutter:previous-hunk
 "gi" #'git-gutter:popup-hunk
 "gs" #'git-gutter:stage-hunk
 "go" #'git-gutter:revert-hunk
 "gt" #'git-timemachine
 "bi" #'ibuffer
 "bz" #'bury-buffer
 "iu" #'counsel-unicode-char)

(load (expand-file-name "mail.el" user-emacs-directory) :noerror)

;; # Local Variables:
;; # flycheck-disabled-checkers: 'emacs-lisp-checkdoc
;; # End: