(recentf-mode t)

(use-package golden-ratio
  :ensure t
  :init 
  (setq golden-ratio-auto-scale t)
  (golden-ratio-mode 1))

(defun kylekrein/duplicate-line()
  "Duplicate current line and move cursor to it"
  (interactive)
  (let ((column (- (point) (point-at-bol)))
        (line (let ((s (thing-at-point 'line t)))
            (if s (string-remove-suffix "\n" s) ""))))
       (move-end-of-line 1)
       (newline)
       (insert line)
       (move-beginning-of-line 1)
       (forward-char column)))

(global-set-key [remap list-buffers] 'ibuffer)
(global-set-key (kbd "M-o") 'other-window)
(global-set-key (kbd "C-c o t") 'vterm-toggle)
(global-set-key (kbd "C-c o a") 'org-agenda)
(global-set-key (kbd "C-c o m") 'magit)

(global-set-key (kbd "C-.") 'kylekrein/duplicate-line)
;;(windmove-default-keybindings) ;; move between windows with S-<left>, S-<right>, S-<up>, S-<down>

(defun split-and-follow-horizontally ()
    (interactive)
    (split-window-below)
    (balance-windows))

  (defun split-and-follow-vertically ()
    (interactive)
    (split-window-right)
    (balance-windows))

  (use-package emacs
    :bind (:map ctl-x-map
    ("2" . split-and-follow-horizontally)
    ("3" . split-and-follow-vertically))
    :custom
    (info-lookup-other-window-flag t)
    (help-window-select t "Switch to help buffers automatically"))
;; Auto-select new Info buffer window when it’s created.
  (advice-add 'info-lookup :after
          (lambda (&rest _)
        (when-let (window (get-buffer-window "*info*"))
          (select-window window))))
  
  ;; Auto-select new window after splitting. Splitting commands almost
  ;;,all use `split-window’, so advice the function for auto selection.
  (advice-add 'split-window :after
          (lambda (&rest _) (select-window (get-lru-window))))

(defun git-package (url)
  (let* ((pkg-name (file-name-base (directory-file-name url)))
         (pkg-sym (intern pkg-name)))
    (eval
     `(use-package ,pkg-sym
        :vc (:url ,url :rev :newest)
        :ensure nil))))

;;(setq select-enable-primary t)
(defun kylekrein/copy-to-clipboard (text)
  (with-temp-buffer
    (insert text)
    (copy-region-as-kill (point-min) (point-max))
    (clipboard-kill-region (point-min) (point-max))))

(defun kylekrein/detect-wsl ()
  (and (eq system-type 'gnu/linux)
       (file-exists-p "/proc/sys/fs/binfmt_misc/WSLInterop")))

(use-package alert
   :ensure t
   )

(use-package alert-toast :ensure t :after alert)

(setq alert-default-style
  	(cond
  	 ((kylekrein/detect-wsl) 'toast)
  	 (t 'libnotify)))

(unless (file-exists-p "~/.cache/emacs/tildafiles")
  (make-directory "~/.cache/emacs/tildafiles"))
(setq backup-directory-alist '((".*" . "~/.cache/emacs/tildafiles")))

(use-package diminish :ensure t)

(defun kylekrein/copy-emoji-to-clipboard()
  (interactive)
  (let ((emoji (emoji--read-emoji)))
    (when emoji
      (kylekrein/copy-to-clipboard emoji)
      (message "Copied: %s" (current-kill 0 t)))))

(setq ediff-split-window-function 'split-window-horizontally)
;;(setq ediff-window-setup-function 'ediff-setup-windows-plain)

(set-face-attribute 'variable-pitch nil
                    :family "DejaVu Sans";;"ET Bembo"
                    :height 160
  		    :weight 'normal)
(set-face-attribute 'default nil
                    :family "Iosevka"
                    :height 150
  		    :weight 'medium)

(set-face-attribute 'fixed-pitch nil
                    :family "Iosevka"
                    :height 150
  		    :weight 'medium)

  ;; Makes commented text and keywords italics.
  ;; This is working in emacsclient but not emacs.
  ;; Your font must have an italic face available.
  (set-face-attribute 'font-lock-comment-face nil
  		    :slant 'italic)
  (set-face-attribute 'font-lock-keyword-face nil
  		    :slant 'italic)

  ;; This sets the default font on all graphical frames created after restarting Emacs.
  ;; Does the same thing as 'set-face-attribute default' above, but emacsclient fonts
  ;; are not right unless I also add this method of setting the default font.
  ;;(add-to-list 'default-frame-alist '(font . "Iosevka Mono-20"))

  ;; Uncomment the following line if line spacing needs adjusting.
  (setq-default line-spacing 0.12)

  
  (add-hook 'text-mode-hook #'variable-pitch-mode)
  ;; Enable variable-pitch-mode in Org
  (add-hook 'org-mode-hook #'variable-pitch-mode)

  ;; Ensure code blocks, tables, and special elements remain fixed-pitch
  (custom-set-faces
   ;; Keep code blocks, src, and tables fixed-pitch (Iosevka)
   '(org-block ((t (:inherit fixed-pitch))))
   '(org-block-begin-line ((t (:inherit fixed-pitch))))
   '(org-block-end-line ((t (:inherit fixed-pitch))))
   '(org-table ((t (:inherit fixed-pitch))))
   '(org-code ((t (:inherit fixed-pitch))))
   '(org-verbatim ((t (:inherit fixed-pitch))))
   '(org-meta-line ((t (:inherit fixed-pitch))))
   '(org-checkbox ((t (:inherit fixed-pitch))))
   )

(electric-indent-mode -1)    ;; Turn off the weird indenting that Emacs does by default.
(electric-pair-mode 1)       ;; Turns on automatic parens pairing
;; The following prevents <> from auto-pairing when electric-pair-mode is on.
;; Otherwise, org-tempo is broken when you try to <s TAB...
(add-hook 'org-mode-hook (lambda ()
           (setq-local electric-pair-inhibit-predicate
                   `(lambda (c)
                  (if (char-equal c ?<) t (,electric-pair-inhibit-predicate c))))))
(global-auto-revert-mode t)  ;; Automatically show changes if the file has changed
;;(global-display-line-numbers-mode 1) ;; Display line numbers
(add-hook 'prog-mode-hook #'display-line-numbers-mode)
(global-visual-line-mode t)  ;; Enable truncated lines
(winner-mode t) ;; Undo (C-c <left>) and Redo (C-c <right>) for windows
(setq sentence-end-double-space t) ;; Single space doesn't end a sentence

(save-place-mode t) ;; Restore cursor place in file

(use-package nov :ensure t)
(add-to-list 'auto-mode-alist '("\\.epub\\'" . nov-mode))

(use-package magit
  :ensure t)

(use-package doom-modeline
  :ensure t
  :init (doom-modeline-mode 1)
  :config
  (setq doom-modeline-height 35      ;; sets modeline height
        doom-modeline-bar-width 5    ;; sets right bar width
        doom-modeline-persp-name nil   ;; adds perspective name to modeline
	doom-modeline-time t         ;; shows time
        doom-modeline-persp-icon nil)) ;; adds folder icon next to persp name

(use-package rainbow-delimiters
  :ensure t
  :hook ((emacs-lisp-mode . rainbow-delimiters-mode)
         (clojure-mode . rainbow-delimiters-mode)))

(setq calendar-date-style "european")
(setq calendar-week-start-day 1)

;;Line truncation
(defun kylekrein/truncate-calendar-hook ()
  "Turn line truncation on."
  (toggle-truncate-lines 1))

(add-hook 'calendar-mode-hook #'kylekrein/truncate-calendar-hook)

;;Current month is the first
(add-hook 'calendar-initial-window-hook #'calendar-scroll-left)

;;Calendar in org agenda
(setq org-agenda-include-diary t)

(defadvice revert-buffer (after refresh-org-agenda-on-revert activate)
(if (member (buffer-file-name (current-buffer)) org-agenda-files)
    (org-agenda-redo-all t)))

(org-babel-do-load-languages
 'org-babel-load-languages
 '((shell . t)
   (C . t)
   (python . t)))

(use-package org
  :config
  (org-link-set-parameters
   "copy"
   :follow (lambda (link) (kill-new link))
   :export (lambda (_ desc &rest _) desc)))

;;;; Better Looking Bullets
(add-hook 'org-mode-hook 'org-indent-mode)
(use-package org-bullets :ensure t)
(add-hook 'org-mode-hook (lambda () (org-bullets-mode 1)))

(custom-set-faces
 '(org-level-1 ((t (:inherit outline-1 :height 1.45))))
 '(org-level-2 ((t (:inherit outline-2 :height 1.35))))
 '(org-level-3 ((t (:inherit outline-3 :height 1.30))))
 '(org-level-4 ((t (:inherit outline-4 :height 1.25))))
 '(org-level-5 ((t (:inherit outline-5 :height 1.20))))
 '(org-level-6 ((t (:inherit outline-5 :height 1.15))))
 '(org-level-7 ((t (:inherit outline-5 :height 1.10)))))

(require 'org-tempo)

(defun org-update-table-by-name (name)
      "Update the named table."
      (org-table-map-tables
      (lambda ()
      (let ((table_name (org-element-property :name (org-element-at-point))))
      (if (and table_name (string-match-p name table_name))
             (org-table-recalculate))))))

(defun org-update-and-realign-tables ()
    (interactive)
    (org-map-dblocks 'org-update-dblock)
    (redisplay)
    (org-table-map-tables 'org-table-recalculate)
    (org-table-map-tables 'org-table-align))

(global-set-key (kbd "C-c n u") 'org-update-and-realign-tables)

(use-package org-transclusion :ensure t)
(custom-set-faces
 '(org-transclusion-fringe
   ((t
     (:background "green"))))
 '(org-transclusion-source-fringe
   ((t
     (:background "blue")))))

(use-package org-roam
  :ensure t
  :after org
  :init
  (setq org-roam-v2-ack t)
  (when (file-exists-p "~/Документы/org")
    (setq org-roam-directory "~/Документы/org"))
  (when (file-exists-p "~/Documents/org")
    (setq org-roam-directory "~/Documents/org"))
  :custom
  (org-roam-completion-everywhere t)
  (org-roam-capture-templates
    '(("d" "default" plain
       "%?"
       :if-new (file+head "%<%Y%m%d%H%M%S>-${slug}.org" "#+title: ${title}\n#+category: ${title}\n")
       :unnarrowed t)
       ("p" "project" plain "* Goals\n\n%?\n\n* Tasks\n\n** TODO Add initial tasks\n\n* Dates\n\n"
       :if-new (file+head "%<%Y%m%d%H%M%S>-${slug}.org" "#+title: ${title}\n#+category: ${title}\n#+filetags: Project")
       :unnarrowed t))
	)
  (org-roam-dailies-capture-templates
   '(("d" "default" entry "* %<%I:%M %p>: %?"
     :if-new (file+head "%<%Y-%m-%d>.org" "#+title: %<%Y-%m-%d>\n"))))
  :bind (("C-c n l" . org-roam-buffer-toggle)
         ("C-c n f" . org-roam-node-find)
         ("C-c n i" . org-roam-node-insert)
	 :map org-mode-map
	 ("C-M-i"    . completion-at-point))
  :bind-keymap
  ("C-c n d" . org-roam-dailies-map)
  :config
  (require 'org-roam-dailies) ;; Ensure the keymap is available
  (org-roam-db-autosync-mode)
  (org-roam-setup))


(defun kylekrein/org-roam-capture-inbox ()
  (interactive)
  (org-roam-capture- :node (org-roam-node-create)
                     :templates '(("i" "inbox" plain "* %?"
                                   :if-new (file+head "Inbox.org" "#+title: Inbox\n#+category: Inbox\n#+filetags: Project")))))
(global-set-key (kbd "C-c n b") #'kylekrein/org-roam-capture-inbox)

(defun org-agenda-refresh ()
  "Refresh all `org-agenda' buffers."
  (dolist (buffer (buffer-list))
    (with-current-buffer buffer
      (when (derived-mode-p 'org-agenda-mode)
        (org-agenda-maybe-redo)))))

(defadvice org-schedule (after refresh-agenda activate)
  "Refresh org-agenda."
  (org-agenda-refresh))

(require 'org-roam-node)
(defun kylekrein/org-roam-filter-by-tag (tag-name)
  (lambda (node)
    (member tag-name (org-roam-node-tags node))))

(defun kylekrein/org-roam-list-notes-by-tag (tag-name)
  (mapcar #'org-roam-node-file
          (seq-filter
           (kylekrein/org-roam-filter-by-tag tag-name)
           (org-roam-node-list))))

(defun kylekrein/org-roam-refresh-agenda-list ()
  (interactive)
  (setq org-agenda-files (kylekrein/org-roam-list-notes-by-tag "Project")))


(setq org-agenda-files nil
      org-roam-node-display-template "${title} ${tags}"
      org-agenda-start-on-weekday 1 ;; Week starts on Monday instead of Sunday
      )
;; Build the agenda list the first time for the session
(kylekrein/org-roam-refresh-agenda-list)

;; Log time a task was set to DONE.
(setq org-log-done (quote time))

;; Don't log the time a task was rescheduled or redeadlined.
(setq org-log-redeadline nil)
(setq org-log-reschedule nil)

;; Prefer rescheduling to future dates and times
(setq org-read-date-prefer-future 'time)

(use-package emacs
    :config
    ;; start warning 60 minutes before the appointment
    (setq appt-message-warning-time 60)

    ;; warn me every 5 minutes
    (setq appt-display-interval 15)
    (setq appt-disp-window-function
          (lambda (remaining new-time msg)
              (alert (format "In %s minutes" remaining)
               :title msg
               :severity 'moderate
  	     :category 'org-agenda
  	     :id (intern msg))))

    (advice-add 'appt-check
                :before
                (lambda (&rest args)
                  (org-agenda-to-appt t)))

    (appt-activate t))
(setq alert-fade-time 50)

(use-package org-upcoming-modeline
  :ensure t
  :after org
  :config
  (setq appt-display-mode-line nil)
  (org-upcoming-modeline-mode))

(use-package rainbow-mode
  :ensure t
  :hook 
  ((org-mode prog-mode) . rainbow-mode))

(use-package gptel
      :ensure t
      :bind 
      ("C-c a c" . gptel)
      ("C-c a r" . gptel-rewrite)
      ("C-c a s" . gptel-send)
      ("C-c a f" . gptel-add-file))
  (setq
   gptel-model 'llama3.1
   gptel-backend (gptel-make-ollama "Ollama"
                   :host "localhost:11434"
                   :stream t
                   :models '(llama3.1 qwen2.5-coder:7b))
   gptel-track-media t
   gptel-default-mode 'org-mode)
(add-hook 'gptel-post-stream-hook 'gptel-auto-scroll)
(add-hook 'gptel-post-response-functions 'gptel-end-of-response)

(use-package eshell-syntax-highlighting
  :ensure t
  :after esh-mode
  :config
  (eshell-syntax-highlighting-global-mode +1))

(use-package vterm
    :ensure t
)

(use-package vterm-toggle
  :ensure t
  :after vterm
  :config
  (setq vterm-toggle-fullscreen-p nil)
  (setq vterm-toggle-scope 'project)
  (add-to-list 'display-buffer-alist
               '((lambda (buffer-or-name _)
                     (let ((buffer (get-buffer buffer-or-name)))
                       (with-current-buffer buffer
                         (or (equal major-mode 'vterm-mode)
                             (string-prefix-p vterm-buffer-name (buffer-name buffer))))))
                  (display-buffer-reuse-window display-buffer-at-bottom)
                  ;;(display-buffer-reuse-window display-buffer-in-direction)
                  ;;display-buffer-in-direction/direction/dedicated is added in emacs27
                  ;;(direction . bottom)
                  ;;(dedicated . t) ;dedicated is supported in emacs27
                  (reusable-frames . visible)
                  (window-height . 0.3))))

(git-package "https://github.com/darcamo/cmake-integration.git")
(use-package cmake-integration
  :commands (cmake-integration-transient)
  :custom
  (cmake-integration-generator "Ninja") 
  (cmake-integration-use-separated-compilation-buffer-for-each-target t))

(defun is-cmake-project? ()
  "Determine if the current directory is a CMake project."
  (interactive)
  (if-let* ((project (project-current))
            (project-root (project-root project))
            (cmakelist-path (expand-file-name "CMakeLists.txt" project-root)))
      (file-exists-p cmakelist-path)))


(defun cmake-integration-keybindings-mode-turn-on-in-cmake-projects ()
  "Turn on `cmake-integration-keybindings-mode' in CMake projects."
  (when (is-cmake-project?)
    (cmake-integration-keybindings-mode 1)))

      
(define-minor-mode cmake-integration-keybindings-mode
  "A minor-mode for adding keybindings to compile C++ code using cmake-integration package."
  nil
  "cmake"
  '(
    ([f5] . cmake-integration-transient)                         ;; Open main transient menu
    ([M-f9] . cmake-integration-save-and-compile)                ;; Ask for the target name and compile it
    ([f9] . cmake-integration-save-and-compile-last-target)      ;; Recompile the last target
    ([C-f9] . cmake-integration-run-ctest)                       ;; Run CTest
    ([f7] . cmake-integration-run-last-target)                  ;; Run the target (using any previously set command line parameters)
    ([S-f7] . kill-compilation)
    ([C-f7] . cmake-integration-debug-last-target)              ;; Debug the target (using any previously set command line parameters)
    ([M-f7] . cmake-integration-run-last-target-with-arguments) ;; Ask for command line parameters to run the target
    ([M-f8] . cmake-integration-select-configure-preset)         ;; Ask for a preset name and call CMake to configure the project
    ([f8] . cmake-integration-cmake-reconfigure)                 ;; Call CMake to configure the project using the last chosen preset
    )
  )

(define-globalized-minor-mode global-cmake-integration-keybindings-mode
  cmake-integration-keybindings-mode cmake-integration-keybindings-mode-turn-on-in-cmake-projects)


(global-cmake-integration-keybindings-mode)

;; Extend project.el to recognize local projects based on a .project file
(cl-defmethod project-root ((project (head local)))
  (cdr project))

(defun mu--project-files-in-directory (dir)
  "Use `fd' to list files in DIR."
  (let* ((default-directory dir)
         (localdir (file-local-name (expand-file-name dir)))
         (command (format "fd -t f -0 . %s" localdir)))
    (project--remote-file-names
     (sort (split-string (shell-command-to-string command) "\0" t)
           #'string<))))

(cl-defmethod project-files ((project (head local)) &optional dirs)
  "Override `project-files' to use `fd' in local projects."
  (mapcan #'mu--project-files-in-directory
          (or dirs (list (project-root project)))))

(defun mu-project-try-local (dir)
  "Determine if DIR is a non-Git project.
DIR must include a .project file to be considered a project."
  (let ((root (locate-dominating-file dir ".project")))
    (and root (cons 'local root))))

(use-package project
  :defer t
  :config
  (add-to-list 'project-find-functions 'mu-project-try-local)
  )

(use-package direnv
  :ensure t
  :config
  (direnv-mode))

(defun kylekrein/project-enable-direnv-flake ()
  "Add `use flake` to .envrc and run `direnv allow` in the project root."
  (interactive)
  (let* ((project (project-current t))
         (root (project-root project))
         (envrc-path (expand-file-name ".envrc" root)))
    (unless (file-exists-p envrc-path)
      (with-temp-buffer
        (insert "use flake\n")
        (write-file envrc-path)))
    (unless (string-match-p "use flake" (with-temp-buffer
                                          (insert-file-contents envrc-path)
                                          (buffer-string)))
      (with-temp-buffer
        (insert-file-contents envrc-path)
        (goto-char (point-max))
        (insert "\nuse flake\n")
        (write-file envrc-path)))
    (let ((default-directory root))
      (direnv-allow))
    (message "Added 'use flake' to .envrc and ran direnv allow in %s" root)))

(use-package glsl-mode
    :ensure t)

(add-to-list 'auto-mode-alist '("\\.rml\\'" . html-ts-mode))
(add-to-list 'auto-mode-alist '("\\.rcss\\'" . css-ts-mode))

(add-to-list 'auto-mode-alist '("CMakeLists\\.txt\\'" . cmake-ts-mode))
(add-to-list 'auto-mode-alist '("\\.cmake\\'" . cmake-ts-mode))

(use-package zig-mode
  :ensure t)

(autoload 'zig-mode "zig-mode" nil t)
(add-to-list 'auto-mode-alist '("\\.\\(zig\\|zon\\)\\'" . zig-mode))

(use-package treesit-auto
  :ensure t
  :demand t
  :config
  (global-treesit-auto-mode))

(use-package eldoc
    :init
    (global-eldoc-mode))

  (use-package eglot
    :hook (prog-mode . eglot-ensure)
    ;;:init
    ;;(setq eglot-stay-out-of '(flymake))
    :bind (:map
           eglot-mode-map
           ("C-c c a" . eglot-code-actions)
           ;;("C-c c o" . eglot-code-actions-organize-imports)
           ("C-c c r" . eglot-rename)
           ("C-c c f" . eglot-format)))

  (use-package flymake
    :hook (prog-mode . flymake-mode)
    :bind (:map flymake-mode-map
                ("C-c ! n" . flymake-goto-next-error)
                ("C-c ! p" . flymake-goto-prev-error)
                ("C-c ! l" . flymake-show-buffer-diagnostics)))

(with-eval-after-load 'eglot
    (add-to-list 'eglot-server-programs
        '((c-ts-mode c++-ts-mode)
             . ("clangd"
		"-j=8"
                     "--log=error"
                     "--malloc-trim"
                     "--background-index"
                     "--clang-tidy"
                     "--cross-file-rename"
                     "--completion-style=detailed"
                     "--pch-storage=memory"
                     "--header-insertion=never"
                     "--header-insertion-decorators=0")))
    (add-hook 'c-ts-mode-hook #'eglot-ensure)
    (add-hook 'c++-ts-mode-hook #'eglot-ensure))

(with-eval-after-load 'eglot
  (add-to-list 'eglot-server-programs
    '(zig-mode . (
      ;; Use `zls` if it is in your PATH
      "zls"
      ;; There are two ways to set config options:
      ;;   - edit your `zls.json` that applies to any editor that uses ZLS
      ;;   - set in-editor config options with the `initializationOptions` field below.
      ;;
      ;; Further information on how to configure ZLS:
      ;; https://zigtools.org/zls/configure/
      :initializationOptions 
        (;; Whether to enable build-on-save diagnostics
         ;;
         ;; Further information about build-on save:
         ;; https://zigtools.org/zls/guides/build-on-save/
         ;;enable_build_on_save t
         
         ;; omit the following line if `zig` is in your PATH
         ;:zig_exe_path "/path/to/zig_executable"
         ))))
    (add-hook 'zig-mode-hook #'eglot-ensure))

(with-eval-after-load 'eglot
    (add-to-list 'eglot-server-programs
        '(csharp-ts-mode
             . ("csharp-ls")))
    (add-hook 'csharp-ts-mode-hook #'eglot-ensure))

(with-eval-after-load 'eglot
      (add-to-list 'eglot-server-programs
          '(python-ts-mode
               . ("ty")))
      (add-hook 'python-ts-mode-hook #'eglot-ensure))

(use-package nerd-icons
  :ensure t
  ;; :custom
  ;; The Nerd Font you want to use in GUI
  ;; "Symbols Nerd Font Mono" is the default and is recommended
  ;; but you can use any other Nerd Font if you want
  ;; (nerd-icons-font-family "Symbols Nerd Font Mono")
  )

(use-package nerd-icons-completion
  :ensure t
  :after marginalia
  :config
  (nerd-icons-completion-mode)
  (add-hook 'marginalia-mode-hook #'nerd-icons-completion-marginalia-setup))

(use-package persist-state
  :ensure t
  :after server
  :if server-process
  :config
  (persist-state-mode))

(use-package multiple-cursors
:ensure t
:bind (
("C-S-c C-S-c" . mc/edit-lines)
("C->" . mc/mark-next-like-this)
("C-<" . mc/mark-previous-like-this)
("C-C C-<" . mc/mark-all-like-this)
("C-\"" . mc/skip-to-next-like-this)
("C-:" . mc/skip-to-previous-like-this)
("C-C C->" . mc/mark-more-like-this-extended)
("C-S-<mouse-1>" . mc/add-cursor-on-click)
))

(require 'windmove)

;;;###autoload
(defun buf-move-up ()
  "Swap the current buffer and the buffer above the split.
If there is no split, ie now window above the current one, an
error is signaled."
;;  "Switches between the current buffer, and the buffer above the
;;  split, if possible."
  (interactive)
  (let* ((other-win (windmove-find-other-window 'up))
	 (buf-this-buf (window-buffer (selected-window))))
    (if (null other-win)
        (error "No window above this one")
      ;; swap top with this one
      (set-window-buffer (selected-window) (window-buffer other-win))
      ;; move this one to top
      (set-window-buffer other-win buf-this-buf)
      (select-window other-win))))

;;;###autoload
(defun buf-move-down ()
"Swap the current buffer and the buffer under the split.
If there is no split, ie now window under the current one, an
error is signaled."
  (interactive)
  (let* ((other-win (windmove-find-other-window 'down))
	 (buf-this-buf (window-buffer (selected-window))))
    (if (or (null other-win) 
            (string-match "^ \\*Minibuf" (buffer-name (window-buffer other-win))))
        (error "No window under this one")
      ;; swap top with this one
      (set-window-buffer (selected-window) (window-buffer other-win))
      ;; move this one to top
      (set-window-buffer other-win buf-this-buf)
      (select-window other-win))))

;;;###autoload
(defun buf-move-left ()
"Swap the current buffer and the buffer on the left of the split.
If there is no split, ie now window on the left of the current
one, an error is signaled."
  (interactive)
  (let* ((other-win (windmove-find-other-window 'left))
	 (buf-this-buf (window-buffer (selected-window))))
    (if (null other-win)
        (error "No left split")
      ;; swap top with this one
      (set-window-buffer (selected-window) (window-buffer other-win))
      ;; move this one to top
      (set-window-buffer other-win buf-this-buf)
      (select-window other-win))))

;;;###autoload
(defun buf-move-right ()
"Swap the current buffer and the buffer on the right of the split.
If there is no split, ie now window on the right of the current
one, an error is signaled."
  (interactive)
  (let* ((other-win (windmove-find-other-window 'right))
	 (buf-this-buf (window-buffer (selected-window))))
    (if (null other-win)
        (error "No right split")
      ;; swap top with this one
      (set-window-buffer (selected-window) (window-buffer other-win))
      ;; move this one to top
      (set-window-buffer other-win buf-this-buf)
      (select-window other-win))))

(use-package windmove
  :bind
  (("<C-up>" . windmove-up)
   ("<C-down>" . windmove-down)
   ("<C-left>" . windmove-left)
   ("<C-right>" . windmove-right) 
   ("<C-M-S-up>" . buf-move-up)
   ("<C-M-S-down>" . buf-move-down)
   ("<C-M-S-left>" . buf-move-left)
   ("<C-M-S-right>" . buf-move-right)))

(use-package corfu
  :ensure t
  ;; Optional customizations
  :custom
  (corfu-cycle t)                ;; Enable cycling for `corfu-next/previous'
  (corfu-auto t)
  (corfu-auto-prefix 2)
  (corfu-quit-at-boundary 'separator)
  (corfu-echo-documentation 0.25)
  (corfu-preselect-first nil)
  (corfu-popupinfo-delay '(1.0 . 0.3)) ;;default '(2.0 . 1.0)
  ;; (corfu-quit-no-match nil)      ;; Never quit, even if there is no match
  ;; (corfu-preview-current nil)    ;; Disable current candidate preview
  ;; (corfu-preselect 'prompt)      ;; Preselect the prompt
  ;; (corfu-on-exact-match nil)     ;; Configure handling of exact matches

  ;; Enable Corfu only for certain modes. See also `global-corfu-modes'.
  ;; :hook ((prog-mode . corfu-mode)
  ;;        (shell-mode . corfu-mode)
  ;;        (eshell-mode . corfu-mode))
  :bind (:map corfu-map
	("M-SPC" . corfu-insert-separator)
	("RET" . nil)
	("TAB" . corfu-next)
	([tab] . corfu-next)
	("SHIFT-TAB" . corfu-previous)
	([backtab] . corfu-previous)
	("S-<return>" . corfu-insert))

  ;; Recommended: Enable Corfu globally.  This is recommended since Dabbrev can
  ;; be used globally (M-/).  See also the customization variable
  ;; `global-corfu-modes' to exclude certain modes.
  :init
  (global-corfu-mode)
  (corfu-history-mode)
  (corfu-popupinfo-mode))

;; A few more useful configurations...
(use-package emacs
  :custom
  ;; TAB cycle if there are only few candidates
  ;; (completion-cycle-threshold 3)

  ;; Enable indentation+completion using the TAB key.
  ;; `completion-at-point' is often bound to M-TAB.
  (tab-always-indent 'complete)

  ;; Emacs 30 and newer: Disable Ispell completion function.
  ;; Try `cape-dict' as an alternative.
  (text-mode-ispell-word-completion nil)

  ;; Hide commands in M-x which do not apply to the current mode.  Corfu
  ;; commands are hidden, since they are not used via M-x. This setting is
  ;; useful beyond Corfu.
  (read-extended-command-predicate #'command-completion-default-include-p))

(use-package cape
  :ensure t
  :defer 10
  :init
(add-to-list 'completion-at-point-functions #'cape-file))

;; Enable vertico
  (use-package vertico
    :ensure t
    :custom
    ;; (vertico-scroll-margin 0) ;; Different scroll margin
    ;; (vertico-count 20) ;; Show more candidates
    ;; (vertico-resize t) ;; Grow and shrink the Vertico minibuffer
    (vertico-cycle t) ;; Enable cycling for `vertico-next/previous'
    :init
    (vertico-mode))

(vertico-mode t) ;; enable vertico for all buffers
  ;; Persist history over Emacs restarts. Vertico sorts by history position.
  (use-package savehist
    :init
    (savehist-mode))

  ;; A few more useful configurations...
  (use-package emacs
    :custom
    ;; Support opening new minibuffers from inside existing minibuffers.
    (enable-recursive-minibuffers t)
    ;; Hide commands in M-x which do not work in the current mode.  Vertico
    ;; commands are hidden in normal buffers. This setting is useful beyond
    ;; Vertico.
    (read-extended-command-predicate #'command-completion-default-include-p)
    :init
    ;; Add prompt indicator to `completing-read-multiple'.
    ;; We display [CRM<separator>], e.g., [CRM,] if the separator is a comma.
    (defun crm-indicator (args)
      (cons (format "[CRM%s] %s"
                    (replace-regexp-in-string
                     "\\`\\[.*?]\\*\\|\\[.*?]\\*\\'" ""
                     crm-separator)
                    (car args))
            (cdr args)))
    (advice-add #'completing-read-multiple :filter-args #'crm-indicator)

    ;; Do not allow the cursor in the minibuffer prompt
    (setq minibuffer-prompt-properties
          '(read-only t cursor-intangible t face minibuffer-prompt))
    (add-hook 'minibuffer-setup-hook #'cursor-intangible-mode))

;; Optionally use the `orderless' completion style.
(use-package orderless
  :ensure t
  :custom
  ;; Configure a custom style dispatcher (see the Consult wiki)
  ;; (orderless-style-dispatchers '(+orderless-consult-dispatch orderless-affix-dispatch))
  ;; (orderless-component-separator #'orderless-escapable-split-on-space)
  (completion-styles '(orderless flex basic partial-completion))

  (completion-category-defaults nil)
  (completion-category-overrides '((file (styles partial-completion)))))

;; Example configuration for Consult
  (use-package consult
    :ensure t
    ;; Replace bindings. Lazily loaded by `use-package'.
    :bind (;; C-c bindings in `mode-specific-map'
           ("C-c M-x" . consult-mode-command)
           ("C-c h" . consult-history)
           ("C-c k" . consult-kmacro)
           ("C-c m" . consult-man)
           ("C-c i" . consult-info)
           ([remap Info-search] . consult-info)
           ;; C-x bindings in `ctl-x-map'
           ("C-x M-:" . consult-complex-command)     ;; orig. repeat-complex-command
           ("C-x b" . consult-buffer)                ;; orig. switch-to-buffer
           ("C-x 4 b" . consult-buffer-other-window) ;; orig. switch-to-buffer-other-window
           ("C-x 5 b" . consult-buffer-other-frame)  ;; orig. switch-to-buffer-other-frame
           ("C-x t b" . consult-buffer-other-tab)    ;; orig. switch-to-buffer-other-tab
           ("C-x r b" . consult-bookmark)            ;; orig. bookmark-jump
           ("C-x p b" . consult-project-buffer)      ;; orig. project-switch-to-buffer
           ;; Custom M-# bindings for fast register access
           ("M-#" . consult-register-load)
           ("M-'" . consult-register-store)          ;; orig. abbrev-prefix-mark (unrelated)
           ("C-M-#" . consult-register)
           ;; Other custom bindings
           ("M-y" . consult-yank-pop)                ;; orig. yank-pop
           ;; M-g bindings in `goto-map'
           ("M-g e" . consult-compile-error)
           ("M-g f" . consult-flymake)               ;; Alternative: consult-flycheck
           ("M-g g" . consult-goto-line)             ;; orig. goto-line
           ("M-g M-g" . consult-goto-line)           ;; orig. goto-line
           ("M-g o" . consult-outline)               ;; Alternative: consult-org-heading
           ("M-g m" . consult-mark)
           ("M-g k" . consult-global-mark)
           ("M-g i" . consult-imenu)
           ("M-g I" . consult-imenu-multi)
           ;; M-s bindings in `search-map'
           ("M-s d" . consult-find)                  ;; Alternative: consult-fd
           ("M-s c" . consult-locate)
           ("M-s g" . consult-grep)
           ("M-s G" . consult-git-grep)
           ("M-s r" . consult-ripgrep)
           ("M-s l" . consult-line)
           ("M-s L" . consult-line-multi)
           ("M-s k" . consult-keep-lines)
           ("M-s u" . consult-focus-lines)
           ;; Isearch integration
           ("M-s e" . consult-isearch-history)
           :map isearch-mode-map
           ("M-e" . consult-isearch-history)         ;; orig. isearch-edit-string
           ("M-s e" . consult-isearch-history)       ;; orig. isearch-edit-string
           ("M-s l" . consult-line)                  ;; needed by consult-line to detect isearch
           ("M-s L" . consult-line-multi)            ;; needed by consult-line to detect isearch
           ;; Minibuffer history
           :map minibuffer-local-map
           ("M-s" . consult-history)                 ;; orig. next-matching-history-element
           ("M-r" . consult-history))                ;; orig. previous-matching-history-element

    ;; Enable automatic preview at point in the *Completions* buffer. This is
    ;; relevant when you use the default completion UI.
    :hook (completion-list-mode . consult-preview-at-point-mode)

    ;; The :init configuration is always executed (Not lazy)
    :init

    ;; Tweak the register preview for `consult-register-load',
    ;; `consult-register-store' and the built-in commands.  This improves the
    ;; register formatting, adds thin separator lines, register sorting and hides
    ;; the window mode line.
    (advice-add #'register-preview :override #'consult-register-window)
    (setq register-preview-delay 0.5)

    ;; Use Consult to select xref locations with preview
    (setq xref-show-xrefs-function #'consult-xref
          xref-show-definitions-function #'consult-xref)

    ;; Configure other variables and modes in the :config section,
    ;; after lazily loading the package.
    :config

    ;; Optionally configure preview. The default value
    ;; is 'any, such that any key triggers the preview.
    ;; (setq consult-preview-key 'any)
    ;; (setq consult-preview-key "M-.")
    ;; (setq consult-preview-key '("S-<down>" "S-<up>"))
    ;; For some commands and buffer sources it is useful to configure the
    ;; :preview-key on a per-command basis using the `consult-customize' macro.

    ;; Optionally configure the narrowing key.
    ;; Both < and C-+ work reasonably well.
    (setq consult-narrow-key "<") ;; "C-+"

    ;; Optionally make narrowing help available in the minibuffer.
    ;; You may want to use `embark-prefix-help-command' or which-key instead.
    ;; (keymap-set consult-narrow-map (concat consult-narrow-key " ?") #'consult-narrow-help)
  )
(require 'consult)
;;(setq read-file-name-function #'consult-find-file-with-preview)

(defun kylekrein/org-roam-ripgrep ()
  (interactive)
  (require 'consult)
  (require 'org-roam)
  (let ((consult-ripgrep-command "rg --null --ignore-case --type org --line-buffered --color=always --max-columns=500 --no-heading --line-number . -e ARG OPTS"))
  (consult-ripgrep org-roam-directory)))
(global-set-key (kbd "C-c n r") #'kylekrein/org-roam-ripgrep)

(load-library "org-element.el") ;; Fix for org-roam notes not creating
;;Previewing files in find-file
(defun consult-find-file-with-preview (prompt &optional dir default mustmatch initial pred)
  (interactive)
  (let ((default-directory (expand-file-name (or dir default-directory)))
        (minibuffer-completing-file-name t))
    (consult--read #'read-file-name-internal :state (consult--file-preview)
                   :prompt prompt
                   :initial initial
                   :require-match mustmatch
                   :predicate pred)))

;;Previewing files for project-find-file
(setq project-read-file-name-function #'consult-project-find-file-with-preview)

(defun consult-project-find-file-with-preview (prompt all-files &optional pred hist _mb)
  (let ((prompt (if (and all-files
                         (file-name-absolute-p (car all-files)))
                    prompt
                  ( concat prompt
                    ( format " in %s"
                      (consult--fast-abbreviate-file-name default-directory)))))
        (minibuffer-completing-file-name t))
    (consult--read (mapcar
                    (lambda (file)
                      (file-relative-name file))
                    all-files)
                   :state (consult--file-preview)
                   :prompt (concat prompt ": ")
                   :require-match t
                   :history hist
                   :category 'file
                   :predicate pred)))

;; Enable rich annotations using the Marginalia package
(use-package marginalia
  :ensure t
  ;; Bind `marginalia-cycle' locally in the minibuffer.  To make the binding
  ;; available in the *Completions* buffer, add it to the
  ;; `completion-list-mode-map'.
  :bind (:map minibuffer-local-map
         ("M-A" . marginalia-cycle))

  ;; The :init section is always executed.
  :init

  ;; Marginalia must be activated in the :init section of use-package such that
  ;; the mode gets enabled right away. Note that this forces loading the
  ;; package.
  (marginalia-mode))

(use-package doom-themes
  :ensure t
  :config
  ;; Global settings (defaults)
  (setq doom-themes-enable-bold t    ; if nil, bold is universally disabled
        doom-themes-enable-italic t) ; if nil, italics is universally disabled
  (load-theme 'doom-one t)

  ;; Enable flashing mode-line on errors
  (doom-themes-visual-bell-config)
  ;; Enable custom neotree theme (nerd-icons must be installed!)
  (doom-themes-neotree-config)
  ;; or for treemacs users
  (setq doom-themes-treemacs-theme "doom-atom") ; use "doom-colors" for less minimal icon theme
  (doom-themes-treemacs-config)
  ;; Corrects (and improves) org-mode's native fontification.
  (doom-themes-org-config))

(unless (kylekrein/detect-wsl)
  (add-to-list 'default-frame-alist '(alpha-background . 90))) ; For all new frames henceforth

(use-package sudo-edit
  :ensure t)

(use-package which-key
  :ensure t
  :init
    (which-key-mode 1)
  :config
  (setq which-key-side-window-location 'bottom
	  which-key-sort-order #'which-key-key-order-alpha
	  which-key-sort-uppercase-first nil
	  which-key-add-column-padding 1
	  which-key-max-display-columns nil
	  which-key-min-display-lines 6
	  which-key-side-window-slot -10
	  which-key-side-window-max-height 0.25
	  which-key-idle-delay 0.8
	  which-key-max-description-length 25
	  which-key-allow-imprecise-window-fit nil
	  which-key-separator " → " ))

(when (kylekrein/detect-wsl)
  (setq select-active-regions nil)
  (setq select-enable-clipboard 't)
  (setq select-enable-primary nil)
  (setq interprogram-cut-function #'gui-select-text)
)
