(setq inhibit-startup-screen t
      initial-scratch-message ""
      initial-major-mode 'text-mode
      package-enable-at-startup nil)

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

;;; Customize

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

(use-package crux
  :custom ((crux-reopen-as-root-mode t)))

;;; evil pre-init
(custom-set-variables '(evil-want-integration t)
		      '(evil-want-keybinding nil))

;;; Styles

;; I prefer an always-visible cursor.  Feels less distracting.
(customize-set-variable 'blink-cursor-mode nil)

;; Disable all the bars, unless on OSX, in which case, keep the menu bar.

(customize-set-variable 'menu-bar-mode nil)
(customize-set-variable 'scroll-bar-mode nil)
(customize-set-variable 'tool-bar-mode nil)
(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 'eink t))

(global-hl-line-mode +1)

;;; Chrome
(use-package minions
  :custom ((minions-mode-line-lighter "#")
           (minions-mode t)))

(use-package moody
  :config (progn
            (setq x-underline-at-descent-line t)
            (moody-replace-mode-line-buffer-identification)
            (moody-replace-vc-mode)))

(setq frame-title-format (list "Emacs"))

;;; Dates & Times

(custom-set-variables
 '(calendar-week-start-day 1)
 '(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

;; I think =set-keyboard-coding-system= stops OS X from doing something
;; annoying to add accents.  The modifier setup is to match my
;; re-arrangement of modifiers on OSX: Cmd on the outside, then
;; Option/alt, then Control.

(when (eq system-type 'darwin)
  (custom-set-variables
   '(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 ace-link
  :after avy
  :config (ace-link-setup-default))

;; Popup keybindings following a prefix automatically.

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

;;; Modeline

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

(use-package relative-buffers
  :custom ((global-relative-buffers-mode t)))

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

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

;;; Minibuffer

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

;;; Windows

(use-package eyebrowse
  :after (evil)
  :custom ((eyebrowse-new-workspace #'counsel-projectile-switch-project)
           (eyebrowse-mode t))
  :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
  :custom ((winner-boring-buffers '("*Completions*" "*Help*" "*Apropos*" "*Buffer List*" "*info*" "*Compile-Log*")))
  :general (:keymaps 'evil-window-map
                    "u" #'winner-undo
                    "r" #'winner-redo
                    "C-r" #'winner-redo))
;;; Evil

(use-package evil
  :demand t
  :after (undo-tree)
  :custom ((evil-shift-width 2)
           (evil-mode-line-format '(before . mode-line-front-space)))
  :general
  (:states 'motion
           "C-;" #'evil-avy-goto-line)
  (:states 'normal
           ";" #'evil-ex)
  :config (progn
            (evil-mode +1)))

(use-package evil-collection
  :custom ((evil-collection-company-use-tng nil))
  :config (evil-collection-init))

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

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

(use-package evil-surround
  :custom ((global-evil-surround-mode t)))

(use-package evil-commentary
  :custom ((evil-commentary-mode t)))

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

(use-package evil-quickscope
  :custom ((global-evil-quickscope-mode t)))

(use-package evil-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
  :ghook ('org-agenda-mode-hook #'evil-org-agenda-set-keys))

;;; Projects

(use-package projectile
  :defines projectile-command-map
  :custom ((projectile-mode +1)
           (projectile-completion-system 'ivy)))

(use-package counsel-projectile
  :after (counsel projectile ivy-hydra)
  :custom ((counsel-projectile-mode +1)))

(use-package magit
  :custom ((global-magit-file-mode +1)
           (magit-completing-read-function #'ivy-completing-read)))

(eval-when-compile (require 'fringe-helper))
(use-package git-gutter)
(use-package git-gutter-fringe
  :config (progn
            (global-git-gutter-mode 1)
            ;; 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
  :defer 5
  :custom ((git-messenger:use-magit-popup t)))

(use-package git-timemachine)

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

;;; Completion

(use-package company
  :custom ((global-company-mode +1)
           (company-idle-delay .2)
           (company-begin-commands '(self-insert-command))
           (company-auto-complete #'company-explicit-action-p)
           (company-auto-complete-chars '(?\ ?\( ?\) ?.)))
  :general (:states 'insert
                    "TAB" #'company-complete))

(use-package company-box
  :custom ((company-box-enable-icon nil))
  :ghook 'company-mode-hook)

;;; Documentation

(use-package eldoc
  :custom ((global-eldoc-mode +1)
           (eldoc-idle-delay 0.5)))

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

(use-package ehelp)

(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
  :custom ((global-auto-revert-mode t)
           (auto-revert-verbose 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))

(if (eq system-type 'darwin)
    (setq delete-by-moving-to-trash t)
  (if (and (executable-find "trash") (not (fboundp #'system-move-file-to-trash)))
      (defun system-move-file-to-trash (file)
        (call-process (executable-find "trash")
                      nil 0 nil
                      file))))

(use-package undo-tree
  :defer 1
  :config (global-undo-tree-mode))
(use-package goto-chg
  :defer 1)

;;; Directories

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

;;; Shells

(use-package eshell
  :functions (eshell/pwd)
  :custom ((eshell-prompt-function (lambda ()
                                     (concat (eshell/pwd) "\n$ ")))
           (eshell-prompt-regexp "^[$][[:blank:]]")))

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

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

;;; Major modes

;;;; js
(custom-set-variables '(js-indent-level 2)
                      '(js-enabled-frameworks '(javascript)))

;;;; typescript
(custom-set-variables '(typescript-indent-level 2))

;;;; 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)

;;;; nix
(custom-set-variables '(nix-indent-function #'nix-indent-line))
;;;; gitlab-ci.yml
(with-eval-after-load 'git-gutter-fringe
              (fringe-helper-define 'flycheck-fringe-bitmap-double-arrow '(center repeated)
                "XXX....."))

(custom-set-variables '(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
(custom-set-variables '(k8s-site-docs-version "v1.13"))

;;;; beancount

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

;;;; org

(custom-set-variables '(org-ellipsis "…")
                      `(org-directory "~/Documents/org"))

(use-package org-journal
  :gfhook (#'variable-pitch-mode)
  :custom ((org-journal-date-format "%A, %d %B %Y")
           (org-journal-dir "~/Documents/journal")))

;;;; emacs-lisp

(use-package auto-async-byte-compile
  :ghook ('emacs-lisp-mode-hook #'enable-auto-async-byte-compile-mode))

;;;; web modes (tsx, html)

(use-package web-mode
  :mode (("\\.tsx" . web-mode))
  :custom ((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
  :custom ((flymake-error-bitmap '(left-vertical-bar compilation-error))
           (flymake-warning-bitmap '(left-vertical-bar compilation-warning))))

(use-package lsp-mode
  :ghook ('typescript-mode-hook #'lsp)
  :custom ((lsp-auto-guess-root t)
           (lsp-prefer-flymake t)
           (lsp-enable-symbol-highlighting nil)))

(use-package lsp-ui
  :after lsp-mode
  :ghook ('lsp-mode-hook)
  :custom ((lsp-ui-sideline-enable nil)))

;; 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
  :ghook ('(js2-mode-hook
            typescript-mode-hook) #'add-node-modules-path))

;;;; Reformat on save

(use-package prettier-js
  :ghook ('(typescript-mode-hook js2-mode-hook) #'prettier-js-mode t))

;;; Take me to my leader

(my-leader-def
 "" nil
 "h" '(:keymap ehelp-map)
 "w" '(:keymap evil-window-map)
 "x" '(:keymap ctl-x-map)
 "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
 "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)

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