Skip to content

veracioux/literatemacs

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

50 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

LiteratEmacs

This file IS the program.

A literate README that produces an init.el, personalized to my tastes. Uses package.el, use-package, and quelpa.

Introduction

All of the tangled code in this README.org is Emacs Lisp, though there are some snippets of Bash. That is because we want to tangle to one Emacs Lisp file, init.el.

To stop Emacs from tangling a block of code, add :tangle no like this: #+begin_src emacs-lisp :tangle no

Requirements

These may be edited out by setting :tangle no or changing them to suit.

  • silversearcher-ag needed by dumb-jump and ag
  • Requirements needed for vterm

Installation

Emacs Service

Here’s my ~/.config/systemd/user/emacs.service file. The path may change per Emacs installation.

[Unit]
Description=Emacs text editor
Documentation=info:emacs man:emacs(1) https://gnu.org/software/emacs/

[Service]
Type=simple
ExecStart=/usr/bin/emacs --fg-daemon
ExecStop=/usr/bin/emacsclient --eval "(kill-emacs)"
Environment=SSH_AUTH_SOCK=%t/keyring/ssh
Restart=on-failure

[Install]
WantedBy=default.target
  • Now you’ll want to do systemctl --user enable emacs && systemctl --user start emacs
  • To restart the Emacs server, exit all clients, and do systemctl --user restart emacs
  • To connect an Emacs client to the Emacs server, do emacsclient or emacsclient -t

LiteratEmacs

To install, just (fork and) clone this repository somewhere locally where it can stay. If you’re not forking, then you can’t push your changes, but you can still branch locally, or fork manually later.

# If cloning directly from me
git clone https://github.com/joseph8th/literatemacs.git .

# If forking, then cloning from yourself
git clone [email protected]:YOURUSERNAME/literatemacs.git .

First Use

Use this file and Emacs’ org-babel feature to:

  1. generate an init.el file in the same directory as this file, and
  2. create a symbolic link from ./init.el to $HOME/emacs.d/init.el .

Carefully follow the instructions below to generate your first init.el in the same directory as this file.

Backup

Before using this literate program, first backup current $HOME/emacs.d/init.el

Edit

Now edit this README.org file to suit. That means:

  1. Set :tangle no to any blocks you want to disable, or delete them entirely, and
  2. edit any blocks to configure Emacs differently on the first run.

Edit the following, unless you are me. This is the first thing that will be tangled.

;;; init.el --- Emacs initialization file tangled from a README.org file
;;
;;  Author: Joseph Edwards VIII <jedwards8th at gmail.com>
;;  URL: https://github.com/joseph8th/literatemacs
;;  ============================================================================

;;; User setting
;;  ----------------------------------------------------------------------------

(setq user-full-name "Joseph Edwards VIII"
      user-mail-address "")

Save

When you save, Emacs will ask if you want to “Tangle?” and then “Reload?” On first save, say “Yes” to “Tangle?” and say “No” to “Reload?”

Now the file init.el exists in the same directory as this README.org file.

Link

Finally, link the ./init.el file and required elisp/ directory into your $HOME/.emacs.d/ directory:

mv ~/.emacs.d/init.el ~/.emacs.d/init.el.bak
ln -s ./init.el ~/.emacs.d/
ln -s ./elisp ~/.emacs.d/

Now when Emacs asks if you want to “Tangle?” and “Reload?” you can say “Yes” and the updated and re-tangled init.el will be loaded as the user-init-file.

Tangle and Reload

There are three ways to tangle this file:

  1. Use M-x org-babel-tangle (C-c C-v t).
  2. Position cursor inside the following code block and do C-c C-c:
    (org-babel-tangle)
        
  3. Just edit and save, and let the after-save-hook, defined in the Conclusion, do its thing.

Reload

To reload, just do M-: (load-file user-init-file) RET

Or, position cursor inside the following and do C-c C-c:

(load-file user-init-file)

Initialize

TL;DR: use-package and quelpa bootstrapping, some optimizations picked up here and there.

Package init and sources

Elpa (GNU) is the default, but I add it anyway. Disabling package-enable-at-startup is SOP to make startup a little quicker.

;;; Initialize
;;  -----------------------------------------------------------------------------

(require 'package)

;; Set package archives
(setq package-check-signature nil)  ; because GNU ELPA keeps choking on the sigs
(add-to-list 'package-archives '("gnu" . "http:https://elpa.gnu.org/packages/"))
(add-to-list 'package-archives '("melpa" . "http:https://melpa.org/packages/"))
(add-to-list 'package-archives '("org" . "http:https://orgmode.org/elpa/"))
(setq package-enable-at-startup nil)

(package-initialize)
(unless package-archive-contents
  (package-refresh-contents))

Bootstrap use-package

Fire up use-package by itself. Configured so that it will install and compile any missing dependencies on load.

;; Bootstrap use-package
(unless (package-installed-p 'use-package)
  (package-install 'use-package))

(setq use-package-verbose t)
(setq use-package-always-ensure t)
(eval-when-compile (require 'use-package))
(use-package auto-compile
  :config (auto-compile-on-load-mode))
(setq load-prefer-newer t)

Bootstrap quelpa

Fire up quelpa AND quelpa-use-package.

;; Boostrap quelpa
(unless (package-installed-p 'quelpa)
  (with-temp-buffer
    (url-insert-file-contents "https://raw.githubusercontent.com/quelpa/quelpa/master/quelpa.el")
    (eval-buffer)
    (quelpa-self-upgrade)))

;; Bootstrap quelpa-use-package
(quelpa
 '(quelpa-use-package
   :fetcher git
   :url "https://github.com/quelpa/quelpa-use-package.git"))
(require 'quelpa-use-package)

Custom Sources and Settings

(add-to-list 'load-path "~/.emacs.d/elisp")
(add-to-list 'load-path "~/.emacs.d/elpa")

;; Keep custom settings in a separate file to not pollute this one
(setq custom-file "~/.emacs.d/custom-settings.el")
(load custom-file t)

Increase garbage collector

This is a nice little tidbit copied directly from Jamie Collinson’s config. Bumps startup speed.

;; Increase garbage collection during startup
(setq gc-cons-threshold 10000000)

;; Restore after startup
(add-hook 'after-init-hook
          (lambda ()
            (setq gc-cons-threshold 1000000)
            (message "gc-cons-threshold restored to %S"
                     gc-cons-threshold)))

Get shell PATH

Ensures environment variables in Emacs are the same as user shell.

;; Get user PATH
(use-package exec-path-from-shell
  :config
  (when (memq window-system '(mac ns x))
    (exec-path-from-shell-initialize)))

Preferences

All the miscellaneous tweaks that customize Emacs just the way I like it. Much of this was taken from “Dave’s .emacs” but the rest was just ad hoc.

Short “yes” and “no”

;; Ask "y" or "n" instead of "yes" or "no". Yes, laziness is great.
(fset 'yes-or-no-p 'y-or-n-p)

Parentheses configuration

;; Highlight corresponding parentheses when cursor is on one
(show-paren-mode t)

Clean up whitespace

;; Remove useless whitespace before saving a file
(setq-default nuke-trailing-whitespace-p t)
(add-hook 'before-save-hook 'whitespace-cleanup)
(add-hook 'before-save-hook (lambda() (delete-trailing-whitespace)))

Revert file when changed on disk

Saved me many a time. No more having to say “no” on save because the file on disk has changed since I started editing it.

;; Auto-revert to disk on file change
(global-auto-revert-mode t)

Set UTF-8 locale defaults

;; Set locale to UTF8
(set-language-environment 'utf-8)
(set-terminal-coding-system 'utf-8)
(setq locale-coding-system 'utf-8)
(set-default-coding-systems 'utf-8)
(set-selection-coding-system 'utf-8)
(prefer-coding-system 'utf-8)

No more Windows line-endings

Because, yuck!

;; hide DOS ^M line-endings
(defun remove-dos-eol ()
  "Do not show ^M in files containing mixed UNIX and DOS line endings."
  (interactive)
  (setq buffer-display-table (make-display-table))
  (aset buffer-display-table ?\^M []))
(add-hook 'text-mode-hook 'remove-dos-eol)

Shell mode configuration

Couple items here taken from “Dave’s .emacs” (marked DHA)

;; truncate shell buffer to 1024 - comint-buffer-maximum-size
(setq comint-buffer-maximum-size 2048)
(add-hook 'comint-output-filter-functions
          'comint-truncate-buffer)

;; Disable undo in shell
(add-hook 'shell-mode-hook 'buffer-disable-undo)

;; shell-switcher
(setq shell-switcher-mode t)

;;; [DHA] Keep a much bigger shell command history for M-p
(setq comint-input-ring-size 1000)

Better file buffer

;; Make ibuffer default instead of list-buffers
(defalias 'list-buffers 'ibuffer)

History configuration

This one is copied directly from Sacha Chua’s config:

;; Better history
(setq savehist-file "~/.emacs.d/savehist")
(savehist-mode 1)
(setq history-length t
      history-delete-duplicates t
      savehist-save-minibuffer-history 1)
(setq savehist-additional-variables
      '(kill-ring
        search-ring
        regexp-search-ring))

Backup configuration

I used to just disable backup files (very annoying in production environments), but this solution is far more elegant. Both Sacha and Jamie do something like this to keep backups in a separate location.

;; Keep backup files in their own directory
(setq backup-directory-alist '(("." . "~/.emacs.d/backups"))
      backup-by-copying 1
      delete-old-versions -1
      version-control t
      vc-make-backup-files t)
(setq auto-save-file-name-transforms '((".*" "~/.emacs.d/auto-save-list/" t)))

In case I want to go back to no backups, I move the :tangle no to the previous block and re-tangle with this:

;; Disable backup files
(setq make-backup-files nil
      backup-inhibited t
      auto-save-default nil)

Quick-start Tramp

According to emacswiki.org, this is faster than the default scp method:

;; Faster than the default scp
(setq tramp-default-method "ssh")

Dired

Adapted from Emacs from Scratch:

;; Better dired config
(use-package dired
  :ensure nil
  :commands (dired dired-jump)
  :bind (("C-x C-j" . dired-jump))
  :custom ((dired-listing-switches "-agho --group-directories-first")))

(use-package dired-single)

(use-package all-the-icons-dired
  :hook (dired-mode . all-the-icons-dired-mode))

(use-package dired-open
  :config
  ;; Doesn't work as expected!
  ;;(add-to-list 'dired-open-functions #'dired-open-xdg t)
  (setq dired-open-extensions '(("png" . "feh")
                                ("mkv" . "mpv"))))

(use-package dired-hide-dotfiles
  :hook (dired-mode . dired-hide-dotfiles-mode))

Interface

Couple items here taken from “Daves .emacs”, and some were lifted out of my old custom-set-variables.

;;; Interface
;;  ----------------------------------------------------------------------------

;; [DHA] Show the time in the mode line
;; (display-time)                              ; how late am I?

;; [DHA] Don't show the 'startup screen'
(setq inhibit-startup-message t)            ; ok I've seen the copyleft &c

;; No alarm on C-g FFS!
(setq ring-bell-function 'ignore)

Frame configuration

I like a single maximized frame per display these days.

;; Fullscreen maximized frame in GUI mode
; (custom-set-variables
;  '(initial-frame-alist (quote ((fullscreen . maximized))))
(modify-all-frames-parameters '((fullscreen . maximized)))

;; disable toolbar-mode in GUI
(tool-bar-mode -1)

;; disable scroll-bar-mode
(scroll-bar-mode -1)

;; [DHA] I *never* use the stupid thing..
(menu-bar-mode -1)
;; [JEE] I do, so mapped to a toggle keybinding
(global-set-key (kbd "<C-menu>") 'menu-bar-mode)

Window configuration

Good ole winner-mode and switch-window are enough for most things, but burly has its uses.

;; Undo and redo window configurations C-c left and C-c right
(winner-mode 1)

;; Word wrap on vertical split
(setq truncate-partial-width-windows nil)

;; switch-window
(use-package switch-window)
(global-set-key (kbd "C-x o") 'switch-window)

;; split window vertically
;; (split-window-right)

Burly

Want that crazy window config for D&D back, with all 11 windows and indirect buffers open? Burly time!

(use-package burly
  :quelpa (burly :fetcher github :repo "alphapapa/burly.el"))

Popwin

Groovy ‘lil package popwin.el does nothin’ but put all the noise buffers like *Completions* in an easily dismissed popup window.

(use-package popwin)
(popwin-mode 1)

Appearance

Don’t forget to do M-x all-the-icons-install-fonts for all-the-icons.

;;; Appearance
;;  ----------------------------------------------------------------------------

;; Did you run all-the-icons-install-fonts?
(use-package all-the-icons)

;; Set the default face
(set-face-attribute 'default nil :font "Fira Code Retina" :height 98)

;; Set the fixed pitch face
(set-face-attribute 'fixed-pitch nil :font "Fira Code Retina" :height 98)

Themes

Move the :tangle no from theme to theme to enable different themes on reload.

Doom Themes

Currently using Doom themes. These are a package deal, so change the line (load-theme 'doom-dark+ t) to whichever theme you want to try.

(use-package doom-themes
  :custom-face
  ;; I want the background darker (TODO: tweak to perfection)
  (default ((t (:inherit nil :stipple nil :background "#202023" :foreground "#ffffff" :inverse-video nil :box nil :strike-through nil :overline nil :underline nil :slant normal :weight normal :height 98 :width normal :foundry "CTDB" :family "Fira Code Retina"))))

  (org-block-background ((t (background: "#121215"))))

  ;; Give Level 1 & 2 org headings some oomph
  (org-level-1 ((t (:inherit outline-1 :background "#455A64" :box (:line-width 1 :style released-button) :weight bold :height 1.1))))
  (org-level-2 ((t (:inherit outline-2 :background "#35575b" :box (:line-width 1 :style released-button) :height 1.1))))

  :config
  ;; Global settings (defaults)
  (setq doom-themes-enable-bold t    ; if nil, bold is universally disabled
        doom-themes-enable-italic t) ; if nil, italics is universally disabled

  ;; Actual load here
  (load-theme 'doom-dark+ t)

  ;; Enable flashing mode-line on errors
  (doom-themes-visual-bell-config)

  ;; Enable custom neotree theme (all-the-icons must be installed!)
  ;(doom-themes-neotree-config)
  ;; or for treemacs users
  ;(setq doom-themes-treemacs-theme "doom-colors") ; use the colorful treemacs theme
  ;(doom-themes-treemacs-config)

  ;; Corrects (and improves) org-mode's native fontification.
  (doom-themes-org-config))

Deprecate Material Design theme

Used Material theme for years. Lovely.

;defface org-block-begin-line
; '((t (:underline "#A7A6AA" :foreground "#008ED1" :background "#EAEAFF")))
; "Face used for the line delimiting the begin of source blocks.")

(defface org-block-background
  '((t (:background "#121212")))
  "Face used for the source block background.")

;defface org-block-end-line
; '((t (:overline "#A7A6AA" :foreground "#008ED1" :background "#EAEAFF")))
; "Face used for the line delimiting the end of source blocks.")

;; Extra dark BG with bright chars
(use-package color-theme-sanityinc-tomorrow
  )
;; (load-theme 'sanityinc-tomorrow-bright)

;; Material design has lots of nice features (TODO: customize)
(use-package material-theme
  :custom-face
  ;; I want the background darker (TODO: tweak to perfection)
  (default ((t (:inherit nil :stipple nil :background "#202023" :foreground "#ffffff" :inverse-video nil :box nil :strike-through nil :overline nil :underline nil :slant normal :weight normal :height 98 :width normal :foundry "PfEd" :family "DejaVu Sans Mono"))))

  ;; Document Info and keywords are too big in Material
  (org-document-info ((t (:foreground "#81d4fa" :height 1.0))))
  (org-document-info-keyword ((t (:inherit font-lock-comment-face :foreground "#8bc34a" :height 1.0))))

  ;; Have to set the "org-hide" face to same as background esp for hide-stars/indent
  (org-hide ((t (:background "#202023" :foreground "#202023"))))

  ;; Material's level 1 & 2 headings are too big
  (org-level-1 ((t (:inherit outline-1 :background "#455A64" :box (:line-width 1 :style released-button) :weight bold :height 1.1))))
  (org-level-2 ((t (:inherit outline-2 :background "#35575b" :box (:line-width 1 :style released-button) :height 1.1))))
  )

Mode Lines

Move the :tangle no.

Doom Modeline

Settled on Doom Modeline for a while. Many of Doom’s nice-to-haves are available this way.

;; Doom-modeline might be fun, the themes are sweet
(use-package doom-modeline
  :ensure t
  :init (doom-modeline-mode 1))

Telephone Line

Used Telephone Line for years. Highly extensible stand-alone, but the Doom mode-line seems to play nice with the Doom theme, so…

;; Improved mode-line (TODO: customize)
(use-package telephone-line
  :init
  (setq telephone-line-primary-left-separator 'telephone-line-cubed-left
        telephone-line-secondary-left-separator 'telephone-line-cubed-hollow-left
        telephone-line-primary-right-separator 'telephone-line-cubed-right
        telephone-line-secondary-right-separator 'telephone-line-cubed-hollow-right)
  (setq telephone-line-height 24
        telephone-line-evil-use-short-tag t)
  :config
  (telephone-line-mode 1))

Packages

I’ve grouped these so that packages with system dependencies can be ignored by adding :tangle no.

Websocket and Webserver

Several later packages need these, so let’s just always require them. Here’s links for reference:

;; Websocket, webserver, and request
(use-package websocket)
(use-package web-server)
(use-package request)

Programming utilities

General programming utility packages with no system dependencies.

EditorConfig

Because setting indents for each language is a PITA. EditorConfig is the solution!

Here’s a link to editorconfig-emacs. This used to require a system dependency, but that no longer appears to be the case.

;;; Programming utilities
;;  ----------------------------------------------------------------------------

;; Editorconfig
(use-package editorconfig
  :config
  (editorconfig-mode 1))

;; Autopair
(use-package autopair
  :config
  (autopair-global-mode))

;; Silversearcher support - faster-than-grep
(use-package ag)

Prettify Stuff

Rainbow colored nested parens, etc. Colorified color references. Fill column indicator.

;; Highlight nested parentheses (from Jamie's)
(use-package rainbow-delimiters
  :config
  (add-hook 'prog-mode-hook 'rainbow-delimiters-mode))

;; Color comprehension
(use-package rainbow-mode
  :config
  (setq rainbow-x-colors nil)
  (add-hook 'prog-mode-hook 'rainbow-mode))

;; Init fill-column indicator (add to mode hooks per language)
(use-package fill-column-indicator
  :init
  (setq-default fci-rule-column 80))

Syntax Checking and Completion

There’s about 90 billion different ways to do these things. I’m still figuring out the best way for me.

Auto-complete

I’ve been very unhappy with auto-complete, as the popup.el bit keeps breaking and looking ugly. But company wasn’t doing it for me, so here I am still using it as my primary auto-completion framework.

;; Auto-complete (TODO: enable ac-company?)
(setq popup-use-optimized-column-computation nil)
(use-package auto-complete
  :init
  (require 'auto-complete-config)
  (add-to-list 'ac-dictionary-directories "~/.emacs.d/ac-dict")
  :config
  (ac-config-default))

Flycheck

Was trying Flycheck in combination with Elpy and discovered that it’s too much noise for my taste. Might revisit later. Untangled by default.

;; Flycheck (add to mode hooks per language)
(use-package flycheck
  :config
  (use-package flycheck-inline
    :hook (flycheck-mode . turn-on-flycheck-inline)))

Company mode

Need to revisit company-mode as a possible completion framework. Untangled by default.

;; Company support - text completion
(use-package company
  :config
  (setq company-idle-delay 0)
  (use-package company-quickhelp
    :config
    (company-quickhelp-mode)))

Magit

Don’t use magit as often as I should. Old habits and all that.

;; Magit, of course
(use-package magit
  :bind (("C-x g" . magit-status))
  :config
  (use-package magit-popup))

Yasnippet

Don’t use yasnippet as often as I could. Usually keyboard macros get me there.

;; Yasnippet
(use-package yasnippet
  :init
  (setq yas-snippet-dirs '("~/.emacs.d/snippets"))
  :bind (:map yas-minor-mode-map
              ("<tab>" . nil)
              ("TAB" . nil)
              ("<C-tab>" . yas-expand)
              ("C-j" . yas-next-field))
  :config
  (yas-global-mode 1))

Multiple cursors

So very, very useful. Here’s a link.

;; Multiple-cursors
(use-package multiple-cursors
  :config
  (define-key mc/keymap (kbd "<return>") nil)
  :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)
         ("C-S-<mouse-1>" . mc/add-cursor-on-click)))

Restclient

Say buh-bye to Postman! Say hello to restclient.el. Includes org-babel support with the ob-restclient language backend.

;; REST Client in Emacs? But of course!
(use-package restclient
  :config
  (use-package ob-restclient))

Docker support

Because Emacs is the best way to deal with docker and docker-compose, hands down.

Here’s package links, as this is a deep topic:

;; Emacs interface to Docker
(use-package docker
  :ensure t
  :bind ("C-c d" . docker)
  :config
  (use-package dockerfile-mode)
  (use-package docker-tramp)
  (use-package docker-compose-mode))

Vagrant support

Because why not? Use vagrant-tramp to easily tramp around in running Vagrant VMs.

;; Emacs interface to vagrant
(use-package
  vagrant-tramp)

Navigation and projects

Projectile

I could definitely leverage Projectile more than I do.

;;; Navigation and projects
;;  ----------------------------------------------------------------------------

;; Projectile
(use-package projectile
  :bind-keymap
  ("C-c p" . projectile-command-map)
  :config
  (projectile-mode +1))

Ivy & Counsel

I used to use only Ido but trying Ivy out for a while.

;; Counsel with Ivy and Swiper - config cribbed as-is from Ivy documentation
;; Add to :bind to use swiper instead of incremental search:
;; ("C-s" . swiper)

(use-package ivy
  :diminish
  :bind (:map ivy-minibuffer-map
         ("TAB" . ivy-alt-done)
         ("C-m" . ivy-alt-done)
         ("C-j" . ivy-immediate-done)
         ("C-c C-r" . ivy-resume)
         ("<f6>" . ivy-resume))
  :config
  (setq ivy-use-virtual-buffers t)
  (setq ivy-count-format "(%d/%d) ")
  (setq ivy-extra-directories nil)
  (ivy-mode 1))

(use-package ivy-rich
  :init
  (ivy-rich-mode 1))

(use-package counsel
  :bind (("M-x" . counsel-M-x)
         ("C-x C-f" . counsel-find-file)
         ("<f1> f" . counsel-describe-function)
         ("<f1> v" . counsel-describe-variable)
         ("<f1> o" . counsel-describe-symbol)
         ("<f1> l" . counsel-find-library)
         ("<f2> i" . counsel-info-lookup-symbol)
         ("<f2> u" . counsel-unicode-char)
         ("C-c g" . counsel-git)
         ("C-c j" . counsel-git-grep)
         ("C-c k" . counsel-ag)
         ("C-c l" . counsel-locate)
         ("C-S-o" . counsel-rhythmbox)
         ("C-M-j" . counsel-switch-buffer)
         :map minibuffer-local-map
         ("C-r" . counsel-minibuffer-history))
  :config
  (counsel-mode 1))

Jumping

Sticking with dumb-jump for now I guess.

;; Dumb jump
(use-package dumb-jump
  :config
  (add-hook 'xref-backend-functions #'dumb-jump-xref-activate)
  (dumb-jump-mode))

Highlight Symbol

I should use highlight-symbol more.

;; Highlight-symbol
(use-package highlight-symbol
  :bind (([(control f3)] . highlight-symbol-at-point)
         ([f3] . highlight-symbol-next)
         ([(shift f3)] . highlight-symbol-prev)
         ([(meta f3)] . highlight-symbol-query-replace)))

Posframe

Tried Posframe out for a while, and might revisit for some things later, but it changed my workflow more than I thought. Seems handy from what I’ve seen. Might need to setup some hydras later tho?

Apparently cannot be installed with use-package, so use M-x package-list-packages to install it and related.

Untangled by default.

;; posframe - Is very general so there is lots of setup involved
(defun my-posframe-arghandler (buffer-or-name arg-name value)
  (let ((info '(:internal-border-width 10 :background-color "black")))
    (or (plist-get info arg-name) value)))

(use-package posframe
  :config
  ;; (setq posframe-arghandler #'my-posframe-arghandler)
  (use-package which-key-posframe
    :config
    (which-key-posframe-mode))
  (use-package ivy-posframe
    :config
    (setq ivy-posframe-parameters
          '((left-fringe . 8)
            (right-fringe . 8)))
    ;; display at `ivy-posframe-style'
    (setq ivy-posframe-display-functions-alist '((t . ivy-posframe-display)))
    ;; (setq ivy-posframe-display-functions-alist '((t . ivy-posframe-display-at-frame-center)))
    ;; (setq ivy-posframe-display-functions-alist '((t . ivy-posframe-display-at-window-center)))
    ;; (setq ivy-posframe-display-functions-alist '((t . ivy-posframe-display-at-frame-bottom-left)))
    ;; (setq ivy-posframe-display-functions-alist '((t . ivy-posframe-display-at-window-bottom-left)))
    ;; (setq ivy-posframe-display-functions-alist '((t . ivy-posframe-display-at-frame-top-center)))
    (ivy-posframe-mode 1))
  )

(set-face-attribute 'ivy-posframe nil :foreground "white" :background "black")

Terminals

I generally use a separate terminal emulator, but sometimes I wanna use a terminal in Emacs, and when I do, I want that emulator to be vterm.

(use-package vterm
  :commands vterm
  :config
  (setq term-prompt-regexp "^[^#$%>\n]*[#$%>] *")  ;; Set this to match your custom shell prompt
  ;;(setq vterm-shell "zsh")                       ;; Set this to customize the shell to launch
  (setq vterm-max-scrollback 10000))

Integrations

Access web resources and tools using Emacs as a client.

Blogging with Nikola

Easily write and publish blog post to GitHub Pages without ever leaving Emacs. Uses Nikola static site generator.

;;; Blogging with Nikola
;;  ----------------------------------------------------------------------------

;; Nikola.el config
(use-package nikola
  :config
  (setq nikola-output-root-directory "~/Dev/mine/joseph8th.github.io/")
  (setq nikola-verbose t)
  (setq nikola-webserver-auto t)
  (setq nikola-new-post-extension "org")
  (setq nikola-new-page-extension "org"))

;; Custom nikola-github-deploy function
(defun nikola-github-deploy ()
  "Deploys the site to GitHub using github_deploy subcommand."
  (interactive)
  (message "Deploying the site to GitHub pages...")
  (async-start
   `(lambda ()
      ,(async-inject-variables "\\(nikola-\\)")
      (setq output nil)
      (let ((default-directory nikola-output-root-directory))
        (run-hook-with-args 'nikola-deploy-before-hook "")
        (if (not (eq nikola-deploy-before-hook-script nil))
            (setq output (shell-command-to-string
                          nikola-deploy-before-hook-script)))
        (setq output (shell-command-to-string (concat nikola-command " github_deploy")))
        (if (not (eq nikola-deploy-after-hook-script nil))
            (setq output (shell-command-to-string
                          nikola-deploy-after-hook-script)))
        (run-hook-with-args 'nikola-deploy-before-hook ""))
      output)
   (lambda (result)
     (if (cl-search "This command needs to run inside an existing Nikola site."
                    result)
         (if (eq nikola-verbose t)
             (message "Something went wrong. You may want to set nikola-verbo\
se to t and retry it.")
           (message "Something went wrong. You may want to check the *Nikola*\
buffer."))
       (message "Site deployed correctly."))
     (if (eq nikola-verbose t)
         (save-window-excursion
           (switch-to-buffer "*Nikola*")
           (let ((inhibit-read-only t))
             (insert result)))))))

StackExchange integration

As in, search SO from inside Emacs. Untangled by default.

;;; Integrations
;;  ----------------------------------------------------------------------------

;; SO in Emacs? Uh-huh. (Thx Jamie)
(use-package sx
  :config
  (bind-keys :prefix "C-c s"
             :prefix-map my-sx-map
             :prefix-docstring "Global keymap for SX."
             ("q" . sx-tab-all-questions)
             ("i" . sx-inbox)
             ("o" . sx-open-link)
             ("u" . sx-tab-unanswered-my-tags)
             ("a" . sx-ask)
             ("s" . sx-search)))

Slack integration

Yes, that Slack.

Untangled by default. See here for config details.

FIRST put your private credentials in a file like .emacs.d/slack-creds.el filling out the following (do NOT ever tangle this block, it’s just an example):

;; Example ONLY
(setq my-slack-credentials
      '((name "ACCOUNT")
        (client-id "XXXXXXXXXXXX.YYYYYYYYYYYYY")
        (client-secret "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZ")
        (token "xoxs-XXXXXXXXXXXX-AAAAAAAAAAAA-BBBBBBBBBBBB-CCCCCCCCCCCCCCCCCCC")
        (subscribed-channels CHANNEL1 CHANNEL2 CHANNEL3)))

THEN tangle the following.

;; Show desktop notifications
(use-package alert
  :commands (alert)
  :init
  (setq alert-default-style 'notifier))

;; An IRC client
(use-package circe)

;; Show emojis in Emacs
(use-package emojify)

;; Make SURE this file exists and is valid
(load ~/.emacs.d/slack-creds.el)

(use-package slack
  :commands (slack-start)
  :init
  (setq slack-buffer-emojify t)
  (setq slack-prefer-current-team t)
  (slack-register-team
   :name (mapconcat 'identity (alist-get 'name my-slack-credentials) " ")
   :default t
   :client-id (mapconcat 'identity (alist-get 'client-id my-slack-credentials) " ")
   :client-secret (mapconcat 'identity (alist-get 'client-secret my-slack-credentials) " ")
   :token (mapconcat 'identity (alist-get 'token my-slack-credentials) " ")
   :subscribed-channels '(alist-get 'subscribed-channels my-slack-credentials)
   :full-and-display-names t))

Language-specific

Language-specific packages and configuration.

Emacs-Lisp

;;; Emacs-Lisp
;;  ----------------------------------------------------------------------------

(add-hook 'emacs-lisp-mode-hook 'fci-mode)

Python

I use the iPython3 interpreter, so make sure it’s installed.

;;; Python
;;  ----------------------------------------------------------------------------

;; Basic python-mode config. I've been using this for years with no problems.
(use-package python
  :mode ("\\.py\\'" . python-mode)
  :interpreter ("/usr/bin/python3" . python-mode)
  :hook (python-mode . fci-mode)
  :config
  (use-package pyvenv))

Tried elpy and, honestly, it’s overkill for me. I do like being able to execute regions in the interpreter, so I’m leaving it here to play with down the line. Maybe I’ll change my mind.

;; Elpy makes Emacs a full Python IDE. Do I want that? I dunno yet. Guess I'll try it...
(use-package py-autopep8)
(use-package elpy
  :init (setq python-shell-interpreter "ipython"
              python-shell-interpreter-args "-i --simple-prompt")
  :hook (elpy-mode . py-autopep8-enable-on-save)
  :config
  (elpy-enable))

PHP and HTML

Honestly, web-mode is the way to go with mixed language files.

;;; PHP and HTML
;;  ----------------------------------------------------------------------------

(use-package web-mode)
(use-package mmm-mode
  :init
  (setq mmm-global-mode 'maybe)
  :config
  (mmm-add-mode-ext-class 'html-mode "\\.php\\'" 'html-php))

(use-package php-mode
  :hook ((php-mode . editorconfig-mode)
         (php-mode . fci-mode)
         (php-mode . (lambda ()
           (defun ywb-php-lineup-arglist-intro (langelem)
             (save-excursion
               (goto-char (cdr langelem))
               (vector (+ (current-column) c-basic-offset))))
           (defun ywb-php-lineup-arglist-close (langelem)
             (save-excursion
               (goto-char (cdr langelem))
               (vector (current-column))))
           (c-set-offset 'arglist-intro 'ywb-php-lineup-arglist-intro)
           (c-set-offset 'arglist-close 'ywb-php-lineup-arglist-close)))))

Markdown

;;; Markdown (from Jamie's)
;;  ----------------------------------------------------------------------------

(use-package markdown-mode
  :commands (markdown-mode gfm-mode)
  :mode (("README\\.md\\'" . gfm-mode)
         ("\\.md\\'" . markdown-mode)
         ("\\.markdown\\'" . markdown-mode))
  :init (setq markdown-command "multimarkdown"))

(use-package markdown-preview-mode
  :config
  (add-to-list 'markdown-preview-stylesheets "https://raw.githubusercontent.com/richleland/pygments-css/master/emacs.css"))

JSON

;;; JSON
;;  ----------------------------------------------------------------------------

(use-package json-mode)
(use-package json-reformat)
(use-package jsonrpc)

XML

;;; XML
;;  ----------------------------------------------------------------------------

;; pretty print xml region
(defun pretty-print-xml-region (begin end)
  "Pretty format XML markup in region. You need to have nxml-mode
http:https://www.emacswiki.org/cgi-bin/wiki/NxmlMode installed to do
this.  The function inserts linebreaks to separate tags that have
nothing but whitespace between them.  It then indents the markup
by using nxml's indentation rules."
  (interactive "r")
  (save-excursion
    (nxml-mode)
    (goto-char begin)
    ;; split <foo><foo> or </foo><foo>, but not <foo></foo>
    (while (search-forward-regexp ">[ \t]*<[^/]" end t)
      (backward-char 2) (insert "\n") (incf end))
    ;; split <foo/></foo> and </foo></foo>
    (goto-char begin)
    (while (search-forward-regexp "<.*?/.*?>[ \t]*<" end t)
      (backward-char) (insert "\n") (incf end))
    (indent-region begin end nil)
    (normal-mode))
  (message "All indented!"))

(eval-after-load 'nxml-mode
  '(define-key nxml-mode-map (kbd "C-c C-f") 'pretty-print-xml-region))

Yaml

;;; Yaml
;;  ----------------------------------------------------------------------------

(use-package yaml-mode
  :init
  (setq indent-tabs-mode nil)
  :mode "\\.yml\\'"
  :bind (:map yaml-mode-map
              ("C-m" . newline-and-indent)))

Optional

These are all untangled, because either I don’t use them very often, or they require additional setup that I haven’t bothered to document, yet.

Emacs iPython Notebook (Jupyter)

This is another WIP and requires that Anaconda is being used, one way or another (or manual installation).

See EIN for requirements and other caveats.

;;; Emacs iPython Notebook (EIN) with Jupyter support
;;  ----------------------------------------------------------------------------

(use-package ein
  :config
  (use-package ein-notebook)
  (use-package ein-subpackages)
  (use-package ein-mumamo))

Rust

There’s a bunch of dependencies required for rust-playground and racer-mode to work, so the init code for those sections are not tangled by default.

;;; Rust
;;  TODO: change all 'add-hook' to use use-package 'hook:'
;;  ----------------------------------------------------------------------------

;; Rust lang
(add-to-list 'exec-path "~/.cargo/bin")
(use-package rust-mode
  :mode "\\.rs\\'"
  :config (setq rust-format-on-save t)
  :init
  (add-hook 'rust-mode-hook 'fci-mode))

(use-package rustic)
(use-package cargo
  :init
  (add-hook 'rust-mode-hook 'cargo-minor-mode)
  (add-hook 'rust-mode-hook
            (lambda ()
              (local-set-key (kbd "C-c <tab>") #'rust-format-buffer)))
  (add-hook 'toml-mode-hook 'cargo-minor-mode))

(use-package racer
  :init
  (add-hook 'rust-mode-hook #'racer-mode)
  (add-hook 'racer-mode-hook #'eldoc-mode)
  (add-hook 'racer-mode-hook #'company-mode)
  :config
  (use-package company-racer)
  (define-key rust-mode-map (kbd "TAB") #'company-indent-or-complete-common)
  (setq racer-cmd "~/.cargo/bin/racer")
  (setq racer-rust-src-path "~/Dev/rust/rust/src"))

;; Flycheck for Rust
(use-package flycheck-rust
  :init
  (add-hook 'flycheck-mode-hook #'flycheck-rust-setup))

Go

I don’t use Go very often, so it’s not tangled by default.

;;; Go
;;  ----------------------------------------------------------------------------

(use-package go-mode)

Javascript

Built-in js-mode works better for me than js2-mode or js3 in Emacs 26.

;;; Javascript
;;  ----------------------------------------------------------------------------

(add-hook 'js3-mode-hook 'fci-mode)
(add-to-list 'auto-mode-alist '("\\.js\\'" . js2-mode))
(add-hook 'js-mode-hook 'js2-minor-mode)

LaTeX

I’ll get around to tricking out \LaTeX\ one of these days.

;;; LaTeX
;;  ----------------------------------------------------------------------------

;;(load "auctex.el" nil t t)
;;(load "preview-latex.el" nil t t)

DITAA

Really, really nice to have for rendering ASCII charts and embedding them in org-mode exports. Must install ditaa.jar and update the path for this to work.

;;; ditaa.jar
;;  ----------------------------------------------------------------------------

(setq org-ditaa-jar-path "/usr/bin/ditaa")

GNUPlot

GNUPlot can be pretty handy, but I don’t use it very often.

;;; GNUPlot
;;  ----------------------------------------------------------------------------

;; move the files gnuplot.el to someplace in your lisp load-path or
;; use a line like
;;  (setq load-path (append (list "/path/to/gnuplot") load-path))

;; these lines enable the use of gnuplot mode
(autoload 'gnuplot-mode "gnuplot" "gnuplot major mode" t)
(autoload 'gnuplot-make-buffer "gnuplot" "open a buffer in gnuplot mode" t)

;; this line automatically causes all files with the .gp extension to
;; be loaded into gnuplot mode
(setq auto-mode-alist (append '(("\\.gp$" . gnuplot-mode)) auto-mode-alist))

;; This line binds the function-9 key so that it opens a buffer into
;; gnuplot mode
(global-set-key [(f9)] 'gnuplot-make-buffer)

PDF-Tools

Next level PDF viewer, editor.

;;; PDF-Tools
;;  ----------------------------------------------------------------------------

(use-package pdf-tools
  :pin manual ;; manually update
  :config
  ;; initialise
  (pdf-tools-install)
  ;; open pdfs scaled to fit page
  (setq-default pdf-view-display-size 'fit-page)
  ;; automatically annotate highlights
  (setq pdf-annot-activate-created-annotations t)
  ;; use normal isearch
  (define-key pdf-view-mode-map (kbd "C-s") 'isearch-forward))

Org-mode

Some basic org-mode preferences. Mostly vanilla, except I like visual-line-mode for org files.

Oh yeah, and have to completely override org-html-fontify-code for export to work with fci-mode, or get a bunch of weird extraneous characters on newline. See this thread.

;;; Org-mode
;;  ----------------------------------------------------------------------------

;; Get htmlize
(use-package htmlize)

;; Install ob-php into symlinked ./elisp from GitHub
(require 'ob-php)

;; Tell Emacs where to find ditaa.jar
(setq org-ditaa-jar-path "/usr/share/ditaa/ditaa.jar")

;; Want a more local copy of org-mode so that I can override stuff if I want
(use-package org
  :mode ("\\.org\\'" . org-mode)
  :hook ((org-mode . visual-line-mode)
         (org-mode . org-indent-mode))
  :config
  (setq org-confirm-babel-evaluate nil)
  (setq org-todo-keywords
        '((sequence "TODO" "DEV" "READY" "QA" "FAILED" "|" "DONE" "CLOSED")))
  (org-babel-do-load-languages
   'org-babel-load-languages
   '((shell . t)
     (python . t)
     (ditaa . t)
     (sql . t)
     (restclient . t)
     (php . t)
     )))

;; Want to ignore headlines but not section contents if :ignore: added
;(use-package titletoc)
;(require 'ox-extra)
;(ox-extras-activate '(ignore-headlines)

Custom subtree narrowing

;; Better subtree narrowing to indirect buffer
(defun vimacs/org-narrow-to-subtree
    ()
  (interactive)
  (let ((org-indirect-buffer-display 'current-window))
    (if (not (boundp 'org-indirect-buffer-file-name))
        (let ((above-buffer (current-buffer))
              (org-filename (buffer-file-name)))
          (org-tree-to-indirect-buffer (1+ (org-current-level)))
          (setq-local org-indirect-buffer-file-name org-filename)
          (setq-local org-indirect-above-buffer above-buffer))
      (let ((above-buffer (current-buffer))
            (org-filename org-indirect-buffer-file-name))
        (org-tree-to-indirect-buffer (1+ (org-current-level)))
        (setq-local org-indirect-buffer-file-name org-filename)
        (setq-local org-indirect-above-buffer above-buffer)))))

(defun vimacs/org-widen-from-subtree
    ()
  (interactive)
  (let ((above-buffer org-indirect-above-buffer)
        (org-indirect-buffer-display 'current-window))
    (kill-buffer)
    (switch-to-buffer above-buffer)))

(define-key org-mode-map (kbd "<M-tab>") 'vimacs/org-narrow-to-subtree)
(define-key org-mode-map (kbd "<M-iso-lefttab>") 'vimacs/org-widen-from-subtree)

Fix fci-mode conflict

;; Patch ox-html.el org-html-fontify-code to avoid weird newline chars with fci-mode
(eval-after-load "ox-html"
  '(defun org-html-fontify-code (code lang)
     "Color CODE with htmlize library.
CODE is a string representing the source code to colorize.  LANG
is the language used for CODE, as a string, or nil."
     (when code
       (cond
        ;; No language.  Possibly an example block.
        ((not lang) (org-html-encode-plain-text code))
        ;; Plain text explicitly set.
        ((not org-html-htmlize-output-type) (org-html-encode-plain-text code))
        ;; No htmlize library or an inferior version of htmlize.
        ((not (and (or (require 'htmlize nil t)
                       (error "Please install htmlize from \
https://github.com/hniksic/emacs-htmlize"))
                   (fboundp 'htmlize-region-for-paste)))
         ;; Emit a warning.
         (message "Cannot fontify src block (htmlize.el >= 1.34 required)")
         (org-html-encode-plain-text code))
        (t
         ;; Map language
         (setq lang (or (assoc-default lang org-src-lang-modes) lang))
         (let* ((lang-mode (and lang (intern (format "%s-mode" lang)))))
           (cond
            ;; Case 1: Language is not associated with any Emacs mode
            ((not (functionp lang-mode))
             (org-html-encode-plain-text code))
            ;; Case 2: Default.  Fontify code.
            (t
             ;; htmlize
             (setq code
                   (let ((output-type org-html-htmlize-output-type)
                         (font-prefix org-html-htmlize-font-prefix))
                     (with-temp-buffer
                       ;; Switch to language-specific mode.
                       (funcall lang-mode)

                       ;; BEGIN PATCH: fix fci-mode export
                       (when (require 'fill-column-indicator nil 'noerror)
                         (fci-mode -1))
                       ;; END PATCH

                       (insert code)
                       ;; Fontify buffer.
                       (org-font-lock-ensure)
                       ;; Remove formatting on newline characters.
                       (save-excursion
                         (let ((beg (point-min))
                               (end (point-max)))
                           (goto-char beg)
                           (while (progn (end-of-line) (< (point) end))
                             (put-text-property (point) (1+ (point)) 'face nil)
                             (forward-char 1))))
                       (org-src-mode)
                       (set-buffer-modified-p nil)
                       ;; Htmlize region.
                       (let ((org-html-htmlize-output-type output-type)
                             (org-html-htmlize-font-prefix font-prefix))
                         (org-html-htmlize-region-for-paste
                          (point-min) (point-max))))))
             ;; Strip any enclosing <pre></pre> tags.
             (let* ((beg (and (string-match "\\`<pre[^>]*>\n?" code) (match-end 0)))
                    (end (and beg (string-match "</pre>\\'" code))))
               (if (and beg end) (substring code beg end) code)))))))))
  )

Structure Templates

;; 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"))

Custom org config

For my oporg project (WIP). Untangled by default.

;;; oporg - Settings and convenience for oporg and org-ehtml
;;  ----------------------------------------------------------------------------

(use-package org-ehtml
  :init
  (setq org-ehtml-docroot (expand-file-name "~/public_org"))
  (setq org-ehtml-everything-editable t))

(defun public-org-start (approot port)
  "Prompts for APPROOT and PORT to the run the web-server."
  (interactive
   (list
    (read-string
     (format "Path to app root (%s): " org-ehtml-docroot)
     nil nil org-ehtml-docroot)
    (read-string
     (format "Port to run the web-server on (%d): " 8888)
     nil nil 8888)
    ))
  (setq org-ehtml-docroot (expand-file-name approot))
  (ws-start org-ehtml-handler port))

(defun public-org-stop ()
  (interactive)
  (ws-stop-all))

Dungeons & Dragons

Because Emacs rocks.

(use-package org-d20)

Writing mode

Based on this post. For creative writing.

;;; Writing mode
;;  ----------------------------------------------------------------------------

(use-package writeroom-mode)

(defun writing-mode ()
  (interactive)
  (setq buffer-face-mode-face '(:family "dejavu sans mono" :height 150))
  (buffer-face-mode)
  (linum-mode 0)
  (writeroom-mode 1)
  (blink-cursor-mode)
  (visual-line-mode 1)
  (setq truncate-lines nil)
  (setq-default line-spacing 5)
  (setq global-hl-line-mode nil)
  )

;;; Standard Manuscript Format using LaTeX sffms package
;;  ----------------------------------------------------------------------------
;; org mode latex standard manuscript formats
;; https://chrismaiorana.com/org-mode-standard-manuscript-format/
;;
;; For your org file heading
;; #+LaTeX_CLASS: novel (or, short, whichever)
;; #+LATEX_HEADER: \runningtitle{Shortened Title}
;; #+LATEX_HEADER: \wordcount{?}

(require 'ox-latex)
(unless (boundp 'org-latex-classes)
  (setq org-latex-classes nil))
(add-to-list 'org-latex-classes
             '("novel"
               "\\documentclass[novel,geometry,letterpaper,courier]{sffms}
                \\surname{Edwards}
                \\address{7770 SW 90th St., J-10\u005C\u005C Miami, FL\u005C\u005C [email protected]}
                \\disposable
                \\usepackage{hyperref}
               [NO-DEFAULT-PACKAGES]
               [NO-PACKAGES]"
               ("\\chapter*{%s}")
               ("\\chapter*{%s}")
               ("\\chapter*{%s}")
               ("%s")
               ))

;; (setq org-latex-with-hyperref nil)
;; (setq org-latex-pdf-process '("pdflatex  %f"))

Custom Functions

Random functions that I have found handy at one point or another, and somehow made their way into my init.el.

Batch replace strings

Very handy when you want to search-and-replace a lot of pairs in one go.

;;; Custom functions
;;  ----------------------------------------------------------------------------

;; Search and replace pair-by-pair
(defun batch-replace-strings (replacement-alist)
  "Prompt user for pairs of strings to search/replace, then do so in the current buffer"
  (interactive (list (batch-replace-strings-prompt)))
  (dolist (pair replacement-alist)
    (save-excursion
      (replace-string (car pair) (cdr pair)))))

(defun batch-replace-strings-prompt ()
  "prompt for string pairs and return as an association list"
  (let (from-string
        ret-alist)
    (while (not (string-equal "" (setq from-string (read-string "String to search (RET to stop): "))))
      (setq ret-alist
            (cons (cons from-string (read-string (format "Replace %s with: " from-string)))
                  ret-alist)))
    ret-alist))

Emacs X Window Manager (EXWM)

Makes the old joke about Emacs being a great OS…

exwm-ex.png

EXWM is special, and I don’t always want it, or some of the customizations it necessitates, but when I do, I want it to be easy and not interfere with my existing desktop environment (if any).

The easiest thing was to tangle it to a separate file, exwm-init.el, which I then symlink into my .emacs.d/ when I want it enabled.

This literate README works for me as both a stand-alone WM on a bare-bones Arch Linux VM with no DE, and on my laptop as an alternative window manager to the default (Pop!_OS/Gnome). I can do the ole switcheroo at login.

Install EXWM

EXWM has its own system dependencies (like xorg-xinit) so refer to the EXWM User Guide before tangling this section.

For now, I just used package-list-packages to install everything default. I’ll revisit with use-package later.

  • exwm - Will install requirements as well

Optional packages:

  • exwm-edit - Edit any editable element in a buffer
  • exwm-mff - Mouse follows focus
  • helm-exwm - Because sometimes helm’s the way to go, and it needs to work

EXWM Config

To Tangle: delete :tangle no from the block header.

;;; Load the EXWM config from a separate file if it exists

(if (file-exists-p "~/.emacs.d/exwm-init.el")
    (load "~/.emacs.d/exwm-init.el"))

To Tangle: change :tangle no to :tangle exwm-init.el.

;;; ----------------------------------------------------------------------------
;;  exwm - emacs X window manager

;; (menu-bar-mode -1)
;; (tool-bar-mode -1)
;; (scroll-bar-mode -1)
(fringe-mode 1)
;; (ido-mode 1)
;; (server-start)

(require 'exwm)
(require 'exwm-config)
(exwm-config-ido)

(setq exwm-workspace-number 4)

;; All buffers created in EXWM mode are named "*EXWM*". You may want to
;; change it in `exwm-update-class-hook' and `exwm-update-title-hook', which
;; are run when a new X window class name or title is available.  Here's
;; some advice on this topic:
;; + Always use `exwm-workspace-rename-buffer` to avoid naming conflict.
;; + For applications with multiple windows (e.g. GIMP), the class names of
;    all windows are probably the same.  Using window titles for them makes
;;   more sense.
;; In the following example, we use class names for all windows except for
;; Java applications and GIMP.
(add-hook 'exwm-update-class-hook
          (lambda ()
            (unless (or (string-prefix-p "sun-awt-X11-" exwm-instance-name)
                        (string= "gimp" exwm-instance-name))
              (exwm-workspace-rename-buffer exwm-class-name))))
(add-hook 'exwm-update-title-hook
          (lambda ()
            (when (or (not exwm-instance-name)
                      (string-prefix-p "sun-awt-X11-" exwm-instance-name)
                      (string= "gimp" exwm-instance-name))
              (exwm-workspace-rename-buffer exwm-title))))

;; Global keybindings can be defined with `exwm-input-global-keys'.
;; Here are a few examples:
(setq exwm-input-global-keys
      `(
        ;; Bind "s-r" to exit char-mode and fullscreen mode.
        ([?\s-r] . exwm-reset)
        ;; Bind "s-w" to switch workspace interactively.
        ([?\s-w] . exwm-workspace-switch)
        ;; Bind "s-0" to "s-9" to switch to a workspace by its index.
        ,@(mapcar (lambda (i)
                    `(,(kbd (format "s-%d" i)) .
                      (lambda ()
                        (interactive)
                        (exwm-workspace-switch-create ,i))))
                  (number-sequence 0 9))
        ;; Bind "s-&" to launch applications ('M-&' also works if the output
        ;; buffer does not bother you). [?\s-&]
        ([?\s-&] . (lambda (command)
                     (interactive (list (read-shell-command "$ ")))
                     (start-process-shell-command command nil command)))
        ;; Bind "s-<f2>" to "slock", a simple X display locker.
        ([s-f2] . (lambda ()
                    (interactive)
                    (start-process "" nil "/usr/bin/slock")))))

;; To add a key binding only available in line-mode, simply define it in
;; `exwm-mode-map'.  The following example shortens 'C-c q' to 'C-q'.
(define-key exwm-mode-map [?\C-q] #'exwm-input-send-next-key)

;; The following example demonstrates how to use simulation keys to mimic
;; the behavior of Emacs.  The value of `exwm-input-simulation-keys` is a
;; list of cons cells (SRC . DEST), where SRC is the key sequence you press
;; and DEST is what EXWM actually sends to application.  Note that both SRC
;; and DEST should be key sequences (vector or string).
;;(setq exwm-input-simulation-keys
;;      '(
;;        ;; movement
;;        ([?\C-b] . [left])
;;        ([?\M-b] . [C-left])
;;        ([?\C-f] . [right])
;;        ([?\M-f] . [C-right])
;;        ([?\C-p] . [up])
;;        ([?\C-n] . [down])
;;        ([?\C-a] . [home])
;;        ([?\C-e] . [end])
;;        ([?\M-v] . [prior])
;;        ([?\C-v] . [next])
;;        ([?\C-d] . [delete])
;;        ([?\C-k] . [S-end delete])
;;        ;; cut/paste.
;;        ([?\C-w] . [?\C-x])
;;        ([?\M-w] . [?\C-c])
;;        ([?\C-y] . [?\C-v])
;;        ;; search
;;        ([?\C-s] . [?\C-f])))

;; You can hide the minibuffer and echo area when they're not used, by
;; uncommenting the following line.
;(setq exwm-workspace-minibuffer-position 'bottom)

;; Multiple-screen support
;; (require 'exwm-randr)
;; (setq exwm-randr-workspace-output-plist '(0 "VGA1"))
;; (add-hook 'exwm-randr-screen-change-hook
;;           (lambda ()
;;             (start-process-shell-command
;;              "xrandr" nil "xrandr --output VGA1 --left-of LVDS1 --auto")))
;; (exwm-randr-enable)

;;; In case we're embedded in LXDE
;;  (defun exwm-logout ()
;;    (interactive)
;;    (recentf-save-list)
;;    (save-some-buffers)
;;    (start-process-shell-command "logout" nil "lxsession-logout"))

;; Do not forget to enable EXWM. It will start by itself when things are
;; ready.  You can put it _anywhere_ in your configuration.
(exwm-enable)

XOrg XInit

As described in the EXWM documentation, make sure xorg-xinit is installed on the system.

Using GDM, .xinitrc will not interfere with the existing Gnome DE. BUT, if the .xinitrc or .xsessions files already exist, this will BREAK SHIT. It’s still doable but not in the scope of this README.

To Tangle: change :tangle no to :tangle xinitrc.exwm. Otherwise, just use as reference.

# You may need to comment out the next line to disable access control.
#xhost +SI:localuser:$USER

# Set themes, etc.
gnome-settings-daemon &

# Set fallback cursor.
xsetroot -cursor_name left_ptr

# Set keyboard repeat rate.
xset r rate 200 60

# If Emacs is started in server mode, `emacsclient` is a convenient way to
# edit files in place (used by e.g. `git commit`).
export VISUAL=emacsclient
export EDITOR="$VISUAL"

# Finally launch emacs.
#exec dbus-launch --exit-with-session emacs
#compton &
# guake &
# emacs --daemon -f exwm-enable
# exec emacsclient -a "" -c
# exec emacs -f exwm-enable
exec emacs

To use the tangled xinitrc.exwm, copy or symlink it to $HOME/.xinitrc.

Testing

Switch to a new TTY console. Usually tty1 is the Desktop Manager itself, and tty2 is you as you read this. To change TTY, just do C-M-F# where “#” is the Function Key number. Ie, C-M-F3 will get tty3.

Then just login to the console, and run startx. Emacs should start, running as an X Window Manager.

If there is no other X Window server running on the system (ie, you logged in to the console directly, not from a graphical DM) then you can start EXWM with xinit -- vt0# where ‘#’ is the TTY number.

Desktop Manager

EXWM may be an option from the existing DM login screen. First do: ln -s ~/.xinitrc ~/.xsessions

Then create the file /usr/share/xsessions/exwm.desktop, like the following.

NOTE: you need to hardcode your <PATHTO> since this is a config file not a bash script.

[Desktop Entry]
Name=EXWM
Comment=Emacs X Window Manager
Exec=<PATHTO>/.xsessions
Type=Application

Now, you should have an ‘EXWM’ option in the gear icon on the GDM login screen. If the testing went OK, then this should work, too since it’s calling the same init script.

Load custom init

In case I want to only load configs for this machine, not keeping it in this file.

;; Load custom init file?
(if (file-exists-p "~/.emacs.d/custom-init.el")
    (load "~/.emacs.d/custom-init.el"))

Conclusion

Tangle on save? Reload after tangle? These hooks will ask you after every save.

;; Local Variables: ;; eval: (add-hook ‘after-save-hook (lambda ()(if (y-or-n-p “Reload?”)(load-file user-init-file))) nil t) ;; eval: (add-hook ‘after-save-hook (lambda ()(if (y-or-n-p “Tangle?”)(org-babel-tangle))) nil t) ;; End:

About

My literate Emacs config

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published