#+TITLE: Emacs configuration
#+AUTHOR: Oreodave
#+DESCRIPTION: My new Emacs configuration
* Initial
** Setup straight
Bootstrap of straight (from github)
#+BEGIN_SRC emacs-lisp
(defvar bootstrap-version)
(let ((bootstrap-file
(expand-file-name "straight/repos/straight.el/bootstrap.el" user-emacs-directory))
(bootstrap-version 5))
(unless (file-exists-p bootstrap-file)
(with-current-buffer
(url-retrieve-synchronously
"https://raw.githubusercontent.com/raxod502/straight.el/develop/install.el"
'silent 'inhibit-cookies)
(goto-char (point-max))
(eval-print-last-sexp)))
(load bootstrap-file nil 'nomessage))
#+END_SRC
** Setup use package
Straight clone use-package and state that all use-package statements implicity use straight.
#+BEGIN_SRC emacs-lisp
(straight-use-package 'use-package)
(setq straight-use-package-by-default t)
#+END_SRC
** Setup alpha and yes-or-no-p
This just sets the alpha to 85% and all yes or no questions to single letter responses.
#+BEGIN_SRC emacs-lisp
(add-to-list 'default-frame-alist '(alpha . 85))
(fset 'yes-or-no-p 'y-or-n-p)
#+END_SRC
** Hs Minor mode
Turn on hs minor mode for all prog-mode.
#+BEGIN_SRC emacs-lisp
(add-hook 'prog-mode-hook #'hs-minor-mode)
#+END_SRC
** Set backup directory
Set the backup directory to =user-emacs-directory=/saves so I don't get those annoying '~' files.
#+BEGIN_SRC emacs-lisp
(setq backup-directory-alist `(("." . "~/.config/emacs/saves")))
#+END_SRC
** Turn off the bars
Turn off all the bars, don't need them anymore!
#+BEGIN_SRC emacs-lisp
(tool-bar-mode -1)
(scroll-bar-mode -1)
(menu-bar-mode -1)
#+END_SRC
** Themes
Load my custom "Grayscale" theme (look at [[file:Grayscale-theme.el][this file]]).
#+BEGIN_SRC emacs-lisp
(load-theme 'Grayscale t)
#+END_SRC
** Turn off startup buffer
#+BEGIN_SRC emacs-lisp
(setq inhibit-startup-screen t)
#+END_SRC
* Emacs Mode-line
Firstly, declare a variable for the number of spaces between each module in the modeline.
#+BEGIN_SRC emacs-lisp
(defconst +modeline/sep-spaces 4 "Number of spaces separating modules.")
#+END_SRC
Then, declare a list of reserved characters for which the previously declared seperator won't be applied when placed at the end of a module string.
#+BEGIN_SRC emacs-lisp
(defconst +modeline/reserved-chars (list "[" "(")
"Characters that, when at the end of a module string, won't have the separator applied to them.")
#+END_SRC
Now declare a function that applies the separator with respect to the reserved characters to any one string.
#+BEGIN_SRC emacs-lisp
(defun +modeline/handle-string (STR)
(condition-case nil
(progn
(string-blank-p STR)
(cond ((cl-member (car (last (split-string STR "" t))) +modeline/reserved-chars :test #'string=) STR)
(t (concat STR (cl-reduce #'concat (cl-loop for i from 1 to +modeline/sep-spaces collect " "))))))
(error STR)))
#+END_SRC
Finally, set the mode-line-format.
#+BEGIN_SRC emacs-lisp
(setq-default
mode-line-format
(mapcar #'+modeline/handle-string
(list "%l:%c"
"%p["
'(:eval (upcase
(substring
(format "%s" (if (bound-and-true-p evil-state) evil-state ""))
0 1)))
"]"
"%+%b("
'(:eval (format "%s" major-mode))
")"
"%I"
vc-mode
mode-line-end-spaces)))
#+END_SRC
* Custom Functions
These are custom functions I have defined
** New line function
Vim bindings don't have a nice way of adding new lines before or after the current line while staying in normal mode.
You can use =o/O= to enter insert mode at a new line, but this isn't the same as being able to stay in normal mode, and only adds extra keypresses if your only purpose was to open up some lines.
As this is Emacs I can extend it as I wish, so I decided to define a new line function that won't remove me from normal state.
The logic is pretty simple:
- Use the predefined vim functions for opening new lines above and below with insert mode
- Given the argument =BACKWARD= to assess whether to open lines above or below
- Return to previous location
- Enter normal state
#+BEGIN_SRC emacs-lisp
(with-eval-after-load "evil"
(defun dx:newline (&optional BACKWARD)
(interactive)
(let ((old (point)))
(cond ((and BACKWARD (= BACKWARD 1)) (evil-open-below 1))
(t (evil-open-above 1)))
(goto-char (+ old 1))
(evil-normal-state))))
#+END_SRC
** Generate tags
For some reason, I can't seem to rely on the =projectile-regenerate-tags= function, so define my own.
Steps are as follows:
- Consider the =root= to be either =default-directory= or the =projectile-project-root= depending on if it's loaded and set.
- Start a process (shell command) by changing to =root= and executing =ctags -Re=
#+BEGIN_SRC emacs-lisp
(defun dx:generate-tags ()
(interactive)
(let ((root (if (bound-and-true-p projectile-project-root)
projectile-project-root
default-directory)))
(start-process-shell-command "" nil (format "cd %s; ctags -Re ." root))))
#+END_SRC
* Keybindings
** Global map
Any functions that are already loaded, set them to the global map.
#+BEGIN_SRC emacs-lisp
(bind-keys
:map global-map
("