#+TITLE: luxicks Emacs Configuration
#+PROPERTY: header-args :results silent
This is my configuration for the emacs editor.

- Personal information (name, email) are stored in a separate ~personal.el~ file.
- Machine specific settings are stored in a separate ~custom.el~ file.
- Both files are loaded automatically.

* Set Up use-package
Packages provided by =straight.el= https://github.com/radian-software/straight.el
#+BEGIN_SRC emacs-lisp
  (defvar bootstrap-version)
  (let ((bootstrap-file
         (expand-file-name "straight/repos/straight.el/bootstrap.el" user-emacs-directory))
        (bootstrap-version 6))
    (unless (file-exists-p bootstrap-file)
      (with-current-buffer
          (url-retrieve-synchronously
           "https://raw.githubusercontent.com/radian-software/straight.el/develop/install.el"
           'silent 'inhibit-cookies)
        (goto-char (point-max))
        (eval-print-last-sexp)))
    (load bootstrap-file nil 'nomessage))

  (straight-use-package 'use-package)
  (setq straight-use-package-by-default t)

  (package-initialize)

  (straight-use-package 'bind-key)
#+end_src

* Theming
#+begin_src emacs-lisp
  (use-package modus-themes
    :straight t
    :config
    ;; Add all your customizations prior to loading the themes
    (setq modus-themes-italic-constructs t
          modus-themes-bold-constructs nil)

    ;; Maybe define some palette overrides, such as by using our presets
    (setq modus-themes-common-palette-overrides
          modus-themes-preset-overrides-intense)

    ;; Load the theme of your choice.
    (load-theme 'modus-operandi t)

    (define-key global-map (kbd "<f5>") #'modus-themes-toggle))
#+end_src

*Set up the default frame look*
#+begin_src emacs-lisp
  (setq default-frame-alist
        (append (list '(width  . 90) '(height . 50)
                      '(vertical-scroll-bars . nil)
                      '(internal-border-width . 5))))
#+end_src

* Fix Defaults
** Hide UI elements
Remove all those UI elements. They do not look good and waste space.
#+BEGIN_SRC emacs-lisp
  (tool-bar-mode -1)
  (menu-bar-mode -1)
  (scroll-bar-mode -1)
  (tooltip-mode -1)
  (fringe-mode -1)
#+END_SRC

** Disable file backups
Emacs sure loves to clutter directories with backup files.
#+BEGIN_SRC emacs-lisp
  (setq make-backup-files nil)
  (setq auto-save-default nil)
  (setq create-lockfiles nil)
#+END_SRC

** Setup dired
Configure dired to only use one buffer.
#+BEGIN_SRC emacs-lisp
  (eval-after-load "dired"
    '(progn
       (defadvice dired-advertised-find-file (around dired-subst-directory activate)
         "Replace current buffer if file is a directory."
         (interactive)
         (let* ((orig (current-buffer))
                ;; (filename (dired-get-filename))
                (filename (dired-get-filename t t))
                (bye-p (file-directory-p filename)))
           ad-do-it
           (when (and bye-p (not (string-match "[/\\\\]\\.$" filename)))
             (kill-buffer orig))))))

  (eval-after-load "dired"
    ;; don't remove `other-window', the caller expects it to be there
    '(defun dired-up-directory (&optional other-window)
       "Run Dired on parent directory of current directory."
       (interactive "P")
       (let* ((dir (dired-current-directory))
              (orig (current-buffer))
              (up (file-name-directory (directory-file-name dir))))
         (or (dired-goto-file (directory-file-name dir))
             ;; Only try dired-goto-subdir if buffer has more than one dir.
             (and (cdr dired-subdir-alist)
                  (dired-goto-subdir up))
             (progn
               (kill-buffer orig)
               (dired up)
               (dired-goto-file dir))))))
#+END_SRC

** Other Settings
#+begin_src emacs-lisp
  ;; The default encoding should be utf-8 everywhere
  (prefer-coding-system 'utf-8)

  ;; All "Yes or No" questions can be shortend to "y or n".
  (defalias 'yes-or-no-p 'y-or-n-p)

  ;; No more startup messages and screens
  (setq inhibit-startup-screen t)
  (setq initial-buffer-choice  nil)
  (defun display-startup-echo-area-message ()
    (message "Welcome Back!"))

  ;; Highlight matching braces
  (show-paren-mode t)

  ;; cua-mode. Like any other editor
  (cua-mode t)

  ;; Configure the cursor
  (setq-default
   cursor-type 'bar
   indent-tabs-mode nil
   cursor-in-non-selected-windows nil)
  (blink-cursor-mode 0)

  ;; Default column with
  (set-fill-column 95)

  ;; Start up in the home directory
  (setq default-directory "~/")

  ;; Make C-k always kill the whole line
  (setq kill-whole-line t)

  ;; Do not ding. Ever.
  (setq ring-bell-function 'ignore)

  ;; Dialogues always go in the modeline.
  (setq use-dialog-box nil)

  ;; Show tooltips on hover and not in the echo area.
  ;; Those are often cut of.
  (tooltip-mode)

  ;; Better line wraping
  (global-visual-line-mode 1)
#+end_src

** Keybindings
#+BEGIN_SRC emacs-lisp
  (bind-key "C-x k"      'kill-buffer-with-prejudice)
  (bind-key "C-x C-k"    'kill-buffer-and-window)
  (bind-key "M-p"        'switch-to-previous-buffer)
  (bind-key "M-i"        'delete-indentation)
  (bind-key "C-+"        'text-scale-increase)
  (bind-key "C--"        'text-scale-decrease)
  ;; buffer-list is not a good default
  (bind-key "C-x C-b"    'ibuffer)
  (bind-key "C-c n"      'display-line-numbers-mode)
  (global-set-key (kbd "<f12>") 'menu-bar-mode)
#+END_SRC

Unbind useless keys.
#+BEGIN_SRC emacs-lisp
  (unbind-key "C-<tab>") ;; prevent switching to tab mode randomly
  (unbind-key "C-h n")   ;; I have never wanted to see emacs news ever
  (unbind-key "C-h C-n") ;; why on earth is it bound to two keybindings??
  (unbind-key "C-x C-d") ;; list-directory is utterly useless given the existence of dired
  (unbind-key "C-x C-r") ;; as is find-file-read-only
#+END_SRC

* Useful Functions
** Edit This File
A simple funtion to open this file for quick editing.
#+BEGIN_SRC emacs-lisp
  (defun edit-config ()
    (interactive)
    (find-file "~/.emacs.d/README.org"))
#+END_SRC

** Reformating
Reindet the whole buffer with ~F1~
#+BEGIN_SRC emacs-lisp
  (defun lux/indent-buffer ()
    "Reindents the whole buffer"
    (interactive)
    (save-excursion
      (indent-region (point-min) (point-max) nil)))
  (global-set-key [f1] 'lux/indent-buffer)
#+END_SRC

** Window Splitting
These are functions for splitting windows and move the cursor over immediately.
#+BEGIN_SRC emacs-lisp
  (defun lux/split-right-and-enter ()
    "Split the window to the right and enter it."
    (interactive)
    (split-window-right)
    (other-window 1))
  (bind-key "M-3" 'lux/split-right-and-enter)

  (defun lux/split-below-and-enter ()
    "Split the window down and enter it."
    (interactive)
    (split-window-below)
    (other-window 1))
  (bind-key "M-2" 'lux/split-below-and-enter)
#+END_SRC

Rebind the default window controls to use "M-*" keys for ease-of-use
#+begin_src emacs-lisp
  (bind-key "M-1" 'delete-other-windows)
  (bind-key "C-M-1" 'delete-other-windows)
  (bind-key "M-0" 'delete-window)
#+end_src

** Quick buffer switching
#+BEGIN_SRC emacs-lisp
  (defun switch-to-previous-buffer ()
    "Switch to previously open buffer.Repeated invocations toggle between the two most recently open buffers."
    (interactive)
    (switch-to-buffer (other-buffer (current-buffer) 1)))
#+END_SRC

* Fonts
Set up the fonts to use.
#+begin_src emacs-lisp
  (set-face-attribute 'default nil :font "Fantasque Sans Mono-12")
  (set-face-attribute 'fixed-pitch nil :font "Fantasque Sans Mono-12")
  (set-face-attribute 'variable-pitch nil :font "Fantasque Sans Mono-12")


  (let* ((variable-tuple
          (cond ((x-list-fonts "Calibri")         '(:font "Calibri"))
                ((x-list-fonts "Source Sans Pro") '(:font "Source Sans Pro"))
                ((x-list-fonts "Lucida Grande")   '(:font "Lucida Grande"))
                ((x-list-fonts "Verdana")         '(:font "Verdana"))
                ((x-family-fonts "Sans Serif")    '(:family "Sans Serif"))
                (nil (warn "Cannot find a Sans Serif Font.  Install Source Sans Pro."))))
         (base-font-color     (face-foreground 'default nil 'default))
         (headline           `(:inherit default :weight bold :foreground ,base-font-color)))

    (custom-theme-set-faces
     'user
     `(org-level-8 ((t (,@headline ,@variable-tuple))))
     `(org-level-7 ((t (,@headline ,@variable-tuple))))
     `(org-level-6 ((t (,@headline ,@variable-tuple))))
     `(org-level-5 ((t (,@headline ,@variable-tuple))))
     `(org-level-4 ((t (,@headline ,@variable-tuple :height 1))))
     `(org-level-3 ((t (,@headline ,@variable-tuple :height 1))))
     `(org-level-2 ((t (,@headline ,@variable-tuple :height 1))))
     `(org-level-1 ((t (,@headline ,@variable-tuple :height 1))))
     `(org-document-title ((t (,@headline ,@variable-tuple :height 2.0 :underline nil))))))

  (custom-theme-set-faces
   'user
   '(variable-pitch ((t (:family "Calibri" :height 120 :weight thin))))
   '(fixed-pitch ((t ( :family "Fantasque Sans Mono" :height 120)))))
#+end_src

* Completion
** Ivy
Use Ivy to make minibuf promts better. Adds the ability to sort and filter.
#+BEGIN_SRC emacs-lisp
  (use-package ivy
    :straight t
    :diminish
    :init
    (ivy-mode 1)
    (unbind-key "S-SPC" ivy-minibuffer-map)
    (setq ivy-height 30
          ivy-use-virtual-buffers t
          ivy-use-selectable-prompt t)
    :bind (("C-x b"   . ivy-switch-buffer)
           ("C-c C-r" . ivy-resume)
           ("C-s"     . swiper)))

  ;; ivy-rich makes Ivy look a little bit more like Helm.
  (use-package ivy-rich
    :straight t
    :after counsel
    :custom
    (ivy-virtual-abbreviate 'full
                            ivy-rich-switch-buffer-align-virtual-buffer t
                            ivy-rich-path-style 'abbrev)
    :init
    (ivy-rich-mode))

  (use-package ivy-hydra
    :straight t)
#+END_SRC

** Smex
Sort commands by recency in ivy windows
#+BEGIN_SRC emacs-lisp
  (use-package smex
    :straight t)
#+END_SRC

** Counsel
#+BEGIN_SRC emacs-lisp
  (use-package counsel
    :straight t
    :after ivy
    :init (counsel-mode 1)
    :bind (("C-c ;" . counsel-M-x)
           ("C-c U" . counsel-unicode-char)
           ("C-c i" . counsel-imenu)
           ("C-c y" . counsel-yank-pop)
           ("C-c r" . counsel-recentf)
           :map ivy-minibuffer-map
           ("C-r" . counsel-minibuffer-history))
    :diminish)
#+END_SRC

** Ido
#+begin_src emacs-lisp
  (use-package ido
    :straight t
    :config (ido-mode 1)
    :bind (("C-x f" . ido-find-file)))
#+end_src

** Autocompletion
#+BEGIN_SRC emacs-lisp
  (use-package auto-complete
    :straight t
    :config
    (ac-config-default))
#+END_SRC

* Magit
Magit is THE go to package for using git in emacs.
#+BEGIN_SRC emacs-lisp
  (use-package magit
    :straight t
    :bind (("C-c g" . magit-status))
    :diminish magit-auto-revert-mode
    :diminish auto-revert-mode
    :custom
    (magit-remote-set-if-missing t)
    (magit-diff-refine-hunk t)
    :config
    (magit-auto-revert-mode t)
    (advice-add 'magit-refresh :before #'maybe-unset-buffer-modified)
    (advice-add 'magit-commit  :before #'maybe-unset-buffer-modified)
    (setq magit-completing-read-function 'ivy-completing-read)
    (add-to-list 'magit-no-confirm 'stage-all-changes))

  (use-package libgit
    :straight t
    :disabled
    :after magit)
#+END_SRC
The ~advice-add~ entries are thereto stop magit from bugging us to save buffers when commiting and refreshing.

** Helper Functions
#+BEGIN_SRC emacs-lisp
  (autoload 'diff-no-select "diff")
  (defun current-buffer-matches-file-p ()
    "Return t if the current buffer is identical to its associated file."
    (when (and buffer-file-name (buffer-modified-p))
      (diff-no-select buffer-file-name (current-buffer) nil 'noasync)
      (with-current-buffer "*Diff*"
        (and (search-forward-regexp "^Diff finished \(no differences\)\." (point-max) 'noerror) t))))
#+END_SRC

Clear modified bit on all unmodified buffers
#+BEGIN_SRC emacs-lisp
  (defun maybe-unset-buffer-modified (&optional _)
    (interactive)
    (dolist (buf (buffer-list))
      (with-current-buffer buf
        (when (and buffer-file-name (buffer-modified-p) (current-buffer-matches-file-p))
          (set-buffer-modified-p nil)))))

#+END_SRC

Don't prompt to save unmodified buffers on exit.
#+BEGIN_SRC emacs-lisp
  (advice-add 'save-buffers-kill-emacs :before #'maybe-unset-buffer-modified)
#+END_SRC

#+BEGIN_SRC emacs-lisp
  (defun kill-buffer-with-prejudice (&optional _)
    "Kill a buffer, eliding the save dialogue if there are no diffs."
    (interactive)
    (when (current-buffer-matches-file-p) (set-buffer-modified-p nil))
    (kill-buffer))
#+END_SRC

* Org Mode
Configuration to make org-mode better as a word processor
#+BEGIN_SRC emacs-lisp
  (setq org-hide-emphasis-markers t)

  (font-lock-add-keywords 'org-mode
                          '(("^ *\\([-]\\) "
                             (0 (prog1 () (compose-region (match-beginning 1) (match-end 1) "•"))))))

  (add-hook 'org-mode-hook 'variable-pitch-mode)
  (add-hook 'org-mode-hook 'visual-line-mode)

  (custom-theme-set-faces
   'user
   '(org-block ((t (:inherit fixed-pitch))))
   '(org-code ((t (:inherit (shadow fixed-pitch)))))
   '(org-document-info ((t (:foreground "dark orange"))))
   '(org-document-info-keyword ((t (:inherit (shadow fixed-pitch)))))
   '(org-indent ((t (:inherit (org-hide fixed-pitch)))))
   '(org-link ((t (:foreground "royal blue" :underline t))))
   '(org-meta-line ((t (:inherit (font-lock-comment-face fixed-pitch)))))
   '(org-property-value ((t (:inherit fixed-pitch))) t)
   '(org-special-keyword ((t (:inherit (font-lock-comment-face fixed-pitch)))))
   '(org-table ((t (:inherit fixed-pitch :foreground "#83a598"))))
   '(org-tag ((t (:inherit (shadow fixed-pitch) :weight bold :height 0.8))))
   '(org-verbatim ((t (:inherit (shadow fixed-pitch))))))
#+END_SRC

** Archive Location
When archiving items in org files, the default ist to crate a separate file named ~<filename>.org_archive~.
This clutters up my notes folder quite a bit, as I use a lot of separate files with thier respective archives.
All archives should be stored in a single  ~.archive~ file per directory.
#+BEGIN_SRC emacs-lisp
  (setq org-archive-location "./.archive::* From %s")
#+END_SRC
** Templates
*** Babel
Here we set custom templates to be used for structure expansion.
These are used when we type "<" folowed by the shortcut for a template and hit "TAB".
e.g. "<s TAB" expands to ~#+BEGIN_SRC ?\n\n#+END_SRC~

Use ~org-tempo~ to quickly insert the structures
#+begin_src emacs-lisp
  (require 'org-tempo)
#+end_src

Shortcut for creating ~emacs-lisp~ code blocks. This is used extensively in this very file.
#+BEGIN_SRC emacs-lisp
  (add-to-list 'org-structure-template-alist '("el" . "src emacs-lisp"))
#+END_SRC

* Misc Packages
** All The Icons
We want to have some nice looking icons
#+BEGIN_SRC emacs-lisp
  (use-package all-the-icons
    :straight t)
#+END_SRC

** Recentf
Show recent files in the buffer selection
#+BEGIN_SRC emacs-lisp
  (use-package recentf
    :straight t
    :init (recentf-mode t)
    :config
    (add-to-list 'recentf-exclude "\\.emacs.d")
    (add-to-list 'recentf-exclude ".+tmp......\\.org"))
#+END_SRC

** Rainbow Delimiters
We want to have some nicely colored delimiters when reading and writing lisp code
#+BEGIN_SRC emacs-lisp
  (use-package rainbow-delimiters
    :straight t
    :hook (prog-mode . rainbow-delimiters-mode))
#+END_SRC

** Markdown Mode
#+BEGIN_SRC emacs-lisp
  (use-package markdown-mode
    :straight t
    :mode ("\\.md$" . gfm-mode)
    :config
    (when (executable-find "pandoc")
      (setq markdown-command "pandoc -f markdown -t html")))
#+END_SRC

** Duplicate Thing
Quick bind to ~C-c u~ to duplicate the current line
#+BEGIN_SRC emacs-lisp
  (use-package duplicate-thing
    :straight t
    :bind (("C-c u" . duplicate-thing)))
#+END_SRC

** ACE Window
Small package to quickly switch tiled windows.
Use ~M-o~ to quickly switch.
#+BEGIN_SRC emacs-lisp
  (use-package ace-window
    :straight t
    :bind (("M-o" . 'ace-window))
    :config
    (custom-set-faces
     '(aw-leading-char-face
       ((t (:inherit ace-jump-face-foreground :height 3.0))))
     ))
#+END_SRC

** Ag
Ag.el allows you to search using ~ag~ from inside Emacs. You can filter by file type, edit results inline, or find files.

[[https://agel.readthedocs.io/en/latest/index.html][Documentation]]

#+BEGIN_SRC emacs-lisp
  (use-package ag
    :straight t)
#+END_SRC

* Programming
** General
#+BEGIN_SRC emacs-lisp
  (use-package paredit
    :straight t)
  (autoload 'enable-paredit-mode "paredit" "Turn on pseudo-structural editing of Lisp code." t)
  (add-hook 'emacs-lisp-mode-hook       #'enable-paredit-mode)
  (add-hook 'eval-expression-minibuffer-setup-hook #'enable-paredit-mode)
  (add-hook 'ielm-mode-hook             #'enable-paredit-mode)
  (add-hook 'lisp-mode-hook             #'enable-paredit-mode)
  (add-hook 'lisp-interaction-mode-hook #'enable-paredit-mode)
  (add-hook 'scheme-mode-hook           #'enable-paredit-mode)

  (add-hook 'prog-mode-hook 'display-line-numbers-mode)
#+END_SRC

** Copilot
#+begin_src emacs-lisp
  (use-package copilot
    :straight (:host github :repo "zerolfx/copilot.el" :files ("dist" "*.el"))
    :ensure t)

  (add-hook 'prog-mode-hook 'copilot-mode)
  (define-key copilot-completion-map (kbd "<tab>") 'copilot-accept-completion)
  (define-key copilot-completion-map (kbd "TAB") 'copilot-accept-completion)
  (define-key copilot-completion-map (kbd "M--") 'copilot-complete)
#+end_src

** Elisp
Some customization for writing elisp
#+BEGIN_SRC emacs-lisp
  (defun my-elisp-mode-hook ()
    "My elisp customizations."
    (electric-pair-local-mode 1)
    (add-hook 'before-save-hook 'check-parens nil t)
    (auto-composition-mode nil))

  (add-hook 'emacs-lisp-mode-hook 'my-elisp-mode-hook)
#+END_SRC

** Common Lisp
#+BEGIN_SRC emacs-lisp
  (setq inferior-lisp-program "sbcl")
  (use-package slime
    :straight t)
  (slime-setup '(slime-fancy slime-quicklisp slime-asdf))
  (add-hook 'slime-repl-mode-hook (lambda () (paredit-mode +1)))
#+END_SRC

* Load additional files
All information about the current user should reside in the ~personal.el~ file.
This file contains personal information like name, email or other identifying information.
This file should contain definitions, that are the same on every device, but sould not be commited to a repository.
#+BEGIN_SRC emacs-lisp
  (setq personal-file "~/.emacs.d/personal.el")
  (load personal-file 'noerror)
#+END_SRC

Load a custom file from the emacs home dir.
This file is specific to the machine emacs runs on.
It conatins customizations and file locations that are machine dependend.
#+BEGIN_SRC emacs-lisp
  (setq custom-file "~/.emacs.d/custom.el")
  (load custom-file 'noerror)
#+END_SRC


