Emacs From Scratch #7 - Configure Everything with Org Babel

- Re-organize emacs config as org document with org babel
- Add evil-easymotion package
This commit is contained in:
Sravan Balaji
2021-04-28 21:04:04 -04:00
parent da6527073a
commit d83759bb5d
2 changed files with 781 additions and 491 deletions

View File

@@ -1,349 +1,548 @@
#+title: Personal Emacs Configuration
#+author: Sravan Balaji
#+PROPERTY: header-args:emacs-lisp :tangle ./init.el
* Sravan Balaji's Emacs Configuration
** Welcome
My personal configuration of Emacs, written as an [[https://orgmode.org][Org Mode]] document.
* Configuration
Run the block below with `C-c C-c` to tangle code blocks to config file.
** UI Changes
#+begin_src emacs-lisp :tangle no
(org-mode-restart)
(org-babel-tangle)
#+end_src
*** Disable Unnecessary Visual Elements
** Configuration Variables
#+begin_src emacs-lisp
(defvar sb/default-font-size 115)
(defvar sb/default-variable-font-size 115)
(defvar sb/modeline-height 15)
#+end_src
* Package System Setup
Package sources and initialization.
#+begin_src emacs-lisp
;; Initialize package sources
(require 'package)
(setq package-archives '(("melpa" . "https://melpa.org/packages/")
("org" . "https://orgmode.org/elpa/")
("elpa" . "https://elpa.gnu.org/packages/")))
(package-initialize)
(unless package-archive-contents
(package-refresh-contents))
;; Initialize use-package on non-Linux platforms
(unless (package-installed-p 'use-package)
(package-install 'use-package))
(require 'use-package)
(setq use-package-always-ensure t)
#+end_src
* Basic UI Configuration
** Disable Unnecessary Visual Elements
Disable some visual elements that are not necessary like startup message, scrollbar, toolbar, tooltips, menu bar, etc.
Turn on the visual bell and add some breathing room.
#+begin_src emacs-lisp
(setq inhibit-startup-message t) ; Disable startup message
(setq inhibit-startup-message t) ; Disable startup message
(scroll-bar-mode -1) ; Disable visible scrollbar
(tool-bar-mode -1) ; Disable the toolbar
(tooltip-mode -1) ; Disable tooltips
(set-fringe-mode 10) ; Give some breathing room
(scroll-bar-mode -1) ; Disable visible scrollbar
(tool-bar-mode -1) ; Disable the toolbar
(tooltip-mode -1) ; Disable tooltips
(set-fringe-mode 10) ; Give some breathing room
(menu-bar-mode -1) ; Disable the menu bar
(menu-bar-mode -1) ; Disable the menu bar
(setq visible-bell t) ; Set up the visible bell
(setq visible-bell t) ; Set up the visible bell
#+end_src
*** Modeline
** Line Numbers
#+begin_src emacs-lisp
;; Doom Modeline
(use-package doom-modeline
:ensure t
:init (doom-modeline-mode 1)
:custom (doom-modeline-height 15)
)
;; Line Numbers
(column-number-mode)
(global-display-line-numbers-mode t)
;; Disable line numbers for some modes
(dolist (mode '(org-mode-hook
term-mode-hook
shell-mode-hook
eshell-mode-hook))
(add-hook mode (lambda () (display-line-numbers-mode 0))))
#+end_src
*** Theme
** Font Configuration
Setup FiraCode and Cantarell fonts with specified size.
#+begin_src emacs-lisp
;; ;; Doom Themes
;; (use-package doom-themes
;; :init (load-theme 'doom-dracula t)
;; )
(set-face-attribute 'default nil :font "FiraCode Nerd Font" :height sb/default-font-size)
;; Dracula Pro Theme
(add-to-list 'custom-theme-load-path "~/.config/emacs/themes")
(load-theme 'dracula-pro t)
;; Set the fixed pitch face
(set-face-attribute 'fixed-pitch nil :font "FiraCode Nerd Font" :height sb/default-font-size)
;; Set the variable pitch face
(set-face-attribute 'variable-pitch nil :font "Cantarell" :height sb/default-font-size :weight 'regular)
#+end_src
*** Line Numbers
* Keybindings
** General
#+begin_src emacs-lisp
;; Line Numbers
(column-number-mode)
(global-display-line-numbers-mode t)
;; Disable line numbers for some modes
(dolist (mode '(
org-mode-hook
term-mode-hook
shell-mode-hook
eshell-mode-hook
)
)
(add-hook mode (lambda () (display-line-numbers-mode 0))))
;; Make ESC quit prompts
(global-set-key (kbd "<escape>") 'keyboard-escape-quit)
;; General (for easy keybindings)
(use-package general
:config
(general-create-definer sb/leader-keys
:keymaps '(normal insert visual emacs)
:prefix "SPC"
:global-prefix "C-SPC")
(sb/leader-keys
"t" '(:ignore t :which-key "toggles")
"tc" '(comment-line :which-key "toggle comment")
"s" '(:ignore s :which-key "settings")))
(general-define-key
"C-M-j" 'counsel-switch-buffer
)
#+end_src
*** Rainbow Delimeters
** Evil Keybindings
#+begin_src emacs-lisp
;; Rainbow Delimiters
(use-package rainbow-delimiters
:hook (prog-mode . rainbow-delimiters-mode))
;; Evil Keybindings
(use-package evil
:init
(setq evil-want-integration t)
(setq evil-want-keybinding nil)
(setq evil-want-C-u-scroll t)
(setq evil-want-C-i-jump nil)
:config
(evil-mode 1)
(define-key evil-insert-state-map (kbd "C-g") 'evil-normal-state)
(define-key evil-insert-state-map (kbd "C-h") 'evil-delete-backward-char-and-join)
;; Use visual line motions even outside of visual-line-mode buffers
(evil-global-set-key 'motion "j" 'evil-next-visual-line)
(evil-global-set-key 'motion "k" 'evil-previous-visual-line)
(evil-set-initial-state 'messages-buffer-mode 'normal)
(evil-set-initial-state 'dashboard-mode 'normal)
)
(use-package evil-collection
:after evil
:config
(evil-collection-init)
)
;; Easymotion
(use-package evil-easymotion
:config
(evilem-default-keybindings "SPC"))
#+end_src
** Fonts
* UI Configuration
Setup FiraCode font with specified size.
** Color Theme
NOTE: The first time you load your configuration on a new machine, you'll
#+begin_src emacs-lisp
;; ;; Dracula Doom Theme
;; (use-package doom-themes
;; :init (load-theme 'doom-dracula t))
;; Dracula Pro Theme
(add-to-list 'custom-theme-load-path "~/.config/emacs/themes")
(load-theme 'dracula-pro t)
#+end_src
** Better Modeline
*NOTE*: The first time you load your configuration on a new machine, you'll
need to run the following command interactively so that mode line icons
display correctly:
M-X all-the-icons-install-fonts
`M-x all-the-icons-install-fonts`
#+begin_src emacs-lisp
;; Font setup
(set-face-attribute 'default nil :font "FiraCode Nerd Font" :height 120)
;; Icon Fonts
(use-package all-the-icons)
;; Icon Fonts
(use-package all-the-icons)
;; Doom Modeline
(use-package doom-modeline
:ensure t
:init (doom-modeline-mode 1)
:custom ((doom-modeline-height sb/modeline-height)))
#+end_src
** Keybindings
Evil mode keybindings.
*** General
** Which Key
#+begin_src emacs-lisp
;; Make ESC quit prompts
(global-set-key (kbd "<escape>") 'keyboard-escape-quit)
(use-package which-key
:init (which-key-mode)
:diminish which-key-mode
:config
(setq which-key-idle-delay 1))
#+end_src
;; General (for easy keybindings)
(use-package general
:config
(general-create-definer sb/leader-keys
:keymaps '(normal insert visual emacs)
:prefix "SPC"
:global-prefix "C-SPC")
** Ivy and Counsel
#+begin_src emacs-lisp
;; Ivy Autocompletion
(use-package ivy
:diminish
:bind (("C-s" . swiper)
:map ivy-minibuffer-map
("TAB" . ivy-alt-done)
("C-l" . ivy-alt-done)
("C-j" . ivy-next-line)
("C-k" . ivy-previous-line)
:map ivy-switch-buffer-map
("C-k" . ivy-previous-line)
("C-l" . ivy-done)
("C-d" . ivy-switch-buffer-kill)
:map ivy-reverse-i-search-map
("C-k" . ivy-previous-line)
("C-d" . ivy-reverse-i-search-kill))
:config
(ivy-mode 1))
;; Ivy Rich
(use-package ivy-rich
:init
(ivy-rich-mode 1))
;; Counsel
(use-package counsel
:bind (("C-M-j" . 'counsel-switch-buffer)
:map minibuffer-local-map
("C-r" . 'counsel-minibuffer-history))
:config
(counsel-mode 1))
(sb/leader-keys
"t" '(:ignore t :which-key "toggles")
"tc" '(comment-line :which-key "toggle comment")
"s" '(:ignore s :which-key "settings")
"st" '(counsel-load-theme :which-key "choose theme")
"ss" '(hydra-text-scale/body :which-key "scale text")
)
)
(general-define-key
"C-M-j" 'counsel-switch-buffer
)
"st" '(counsel-load-theme :which-key "choose theme"))
#+end_src
*** Evil Keybindings
** Helpful Help Commands
#+begin_src emacs-lisp
;; Evil Keybindings
(use-package evil
:init
(setq evil-want-integration t)
(setq evil-want-keybinding nil)
(setq evil-want-C-u-scroll t)
(setq evil-want-C-i-jump nil)
:config
(evil-mode 1)
(define-key evil-insert-state-map (kbd "C-g") 'evil-normal-state)
(define-key evil-insert-state-map (kbd "C-h") 'evil-delete-backward-char-and-join)
;; Use visual line motoins even outside of visual-line-mode buffers
(evil-global-set-key 'motion "j" 'evil-next-visual-line)
(evil-global-set-key 'motion "k" 'evil-previous-visual-line)
(evil-set-initial-state 'messages-buffer-mode 'normal)
(evil-set-initial-state 'dashboard-mode 'normal)
)
(use-package evil-collection
:after evil
:config
(evil-collection-init)
)
;; Helpful
(use-package helpful
:custom
(counsel-describe-function-function #'helpful-callable)
(counsel-describe-variable-function #'helpful-variable)
:bind
([remap describe-function] . counsel-describe-function)
([remap describe-command] . helpful-command)
([remap describe-variable] . counsel-describe-variable)
([remap describe-key] . helpful-key))
#+end_src
*** Hydra
** Text Scaling
Use Hydra to design a transient key binding for quickly adjusting the scale of the text on screen.
#+begin_src emacs-lisp
;; Hydra
(use-package hydra)
;; Hydra
(use-package hydra)
(defhydra hydra-text-scale (:timeout 4)
"scale text"
("j" text-scale-increase "in")
("k" text-scale-decrease "out")
("f" nil "finished" :exit t)
)
#+end_src
** Package Sources
Package sources and initialization.
#+begin_src emacs-lisp
;; Initialize package sources
(require 'package)
(setq package-archives
'(("melpa" . "https://melpa.org/packages/")
("org" . "https://orgmode.org/elpa/")
("elpa" . "https://elpa.gnu.org/packages/")
)
)
(package-initialize)
(unless package-archive-contents
(package-refresh-contents))
;; Initialize use-package on non-Linux platforms
(unless (package-installed-p 'use-package)
(package-install 'use-package))
(require 'use-package)
(setq use-package-always-ensure t)
#+end_src
** Autocompletion
#+begin_src emacs-lisp
;; Ivy Autocompletion
(use-package ivy
:diminish
:bind (("C-s" . swiper)
:map ivy-minibuffer-map
("TAB" . ivy-alt-done)
("C-l" . ivy-alt-done)
("C-j" . ivy-next-line)
("C-k" . ivy-previous-line)
:map ivy-switch-buffer-map
("C-k" . ivy-previous-line)
("C-l" . ivy-done)
("C-d" . ivy-switch-buffer-kill)
:map ivy-reverse-i-search-map
("C-k" . ivy-previous-line)
("C-d" . ivy-reverse-i-search-kill))
:config
(ivy-mode 1)
(defhydra hydra-text-scale (:timeout 4)
"scale text"
("j" text-scale-increase "in")
("k" text-scale-decrease "out")
("f" nil "finished" :exit t)
)
;; Ivy Rich
(use-package ivy-rich
:init
(ivy-rich-mode 1))
;; Counsel
(use-package counsel
:bind (("M-x" . counsel-M-x)
("C-x b" . counsel-ibuffer)
("C-x C-f" . counsel-find-file)
:map minibuffer-local-map
("C-r" . 'counsel-minibuffer-history)))
;; Helpful
(use-package helpful
:custom
(counsel-describe-function-function #'helpful-callable)
(counsel-describe-variable-function #'helpful-variable)
:bind
([remap describe-function] . counsel-describe-function)
([remap describe-command] . helpful-command)
([remap describe-variable] . counsel-describe-variable)
([remap describe-key] . helpful-key))
(sb/leader-keys
"ss" '(hydra-text-scale/body :which-key "scale text"))
#+end_src
* Org Mode
** Better Font Faces
#+begin_src emacs-lisp
(defun sb/org-font-setup ()
;; Replace list hyphen with dot
(font-lock-add-keywords 'org-mode
'(("^ *\\([-]\\) "
(0 (prog1 () (compose-region (match-beginning 1) (match-end 1) ""))))))
;; Set faces for heading levels
(dolist (face '((org-level-1 . 1.2)
(org-level-2 . 1.1)
(org-level-3 . 1.05)
(org-level-4 . 1.0)
(org-level-5 . 1.1)
(org-level-6 . 1.1)
(org-level-7 . 1.1)
(org-level-8 . 1.1)))
(set-face-attribute (car face) nil :font "Cantarell" :weight 'regular :height (cdr face)))
;; Ensure that anything that should be fixed-pitch in Org files appears that way
(set-face-attribute 'org-block nil :foreground nil :inherit 'fixed-pitch)
(set-face-attribute 'org-code nil :inherit '(shadow fixed-pitch))
(set-face-attribute 'org-table nil :inherit '(shadow fixed-pitch))
(set-face-attribute 'org-verbatim nil :inherit '(shadow fixed-pitch))
(set-face-attribute 'org-special-keyword nil :inherit '(font-lock-comment-face fixed-pitch))
(set-face-attribute 'org-meta-line nil :inherit '(font-lock-comment-face fixed-pitch))
(set-face-attribute 'org-checkbox nil :inherit 'fixed-pitch))
#+end_src
** Basic Config
#+begin_src emacs-lisp
(defun sb/org-mode-setup ()
(org-indent-mode)
(variable-pitch-mode 1)
(visual-line-mode 1))
(use-package org
:hook (org-mode . sb/org-mode-setup)
:config
(setq org-ellipsis "")
(setq org-agenda-start-with-log-mode t)
(setq org-log-done 'time)
(setq org-log-into-drawer t)
(setq org-agenda-files
'("~/Projects/Code/emacs-from-scratch/OrgFiles/Tasks.org"
"~/Projects/Code/emacs-from-scratch/OrgFiles/Habits.org"
"~/Projects/Code/emacs-from-scratch/OrgFiles/Birthdays.org"))
(require 'org-habit)
(add-to-list 'org-modules 'org-habit)
(setq org-habit-graph-column 60)
(setq org-todo-keywords
'((sequence "TODO(t)" "NEXT(n)" "|" "DONE(d!)")
(sequence "BACKLOG(b)" "PLAN(p)" "READY(r)" "ACTIVE(a)" "REVIEW(v)" "WAIT(w@/!)" "HOLD(h)" "|" "COMPLETED(c)" "CANC(k@)")))
(setq org-refile-targets
'(("Archive.org" :maxlevel . 1)
("Tasks.org" :maxlevel . 1)))
;; Save Org buffers after refiling!
(advice-add 'org-refile :after 'org-save-all-org-buffers)
(setq org-tag-alist
'((:startgroup)
; Put mutually exclusive tags here
(:endgroup)
("@errand" . ?E)
("@home" . ?H)
("@work" . ?W)
("agenda" . ?a)
("planning" . ?p)
("publish" . ?P)
("batch" . ?b)
("note" . ?n)
("idea" . ?i)))
;; Configure custom agenda views
(setq org-agenda-custom-commands
'(("d" "Dashboard"
((agenda "" ((org-deadline-warning-days 7)))
(todo "NEXT"
((org-agenda-overriding-header "Next Tasks")))
(tags-todo "agenda/ACTIVE" ((org-agenda-overriding-header "Active Projects")))))
("n" "Next Tasks"
((todo "NEXT"
((org-agenda-overriding-header "Next Tasks")))))
("W" "Work Tasks" tags-todo "+work-email")
;; Low-effort next actions
("e" tags-todo "+TODO=\"NEXT\"+Effort<15&+Effort>0"
((org-agenda-overriding-header "Low Effort Tasks")
(org-agenda-max-todos 20)
(org-agenda-files org-agenda-files)))
("w" "Workflow Status"
((todo "WAIT"
((org-agenda-overriding-header "Waiting on External")
(org-agenda-files org-agenda-files)))
(todo "REVIEW"
((org-agenda-overriding-header "In Review")
(org-agenda-files org-agenda-files)))
(todo "PLAN"
((org-agenda-overriding-header "In Planning")
(org-agenda-todo-list-sublevels nil)
(org-agenda-files org-agenda-files)))
(todo "BACKLOG"
((org-agenda-overriding-header "Project Backlog")
(org-agenda-todo-list-sublevels nil)
(org-agenda-files org-agenda-files)))
(todo "READY"
((org-agenda-overriding-header "Ready for Work")
(org-agenda-files org-agenda-files)))
(todo "ACTIVE"
((org-agenda-overriding-header "Active Projects")
(org-agenda-files org-agenda-files)))
(todo "COMPLETED"
((org-agenda-overriding-header "Completed Projects")
(org-agenda-files org-agenda-files)))
(todo "CANC"
((org-agenda-overriding-header "Cancelled Projects")
(org-agenda-files org-agenda-files)))))))
(setq org-capture-templates
`(("t" "Tasks / Projects")
("tt" "Task" entry (file+olp "~/Projects/Code/emacs-from-scratch/OrgFiles/Tasks.org" "Inbox")
"* TODO %?\n %U\n %a\n %i" :empty-lines 1)
("j" "Journal Entries")
("jj" "Journal" entry
(file+olp+datetree "~/Projects/Code/emacs-from-scratch/OrgFiles/Journal.org")
"\n* %<%I:%M %p> - Journal :journal:\n\n%?\n\n"
;; ,(dw/read-file-as-string "~/Notes/Templates/Daily.org")
:clock-in :clock-resume
:empty-lines 1)
("jm" "Meeting" entry
(file+olp+datetree "~/Projects/Code/emacs-from-scratch/OrgFiles/Journal.org")
"* %<%I:%M %p> - %a :meetings:\n\n%?\n\n"
:clock-in :clock-resume
:empty-lines 1)
("w" "Workflows")
("we" "Checking Email" entry (file+olp+datetree "~/Projects/Code/emacs-from-scratch/OrgFiles/Journal.org")
"* Checking Email :email:\n\n%?" :clock-in :clock-resume :empty-lines 1)
("m" "Metrics Capture")
("mw" "Weight" table-line (file+headline "~/Projects/Code/emacs-from-scratch/OrgFiles/Metrics.org" "Weight")
"| %U | %^{Weight} | %^{Notes} |" :kill-buffer t)))
(define-key global-map (kbd "C-c j")
(lambda () (interactive) (org-capture nil "jj")))
(sb/org-font-setup))
#+end_src
*** Nicer Heading Bullets
#+begin_src emacs-lisp
(use-package org-bullets
:after org
:hook (org-mode . org-bullets-mode)
:custom
(org-bullets-bullet-list '("" "" "" "" "" "" "")))
#+end_src
*** Center Org Buffers
#+begin_src emacs-lisp
(defun sb/org-mode-visual-fill ()
(setq visual-fill-column-width 100
visual-fill-column-center-text t)
(visual-fill-column-mode 1))
(use-package visual-fill-column
:hook (org-mode . sb/org-mode-visual-fill))
#+end_src
** Configure Babel Languages
#+begin_src emacs-lisp
(org-babel-do-load-languages
'org-babel-load-languages
'((emacs-lisp . t)
(python . t)))
(push '("conf-unix" . conf-unix) org-src-lang-modes)
#+end_src
** Structure Templates
#+begin_src emacs-lisp
;; This is needed as of Org 9.2
(require 'org-tempo)
(add-to-list 'org-structure-template-alist '("sh" . "src shell"))
(add-to-list 'org-structure-template-alist '("el" . "src emacs-lisp"))
(add-to-list 'org-structure-template-alist '("py" . "src python"))
#+end_src
** Auto-Tangle Configuration Files
Automatically tangle code blocks in file everytime it is saved.
#+begin_src emacs-lisp
;; Automatically tangle our Emacs.org config file when we save it
(defun sb/org-babel-tangle-config ()
(when (string-equal (buffer-file-name)
(expand-file-name "~/.config/emacs/README.org"))
;; Dynamic scoping to the rescue
(let ((org-confirm-babel-evaluate nil))
(org-babel-tangle))))
(add-hook 'org-mode-hook (lambda () (add-hook 'after-save-hook #'sb/org-babel-tangle-config)))
#+end_src
* Development
** Projectile
#+begin_src emacs-lisp
;; Projectile
(use-package projectile
:diminish projectile-mode
:config (projectile-mode)
:custom ((projectile-completion-system 'ivy))
:bind-keymap
("C-c p" . projectile-command-map)
:init
(when (file-directory-p "~/Projects")
(setq projectile-project-search-path '("~/Projects/Personal" "~/Projects/UMICH")))
(when (file-directory-p "~/.config")
(setq projectile-project-search-path '("~/.config")))
(setq projectile-switch-project-action #'projectile-dired))
(use-package counsel-projectile
:config (counsel-projectile-mode))
#+end_src
** Magit
#+begin_src emacs-lisp
;; Projectile
(use-package projectile
:diminish projectile-mode
:config (projectile-mode)
:custom ((projectile-completion-system 'ivy))
:bind-keymap
("C-c p" . projectile-command-map)
:init
(when (file-directory-p "~/Projects")
(setq projectile-project-search-path '("~/Projects/Personal" "~/Projects/UMICH"))
;; Magit
(use-package magit
:custom
(magit-display-buffer-function #'magit-display-buffer-same-window-except-diff-v1)
)
(when (file-directory-p "~/.config")
(setq projectile-project-search-path '("~/.config"))
)
(setq projectile-switch-project-action #'projectile-dired)
)
(use-package counsel-projectile
:config (counsel-projectile-mode)
)
;; NOTE: evil-magit was removed from MELPA and now a part of evil-collection
;; (use-package evil-magit
;; :after magit
;; )
;; ;; NOTE: Make sure to configure a GitHub token before using this package!
;; ;; - https://magit.vc/manual/forge/Token-Creation.html#Token-Creation
;; ;; - https://magit.vc/manual/ghub/Getting-Started.html#Getting-Started
;; (use-package forge)
#+end_src
** Git Integration
*** Magit
** Rainbow Delimeters
#+begin_src emacs-lisp
;; Magit
(use-package magit
;; :commands (magit-status magit-get-current-branch)
:custom
(magit-display-buffer-function #'magit-display-buffer-same-window-except-diff-v1)
)
;; NOTE: evil-magit was removed from MELPA and now a part of evil-collection
;; (use-package evil-magit
;; :after magit
;; )
;; Rainbow Delimiters
(use-package rainbow-delimiters
:hook (prog-mode . rainbow-delimiters-mode))
#+end_src
*** Forge
#+begin_src emacs-lisp
;; OPTIONAL: This will add GitHub integration to magit, but requires some token setup
;; (use-package forge)
#+end_src
** Org Mode
#+begin_src emacs-lisp
(defun sb/org-mode-setup ()
(org-indent-mode)
;; (variable-pitch-mode 1)
(auto-fill-mode 0)
(visual-line-mode 1)
(setq evil-auto-indent nil)
)
;; Replace list hyphen with dot
(font-lock-add-keywords 'org-mode
'(("^ *\\([-]\\) "
(0 (prog1 () (compose-region (match-beginning 1) (match-end 1) ""))))))
;; ;; Set faces for heading levels
;; (dolist (face '((org-level-1 . 1.2)
;; (org-level-2 . 1.1)
;; (org-level-3 . 1.05)
;; (org-level-4 . 1.0)
;; (org-level-5 . 1.1)
;; (org-level-6 . 1.1)
;; (org-level-7 . 1.1)
;; (org-level-8 . 1.1)))
;; (set-face-attribute (car face) nil :font "Cantarell" :weight 'regular :height (cdr face)))
(use-package org
:hook (org-mode . sb/org-mode-setup)
:config
(setq org-ellipsis "")
(sb/org-font-setup)
)
(use-package org-bullets
:after org
:hook (org-mode . org-bullets-mode)
:custom
(org-bullets-bullet-list '("" "" "" "" "" "" ""))
)
;; ;; Add visual fill (padding)
;; (defun sb/org-mode-visual-fill ()
;; (setq visual-fill-column-width 100
;; visual-fill-column-center-text t)
;; (visual-fill-column-mode 1)
;; )
;; (use-package visual-fill-column
;; :hook (org-mode . sb/org-mode-visual-fill)
;; )
#+end_src