(require 'common-lsp)
(require 'util-tree-sitter)

(defun locate-rust-analyzer ()
  "Find rust-analyzer."
  (let ((rust-analyzer-paths (list (locate-rust-analyzer-rustup) (locate-rust-analyzer-ansible-built) (locate-rust-analyzer-in-path))))
    (let ((first-non-nil-path (seq-find (lambda (elt) elt) rust-analyzer-paths)))
      first-non-nil-path
      )
    )
  )

(defun locate-rust-analyzer-rustup ()
  "Find rust-analyzer through rustup."
  (run-command-in-directory nil "rustup" "which" "rust-analyzer")
  )

(defun locate-rust-analyzer-ansible-built ()
  "Find rust-analyzer where the ansible playbook built it."
  (let ((rust-analyzer-path "/opt/rust-analyzer/target/release/rust-analyzer"))
    (when (file-exists-p rust-analyzer-path)
      rust-analyzer-path
      )
    )
  )

(defun locate-rust-analyzer-in-path ()
  "Find rust-analyzer in $PATH."
  (executable-find "rust-analyzer")
  )

(use-package rust-ts-mode
  :pin manual
  :mode (
         ("\\.rs\\'" . rust-ts-mode)
         )
  :commands (rust-ts-mode)
  :hook (
         (rust-ts-mode . (lambda ()
                           (eglot-ensure)
                           ;; Disable on-type formatting which was incorrectly injecting parenthesis into my code.
                           (make-local-variable 'eglot-ignored-server-capabilities)
                           (add-to-list 'eglot-ignored-server-capabilities :documentOnTypeFormattingProvider)
                           ;; Configure initialization options
                           (let ((rust-analyzer-command (locate-rust-analyzer)))
                             (when rust-analyzer-command
                               ;; (add-to-list 'eglot-server-programs `(rust-ts-mode . (,rust-analyzer-command)))
                               (add-to-list 'eglot-server-programs `(rust-ts-mode . (,rust-analyzer-command :initializationOptions (:imports (:granularity (:enforce t :group "item")
                                                                                                                                                           :merge (:glob nil)
                                                                                                                                                           :prefix "self")
                                                                                                                                             ))))
                               )
                             )
                           (add-hook 'before-save-hook 'eglot-format-buffer nil 'local)
                           ))
         )
  :init
  (add-to-list 'major-mode-remap-alist '(rust-mode . rust-ts-mode))
  (add-to-list 'treesit-language-source-alist '(rust "https://github.com/tree-sitter/tree-sitter-rust"))
  (unless (treesit-ready-p 'rust) (treesit-install-language-grammar 'rust))
  :config
  ;; Add keybindings for interacting with Cargo
  (use-package cargo
    :hook (rust-ts-mode . cargo-minor-mode))
  )

(use-package toml-ts-mode
  :ensure nil
  :pin manual
  :mode (
         ("\\.toml\\'" . toml-ts-mode)
         )
  :commands (toml-ts-mode)
  :init
  (add-to-list 'treesit-language-source-alist '(toml "https://github.com/tree-sitter/tree-sitter-toml"))
  (unless (treesit-ready-p 'toml) (treesit-install-language-grammar 'toml))
  )

;; Set additional rust-analyzer settings:
;;
;; (add-to-list 'eglot-server-programs `(rust-ts-mode . (,rust-analyzer-command :initializationOptions (:cargo (:features "all")))))
;;
;; In addition to the above, directory-specific settings can be written to a .dir-locals.el with the contents:
;;
;; (
;;  (rust-ts-mode . ((eglot-workspace-configuration
;;                    . (:rust-analyzer (:cargo (:noDefaultFeatures t :features ["compare" "tracing"]))))
;;                   ))
;;  )


(provide 'lang-rust)