Skip to content

pkulev/.emacs.d

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Emacs configuration

About

This is my emacs configuration, built with powers of use-package and org-mode. use-package allows declarative description of packages and it’s settings, org-mode allows to represent this configuration as document and generate config from it.

Usage

Just clone repository to ~/.config/emacs (XDG-compatible new directory) ~/.emacs.d (old directory) and run emacs.

$ git clone git@github.com:pkulev/.emacs.d.git ~/.config/emacs  # or
$ git clone git@github.com:pkulev/.emacs.d.git ~/.emacs.d
$ emacs

Where it works

Here the matrix of distributions and environments i’ve tested my configuration deployment.

Emacs/DistEmacs 26.3Emacs 27.1
Fedora 29worksnot tested
Fedora 30worksnot tested
Centos 7worksnot tested
Gentooworksworks
Ubuntu 16 WSLworksworks
Termuxworksnot tested

Table of Contents

Early init

Early init is loaded before package system and GUI is initialized.

;; -*- lexical-binding: t -*-
;; This file was tangled (automatically generated) from `readme.org'

(require 'tool-bar)

(setq load-prefer-newer t)
(when (display-graphic-p)
  (scroll-bar-mode 0))
(tool-bar-mode 0)
(menu-bar-mode 0)
(blink-cursor-mode 0)
(setq frame-resize-pixelwise t)
(add-to-list 'default-frame-alist '(fullscreen . maximized))

Package management

Initial bootstrapping and things related to package management.

;; -*- lexical-binding: t -*-
;; This file was tangled (automatically generated) from `readme.org'

Repositories

Set emacs package repositories.

(require 'package)

(setq package-archives
      (append (eval (car (get 'package-archives 'standard-value)))
              '(("melpa" . "http://melpa.org/packages/"))))

use-package

Bootstrap use-package using built-in package.el. All further configuration will be performed using it’s DSL.

(unless (package-installed-p 'use-package)
  (package-refresh-contents)
  (package-install 'use-package))

(eval-when-compile
  (require 'use-package))


(put 'use-package 'lisp-indent-function 1)

(use-package use-package-core
  :custom
  (use-package-always-defer t)
  (use-package-compute-statistics t)
  (use-package-enable-imenu-support t))

use-package dependencies

(use-package bind-key
  :ensure t
  :demand t)

(use-package delight
  :ensure t
  :demand t)

Garbage collector magic hack

(use-package gcmh
  :ensure t
  :delight
  :init
  (gcmh-mode 1))

System package management

system-packages allows to install packages via system package manager in a configurable way.

(use-package system-packages
  :ensure t
  :demand t
  :custom
  (system-packages-noconfirm t)
  :config
  ;; Termux has no `sudo'
  (when (string-match-p "termux" (getenv "PATH"))
    (setq system-packages-use-sudo t))
  ;; Overwrite guix even if it installed
  (when (string-match-p "redhat" system-configuration)
    (setq system-packages-package-manager 'dnf)))
(use-package use-package-ensure-system-package
  :ensure t
  :demand t)

Installation from sources

Quelpa allows to build and install packages from sources. quelpa-use-package is integration package.

(use-package quelpa
  :ensure t
  :demand t
  :custom (quelpa-update-melpa-p nil))

(use-package quelpa-use-package
  :ensure t
  :demand t)

File Name Handler Hack

Smart hack to slightly speed up emacs startup termporarily setting file-name-handler-alist to nil.

(use-package fnhh
  :quelpa
  (fnhh :repo "a13/fnhh" :fetcher github)
  :config
  (fnhh-mode 1))

Temporarily package installation

try installs package into temp directory without polluting .config/emacs and .emacs.d.

(use-package try
  :ensure t
  :commands (try))

Additional packages

Packages that will be used further in this config.

Anaphoric macroses

(use-package anaphora
  :ensure t)
(use-package f
  :ensure t
  :demand t)
(use-package s
  :ensure t
  :demand t)

Determine if current buffer indented with tabs or spaces

(use-package tos
  :ensure nil
  :demand t
  :quelpa
  (tos :repo "pkulev/tos.el"
       :fetcher github :upgrade t))
(use-package infer-indentation-style
  :ensure nil
  :after tos
  :preface
  (defun infer-indentation-style-js ()
    "Sets proper values depending on buffer indentation mode."
    (when (tos-buffer-tabs?)
        (setq indent-tabs-mode t)))

  (defun infer-indentation-style-python ()
    "Sets proper values depending on buffer indentation mode."
    (if (tos-buffer-tabs?)
        (setq indent-tabs-mode t
              python-indent-offset 4
              tab-width 4)))
  (provide 'infer-indentation-style))

Configuration profiler

(use-package esup
  :ensure t
  :custom
  ;; FIXME: this prevents errors
  (esup-depth 0))

Help & manuals

Better help

(use-package helpful
  :ensure t
  :demand t
  :custom
  (counsel-describe-function-function #'helpful-callable)
  (counsel-describe-variable-function #'helpful-variable)
  :bind
  (:map help-mode-map
        ("f" . helpful-callable)
        ("v" . helpful-variable)
        ("k" . helpful-key)
        ("F" . helpful-at-point)
        ("F" . helpful-function)
        ("C" . helpful-command)))

Useful examples instead of man aka tldr

(use-package tldr
  :ensure t
  :commands (tldr))

Free keys

(use-package free-keys
  :ensure t)

Which key

(use-package which-key
  :ensure t
  :defer 2
  :delight
  :config
  (which-key-mode))

Colorize info nodes

(use-package info-colors
  :ensure t
  :hook (Info-selection . info-colors-fontify-node))

Google translate

Translate in emacs! For example you can translate docstrings of messages from telega.el.

(use-package google-translate
  :ensure t
  :bind
  (:map mode-specific-map
        ("t p" . google-translate-at-point)
        ("t P" . google-translate-at-point-reverse)
        ("t t" . google-translate-query-translate)
        ("t T" . google-translate-query-translate-reverse))
  :custom
  (google-translate-default-source-language "en")
  (google-translate-default-target-language "ru"))

Customize

(use-package cus-edit
  :ensure nil
  :custom
  (custom-file (concat user-emacs-directory "custom-file.el")))

Host-specific private source of data

(use-package my/private-el
  :ensure nil
  :preface
  (defun my/private-el-load ()
    (load (concat user-emacs-directory "private.el") 'noerror))
  (provide 'my/private-el)
  :init
  (my/private-el-load))

GUI Components / Appearance

Don’t show intrusive startup message

(defun display-startup-echo-area-message ())

Emacs variables that defined in C source code

(use-package emacs
  :ensure nil
  :init
  (put 'narrow-to-page 'disabled nil)
  (put 'narrow-to-region 'disabled nil)
  (put 'downcase-region 'disabled nil)
  :hook
  ;; I want to see trailing spaces
  (prog-mode . (lambda () (setq show-trailing-whitespace t)))
  :custom
  (use-dialog-box nil "Dialogs via minibuffer only.")
  (scroll-step 1 "Scroll line by line.")
  (scroll-margin 4 "Top and bottom scrolling margin.")
  (scroll-conservatively 101 "If >100 then never recenter point.")
  (inhibit-splash-screen t "Don't show the splash screen.")
  (initial-scratch-message nil "Disable initial scratch message.")

  (indicate-empty-lines t "Visually indicate empty lines.")
  (indicate-buffer-boundaries 'left "Show buffer boundaries at left fringe.")
  (indent-tabs-mode nil "Tabs are evil.")
  (tab-width 4 "Sane default for me.")
  (read-process-output-max (* 1024 1024) "Increase amount of data read from processes."))

Emacs C source code

I quite often jump into C code from describe-* buffers.

(use-package find-func
  :ensure nil
  :custom
  (find-function-C-source-directory (expand-file-name "~/proj/emacs") "Emacs sources."))

Autorevert

(use-package autorevert
  :ensure nil
  :delight auto-revert-mode)

Frame

Disable suspending (C-z), it’s annoing and doesn’t work properly with WSL.

(use-package frame
  :ensure nil
  :bind
  ("C-z" . nil)
  ("C-c C-z" . nil))

Simple

(use-package simple
  :ensure nil
  :delight
  (visual-line-mode)
  :hook ((before-save . delete-trailing-whitespace))
  :config
  (defalias 'yes-or-no-p 'y-or-n-p)
  :custom
  (line-number-mode t "Show line number in modeline.")
  (column-number-mode t "Show column number in modeline.")
  (size-indication-mode t "Show file size in modeline.")
  (global-visual-line-mode t "Enable visual-line-mode."))

Highlight matching parens

(use-package paren
  :ensure nil
  :demand t
  :custom
  (show-paren-delay 0)
  :config
  (show-paren-mode t))

Fonts & faces

Fira Code font

;; TODO: fix somehow different family across OSes
(use-package faces
  :ensure nil
  :config
  (if (eq system-type 'darwin)
      (set-face-attribute 'default
                          nil
                          :family "Fira Code"
                          :weight 'semi-light
                          :width 'semi-condensed
                          :height 130)
    (set-face-attribute 'default
                        nil
                        :family "FiraCode"
                        :weight 'semi-light
                        :width 'semi-condensed
                        :height 130)))

Current line highlighting

(use-package hl-line
  :ensure nil
  :config
  (global-hl-line-mode 1)
  (set-face-background 'hl-line "#3e4446")
  (set-face-foreground 'highlight nil))

Diff highlighting

(use-package diff-hl
  :ensure t
  :defer t
  :after magit
  :hook
  (prog-mode . diff-hl-mode)
  (org-mode . diff-hl-mode)
  (dired-mode . diff-hl-dired-mode)
  (magit-post-refresh . diff-hl-magit-post-refresh))

Fringe settings

(use-package fringe
  :ensure nil
  :custom
  (fringe-mode '(8 . 0)))

reverse-im

(use-package reverse-im
  :ensure t
  :defer 1
  :config
  (reverse-im-activate "russian-computer"))

Line length indication (boundaries)

(use-package display-fill-column-indicator
  :ensure nil
  :custom
  (display-fill-column-indicator-column 100)
  :custom-face
  (fill-column-indicator ((t (:foreground "VioletRed2"))))
  :hook (prog-mode . display-fill-column-indicator-mode))

Theme

Very cool theme

(use-package zerodark-theme
  :ensure t
  :demand t
  ;;:after flycheck  ; TODO: make PR for fixing this
  :config
  (load-theme 'zerodark 'noconfirm))
  ;;(zerodark-setup-modeline-format))

Icons

(use-package all-the-icons
  :if window-system
  :ensure t
  :config
  (setq all-the-icons-mode-icon-alist
        `(,@all-the-icons-mode-icon-alist
          (package-menu-mode all-the-icons-octicon "package" :v-adjust 0.0))))
(use-package all-the-icons-dired
  :if window-system
  :ensure t
  :hook
  (dired-mode . all-the-icons-dired-mode))
(use-package all-the-icons-ivy
  :if window-system
  :ensure t
  :after ivy
  :custom
  (all-the-icons-ivy-buffer-commands '() "Don't use for buffers.")
  :config
  (unless (file-exists-p "~/.local/share/fonts/all-the-icons.ttf")
    (all-the-icons-install-fonts t))
  (all-the-icons-ivy-setup))

Whistles

(use-package time
  :ensure nil
  :custom
  (display-time-mode nil "Don't display time at modeline."))
(use-package nyan-mode
  :ensure t
  :after zerodark-mode
  :custom
  (nyan-bar-length 16)
  :config
  (nyan-mode)
  (zerodark-modeline-setup-format))
(use-package highlight-indent-guides
  :ensure t
  :defer t
  :delight
  :hook
  (prog-mode . highlight-indent-guides-mode)
  :custom
  (highlight-indent-guides-method 'character))
(use-package lisp-extra-font-lock
  :ensure t
  :custom
  (lisp-extra-font-lock-modes '(emacs-lisp-mode lisp-mode))
  :config
  (lisp-extra-font-lock-global-mode 1))
(use-package beacon
  ;; TODO: fix animation
  :disabled
  :ensure t
  :defer 5
  :config
  (beacon-mode 1))

Show line feed char (^L) as line. It’s useful in help windows like C-c C-h.

(use-package form-feed
  :ensure t
  :defer 5
  :config
  (global-form-feed-mode))

Buffer management

ibuffer

(use-package ibuffer
  :ensure nil
  :defer t
  :config
  (defalias 'list-buffers 'ibuffer))

ace-window

Jump to window by number.

(use-package ace-window
  :ensure t
  :bind ("C-x w" . ace-window))

Dired

Dired is very powerful file manager with tons of extensions.

(use-package dired
  :ensure nil
  :bind ([remap list-directory] . dired)
  :custom
  (dired-recursive-deletes 'top "Confirm deletion for all top non-empty directories.")
  (dired-dwim-target t "Try to guess target for actions."))

Extra dired things.

(use-package dired-x
  :ensure nil)
(use-package dired-subtree
  :ensure t
  :after dired
  :bind
  (:map dired-mode-map
        ([?\t] . dired-subtree-toggle)))

Hide dotfiles.

(use-package dired-hide-dotfiles
  :ensure t
  :bind
  (:map dired-mode-map
        ("." . dired-hide-dotfiles-mode))
  :hook
  (dired-mode . dired-hide-dotfiles-mode))

Image preview support for dired.

(use-package image-dired
  :ensure nil)

(use-package image-dired+
  :ensure t
  :after image-dired)

Navigation

Window navigation

(use-package window
  :ensure nil
  :bind ("M-o" . other-window))

Paragraph movement

(use-package paragraphs
  :ensure nil
  :preface (provide 'paragraphs)
  :bind (("M-n" . #'forward-paragraph)
         ("M-p" . #'backward-paragraph)))

Imenu jumps

(use-package imenu
  :ensure nil
  :bind (("C-c C-j" . imenu)
         ("M-i" . imenu))
  :custom
  (imenu-auto-rescan t)
  (imenu-use-popup-menu nil))

Avy

(use-package avy
  :ensure t
  :bind (("C-c j" . avy-goto-word-or-subword-1)
         ("C-:" . avy-goto-char)
         ("C-'" . avy-goto-char-2)))

Editing

Delete selection

(use-package delsel
  :ensure nil
  :config
  (delete-selection-mode t))

Multiple cursors

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

Copy & Paste things

Copy as format

(use-package copy-as-format
  :ensure t
  :bind
  (:prefix-map
   copy-as-format-prefix-map
   :prefix "C-x c"
   ("f" . copy-as-format)
   ("a" . copy-as-format-asciidoc)
   ("b" . copy-as-format-bitbucket)
   ("d" . copy-as-format-disqus)
   ("g" . copy-as-format-github)
   ("l" . copy-as-format-gitlab)
   ("c" . copy-as-format-hipchat)
   ("h" . copy-as-format-html)
   ("j" . copy-as-format-jira)
   ("m" . copy-as-format-markdown)
   ("w" . copy-as-format-mediawiki)
   ("o" . copy-as-format-org-mode)
   ("p" . copy-as-format-pod)
   ("r" . copy-as-format-rst)
   ("s" . copy-as-format-slack)))

Links

Useful package for manipulating links anywhere in emacs.

(use-package link-hint
  :ensure t
  :bind
  (:map ctl-x-map
        ("M-l c" . link-hint-copy-link)
        ("M-l o" . link-hint-open-link)
        ("M-l p" . link-hint-open-link-at-point)))

Password management

Emacs interface for excellent pass utility.

(use-package password-store
  :ensure t)

Terminals

vterm

(use-package vterm
  :ensure t
  :commands (vterm)
  :custom
  (vterm-max-scrollback 100000))

vterm-toggle

(use-package vterm-toggle
  :ensure t
  :after vterm
  :bind
  (:map ctl-x-map
        ("C-v" . vterm)
        ("v" . vterm-toggle)
        ("p" . vterm-toggle-backward)
        ("n" . vterm-toggle-forward)))

Shell tools

(use-package shell
  :ensure nil
  :custom
  (explicit-shell-file-name (executable-find "zsh") "Default inferior shell."))

Eshell appearance

Eshell is great tool for everyday tasks.

TODO: add short tutorial here

Show command execution status at fringe.

(use-package eshell-fringe-status
  :ensure t
  :hook
  (eshell-mode . eshell-fringe-status-mode))

Prompt customization.

(use-package eshell-prompt-extras
  :ensure t
  ;; FIXME: :commands doesn't work
  ; :commands (eshell eshell-toggle)
  :demand t
  :after (eshell esh-opt)
  :custom
  (eshell-prompt-function #'epe-theme-lambda))

Eshell completion and suggestion

(use-package esh-autosuggest
  :ensure t
  :hook
  (eshell-mode . esh-autosuggest-mode))
(use-package esh-help
  :ensure t
  :defer t
  :config
  (setup-esh-help-eldoc))

Eshell z port.

(use-package eshell-z
  :ensure t
  :after eshell)

Quickly open eshell (or something else) in project

(use-package eshell-toggle
  :ensure t
  :bind
  ("M-`" . eshell-toggle)
  :custom
  (eshell-toggle-use-projectile-root t)
  (eshell-toggle-run-command "ls"))

Path

Syncing PATH

(use-package exec-path-from-shell
  :ensure t
  :defer 1
  :custom
  (exec-path-from-shell-check-startup-files nil)
  :config
  (exec-path-from-shell-initialize))

Autosaves

Don’t spawn them across the filesystem.

(use-package files
  :ensure nil
  :custom
  (require-final-newline t)
  (delete-old-versions t)
  (backup-directory-alist
   `((".*" . ,(expand-file-name (concat user-emacs-directory "autosaves/")))))
  (auto-save-file-name-transforms
   `((".*" ,(expand-file-name (concat user-emacs-directory "autosaves/")) t))))
(use-package recentf
  :defer 0.1
  :custom
  (recentf-auto-cleanup 30)
  :config
  (recentf-mode)
  (run-with-idle-timer 10 t 'recentf-save-list))

Quick emacs configuration file access

(use-package my-config
  :ensure nil
  :after counsel
  :preface
  (defun my-config-open ()
    (interactive)
    (find-file (concat user-emacs-directory "init.el")))

  (defun my-config-open-readme ()
    (interactive)
    (find-file (concat user-emacs-directory "readme.org")))

  (defun my-config-open-private ()
    (interactive)
    (find-file (concat user-emacs-directory "private.el")))

  (defun my-config-eval ()
    (interactive)
    (load-file (concat user-emacs-directory "init.el")))

  (defun my-config-open-and-search ()
    (interactive)
    (my-config-open)
    (counsel-grep-or-swiper))

  (provide 'my-config)

  :bind
  (:map mode-specific-map
        ("e o" . #'my-config-open)
        ("e r" . #'my-config-open-readme)
        ("e p" . #'my-config-open-private)
        ("e e" . #'my-config-eval)
        ("e s" . #'my-config-open-and-search)))

Autocomplete

Prescient

(use-package prescient
  :ensure t
  :defer 0.5)

Company

(use-package company
  :ensure t
  :delight
  :bind
  (:map company-active-map
        ("C-n" . company-select-next-or-abort)
        ("C-p" . company-select-previous-or-abort))
  :hook
  (after-init . global-company-mode))
(use-package company-quickhelp
  :ensure t
  :custom
  (company-quickhelp-delay 3)
  :config
  (company-quickhelp-mode 1))
(use-package company-shell
  :ensure t
  :config
  (add-to-list 'company-backends 'company-shell))

Counsel

The silver searcher (ag) is the faster alternative for grep.

(use-package ag
  :ensure t)
(use-package counsel
  :ensure t
  :delight
  :defer nil
  :bind (([remap menu-bar-open] . counsel-tmm)
         ([remap insert-char] . counsel-unicode-char))
  :config
  (counsel-mode))
(use-package counsel-projectile
  :ensure t
  :after ag counsel projectile
  :bind
  ("C-c p s" . counsel-projectile-ag)
  :config
  (counsel-projectile-mode))
(use-package counsel-dash
  :ensure t
  :after counsel eww
  :requires eww
  :bind
  ;; (:map mode-specific-map ("d i" . counsel-dash-install-docset)
  ;;                         ("d u" . counsel-dash-uninstall-docset))
  ;;                          (""))
  :config
  (add-hook 'python-mode-hook (lambda () (setq-local counsel-dash-docsets '("Python"))))
  :custom
  (counsel-dash-browser-func 'eww-browse-url))

Swiper

(use-package swiper
  :ensure t
  :delight
  :defer nil
  :bind
  ([remap isearch-forward] . swiper-thing-at-point)
  ([remap isearch-backward] . swiper-thing-at-point))

Ivy

(use-package ivy
  :ensure t
  :delight
  :custom
  (ivy-use-virtual-buffers t)
  (ivy-re-builders-alist '((t . ivy--regex-plus) (t . ivy--regex-fuzzy)))
  (ivy-count-format "%d/%d " "Show anzu-like counter.")
  (ivy-use-selectable-prompt t "Make the prompt line selectable.")
  :custom-face
  (ivy-current-match ((t (:inherit 'hl-line))))
  :bind
  (:map ivy-minibuffer-map
        ("C-r" . ivy-previous-line-or-history))
  :config
  (ivy-mode t))

(use-package ivy-rich
  :ensure t
  :after ivy
  :config
  (ivy-rich-mode))
(use-package ivy-prescient
  :ensure t
  :after ivy prescient
  :defer 4
  :config
  (ivy-prescient-mode))

Containers and remote servers

Tramp

(use-package tramp
  :ensure nil
  :defer t
  :custom
  (tramp-terminal-type "tramp" "This allows to distinguish TRAMP from others.")
  (tramp-default-method "ssh" "SSH is slightly faster that default SCP."))

;; TODO
(use-package counsel-tramp
  :after counsel tramp
  :hook ((counsel-tramp-pre-counsel . (lambda () (projectile-mode 0)))
         (consel-tramp-quit . (lambda () (projectile-mode 1))))
  :bind
  (:map mode-specific-map ("s s" . #'counsel-tramp)))

Save local files as root

(use-package sudo-edit
  :ensure t
  :bind
  (:map ctl-x-map
        ("M-s" . #'sudo-edit)))

Docker

(use-package docker
  :ensure t
  :bind
  (:map mode-specific-map
        ("d" . docker)))

(use-package dockerfile-mode
  :ensure t
  :defer t
  :mode "Dockerfile\\'")

(use-package docker-compose-mode
  :ensure t
  :defer t)

Programming modes

Common

Snippets

(use-package yasnippet
  :ensure t
  :defer 2
  :hook (prog-mode . yas-minor-mode)
  :config
  (yas-reload-all))

Syntax checking and linting

(use-package flycheck
  :ensure t
  :delight
  :custom
  (flycheck-clang-language-standard "c++17")
  (flycheck-cppcheck-standards '("c++17"))
  (flycheck-emacs-lisp-load-path 'inherit)
  :init (global-flycheck-mode))
(use-package compile
  :ensure nil
  :bind ([f5] . recompile))
(use-package ispell
  :ensure nil)
(use-package smart-comment
  :ensure t
  :bind ("M-;" . smart-comment))
(use-package fixmee
  :ensure t
  :delight
  (button-lock-mode)
  (fixmee-mode)
  :hook (prog-mode . global-fixmee-mode)
  :init (require 'button-lock))

.env projectile integration

(use-package dotenv
  :ensure nil
  :after projectile
  :demand t
  :quelpa
  (dotenv :repo "pkulev/dotenv.el"
          :fetcher github :upgrade t)
  :init
  (defun dotenv-projectile-hook ()
    "Projectile hook."
    (dotenv-update-project-env (projectile-project-root)))
  :config
  (add-to-list 'prog-mode-hook 'dotenv-projectile-hook))

Projectile

;; TODO: c2 projectile integration
(use-package projectile
  :ensure t
  :defer nil
  :bind
  (:map mode-specific-map ("p" . projectile-command-map))
  :delight '(:eval (concat " [" (projectile-project-name) "]"))
  :custom
  (projectile-completion-system 'ivy)
  :config
  (projectile-mode))

Metrics

SLOC counting.

(use-package sloc
  :ensure nil
  :quelpa
  (sloc :repo "leoliu/sloc.el"
        :fetcher github :upgrade t))

Translations

(use-package po-mode
  :ensure t)

C/C++

(use-package cc-vars
  :ensure nil
  :hook
  (c-mode-hook . (lambda () (c-set-style "k&r")))
  (c++-mode-hook . (lambda () (c-set-style "k&r")))
  :custom
  (c-basic-offset 4))

Build systems

Meson

(use-package meson-mode
  :ensure t)

GLSL

OpenGL Shader Language

(use-package glsl-mode
  :ensure t)
(use-package company-glsl
  :ensure t
  :if (executable-find "glslangValidator")
  :config
  (add-to-list 'company-backends 'company-glsl))

Lisp

;; TODO: parinfer was removed from MELPA and archived
;; make parinfer-rust-mode work under M1 or use something else like lispy

(use-package smartparens
  :ensure smartparens
  :hook (((clojure-mode
           emacs-lisp-mode
           common-lisp-mode
           scheme-mode
           lisp-mode
           racket-mode
           fennel-mode
           cider-repl-mode
           racket-repl-mode
           geiser-repl-mode
           inferior-lisp-mode
           inferior-emacs-lisp-mode
           sly-mrepl-mode)
          . smartparens-strict-mode)
         ((eval-expression-minibuffer-setup
           lisp-data-mode)
          . aorst/minibuffer-enable-sp)
         (prog-mode . smartparens-mode))
  :bind (:map smartparens-mode-map
         ("C-M-q" . sp-indent-defun)
         :map smartparens-strict-mode-map
         (";" . sp-comment))
  :custom
  (sp-highlight-pair-overlay nil)
  (sp-highlight-wrap-overlay nil)
  (sp-highlight-wrap-tag-overlay nil)
  (sp-wrap-respect-direction t)
  (sp-show-pair-delay 0)
  (sp-echo-match-when-invisible nil)
  :config
  (require 'smartparens-config)
  (add-to-list 'sp-lisp-modes 'fennel-mode t)
  (sp-use-paredit-bindings)
  (define-key smartparens-mode-map (kbd "M-r") 'sp-rewrap-sexp) ; needs to be set manually, because :bind section runs before config
  (defun aorst/minibuffer-enable-sp ()
    "Enable `smartparens-strict-mode' in the minibuffer, during `eval-expression'."
    (setq-local comment-start ";")
    (sp-local-pair 'minibuffer-pairs "'" nil :actions nil)
    (sp-local-pair 'minibuffer-pairs "`" nil :actions nil)
    (sp-update-local-pairs 'minibuffer-pairs)
    (smartparens-strict-mode 1))
  (defun aorst/wrap-fix-cursor-position (_ action _)
    "Set cursor position inside expression when wrapping."
    (when (and (eq action 'wrap)
               (eq (point)
                   (marker-position (sp-get sp-last-wrapped-region :beg))))
      (goto-char (sp-get sp-last-wrapped-region :beg-in))))
  (dolist (paren '("(" "[" "{"))
    (sp-pair paren nil :post-handlers '(:add aorst/wrap-fix-cursor-position))))

(use-package parinfer
  :disabled
  :ensure t
  :delight '(:eval (concat " p:" (symbol-name (parinfer-current-mode))))
  :hook ((emacs-lisp-mode . parinfer-mode)
         (common-lisp-mode . parinfer-mode)
         (clojure-mode . parinfer-mode)))

Interactive macro-expander.

(use-package macrostep
  :ensure t
  :bind
  (:map emacs-lisp-mode-map
        ("C-x m e" . #'macrostep-expand)
        ("C-x m c" . #'macrostep-collapse)
        ("C-x m m" . #'macrostep-mode)))

Emacs Lisp

(use-package elisp-mode
  :ensure nil
  :delight "elisp"
  :commands aorst/emacs-lisp-indent-function
  :hook (emacs-lisp-mode . aorst/emacs-lisp-setup)
  :bind (:map emacs-lisp-mode-map
         ("C-c C-M-f" . aorst/indent-buffer))
  :config
  (defun aorst/emacs-lisp-indent-function (indent-point state)
    "A replacement for `lisp-indent-function'.
Indents plists more sensibly. Adapted from DOOM Emacs:
https://github.com/hlissner/doom-emacs/blob/b03fdabe4fa8a07a7bd74cd02d9413339a485253/modules/lang/emacs-lisp/autoload.el#L91"
    (let ((normal-indent (current-column))
          (orig-point (point))
          target)
      (goto-char (1+ (elt state 1)))
      (parse-partial-sexp (point) calculate-lisp-indent-last-sexp 0 t)
      (cond ((and (elt state 2)
                  (or (not (looking-at-p "\\sw\\|\\s_"))
                      (eq (char-after) ?:)))
             (unless (> (save-excursion (forward-line 1) (point))
                        calculate-lisp-indent-last-sexp)
               (goto-char calculate-lisp-indent-last-sexp)
               (beginning-of-line)
               (parse-partial-sexp (point) calculate-lisp-indent-last-sexp 0 t))
             (backward-prefix-chars)
             (current-column))
            ((and (save-excursion
                    (goto-char indent-point)
                    (skip-syntax-forward " ")
                    (not (eq (char-after) ?:)))
                  (save-excursion
                    (goto-char orig-point)
                    (and (eq (char-after) ?:)
                         (eq (char-before) ?\()
                         (setq target (current-column)))))
             (save-excursion
               (move-to-column target t)
               target))
            ((let* ((function (buffer-substring (point) (progn (forward-sexp 1) (point))))
                    (method (or (function-get (intern-soft function) 'lisp-indent-function)
                                (get (intern-soft function) 'lisp-indent-hook))))
               (cond ((or (eq method 'defun)
                          (and (null method)
                               (> (length function) 3)
                               (string-match-p "\\`def" function)))
                      (lisp-indent-defform state indent-point))
                     ((integerp method)
                      (lisp-indent-specform method state indent-point normal-indent))
                     (method
                      (funcall method indent-point state))))))))
  (defun aorst/emacs-lisp-setup ()
    (setq-local lisp-indent-function
                #'aorst/emacs-lisp-indent-function))
  (defun org-babel-edit-prep:emacs-lisp (_info)
    "Setup Emacs Lisp buffer for Org Babel."
    (setq lexical-binding t)
    (setq-local flycheck-disabled-checkers '(emacs-lisp-checkdoc))))
(use-package flycheck-package
  :ensure t
  :hook (emacs-lisp-mode . flycheck-package-setup))

Clojure

(use-package cider
  :ensure t)

Common Lisp

(use-package lisp-mode
  :disabled
  :ensure nil
  :after flycheck
  :hook ((lisp-mode . (lambda () (setq flycheck-enabled-checkers '(sblint)))))
  :config
  (flycheck-define-checker sblint
    "A Common Lisp checker using `sblint'."
    ;; :command ("sblint" source)
    :command ("echo ok" source)
    :error-patterns
    ((error line-start (file-name) ":" line ": error: " (message) line-end))
    :modes lisp-mode)
  (add-to-list 'flycheck-checkers 'sblint))

(use-package sly-asdf
  :ensure t
  :defer t)

(use-package sly-quicklisp
  :ensure t
  :defer t)

(use-package sly
  :ensure t
  :defer t
  :after (sly-asdf sly-quicklisp)
  :custom
  (inferior-lisp-program (executable-find "sbcl")))
;;  (sly-contribs '(sly-asdf sly-quicklisp)))

Scheme

(use-package geiser
  :ensure t
  :if (executable-find "guile")
  :bind
  ("C-c i" . geiser-insert-lambda)
  :custom
  (geiser-default-implementation 'guile))

Hy

Hy is lisp language built on top of Python Virtual Machine.

(use-package hy-mode
  :ensure t)

Python

Python is my primary language for now. I used elpy for many years but now it’s dying. Mainteners cannot mantain, new jedi broke elpy RPC server and so on. elpy has several cool features tho, but lsp as unified protocol for IDEs is the best approach we have now (compared to homebrew ones every language and every IDE creates for it’s own need).

So, for lsp to work with python we need:

  • Python LSP Server I prefer to install it as pip install python-lsp-server[rope] for simplicity. Other modules are not so useful for me.
  • Third parties I use:
    isort plugin
    pip install pyls-isort
    black plugin
    pip install python-lsp-black

Configuration (M-x customize-group RET pylsp RET):

  • lsp-pylsp-plugins-pydocstyle-enabled set to nil
(use-package python
  :ensure nil
  :delight python-mode)
(use-package sphinx-doc
  :ensure t
  :delight
  :hook (python-mode . sphinx-doc-mode))

Poetry

Poetry is the superior tool for python project management from start to publishing. This mode adds magit-like transient interactive popups for calling poetry coomands.

(use-package poetry
  :ensure t
  :config
  (poetry-tracking-mode))

pyvenv

(use-package pyvenv
  :ensure t
  :hook ((python-mode . pyvenv-mode)
         (python-mode . pyvenv-tracking-mode)))

LSP

(use-package lsp-mode
  :ensure t
  :delight
  :preface
  ;; TODO: make configurable
  (defun pkulev/pyvenv-autoload ()
    "Automatically activate pyvenv when .venv directory exists."
    (f-traverse-upwards
     (lambda (path)
       (let ((venv-path (f-expand ".venv" path)))
         (if (f-exists? venv-path)
             (progn
               (pyvenv-activate venv-path)
               t))))))
  (defun pkulev/python-setup-indentation ()
    (setq python-indent-def-block-scale 1)
    (infer-indentation-style-python))
  :hook ((c-mode . lsp)
         (c++-mode . lsp)
         (python-mode . lsp)
         (python-mode . pkulev/pyvenv-autoload)
         (python-mode . pkulev/python-setup-indentation)))

(use-package lsp-ui
  :ensure t
  :commands (lsp)
  :custom
  (lsp-ui-sideline-ignore-duplicate t)
  :hook (lsp-mode . company-mode))

Web

Rest Client

(use-package restclient
  :ensure t)

jq – JSON Query

(use-package restclient-jq
  :ensure t)

JS

(use-package js
  :ensure nil
  :config
  :hook (js-mode . infer-indentation-style-js))

HTML

(use-package mhtml-mode
  :ensure nil
  :defer t
  :custom
  (sgml-basic-offset 4))

Nim

(use-package nim-mode
  :ensure t
  :hook
  ((nim-mode . nimsuggest-mode)
   (nimsuggest-mode . flycheck-mode)))

(use-package flycheck-nim
  :ensure t
  :after nim-mode)

Ocaml

(use-package tuareg
  :ensure t
  :defer t
  :custom
  (tuareg-match-patterns-aligned t))
;; (tuareg-prettify-symbols-full t)
;; TODO:
;; (add-hook 'tuareg-mode-hook
;;           (lambda()
;;             (when (functionp 'prettify-symbols-mode)
;;               (prettify-symbols-mode))))

;; (face-spec-set
;;  'tuareg-font-lock-constructor-face
;;  '((((class color) (background light)) (:foreground "SaddleBrown"))
;;    (((class color) (background dark)) (:foreground "burlywood1")))))

Rust

(use-package racer
  :hook ((rust-mode . racer-mode)
         (racer-mode . eldoc-mode))
  :custom
  (rust-rustfmt-bin "~/.cargo/bin/rustfmt")
  (rust-cargo-bin "~/.cargo/bin/cargo"))

Terraform

(use-package terraform-mode
  :ensure t)

Git things

Magit

(use-package magit
  :ensure t
  :delight
  :custom
  (magit-bury-buffer-function #'quit-window)
  :bind
  (:map mode-specific-map
        :prefix-map magit-prefix-map
        :prefix "m"
        ("b" . #'magit-blame-addition)
        ("B" . #'magit-branch-create)
        ("c" . #'magit-checkout)
        ("C" . #'magit-commit-create)
        ("f" . #'magit-find-file)
        ("l" . #'magit-log-buffer-file)))

Git forges

(use-package forge
  :if (boundp 'my/private-forges)
  :ensure t
  :delight
  :after magit
  :config
  (add-to-list 'forge-alist
               (append 'my/private-forges forge-github-repository)))

Bookmarks

(use-package bookmark
  :ensure nil
  :config
  (when (f-exists? bookmark-default-file)
    (bookmark-load bookmark-default-file t))
  :custom
  (bookmark-save-flag t)
  (bookmark-default-file (f-join user-emacs-directory "bookmarks")))

(use-package bm
  :ensure t
  :bind (("<C-f2>" . bm-toggle)
         ("<f2>"   . bm-next)
         ("<S-f2>" . bm-previous)))

Email & Messengers

(use-package telega
  :if (> emacs-major-version 25)
  :ensure nil
  :quelpa
  (telega :repo "zevlg/telega.el"
          :fetcher github :upgrade t)
  :load-path "~/proj/telega.el"
  :commands (telega)
  :defer t
  :config
  (add-hook 'telega-root-mode-hook (lambda () (telega-notifications-mode 1))))

Productivity & task management

Org mode

(use-package org
  ;; :hook (auto-save . org-save-all-org-buffers)
  :pin gnu
  :ensure t
  :init
  (defun +org/agenda-skip-all-siblings-but-first ()
    "Skip all but the first non-done entry."
    (let (should-skip-entry)
      (unless (+org/current-is-todo)
        (setq should-skip-entry t
              (save-excursion
                (while (and (not should-skip-entry) (org-goto-sibling t))
                  (when (+org/current-is-todo)
                    (setq should-skip-entry t))
                  (when should-skip-entry))
                (or (outline-next-heading
                     (goto-char (point-max)))))))))

  (defun +org/current-is-todo ()
    (string= "TODO" (org-get-todo-state)))

  (defun +org/opened-buffer-files ()
    "Return the list of files currently opened in emacs."
    ;; (remove-if-not #'(lambda (x) (string-match "\\.org$" x))
    ;;                   (delq nil (mapcar #'buffer-file-name (buffer-list))))
    (delq nil
          (mapcar (lambda (x)
                    (if (and (buffer-file-name x)
                             (string-match "\\.org$" (buffer-file-name x)))
                        (buffer-file-name x)))
                  (buffer-list))))

  (defun +org/all-org-files ()
    "Return the list of all org files in `org-directory'."

    (remove-if-not #'(lambda (x) (string-match "\\.org$" x))
                   (directory-files org-directory 'full)))

  :bind (("C-c a" . org-agenda)
         ("C-c b" . org-iswitchb)
         ("C-c l" . org-store-link)
         ("C-c c" . org-capture))
  :custom
  (org-directory "~/orgs")
  (org-log-done 'note)
  (org-log-refile t)
  (org-agenda-files `(,(concat org-directory "/inbox.org")
                      ,(concat org-directory "/next.org")
                      ,(concat org-directory "/tickler.org")))
  ;; (org-refile-targets '((+org/opened-buffer-files :maxlevel . 9)))
  (org-refile-targets '((+org/all-org-files :maxlevel . 9)))
  (org-refile-use-cache t)
  (org-capture-templates
   `(("t" "Todo [inbox]" entry
      (file+headline "/inbox.org" "Tasks")
      "* TODO %i%?")
     ("T" "Tickler" entry (file+headline "/tickler.org" "Tickler")
      "* %i%? \n %U")
     ("P" "Project [projects]" entry
      (file+headline "~/orgs/projects.org", "Projects")
      "* TODO %i%?")
     ("p" "Protocol" entry
      (file+headline "~/orgs/links.org" "Inbox")
      "* %^{Title}\nSource: %u, %c\n #+BEGIN_QUOTE\n%i\n#+END_QUOTE\n\n\n%?")
     ("L" "Protocol Link" entry
      (file+headline "~/orgs/links.org" "Inbox")
      "* %? [[%:link][%:description]] \nCaptured On: %U")))
  (org-todo-keywords '((sequence
                        "NEXT(n)" "TODO(t)" "INPROGRESS(p)" "WAITING(w)"
                        "|" "DONE(d)" "CANCELLED(c)")))
  (org-refile-use-outline-path 'file)
  (org-outline-path-complete-in-steps nil)
  ;; (org-refile-targets '(("~/orgs/next.org" :maxlevel . 3)
  ;;                       ("~/orgs/someday.org" :level . 1)
  ;;                       ("~/orgs/tickler.org" :maxlevel . 2)
  ;;                       ("~/orgs/future-projects.org" :level . 1)))
  (org-agenda-custom-commands
   '(("o" "At the office" tags-todo "@office"
      ((org-agenda-overriding-header "Office")
       (org-agenda-skip-function #'+org/agenda-skip-all-siblings-but-first)))))
  :config
  ;; (run-with-idle-timer 300 t (lambda ()
  ;;                              (org-refile-cache-clear)
  ;;                              (org-refile-get-targets)))
  (org-babel-do-load-languages
   'org-babel-load-languages '((emacs-lisp . t)
                               (python . t)
                               (shell . t)
                               (scheme . t)))
  (add-to-list 'org-structure-template-alist '("ss" . "src scheme"))
  (add-to-list 'org-structure-template-alist '("sp" . "src python"))
  (add-to-list 'org-structure-template-alist '("se" . "src elisp")))

(use-package org-protocol
  :ensure nil)

org-tempo

org-mode now has different system for inserting code sections (C-c C-,), but I like previous one, snippet-like, so let’s enable it.

(use-package org-tempo
  :ensure nil
  :after org-mode)

Some whistles

(use-package org-bullets
  :ensure t
  :custom
  ;; org-bullets-bullet-list
  ;; default: ◉ ○ ✸ ✿
  ;; large: ♥ ● ◇ ✚ ✜ ☯ ◆ ♠ ♣ ♦ ☢ ❀ ◆ ◖ ▶
  ;; Small: ► • ★ ▸
  ;; (org-bullets-bullet-list '("•"))
  ;; others: ▼, ↴, ⬎, ⤷,…, and ⋱
  ;; (org-ellipsis "…")
  (org-ellipsis "")
  :hook
  (org-mode . org-bullets-mode))

Org-Trello sync

(use-package org-trello
  :ensure t)

Generate Table of Contents

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

Org babel

(use-package ob-mongo
  :ensure t)

(use-package ob-async
  :ensure t)

(use-package ob-restclient
  :ensure t
  :mode (("\\.http\\'" . restclient-mode)))

Org export

Org export packages usually have ox- prefix (*o*rg e*x*port).

ox-jira is great package that exports org buffer to JIRA format, that can be pasted into JIRA or Confluence page.

(use-package ox-jira
  :ensure t
  :hook (org-mode . (lambda () (require 'ox-jira))))

Yankpad

(use-package yankpad
  :ensure t
  :defer org
  :bind
  ("C-c y m" . yankpad-map)
  ("C-c y e" . yankpad-expand)
  :config
  (add-to-list 'company-backends #'company-yankpad))

Utils

(defun link-message ()
  "Show org-link in minibuffer."
  (interactive)
  (let ((object (org-element-context)))
    (when (eq (car object) 'message)
      (message "%s" (org-element-property :raw-link object)))))

Productivity

(use-package org-pomodoro
  :ensure nil
  :quelpa
  (org-pomodoro :repo "pkulev/org-pomodoro"
                :fetcher github :branch "feature/customize-mode-line"
                :upgrade t)
  :bind
  (:map mode-specific-map ("o p" . org-pomodoro))
  :custom
  (org-pomodoro-format " 🍅 %s"))
(use-package jira-markup-mode
  :ensure t
  :defer t)

Wakatime

(use-package wakatime-mode
  :ensure t
  :if (boundp 'my/private-wakatime-api-key)
  :delight "👀"
  :custom
  (wakatime-api-key my/private-wakatime-api-key)
  (wakatime-cli-path my/private-wakatime-cli-path)
  :config
  (global-wakatime-mode))

Calendar

(use-package calendar
  :ensure nil
  :commands (calendar)
  :custom
  (calendar-week-start-day 1))

Corporative services

(use-package org-jira
  :if (boundp 'my/private-jira-url)
  :ensure t
  :custom
  (jiralib-url my/private-jira-url))

Local variables

Tangle config on save hook.

;; Local Variables:
;; eval: (add-hook 'after-save-hook (lambda () (let ((inhibit-redisplay t) (inhibit-message t) (emacs-lisp-mode-hook)) (org-babel-tangle))) nil t)
;; flycheck-disabled-checkers: (emacs-lisp-checkdoc)
;; End:

Disable annoying checkdoc linter for cases if I want to open init.el.

;; Local Variables:
;; flycheck-disabled-checkers: (emacs-lisp-checkdoc)
;; End:

Releases

No releases published

Packages

No packages published