;;; programming.el --- core programming settings -*- lexical-binding: t -*-
;;; Commentary:

;; default indentation settings
(setq-default indent-tabs-mode nil)
(setq-default tab-width 4)

;; code diagnostics
(use-package flycheck
  :custom
  (flycheck-display-error-delay 1.0)
  :hook
  (prog-mode . (lambda nil
		         (when (not (derived-mode-p 'emacs-lisp-mode))
		           (flycheck-mode))))
  (yaml-mode . flycheck-mode))

;; lsp support
(use-package lsp-mode
  :custom
  (lsp-keymap-prefix "C-c l")
  (lsp-auto-guess-root t)
  :hook
  (prog-mode . (lambda nil
		         (when (not (derived-mode-p 'emacs-lisp-mode))
		           (lsp-deferred))))
  (TeX-mode . lsp-deferred)
  (lsp-mode . lsp-enable-which-key-integration))

(use-package lsp-ui
  :custom
  (lsp-ui-sideline-show-diagnostics t)
  (lsp-ui-sideline-show-hover nil)
  (lsp-ui-sideline-show-code-actions nil)
  (lsp-ui-sideline-update-mode 'line)
  (lsp-ui-peek-enable t)
  (lsp-ui-peek-show-directory t)
  (lsp-ui-doc-enable t)
  (lsp-ui-doc-position 'top)
  (lsp-ui-doc-side 'right)
  (lsp-ui-doc-show-with-cursor nil)
  (lsp-ui-doc-show-with-mouse nil)
  :bind
  (:map lsp-ui-mode-map
	    ([remap xref-find-definitions] . #'lsp-ui-peek-find-definitions)
	    ([remap xref-find-references] . #'lsp-ui-peek-find-references)
	    ("C-c l d" . #'lsp-ui-doc-toggle)))

;; autocompletion
(defun pt/company-complete-func nil
  "Insert the common part of all candidates, or insert <TAB>."
  (interactive)
  (when (company-manual-begin)
    (let ((tick (buffer-chars-modified-tick)))
      (call-interactively #'company-complete-common)
      (when (eq tick (buffer-chars-modified-tick))
        (call-interactively #'company-abort)
        (call-interactively (key-binding "\t"))))))
(use-package company
  :config
  (define-key company-active-map (kbd "<tab>") #'pt/company-complete-func)
  (define-key company-active-map (kbd "<TAB>") #'pt/company-complete-func)
  (define-key company-active-map (kbd "<backtab>") nil)
  (define-key company-active-map (kbd "C-p") #'company-cycle-backward)
  (define-key company-active-map (kbd "C-n") (lambda (&optional arg)
                                               (interactive "p")
                                               (let ((company-selection-wrap-around t))
                                                 (call-interactively #'company-select-next))))
  :custom
  (company-minimum-prefix-length 1)
  (company-idle-delay (lambda () (if (company-in-string-or-comment) nil 0.3)))
  :hook
  (prog-mode . company-mode)
  (yaml-mode . company-mode)
  (TeX-mode . company-mode)
  (text-mode . company-mode)
  (conf-mode . company-mode))

;; project management
(use-package projectile
  :commands projectile-mode
  :hook (after-init . projectile-mode)
  :bind
  (:map projectile-mode-map
        ("C-c p" . #'projectile-command-map)))

;; web development
(use-package web-mode
  :custom
  (web-mode-enable-auto-closing t)
  (web-mode-enable-auto-pairing t)
  (web-mode-enable-css-colorization t)
  (web-mode-markup-indent-offset 2)
  (web-mode-css-indent-offset 2)
  (web-mode-code-indent-offset 2)
  (web-mode-style-padding 0)
  (web-mode-script-padding 0)
  :mode
  (("\\.html?\\'" . web-mode)
   ("\\.phtml\\'" . web-mode)
   ("\\.php\\'" . web-mode)
   ("\\.tpl\\'" . web-mode)
   ("\\.[agj]sp\\'" . web-mode)
   ("\\.as[cp]x\\'" . web-mode)
   ("\\.erb\\'" . web-mode)
   ("\\.mustache\\'" . web-mode)
   ("\\.djhtml\\'" . web-mode)))

;; formatting code
(use-package format-all
  :commands format-all-mode
  :hook (prog-mode . format-all-mode)
  :bind ("C-c f" . format-all-buffer)
  :config
  (define-format-all-formatter autopep8
    (:executable "autopep8")
    (:install "pip install autopep8")
    (:languages "Python")
    (:features)
    (:format (format-all--buffer-easy executable "-")))
  (define-format-all-formatter dockerfmt
    (:executable "dockerfmt")
    (:install "go install github.com/reteps/dockerfmt@latest")
    (:languages "Dockerfile")
    (:features)
    (:format (format-all--buffer-easy executable)))
  (setq-default format-all-formatters
                '(("C"     (clang-format))
                  ("C++"   (clang-format))
                  ("Shell" (beautysh))
                  ("HTML"  (prettier))
                  ("XHTML" (prettier))
                  ("XML"   (prettier))
                  ("CSS"   (prettier))
                  ("Less"  (prettier))
                  ("SCSS"  (prettier))
                  ("JavaScript" (prettier))
                  ("JSON"  (prettier))
                  ("JSX"   (prettier))
                  ("TypeScript" (prettier))
                  ("TSX"   (prettier))
                  ("YAML"  (prettier))
                  ("TOML"  (prettier))
                  ("LaTeX" (latexindent))
                  ("Lua"   (stylua))
                  ("PHP"   (prettier))
                  ("Python" (autopep8) (isort))
                  ("Perl"  (perltidy))
                  ("Markdown" (prettier))
                  ("Dockerfile" (dockerfmt))
                  ("SQL"   (pgformatter))
                  ("Nginx" (nginxfmt)))))

;; highlight words like todo
(use-package hl-todo
  :custom
  (hl-todo-keyword-faces
   '(("FIXME" error bold)
     ("TODO" org-todo)
     ("DONE" org-done)
     ("NOTE" bold)))
  :hook (prog-mode . hl-todo-mode))

;; various useful modes
(use-package dockerfile-mode :commands dockerfile-mode)
(use-package systemd :commands systemd-mode)
(use-package nginx-mode :commands nginx-mode)
(use-package yaml-mode :commands yaml-mode)

(provide 'programming)
;;; programming.el ends here
