Skip to content

Latest commit

 

History

History
1264 lines (1029 loc) · 36 KB

config.org

File metadata and controls

1264 lines (1029 loc) · 36 KB

Emacs Configuration

Basic setup

figure out the system type

(defconst my-gnu/linux-laptop-p
  (and (equal system-type 'gnu/linux)
       (eq (string-search "android" system-configuration) nil))
  "If the value is t, emacs is running on my gnu/linux laptop.")

(defconst my-android-phone-p
  (not (eq (string-search "android" system-configuration) nil))
  "If the value is t, emacs is running inside termux, on my android phone.")

(defconst my-windows-laptop-p
  (equal system-type 'windows-nt)
  "If the value is t, emacs is running on my windows laptop.")

garbage collection threshold

Set gc-cons-threshold to a higher value for less frequent garbage collections during startup.

(setq gc-cons-threshold (* 64 1000000))

native compilation

If emacs is built with native-compilation support, set the load path for natively compiled *.eln files and suppress the warnings caused while async native compilation process.

(when (native-comp-available-p)
  (setq native-comp-async-report-warnings-errors nil)
  ;; (setq native-comp-async-report-warnings-errors 'silent)
  (add-to-list 'native-comp-eln-load-path
               (expand-file-name "eln-cache/"
                                 user-emacs-directory)))

replace yes-or-no with y-or-n

(fset 'yes-or-no-p 'y-or-n-p)

UTF-8 encoding

Set preferred and default character encodings to UTF-8.

(prefer-coding-system 'utf-8)
(set-default-coding-systems 'utf-8)

echo keystrokes immediately

Show keystrokes immediately in the echo area.

(setq echo-keystrokes 0.01)

minimal look

(menu-bar-mode -1)
(column-number-mode)

(unless my-android-phone-p
  (tool-bar-mode -1)
  (tooltip-mode -1)
  (set-fringe-mode 10)
  (scroll-bar-mode -1))

(setq inhibit-startup-screen t
      scroll-conservatively 101)

;; prevent the annoying bell sounds
(setq visible-bell t)

scratch buffer

Start the *scratch* buffer in fundamental-mode instead of emacs-lisp mode.

(setq initial-major-mode 'fundamental-mode)
(setq initial-scratch-message "")

bury/immediately kill buffers

Bury buffers in my-buffers-to-bury instead of killing. Kill other buffers immediately.

(defvar my-buffers-to-bury (list "*dashboard*" "*scratch*")
  "Bury these buffers instead of killing them.")

(defun my-kill-current-buffer ()
  "Kill current buffer immediately, if it is not present in `my-buffers-to-bury'."
  (interactive)
  (if (member (buffer-name) my-buffers-to-bury)
      (bury-buffer)
    (kill-buffer (current-buffer))))

(global-set-key (kbd "C-x k") 'my-kill-current-buffer)

frame transparency

Make the frame a little transparent on my GNU/Linux laptop.

(when my-gnu/linux-laptop-p
  (set-frame-parameter (selected-frame) 'alpha '(95 . 95))
  (add-to-list 'default-frame-alist `(alpha . (95 . 95))))

maximize frame by default

(add-to-list 'default-frame-alist '(fullscreen . maximized))

font setup

(defvar my-fixed-pitch-font "monospace"
  "Defines a fixed width font.")

(defvar my-variable-pitch-font "sans-serif"
  "Defines a proportional width font.")

(defun my-set-font-variables ()
  "Set `my-fixed-pitch-font' and `my-variable-pitch-font'."
  (setq my-fixed-pitch-font
        (if (find-font (font-spec :name "Cascadia Code"))
            "Cascadia Code"
          (if my-windows-laptop-p "Consolas" "monospace")))
  (setq my-variable-pitch-font
        (if (find-font (font-spec :name "Noto Sans"))
            "Noto Sans"
          (if my-windows-laptop-p "Calibri" "sans-serif"))))

(defun my-set-font-faces ()
  "Set font faces."
  (set-face-attribute 'default nil
                      :font my-fixed-pitch-font
                      :weight 'normal
                      :height 105)

  (set-face-attribute 'fixed-pitch nil
                      :font my-fixed-pitch-font
                      :weight 'regular
                      :height 1.0)

  (set-face-attribute 'variable-pitch  nil
                      :font my-variable-pitch-font
                      :height 1.0))

(my-set-font-variables)
(my-set-font-faces)

handle whitespaces

Cycle between single-space, no-space and original text, around the point by repeatedly pressing M-SPC.

Delete trailing whitespaces before saving a buffer.

(global-set-key (kbd "M-SPC")
                (lambda ()
                  "Instruct `cycle-spacing' to delete newlines too."
                  (interactive)
                  (cycle-spacing -1)))

(add-hook 'before-save-hook
          'delete-trailing-whitespace)

registers

(defun my-set-register-if-file-exists (key filename)
  "Set the register with given KEY if FILENAME exists."
  (if (file-exists-p filename)
      (set-register key `(file . ,filename))))

(my-set-register-if-file-exists ?E
                                (concat (file-name-as-directory user-emacs-directory)
                                        "config.org"))

(when my-gnu/linux-laptop-p
  (my-set-register-if-file-exists ?Q "~/.config/qtile/config.py")
  (my-set-register-if-file-exists ?B "~/.local/data/bookmarks"))

(when my-windows-laptop-p
  (my-set-register-if-file-exists ?B "~/bookmarks.txt"))

unbind C-z

(global-unset-key (kbd "C-z"))

Package archives

elpa & melpa setup

(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))

use-package

(when (< emacs-major-version 29)
  (unless (package-installed-p 'use-package)
    (package-install 'use-package)))

(require 'use-package)
(setq use-package-always-ensure t)
(setq use-package-verbose t)

No littering

Keep the emacs directory clean, and put all backup files in a single place.

(use-package no-littering)

(setq auto-save-file-name-transforms
      `((".*" ,(no-littering-expand-var-file-name "auto-save/") t)))

(setq backup-directory-alist
      `(("." . ,(no-littering-expand-var-file-name "backup/"))))

;; prevent Emacs form littering into init.el
(setq custom-file (no-littering-expand-etc-file-name "custom.el"))

Aesthetics

icons

(use-package all-the-icons
  :if (display-graphic-p)
  :config
  (setq all-the-icons-scale-factor 1.25))

(use-package nerd-icons
  :config
  (setq nerd-icons-scale-factor 1.25))

doom themes and modeline

(defvar my-light-theme 'doom-solarized-light)
(defvar my-dark-theme 'doom-ir-black)
(defvar my-current-theme-variant 'dark)

(defun my-toggle-theme ()
  "Toggle between light and dark themes, set by variables `my-light-theme'
and `my-dark-theme'"
  (interactive)
  (if (eq my-current-theme-variant 'dark)
      (progn
        (disable-theme my-dark-theme)
        (load-theme my-light-theme t)
        (setq my-current-theme-variant 'light))
    (disable-theme my-light-theme)
    (load-theme my-dark-theme t)
    (setq my-current-theme-variant 'dark))
  (message "OK: %s theme activated" (symbol-name my-current-theme-variant)))

(use-package doom-themes
  :config
  (setq doom-themes-enable-bold t)
  (setq doom-themes-enable-italic t)
  (load-theme (if (eq my-current-theme-variant 'dark)
                  my-dark-theme
                my-light-theme) t)
  (set-face-attribute 'font-lock-comment-face  nil
                      :slant 'italic))

(use-package doom-modeline
  :config
  (setq doom-modeline-icon t)
  (setq doom-modeline-height 12))
  (doom-modeline-mode 1)

dashboard

(use-package dashboard
  :config
  (dashboard-setup-startup-hook)
  (if my-windows-laptop-p
      (fset #'dashboard-replace-displayable (lambda (arg &rest _) arg)))
  :init
  (setq dashboard-startup-banner 'logo)
  (setq dashboard-center-content t)
  (setq dashboard-set-heading-icons t)
  (setq dashboard-icon-type 'all-the-icons)
  (setq dashboard-set-file-icons t)
  (setq dashboard-set-init-info t)
  (setq dashboard-projects-backend 'project-el)
  (setq dashboard-items '((recents  . 3)
                          (projects . 5)
                          (registers . 3))))

Completion system

mini-buffer history

(use-package savehist
  :config
  (setq history-length 25)
  (savehist-mode 1))

vertico

(use-package vertico
  :config
  (setq vertico-cycle t)
  (setq vertico-resize t)
  (vertico-mode)
  (vertico-reverse-mode))

orderless

(use-package orderless
  :init
  (setq completion-styles '(orderless)
        completion-category-defaults nil
        completion-category-overrides
        '((file (styles . (partial-completion))))))

marginalia

(use-package marginalia
  :after vertico
  :config
  (setq marginalia-align 'right)
  (setq marginalia-annotators '(marginalia-annotators-heavy
                                marginalia-annotators-light nil))
  (marginalia-mode))

Window management

ace-window

(use-package ace-window
  :bind ("M-o" . ace-window)
  :config
  (set-face-attribute 'aw-leading-char-face nil
                      :font my-fixed-pitch-font
                      :weight 'bold
                      :slant 'normal
                      :foreground "yellow"
                      :height 200)
  :custom
  (aw-keys '(?j ?k ?l ?f ?g ?h ?a ?s ?d ?i ?e ?n ?m)))

winner-mode

Undo/Redo window configuration with C-c <left> and C-c <right>.

(winner-mode)

width/height thresholds

Prefer vertical splits on wide screens. Split vertically if width >= 145 characters

(setq split-height-threshold nil)
(setq split-width-threshold 145)

side windows

;; (setq display-buffer-alist
;;       `((,(concat "\\*.*"
;;                   "\\(Backtrace"
;;                   "\\|Compile-Log"
;;                   "\\|compilation"
;;                   "\\|Warnings"
;;                   "\\|Compile-Log"
;;                   "\\|compilation"
;;                   "\\|Calendar"
;;                   "\\|Flycheck"
;;                   "\\|Flymake"
;;                   "\\|vterm"
;;                   "\\).*\\*")
;;          (display-buffer-in-side-window)
;;          (window-height . 0.25)
;;          (side . bottom))))

(setq display-buffer-alist
      `((,(concat "\\*.*"
                  "\\(Backtrace"
                  "\\|Compile-Log"
                  "\\|compilation"
                  "\\|Warnings"
                  "\\|Compile-Log"
                  "\\|compilation"
                  "\\|Calendar"
                  "\\|Flycheck"
                  "\\|Flymake"
                  "\\|vterm"
                  "\\).*\\*")
         (display-buffer-in-side-window)
         (window-width . 0.40)
         (side . right))))

window divider

(setq-default window-divider-default-places t)
(setq-default window-divider-default-bottom-width 2)
(setq-default window-divider-default-right-width 2)
(window-divider-mode t)
(set-face-attribute 'window-divider nil
                    :foreground "#b16e75")

org-mode

Org Font Setup

(defun my-org-font-face-setup ()
  "Set necessary font faces in `org-mode'."

  (dolist (face '((org-level-1 . 1.25)
                  (org-level-2 . 1.15)
                  (org-level-3 . 1.05)
                  (org-level-4 . 1.0)
                  (org-level-5 . 1.0)
                  (org-level-6 . 1.0)
                  (org-level-7 . 1.0)
                  (org-level-8 . 1.0)))
    (set-face-attribute (car face) nil
                        :height (cdr face)
                        :weight 'bold))

  ;; fixed-pitch face setup
  (dolist (face '(org-table
                  org-formula org-block
                  org-code org-verbatim
                  org-checkbox line-number
                  org-special-keyword
                  line-number-current-line))
    (set-face-attribute face nil :inherit 'fixed-pitch))

  (dolist (face '(org-table
                  org-document-info-keyword
                  org-meta-line))
    (set-face-attribute face nil
                        :foreground 'unspecified
                        :inherit '(shadow fixed-pitch))))

Org

(use-package org
  :pin org
  :commands
  (org-capture org-agenda)
  :hook
  (org-mode . (lambda ()
                (my-org-font-face-setup)
                (if my-gnu/linux-laptop-p (flyspell-mode))
                (org-indent-mode)
                (visual-line-mode 1)))
  :init
  (if my-android-phone-p
      (setq org-directory "~/storage/Org")
    (setq org-directory "~/Org"))
  :custom
  (org-ellipsis "")
  (org-hide-emphasis-markers t)
  (org-startup-folded 'overview)
  :config
  (require 'org-habit)
  (add-to-list 'org-modules 'org-habit)
  (setq org-habit-graph-column 60)
  (advice-add 'org-refile
              :after 'org-save-all-org-buffers)

  ;; Add a clock sound for `org-timer-set-timer'
  (let ((sound-file "~/.local/data/bell.wav"))
    (if (file-exists-p sound-file)
        (setq org-clock-sound sound-file))))

Org Capture

(global-set-key (kbd "C-c c") #'org-capture)

(setq org-capture-templates
      `( ("t" "Todo item" entry
          (file+headline "inbox.org" "Tasks")
          ,(concat "* %^{|TODO|BLOG|SOMEDAY} %^{Title}\n"
                   ":PROPERTIES:\n"
                   ":CREATED: %U\n"
                   ":END:\n"
                   "Note: %?\n"))

         ("i" "Project idea" entry
          (file+headline "inbox.org" "Project Ideas")
          ,(concat "* PROJECT %^{Title}\n"
                   ":PROPERTIES:\n"
                   ":CREATED: %U\n"
                   ":END:\n"
                   "Note: %?\n"))

         ("n" "Quick note" entry
          (file+headline "inbox.org" "Quick Notes")
          ,(concat "* %^{Title}\n"
                   ":PROPERTIES:\n"
                   ":CREATED: %U\n"
                   ":END:\n"
                   "Note: %?")
          :empty-lines-after 1)

         ("r" "Reading list item" entry
          (file+headline "inbox.org" "Reading List")
          ,(concat "* READ %^{Description}\n"
                   ":PROPERTIES:\n"
                   ":CREATED: %U\n"
                   ":TOPIC: %^{Topic}\n"
                   ":END:\n"
                   "URL: %(current-kill 0)\n"
                   "Note: %?\n")
          :empty-lines-after 1)))

Org Agenda

(global-set-key (kbd "C-c a") #'org-agenda)

(setq org-agenda-files (list (expand-file-name "inbox.org" org-directory)))
(setq org-agenda-start-with-log-mode t)
(setq org-log-done 'time)
(setq org-log-into-drawer t)
(setq org-todo-keywords
      '((sequence "TODO(t)" "SOMEDAY(s)" "|" "DONE(x!)")
        (sequence "READ(r)" "BLOG(b)" "PROJECT(p)" "|" "DONE(x!)")))

(setq org-enforce-todo-dependencies t)
(setq org-track-ordered-property-with-tag t)
(setq org-agenda-dim-blocked-tasks t)

org custom agenda

Org Bullets

(use-package org-bullets
  :hook (org-mode . org-bullets-mode)
  :custom
  (org-bullets-bullet-list '("")))

Structure Templates

(with-eval-after-load 'org
  (require 'org-tempo)
  (dolist (language '(("el" . "src emacs-lisp")
                      ("py" . "src python")
                      ("sh" . "src shell")
                      ("js" . "src js")))
    (add-to-list 'org-structure-template-alist language)))

org babel languages

(with-eval-after-load 'org
  (org-babel-do-load-languages
   'org-babel-load-languages
   '((python . t)
     (emacs-lisp . t))))

(setq org-confirm-babel-evaluate nil)

org-roam

(use-package org-roam
  :init
  (setq org-roam-directory (expand-file-name "Roam" org-directory))
  :custom
  (org-roam-dailies-directory "Journal/")
  (org-roam-db-location "~/.cache/org-roam.db")
  (org-roam-capture-templates
   '(("d" "default" plain "%?"
      :target
      (file+head "%<%Y%m%d%H%M%S>-${slug}.org" "#+title: ${title}\n\n")
      :unnarrowed t)
     ("f" "fleeting" plain "%?"
      :target
      (file+head "%<%Y%m%d%H%M%S>-${slug}.org" "#+TITLE: ${title}\n#+FILETAGS: :fleeting:\n")
      :unnarrowed t)))
  :bind (("C-c n f" . org-roam-node-find)
         ("C-c n i" . org-roam-node-insert)
         ("C-c n l" . org-roam-buffer-toggle)
         :map org-mode-map
         ("C-M-i" . completion-at-point)
         :map org-roam-dailies-map
         ("Y" . org-roam-dailies-capture-yesterday)
         ("T" . org-roam-dailies-capture-tomorrow))
  :bind-keymap
  ("C-c n d" . org-roam-dailies-map)
  :config
  (require 'org-roam-dailies)
  (org-roam-db-autosync-enable))

Development setup

editorconfig

(use-package editorconfig
  :ensure t)

prog-mode-hook

(setq-default c-basic-offset 2)
(setq-default indent-tabs-mode nil)
(setq-default lsp-enable-indentation nil) ;; EXPERIMENTAL

(add-hook 'prog-mode-hook
          (lambda ()
            ;;(local-set-key (kbd "C-<tab>") 'yas-expand)
            ;; (set-fringe-style 8)
            (editorconfig-mode 1)
            (hl-line-mode)
            (electric-pair-local-mode)))

project management

(use-package project
  :demand t)

(use-package magit
  :commands magit-status
  :config
  (setq magit-display-buffer-function
        #'magit-display-buffer-same-window-except-diff-v1))

completions with company-mode

Enable company-mode in prog-mode, text-mode, and org-mode buffers. Company mode can be enabled globally with M-x global-company-mode RET.

(use-package company
  :commands (company-mode global-company-mode)
  :hook ((prog-mode text-mode org-mode) . company-mode)
  :bind
  (:map company-active-map
        ("<tab>" . company-complete-selection))
  :config
  (setq company-minimum-prefix-length 2)
  (setq company-idle-delay 0.2))

syntax checking with flycheck-mode

The documentation for flycheck-mode can be found on their website https://flycheck.org. You can also run M-x flycheck-manual RET to open it in your web browser.

(use-package flycheck
  :commands (global-flycheck-mode flycheck-mode)
  :hook (prog-mode . flycheck-mode))

snippets

(use-package yasnippet
  :requires warnings  ;; for `warning-suppress-types' below
  :config
  (setq yas-snippet-dirs
        `( ,(concat user-emacs-directory "snippets")))
  (add-to-list 'warning-suppress-types '(yasnippet backquote-change))
  (yas-global-mode 1)
  (yas-reload-all))

language server protocol

eglot

https://joaotavora.github.io/eglot

(use-package eglot
  :disabled t
  :ensure nil
  :commands eglot
  :autoload eglot-ensure
  :config
  (fset #'jsonrpc--log-event #'ignore)
  (setq eglot-events-buffer-size 0)
  (setq eglot-sync-connect nil)
  (setq eglot-connect-timeout nil)
  (setq eglot-autoshutdown t)
  (setq eglot-send-changes-idle-time 3)
  (setq eglot-ignored-server-capabilities '( :documentHighlightProvider)))

;; exclude modes from eglot
;; (defun maybe-start-eglot ()
;;   "Exlude some mode from eglot."
;;   (let ((disabled-modes '(emacs-lisp-mode dockerfile-ts-mode)))
;;     (unless (apply 'derived-mode-p disabled-modes)
;;       (eglot-ensure))))

;; (add-hook 'prog-mode-hook #'maybe-start-eglot)

lsp-mode

(use-package lsp-mode
  ;; :disabled t
  :commands
  (lsp lsp-deferred)
  :hook
  ((java-mode java-ts-mode python-mode python-ts-mode go-mode) . lsp)
  :init
  (setq lsp-headerline-breadcrumb-enable 'nil)
  (setq lsp-keymap-prefix "C-c l")
  :config
  (setq-default lsp-clients-clangd-args
                '("--cross-file-rename"
                  "--enable-config"
                  "--fallback-style=WebKit"
                  "--clang-tidy"
                  "--clang-tidy-checks='*'"
                  "--suggest-missing-includes"
                  "--header-insertion=iwyu"
                  "--header-insertion-decorators=0")))

(use-package lsp-ui
  ;; :disabled t
  :after lsp-mode)

Multiple cursors

(use-package multiple-cursors
  :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))

Language specific configurations

html

Examples on how to use emmet-mode can be found at https://github.com/smihica/emmet-mode.

(use-package web-mode
  :mode ("\\.html?$" "\\.djhtml$" "\\.mustache\\'" "\\.phtml\\'"
         "\\.as[cp]x\\'" "\\.erb\\'" "\\.hbs\\'" "\\.jsp\\'")
  :config
  (setq web-mode-markup-indent-offset 2)
  (setq web-mode-css-indent-offset 2)
  (setq web-mode-code-indent-offset 2)
  (setq web-mode-enable-html-entities-fontification t)
  (setq web-mode-enable-current-column-highlight t)
  (setq web-mode-auto-close-style 2))

(use-package emmet-mode
  :hook ((web-mode css-mode sgml-mode) . emmet-mode))

python

(use-package python
  :interpreter
  ("python" . python-mode)
  ("python3" . python-mode))

(use-package pyvenv
  :disabled t)

javascript/typescript

(use-package js
  :interpreter "node"
  :init
  (setq js-jsx-syntax t)
  :config
  (setq js-indent-level 2))

(use-package typescript-mode
  :mode "\\.ts\\'"
  :config
  (setq typescript-indent-level 2))

emacs-lisp

(add-hook 'emacs-lisp-mode-hook
          (lambda ()
            (setq-local company-backends
                        '(company-elisp
                          company-files
                          company-yasnippet))
            (company-mode)))

java

(use-package lsp-java
  :hook (java-mode . lsp-java))
(if (not my-windows-laptop-p)
    (use-package java-ts-mode
      :config
      (setq java-ts-mode-indent-offset 2)))

(use-package eglot-java
  :disabled t
  :init
  (fset #'eglot-path-to-uri #'eglot--path-to-uri)
  :bind
  (:map eglot-java-mode-map
        ("C-c l n" . eglot-java-file-new)
        ("C-c l x" . eglot-java-run-main)
        ("C-c l t" . eglot-java-run-test)
        ("C-c l N" . eglot-java-project-new)
        ("C-c l T" . eglot-java-project-build-task)
        ("C-c l R" . eglot-java-project-build-refresh))
  :custom
  (eglot-java-server-install-dir
   (concat user-emacs-directory "var/eclipse.jdt.ls"))

  (eglot-java-junit-platform-console-standalone-jar
   (concat user-emacs-directory
           "var/junit-platform-console-standalone"
           "/junit-platform-console-standalone.jar"))
  (eglot-java-eclipse-jdt-cache-directory
   (concat user-emacs-directory "var/eglot-java-eclipse-jdt-cache")))

Formatting java buffers

(defvar my-google-java-format-jar-file
  (concat (file-name-as-directory (concat user-emacs-directory "var"))
          "google-java-format.jar")
  "Complete path of the google java formatter jar file.")

(defun my-download-latest-google-java-format-jar-file ()
  "Download the latest google java formatter jar file from github."
  (interactive)
  (require 'url)
  (url-retrieve
   "https://github.com/google/google-java-format/releases/latest"
   (lambda (status)
     (when (plist-get status :redirect)
       (let* ((redirect-url (plist-get status :redirect))
              (latest-version (substring redirect-url
                                         (+ 2 (string-search "/v" redirect-url)))))
         (my-download-google-java-format-jar-file latest-version))))))

(defun my-download-google-java-format-jar-file (version)
  "Download the google java formatter jar file of given VERSION from github."
  (let ((url (format (concat "https://github.com"
                             "/google/google-java-format"
                             "/releases/download/v%s/google-java-format-%s-all-deps.jar")
              version version)))
    (url-copy-file url my-google-java-format-jar-file 1)))

(defun my-format-java-buffer ()
  "Format current java buffer to comply with google style."
  (interactive nil 'java-mode)
  (let ((temp-buffer (generate-new-buffer "*java-format*"))
        (temp-file (make-temp-file "java-format-error" nil))
        ;; Always use 'utf-8-unix' & ignore the buffer coding system.
        (default-process-coding-system '(utf-8-unix . utf-8-unix)))
    (call-process-region nil nil "java" nil
                         `(,temp-buffer ,temp-file) nil
                         "-jar" my-google-java-format-jar-file "-")
    (if (> (buffer-size temp-buffer) 0)
        ;; Replace buffer with formatted code
        (replace-buffer-contents temp-buffer)
      (message "Error: could not format current buffer!"))
    (kill-buffer temp-buffer)
    (delete-file temp-file)))

(add-hook 'java-mode-hook #'(lambda () (local-set-key (kbd "C-c l f") #'my-format-java-buffer)))
(add-hook 'java-ts-mode-hook #'(lambda () (local-set-key (kbd "C-c l f") #'my-format-java-buffer)))

clojure

(use-package clojure-ts-mode
  :ensure t)

(use-package cider
  :ensure t
  :config
  (setq cider-repl-display-help-banner nil))

golang

(use-package go-ts-mode
  :ensure nil
  :mode "\\.go\\'"
  :hook (go-ts-mode . format-all-mode))

Auto format buffers using format-all

(use-package format-all
  :commands (format-all-buffer format-all-region format-all-mode)
  :autoload fomat-all-ensure-formatter
  :hook
  (prog-mode . format-all-ensure-formatter)
  (python-ts-mode . format-all-mode)
  (clojure-ts-mode . format-all-mode)
  :config
  (setq-default format-all-formatters
                '(("C" (clang-format "-style=file"))
                  ("C++" (clang-format "-style=file"
                                       "--fallback-style=WebKit"))
                  ("CSS" prettier)
                  ("Emacs Lisp" emacs-lisp)
                  ("Go" gofmt)
                  ("Java" (clang-format "-style=file"))
                  ("JavaScript" prettier)
                  ("Markdown" prettier)
                  ("Python" black)
                  ("TypeScript" prettier))))

Make REST API calls using restclient

(use-package restclient
  :commands (restclient-mode))

remap major modes to user *-ts-mode

(setq major-mode-remap-alist
      '((yaml-mode . yaml-ts-mode)
        (bash-mode . bash-ts-mode)
        (js-mode . js-ts-mode)
        (typescript-mode . typescript-ts-mode)
        (json-mode . json-ts-mode)
        (css-mode . css-ts-mode)
        (python-mode . python-ts-mode)))

Tweaks with some built-in modes

dired

(use-package dired
  :ensure nil
  :commands (dired dired-jump)
  :bind (("C-x C-j" . dired-jump)
         ("C-x C-d" . dired))
  :hook
  (dired-mode . dired-hide-details-mode)
  :config
  (setq dired-listing-switches "-lhAX --group-directories-first"))


(use-package all-the-icons-dired
  :commands all-the-icons-dired-mode
  :hook
  (dired-mode . (lambda ()
                  (if (window-system)
                      (all-the-icons-dired-mode)))))

tab-bar-mode

(use-package tab-bar
  :config
  (setq tab-bar-show nil)  ;; don't show the tab-bar
  (setq tab-bar-new-tab-choice t) ;; open the current buffer in new tab
  (setq tab-bar-close-button-show nil)
  (setq tab-bar-new-button-show nil)
  (setq tab-bar-close-last-tab-choice 'tab-bar-mode-disable)
  (tab-bar-mode))

ibuffer

(global-unset-key (kbd "C-x C-b"))
(global-set-key (kbd "C-x C-b") 'ibuffer)

Center buffers with olivetti-mode

(unless my-android-phone-p
  (use-package olivetti
    :commands olivetti-mode
    :hook
    ((org-mode Info-mode) . olivetti-mode)
    :config
    (set-default 'olivetti-body-width 100)))

elfeed

(use-package elfeed
  :hook
  (elfeed-show-mode . visual-line-mode)
  :config
  (set-face-attribute 'elfeed-search-unread-title-face nil
                      :font my-fixed-pitch-font
                      :slant 'italic
                      :weight 'bold)
  (setq elfeed-feeds
   '("http://nullprogram.com/feed/"
     "https://levelofindirection.com/main.rss"
     "https://blog.petrzemek.net/feed/"
     "https://planet.emacslife.com/atom.xml")))

Use IRC with erc

(use-package erc
  :commands
  (erc-tls erc)
  :config
  (setq erc-server "irc.libera.chat")
  (setq erc-port 6697)
  (setq erc-prompt (lambda () (concat (buffer-name) ">")))
  (setq erc-nick "px86")
  (setq erc-fill-column 100))

Other little tweaks

Visual feedback using pulse

(use-package pulse
  :ensure nil  ; built-in package
  :autoload
  (pulse-momentary-highlight-one-line pulse-momentary-highlight-region)
  :config
  (setq pulse-flag t)
  (setq pulse-delay 0.04)
  (set-face-attribute 'pulse-highlight-start-face nil
                      :background "#87ceeb"))


(dolist (command '(scroll-up-command
                   scroll-down-command
                   recenter-top-bottom
                   other-window
                   isearch-repeat-forward
                   isearch-repeat-backward
                   ace-window))
  (advice-add command :after
              (lambda (&rest args)
                "Momentarily highlight current line."
                (pulse-momentary-highlight-one-line (point)))))


(advice-add 'kill-ring-save :after
            (lambda (&rest args)
              "Momentarily highlight currently active region, otherwise the current line."
              (if mark-active
                  (pulse-momentary-highlight-region (region-beginning) (region-end))
                (pulse-momentary-highlight-one-line (point)))))

Launch an external terminal

The my-launch-terminal function launches a terminal in the project root of the current buffer. If project root can not be determined, or does not exists, the terminal is launched in the current working directory. The my-launch-terminal-in-cwd simply launches a terminal in the current working directory.

What terminal to launch depends on the value of the TERMINAL environment variable. If this variable is not set, xterm is assumed by default.

These functions are only enabled on my =gnu/linux= laptop.

(when my-gnu/linux-laptop-p
  ;; needed for vc-git-root function
  (require 'vc-git)

  (defun my-launch-terminal ()
    "Launch a terminal in project root or in current working directory."
    (interactive)
    (let* ((term (getenv "TERMINAL"))
           (terminal (if term term "xterm"))
           (filename (buffer-file-name))
           (dir (if filename
                    (vc-git-root filename)
                  nil))
           (default-directory (or dir
                                  default-directory)))
      (start-process "Terminal" nil terminal)))

  (defun my-launch-terminal-in-cwd ()
    "Launch a terminal in the current working directory."
    (interactive)
    (let* ((term (getenv "TERMINAL"))
           (terminal (if term term "xterm")))
      (start-process "Terminal" nil terminal)))

  (global-set-key (kbd "s-t") #'my-launch-terminal))

Create new blog posts

(defun my-normalize-string-for-filename (string)
  "Downcase and remove special charactes from string."
  (downcase (string-join
             (split-string string
                           "[] ~!@#$%^&*()+={}[\\|/;:,.'\"<>?]+" t "[ _-]") "-")))

(defun my-filter-list (predicate list)
  "Filter LIST items through the PREDICATE function."
  (let ((newlist '()))
    (dolist (item list)
      (if (funcall predicate item)
          (push item newlist)))
    newlist))


(defvar my-full-name "Pushkar Raj" "My full name")
(defvar my-email "px86@protonmail.com" "My email address.")
(defvar my-blog-dir
  (expand-file-name "~/Programming/repos/px86.github.io")
  "My blog's root directory.")

(defun my-new-blog-post ()
  "Create a new blog post."
  (interactive)
  (let* ((folder (completing-read "Select blog subfolder: "
                                  (my-filter-list
                                   (lambda (f)
                                     (and (file-directory-p f)
                                          (not (member (file-name-base f)
                                                       '("." ".." "assets")))))
                                   (directory-files (expand-file-name "source" my-blog-dir) t))
                                  nil t))
         (title (read-string (format "[%s] Title: " (file-name-base folder))))
         (subtitle (read-string "Subtitle: " ))
         (filename (concat (my-normalize-string-for-filename title) ".org"))
         (filepath (expand-file-name filename folder)))
    (if (file-exists-p filepath)
        "Error: Oops! file already exists"
      (find-file filepath)
      (insert (format
               (concat
                "#+TITLE: %s\n"
                "#+SUBTITLE: %s\n"
                "#+AUTHOR: %s\n"
                "#+EMAIL: %s\n"
                "#+DATE: %s\n\n")
               title
               subtitle
               my-full-name
               my-email
               (format-time-string "[%Y-%m-%d %a]"))))))

Emacs server setup

(setq initial-buffer-choice
      (lambda () (get-buffer "*dashboard*")))

(defun my-toggle-titlebar ()
  "Toggle titlebar from selected frame."
  (interactive)
  (let* ((frame (selected-frame))
         (visibility (frame-parameter frame 'undecorated)))
    (set-frame-parameter frame 'undecorated (not visibility))))

(add-hook 'server-after-make-frame-hook
          (lambda ()
            (my-set-font-variables)
            (my-set-font-faces)))

Runtime performance

;; Lower the GC threshold, again
(setq gc-cons-threshold 16000000)

Happy Hacking!!