;;; ra.el --- Rust analyzer emacs bindings -*- lexical-binding: t; -*-
;;; Commentary:
;;; Small utilities for interacting with Rust analyzer.
;;; Run
;;;   cargo install --git https://github.com/matklad/rust-analyzer/ ra_cli
;;; to install the analyzer binary. Then copy-paste the bellow code to
;;; your `.init.el` and use `ra-extend-selection` and
;;; `ra-shrink-selection` functions.
;;; Code:


(defvar ra--selections-cache '(0 0 ()))
(defun ra--cache-tick ()
  "Get buffer modification count for cache."
  (nth 0 ra--selections-cache))
(defun ra--cache-sel ()
  "Get current selection for cache."
  (nth 1 ra--selections-cache))
(defun ra--cache-nth-sel (n)
  "Get Nth selection."
  (nth n (nth 2 ra--selections-cache)))
(defun ra--cache-set-nth-sel (n)
  "Get Nth selection."
  (setf (nth 1 ra--selections-cache) n)
  (nth n (nth 2 ra--selections-cache)))


(defun ra-extend-selection ()
  "Extend START END region to contain the encompassing syntactic construct."
  (interactive)
  (let* ((p (point))
         (m (or (and mark-active (mark)) p))
         (start (min p m))
         (end (max p m)))
    (ra--extend-selection start end)))


(defun ra-shrink-selection (start end)
  "Shrink START END region to contain previous selection."
  (interactive "r")
  (ra--freshen-cache start end)
  (let ((sel-id (ra--cache-sel)))
    (if (not (= 0 sel-id))
        (let* ((r (ra--cache-set-nth-sel (- sel-id 1))))
          (push-mark (nth 0 r) t t)
          (goto-char (nth 1 r))
          (setq deactivate-mark nil)))))

; Add this to setup keybinding
; (require 'rust-mode)
; (define-key rust-mode-map (kbd "C-w") 'ra-extend-selection)
; (define-key rust-mode-map (kbd "C-S-w") 'ra-shrink-selection)



(defun ra--extend-selection (start end)
  "Extend START END region to contain the encompassing syntactic construct."
  (ra--freshen-cache start end)
  (let* ((next-sel-idx (+ 1 (ra--cache-sel)))
         (r (ra--cache-set-nth-sel next-sel-idx)))
    (push-mark (nth 0 r) t t)
    (goto-char (nth 1 r))
    (setq deactivate-mark nil)))

(defun ra--selections (start end)
  "Get list of selections for START END from Rust analyzer."
  (read (with-output-to-string
          (call-process-region
           (point-min) (point-max)
           "ra_cli" nil standard-output nil
           "extend-selection"
           (number-to-string start)
           (number-to-string end)))))

(defun ra--freshen-cache (start end)
  "Make selection cache up-to-date for current buffer state and START END."
  (if (not (and
            (= (buffer-modified-tick) (ra--cache-tick))
            (equal `(,start ,end) (ra--cache-nth-sel (ra--cache-sel)))))
      (ra--set-cache start end)))

(defun ra--set-cache (start end)
  "Set selections cache for current buffer state and START END."
  (setq ra--selections-cache `(,(buffer-modified-tick) 0 ,(ra--selections start end))))


(require 'eglot)
(require 'ivy)
(require 'counsel)


(defun workspace-symbols ()
  (interactive)
  (let ((buf (current-buffer)))
    (ivy-read "Symbol name: "
              (lambda (str)
                (with-current-buffer buf
                  (let ((backend (eglot-xref-backend)))
                    (mapcar 
                     (lambda (xref)
                       (let ((loc (xref-item-location xref)))
                         (propertize
                          (concat
                           (when (xref-file-location-p loc)
                             (with-slots (file line column) loc
                               (format "%s:%s:%s:" 
                                       (propertize (file-relative-name file)
                                                   'face 'compilation-info)
                                       (propertize (format "%s" line)
                                                   'face 'compilation-line
                                                   )
                                       column)))
                           (xref-item-summary xref))
                          'xref xref)))
                     (xref-backend-apropos backend str))
                    )))
              :dynamic-collection t
              :action (lambda (item)
                        (xref--pop-to-location (get-text-property 0 'xref item))))))

(add-to-list 'eglot-server-programs '(rust-mode . ("ra_lsp_server")))

; (require 'rust-mode)
; (define-key rust-mode-map (kbd "C-n") 'workspace-symbols)

(define-key)
(provide 'ra)
;;; ra.el ends here