;;;; Tabs, indentation, and the TAB key
(use-package emacs
  :ensure nil
  :demand t
  :config
  (setq tab-always-indent 'complete)
  (setq tab-first-completion nil)
  (setq-default tab-width 4
                indent-tabs-mode nil))

;;;; Parentheses (show-paren-mode)
(use-package paren
  :ensure nil
  :hook (prog-mode . show-paren-local-mode)
  :config
  (setq show-paren-style 'mixed)
  (setq show-paren-when-point-in-periphery nil)
  (setq show-paren-when-point-inside-paren nil)
  (setq show-paren-context-when-offscreen 'overlay)) ; Emacs 29

;;;; Eldoc (Emacs live documentation feedback)
(use-package eldoc
  :ensure nil
  :hook (prog-mode . eldoc-mode)
  :config
  (setq eldoc-message-function #'message)) ; don't use mode line for M-x eval-expression, etc.

;;;; Eglot (built-in client for the language server protocol)
(use-package eglot
  :ensure nil
  :demand t ;; Not a mistake, we need to load Eglot elisp code before
            ;; we open any Python file.
  :functions (eglot-ensure)
  :commands (eglot)
  :bind
  ( :map eglot-mode-map
    ("C-c e r" . eglot-rename)
    ("C-c e o" . eglot-code-action-organize-imports)
    ("C-c e d" . eldoc)
    ("C-c e c" . eglot-code-actions)
    ("C-c e f" . eglot-format)
    ;; Since eglot plugs into flymake anyway
    ("C-c e l" . flymake-show-buffer-diagnostics))
  :config
  (setq eglot-sync-connect nil)
  (setq eglot-autoshutdown t)
  (setq eglot-extend-to-xref t))

;;; Markdown (markdown-mode)
(use-package markdown-mode
  :ensure t
  :defer t
  :config
  (setq markdown-fontify-code-blocks-natively t))

;;; csv-mode
(use-package csv-mode
  :ensure t
  :commands (csv-align-mode))

;;; Flyspell
(use-package flyspell
  :ensure nil
  :bind
  ( :map flyspell-mode-map
    ("C-;" . nil)
    :map flyspell-mouse-map
    ("<mouse-3>" . flyspell-correct-word))
  :config
  (setq flyspell-issue-message-flag nil)
  (setq flyspell-issue-welcome-flag nil)
  (setq ispell-program-name "aspell")
  (setq ispell-dictionary "en_GB"))

;;; Flymake
(use-package flymake
  :ensure nil
  :bind
  ( :map flymake-mode-map
    ("C-c ! s" . flymake-start)
    ("C-c ! l" . flymake-show-buffer-diagnostics)  ; Emacs28
    ("C-c ! L" . flymake-show-project-diagnostics) ; Emacs28
    ("C-c ! n" . flymake-goto-next-error)
    ("C-c ! p" . flymake-goto-prev-error))
  :hook
  (prog-mode . turn-on-flymake)
  :config
  (defun turn-on-flymake () (flymake-mode t))
  (setq flymake-fringe-indicator-position 'left-fringe)
  (setq flymake-suppress-zero-counters t)
  (setq flymake-no-changes-timeout nil)
  (setq flymake-start-on-flymake-mode t)
  (setq flymake-start-on-save-buffer t)
  (setq flymake-proc-compilation-prevents-syntax-check t)
  (setq flymake-wrap-around nil)
  (setq flymake-mode-line-format
        '("" flymake-mode-line-exception flymake-mode-line-counters))
  (setq flymake-mode-line-counter-format
        '("" flymake-mode-line-error-counter
          flymake-mode-line-warning-counter
          flymake-mode-line-note-counter ""))
  (setq flymake-show-diagnostics-at-end-of-line nil) ; Emacs 30
  (remove-hook 'flymake-diagnostic-functions #'flymake-proc-legacy-flymake))

;;; Elisp packaging requirements
(use-package package-lint-flymake
  :ensure t
  :after flymake
  :config
  ;; We can't use `use-package' :hook because the hookname does not
  ;; end in -hook.
  (add-hook 'flymake-diagnostic-functions #'package-lint-flymake))

;;; General configurations for prose/writing

;;;; `outline' (`outline-mode' and `outline-minor-mode')
(use-package outline
  :ensure nil
  :bind
  ("<f10>" . outline-minor-mode)
  :config
  (setq outline-minor-mode-highlight nil) ; emacs28
  (setq outline-minor-mode-cycle t) ; emacs28
  (setq outline-minor-mode-use-buttons nil) ; emacs29---bless you for the nil option!
  (setq outline-minor-mode-use-margins nil)) ; as above

;;;; `dictionary'
(use-package dictionary
  :ensure nil
  :config
  (setq dictionary-server "dict.org"
        dictionary-default-popup-strategy "lev" ; read doc string
        dictionary-create-buttons nil
        dictionary-use-single-buffer t))

(use-package paredit
  :ensure t
  :bind ( :map paredit-mode-map
          ("C-o" . paredit-open-round)
          ("M-D" . paredit-splice-sexp)
          ("C-A-d" . paredit-forward-down)
          ("C-A-u" . paredit-backward-up)
          ;; Unbind things that I don't need
          ("M-s" . nil) ; used for search related keybindings
          ("M-?" . nil) ; `xref-find-references' uses it.
          ("RET" . nil)); `ielm-return' uses it.
  :hook ((lisp-data-mode lisp-mode clojure-mode clojure-ts-mode cider-repl-mode inferior-emacs-lisp-mode) . paredit-mode))

(use-package apheleia
  :ensure t
  :demand t
  :after blackout
  :config
  (apheleia-global-mode +1)
  (with-eval-after-load 'apheleia-formatters
    (push '(zprint . ("zprint")) apheleia-formatters))
  :hook
  (apheleia-mode . (lambda () (blackout 'apheleia-mode)))
  ;;:blackout
  )

(use-package multiple-cursors
  :ensure t
  :bind
  ("C->" . mc/mark-next-like-this)
  ("C-<" . mc/mark-previous-like-this)
  ("C-c C->" . mc/mark-all-like-this))

(use-package lsp-mode
  :ensure t
  :demand t
  :commands lsp
  :init
  (defun my/lsp-mode-setup-completion ()
    (setf (alist-get 'styles (alist-get 'lsp-capf completion-category-defaults))
          '(flex))) ;; Configure flex
  :bind (("C-c C-c Q" . lsp-workspace-shutdown)
         ("C-c C-c q" . lsp-workspace-restart))
  :config
  (setq lsp-rust-all-targets nil
        lsp-rust-analyzer-cargo-all-targets nil
        lsp-log-io nil
        lsp-semgrep-server-command nil
        lsp-completion-provider :none
        lsp-inlay-hint-enable t
        lsp-modeline-diagnostics-enable nil
        lsp-rust-analyzer-import-merge-behaviour "full"
        lsp-rust-analyzer-completion-auto-import-enable t
        lsp-rust-rustfmt-path "/home/ardent/.cargo/bin/rustfmt"
        lsp-rust-unstable-features t
        lsp-semantic-tokens-enable t
        lsp-semantic-tokens-warn-on-missing-face nil
        lsp-treemacs-error-list-current-project-only t
        lsp-ui-sideline-delay 0.1
        lsp-ui-sideline-diagnostic-max-line-length 50
        lsp-ui-sideline-enable nil
        lsp-ui-sideline-show-code-actions t
        lsp-ui-sideline-update-mode 'line
        lsp-eldoc-render-all t
        lsp-idle-delay 0.2
        lsp-enable-snippet t
        ;; enable / disable the hints as you prefer:
        lsp-rust-analyzer-cargo-watch-command "clippy"
        lsp-rust-analyzer-server-display-inlay-hints t
        lsp-rust-analyzer-call-info-full t
        lsp-rust-analyzer-proc-macro-enable t
        lsp-rust-analyzer-display-lifetime-elision-hints-enable "skip_trivial"
        lsp-rust-analyzer-display-chaining-hints t
        lsp-rust-analyzer-display-lifetime-elision-hints-use-parameter-names t
        lsp-rust-analyzer-display-closure-return-type-hints t
        lsp-rust-analyzer-display-parameter-hints nil
        lsp-rust-analyzer-display-reborrow-hints t)
  :hook
  (
   (lsp-mode . lsp-enable-which-key-integration)
   (lsp-mode . subword-mode)
   (lsp-completion-mode . my/lsp-mode-setup-completion)
   ;;(before-save . lsp-format-buffer)
   ))

(use-package cargo
  :ensure t)

(use-package rustic
  :ensure t
  :bind (:map rustic-mode-map
              ("M-?" . lsp-find-references)
              ("C-c C-c a" . lsp-execute-code-action)
              ("C-c C-c r" . lsp-rename)
              ("C-c C-c l" . flycheck-list-errors)
              ("C-c C-c s" . lsp-rust-analyzer-status))
  :hook rust-ts-mode
  :custom
  (rustic-format-on-save t)
  :config
  (setq rustic-format-on-save t
        rust-indent-method-chain t
        rustic-format-trigger 'on-save
        rustic-lsp-format t
        rustic-lsp-client 'lsp)
  (setq-local buffer-save-without-query t)
  (setq-local lsp-semantic-tokens-enable t))

;;;; Configuration for Python Programming

(use-package python
  :ensure nil
;;; Uncomment this if you want Eglot to start automatically. I prefer
;;; calling `M-x eglot' myself.
;;  :hook ((python-base-mode . eglot-ensure))
  :config
  (setq python-shell-dedicated 'project)
  ;; Apheleia is an Emacs package for formatting code as you save
  ;; it. Here we are asking Apheleia to use Ruff for formatting our
  ;; Python code.
  (with-eval-after-load 'apheleia
    (setf (alist-get 'python-mode apheleia-mode-alist)
          '(ruff-isort ruff))
    (setf (alist-get 'python-ts-mode apheleia-mode-alist)
          '(ruff-isort ruff))))

(use-package pet
  :ensure (:host github :repo "vedang/emacs-pet" :branch "fix-eglot-integration"
                 ;; :remotes (("upstream" :repo "wyuenho/emacs-pet" :branch "main"))
                 )
  :ensure-system-package (dasel sqlite3)
  :config
  ;; The -10 here is a way to define the priority of the function in
  ;; the list of hook functions. We want `pet-mode' to run before any
  ;; other configured hook function.
  (add-hook 'python-base-mode-hook #'pet-mode -10))

;;;; Configuration for Zig Programming

(use-package zig-mode
  :ensure t
;;; Uncomment this if you want Eglot to start automatically. I don't
;;; recommend it, but that's just me.
  ;; :hook ((zig-mode . eglot-ensure))
  )

;;; Configuration for Clojure programming
(use-package clojure-mode
  :ensure t)

;;; `clojure-ts-mode' is not stable enough right now. In particular,
;;; it clashes with `paredit-mode' sometimes, leading to Paredit
;;; throwing unbalanced-expression errorsand being unusable. So
;;; keeping this disabled and experimenting with how to fix it, for
;;; them moment.

(defvar enable-clojure-ts-mode nil)

(when (and (treesit-available-p) enable-clojure-ts-mode)
  (use-package clojure-ts-mode
    :ensure t))

;;;; Cider provides tooling over nREPL for Clojure programming
(use-package cider
  :ensure t
  :after (:any clojure-mode clojure-ts-mode)
  :config
  (defun cider-repl-prompt-on-newline (ns)
    "Return a prompt string with newline.
NS is the namespace information passed into the function by cider."
    (concat ns ">\n"))
  (setq cider-repl-prompt-function #'cider-repl-prompt-on-newline))

;;;; clj-refactor enables smart refactoring of Clojure code
(use-package clj-refactor
  :ensure t
  :after (:any clojure-mode clojure-ts-mode)
  :hook
  ((clojure-mode . clj-refactor-mode)
   (clojure-ts-mode . clj-refactor-mode))
  :config
  (cljr-add-keybindings-with-prefix "C-c m")
  ;; Don't magically add stuff to the namespace requires form (because
  ;; for big projects this operation is slow) it's easier to do this
  ;; by hand (=add-missing= operation) after you've typed out what you
  ;; wanted to.
  (setq cljr-magic-requires nil))

;;;; flymake-kondor integrates flymake with clj-kondo, so that we get
;;;; great linting without needing to start a REPL or LSP server.
(use-package flymake-kondor
  :ensure t
  :after (:any clojure-mode clojure-ts-mode flymake)
  :ensure-system-package (clj-kondo)
  :hook
  ((clojure-mode . flymake-kondor-setup)
   (clojure-ts-mode . flymake-kondor-setup)))

;;;; clojure-snippets are handy yasnippets for fast coding
(use-package clojure-snippets
  :ensure t
  :after clojure-mode)

;;;; jet is an external tool to convert between json, transit and edn
(use-package jet
  :ensure t
  :config
  (defun json->edn ()
    "Convert the selected region, or entire file, from JSON to EDN."
    (interactive)
    (let ((b (if mark-active (region-beginning) (point-min)))
          (e (if mark-active (region-end) (point-max)))
          (jet (when (executable-find "jet")
                 "jet --pretty --keywordize keyword --from json --to edn")))
      (if jet
          (let ((p (point)))
            (shell-command-on-region b e jet (current-buffer) t)
            (goto-char p))
        (user-error "Could not find jet installed")))))

;;; Settings for Interaction mode for Emacs-Lisp
(use-package ielm
  :ensure nil
  :bind
  ( :map ielm-map
    ("C-j" . newline-and-indent)))

(provide 'nebkor-langs)