UP | HOME

Emacs configuration for C++/CMake/git

Table of Contents

Intro

I hope this emacs configuration file will help explain a little elisp and how to write your own.

The most important take-away is understanding how emacs can help read and learn from other people's configurations. Once you know how to navigate the documentation you can pick apart any config file and expand your own

Note that I wrote this knowing no lisp at all - so if there are mistakes or you would like to make some suggestions I'd really appreciate you telling me by adding an issue on the github repo

General Settings

Hack for AppImage

There is some issue with the way the Emacs AppImage is setup so it interferes with git

(if (getenv "APPDIR") (setenv "LD_LIBRARY_PATH" nil))

Hide menu bars

(menu-bar-mode -1)
(tool-bar-mode -1)
(scroll-bar-mode -1)

Package managment

To begin extending Emacs we will want to be able to add external packages. Emacs comes with a built in package manager called package which we will use to get external package. We load it by using require and then we initialize it.

(require 'package)
(package-initialize)

To see more specifically what the require function does type C-h f require RET. You'll find that virtually every function will have some associted documentation.

Note the use of a leading quote before the word package; This tells emacs that the word package is a symbol. If you ommit the quote then emacs will try to evaluate package as a function and give you an error.

Next we need to tell the package manager where to look for new packages. There is a limited GNU-blessed repository called ELPA, but we will also want to add another larger repository called MELPA. To do that we we just need to add it to the list called package-archives
To look up a variable, use C-h v [variable] RET So type C-h v package-archives RET to see what it's for

;; (setq package-archives '(("gnu"   . "http://elpa.emacs-china.org/gnu/")
;;                          ("melpa" . "http://elpa.emacs-china.org/melpa/")))

(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t)
(package-initialize)

This C-h [something] [something] pattern repeats for a lot of features. To see the complete list just enter C-h C-h. These key-bindings will be your way of exploring configurations you find online.

The newer fancy way of managing package is using use-package. I've included it as a git submodule in my .emacs.d directory which you can see in the repository (be sure to clone recursively if you decide to use my version). You can get it yourself manually from: https://github.com/jwiegley/use-package

Once we have it in our directory we just need to add it to the load-path and load it in using require like last time

(setq
 load-path
 (cons
  (concat
   (file-name-directory load-file-name)
  "use-package")
  load-path))
(require 'use-package)

setq is the function that sets a variable to a value. The q indicates that we don't need to add a quote to the first arguement. There is also the more cumbersome (set 'arg1 'arg2). The rest of the code is there to append the new path to use-package correctly to the existing load-path.

Auto-update

This will make the packages update to the latest version every few days

;; (use-package auto-package-update
;;    :ensure t
;;    :config
;;    (setq auto-package-update-delete-old-versions t
;;          auto-package-update-interval 4)
;;    (auto-package-update-maybe))

Import shell variables

Especially when we launch emacs in daemon mode, the environmental variables won't be set up. So for example it won't find executables in the PATH b/c the PATH won't be set up the way you have it in your .bashrc file. So there is a special package to fix that

(use-package exec-path-from-shell
  :ensure t
  :config
  (unless (memq system-type '(ms-dos windos-nt cygwin))
    (exec-path-from-shell-initialize)))

Indentation - Change the default indentation from 2 spaces to 4

Indentation is generally govered by two variables
default-tab-width - this is the variable for any text document when you normally type in a TAB
c-basic-offset - when working with source code the indentation is done automatically and based on this offset value (a lot of modes derive from c-mode, hence the name)
More info: https://kb.iu.edu/d/abde

;  (setq c-basic-offset 4)

in ELisp it's also a bit hard to see the indentation level, so I like to add this guide (REMOVED)

;; (use-package indent-guide
;;    :ensure t
;;    :config
;;   (indent-guide-global-mode))

Trying out the very weird Parinfer mode (this code is straight copied from their github

;; (use-package parinfer
;;   :ensure t
;;   :bind
;;   (("C-," . parinfer-toggle-mode))
;;   :init
;;   (progn
;;     (setq parinfer-extensions
;;           '(defaults       ; should be included.
;;              pretty-parens  ; different paren styles for different modes.
;;              evil           ; If you use Evil.
;;              lispy          ; If you use Lispy. With this extension, you should install Lispy and do not enable lispy-mode directly.
;;              paredit        ; Introduce some paredit commands.
;;              smart-tab      ; C-b & C-f jump positions and smart shift with tab & S-tab.
;;              smart-yank))   ; Yank behavior depend on mode.
;;     (add-hook 'clojure-mode-hook #'parinfer-mode)
;;     (add-hook 'emacs-lisp-mode-hook #'parinfer-mode)
;;     (add-hook 'common-lisp-mode-hook #'parinfer-mode)
;;     (add-hook 'scheme-mode-hook #'parinfer-mode)
;;     (add-hook 'lisp-mode-hook #'parinfer-mode)))

Line wrap

Next we need to enable line-wrap in org mode. By default, as you keep typing the page scrolls to the right. So a whole paragraph will appear as one line making it difficult to navigate

(add-hook 'org-mode-hook (lambda () (setq truncate-lines nil)))

again, don't hesitate to look up all the variables and function. Hooks are in general places where you can add function to be called at designated times. Here it's a function that sets a variable each time org-mode is enabled.

Theme

This is the only decent light theme I could find. The advantage over the default theme is that it will color a more things in more modes. The most important to me me is that it will color code blocks in org-mode

  ;; (use-package moe-theme
  ;;   :ensure t
  ;;   :config
  ;;   (moe-light)
  ;;   (set-face-attribute 'default nil :background "#ffffff" :foreground "#5f5f5f"))
  ;; (use-package leuven-theme
  ;;   :init (setq leuven-scale-outline-headlines nil)
  ;;    (setq leuven-scale-org-agenda-structure nil)
  ;;   :ensure t)
(custom-theme-set-faces
 'user
 '(variable-pitch ((t (:family "unifont"))))
 '(fixed-pitch ((t ( :family "unifont"))))
 '(org-block ((t (:inherit fixed-pitch :background "#FFFFEA"))))) 

;; (set-fontset-font t 'han "Zpix")
;; (add-hook 'org-mode-hook 'variable-pitch-mode)
;; (add-hook 'org-mode-hook 'visual-line-mode)

The last line makes the background white (instead of yellow)

Git

For using git we want to have a couple of tools

Autocomplete

Ivy

(use-package ivy
  :ensure t)
(ivy-mode 1)
(setq ivy-use-virtual-buffers t)
(setq ivy-count-format "(%d/%d) ")

Standardizing (WIP)

These are some changes that bring Emacs more in line with how modern applications work. Most people don't do this.. I'm giving it a try

Overwrite selection

This will make it so that if you start typing after selecting some text it will actually overwrite what you selected instead of ignoring the selection and appending to the end. See: https://www.gnu.org/software/emacs/manual/html_node/efaq/Replacing-highlighted-text.html

(delete-selection-mode 1)

CUA Mode

This is the standard copy-cut-paste shortcuts that are different in Emacs by default. By restoring them to the standard Ctrl C/X/V it will interfere with some existing shortcuts in Emacs. So now whenever you see a Ctrl C You need to hit Ctrl C C. Off the top of my head I know this affects CIDER (Clojure code) and orgmode.

;  (cua-mode t)
;;(use-package ergoemacs-mode
;;  :ensure t
;;  :config
;;  (setq ergoemacs-theme nil)
;;  (setq ergoemacs-keyboard-layout "us")
;;  (ergoemacs-mode 1))

Orgmode

Some adjustments to org-mode

see here for reference

(setq org-confirm-babel-evaluate nil ;; don't prompt for confirmation about executing a block
      org-src-tab-acts-natively t
      org-use-sub-superscripts '{}
      org-src-fontify-natively t
      org-clock-into-drawer nil
      org-export-backends (quote (ascii html latex md odt))
      org-cycle-emulate-tab 'white
      org-export-with-timestamps nil)
  (use-package htmlize
    :ensure t)
(setq org-babel-default-header-args:octave '((:results . "org")
                                             (:session . "*Inferior Octave*")
                                             (:eval . "never-export")
                                             (:exports . "both")))

(setq org-babel-default-header-args:org '((:eval . "never")))
  • Turns off the annoying "are you sure?" prompts on tangle export
  • Makes tabs work in the source code blocks the same as it would in a buffer with that source code
  • Makes it so underscores aren't interpreted as subscripts unless used with braces

(I often need underscores for file/variable names)

  • Make source code gets colored based on the language
  • Newer version of Orgmode stick clocks into logbooks which aren't useful for me
  • Enable exporting to Markdown (for a full set of options run customize-option then enter org-export-backends (editing this will modify your init.el)
  • Make collapsing and expanding sections with the TAB button work everywhere (except where it makes sense to insert an actual tab)
  • htmlize will colorize orgmode code-blocks code in the exported HTML
  • org-babel-default-header-args are default ways I like to display run results in orgmode (subject to change)

For more info on any of these variables, again, use C-h v [variable] RET

Plotting

For one of my ongoing "project" I want my ELisp code to be able to output plots. For that we need to add gnuplot and then enable execution of gnuplot blocks in orgmode. The last line lets me make plots interactively in the gnuplot buffer

(use-package gnuplot
  :ensure t)

(org-babel-do-load-languages
 'org-babel-load-languages
 '((gnuplot . t)
   (octave . t)
   (clojure . t)))

 (gnuplot-inline-display-mode)

C++

Here we'll setup a development environment as feature rich as an IDE
We're going to use the new language server protocol way (instead of rtags as before). I'm just following the official guide

lsp-mode

;; (use-package lsp-mode
;;   :ensure t)

emacs-cquery

;; (use-package emacs-cquery
;;   :commands lsp-cquery-enable
;;   :init (setq cquery-executable "~/Programs/cquery/bin/cquery")
;;   (add-hook 'c-mode-hook #'cquery//enable)
;;   (add-hook 'c++-mode-hook #'cquery//enable)
;;   :ensure t)

Clojure

(WIP) Getting Clojure to play friendly with Orgmode is a bit weird. But you can sorta get it to work like ELisp, where you execute blocks within the document itself useing C-c C-c. You just need to open the corresponding tangled .clj file once, launch the REPL using C-c M-J and then make sure it's all loaded with C-c C-k. After this you don't need to really touch the tangled file anymore. You just need to change to the correct namespace in your REPL with the usual (in-ns 'something.somethingelse) and then you can just stick to running code blocks in the org document. The last step is crucial b/c when you run blocks in your org document they will be effectively running in whatever state your REPL is in (though the output will go to RESULTS blocks in the org document and not the REPL output/buffer). So it's a bit goofy.. b/c the org document runs are tied to the current REPL state.

If there is no REPL running then the thing seems to just not run at all. The auto-jack-in option here doesn't seem to really work unfortunately. This might make exporting with no running REPL a bit broken. We'll see how the setup works..

(require 'ob-clojure)
(setq org-babel-clojure-backend 'cider)
(setq ob-clojure-literate-auto-jackin-p t)

Starting to play around with Clojure. The canonical Clojure development environment is CIDER

(use-package cider
  :ensure t
  :init (setq org-babel-clojure-backend 'cider))

Add a default CIDER alias

(setq cider-clojure-cli-global-options "-A:server:client:dev")

Company

Next we turn on company. The package that will do autocompletion for us (it standards for COMPlete ANYthing)

(use-package company
  :config
  (add-hook 'cider-repl-mode-hook #'cider-company-enable-fuzzy-completion)
  (add-hook 'cider-mode-hook #'cider-company-enable-fuzzy-completion)
  (global-set-key (kbd "TAB") #'company-indent-or-complete-common))

  ;(push 'company-rtags company-backends) TODO: FIX this RTags related stuff!
  ;;(global-company-mode)

looking at the documentation we see that push will take the 1st argument and add it to the beginning of the list provided in the 2nd argument. company-backends is "a list of active backends (completion engines)". company-rtags is a backend provided by the rtags guys. See the documentation for more info :)

Autosave/Backup files

By default Emacs will save copies of files with a ~ appended. This ends up cluttering directories and makes any directory touched by Emacs a bit of a mess. Instead we can have Emacs save files to a central directory.

See: https://www.emacswiki.org/emacs/BackupDirectory and https://stackoverflow.com/questions/151945/how-do-i-control-how-emacs-makes-backup-files

(setq
   backup-by-copying t      ; don't clobber symlinks
   backup-directory-alist
    '(("." . "~/.saves/"))    ; don't litter my fs tree
   delete-old-versions t
   kept-new-versions 6
   kept-old-versions 2)

Magit

This is the tool for inspecting and updating out git repository. It's a little complicated to use, so look up documentation for it. It is a must for development in emacs if you use git - so make the investment and learn to use it.

(use-package magit
  :ensure t
  :init)
;;  (setq magit-display-buffer-function #'magit-display-buffer-fullcolumn-most-v1))

Projectile

This will manage our workspaces. Each workspace will be tied to a git repository. This makes it so that our buffer list doesn't get really crowded when we are working on multiple projects

(use-package projectile
  :ensure t
  :config
  (projectile-mode +1))

(define-key projectile-mode-map (kbd "s-p") 'projectile-command-map)
(define-key projectile-mode-map (kbd "C-c p") 'projectile-command-map)
(setq projectile-completion-system 'ivy)
(setq projectile-use-git-grep t)

(setq projectile-use-native-indexing t)
;;(setq projectile-enable-caching t)
(setq projectile-git-command "/usr/bin/git ls-files -zc")

I sometimes use this - and other times I just run separate emacs sessions for different projects.

images

When you open a GIF, make it loop forever (instead of playing through once and stopping

(setq image-animate-loop t)

Hit RET to have it start playing

system-monitor

A tiny in-bar system monitor is convenient (and doesn't need to be part of my desktop).

;; (use-package symon
;;   :ensure t
;;   :config
;;   (add-to-list 'symon-monitors 'symon-linux-battery-monitor)
;;   (symon-mode))

which-key

This is gunna give us tips for different modes so we can learn new key-combos

(use-package which-key
  :ensure t)

mousewheel

make scrolling in Emacs more sane

;(setq mouse-wheel-scroll-amount '(0.07))
(setq mouse-wheel-scroll-amount '(1 ((shift) . 1)))
(setq mouse-wheel-progressive-speed nil)
(setq scroll-step 1)

eww

Set links to open in the Emacs browser (then press & if you need to open them in Firefox)

;(setq browse-url-browser-function 'eww-browse-url)

Fonts

Example of how to set a font from within Emacs

;; (set-face-attribute 'default nil :font "tewi:pixelsize=11:foundry=lucy:weight=normal:slant=normal:width=normal:spacing=110:scalable=false")
;; (set-frame-font "tewi:pixelsize=11:foundry=lucy:weight=normal:slant=normal:width=normal:spacing=110:scalable=false" nil t)

KeyBindings

F-Key shortcuts

  • magit status
  • git gutter
  • export to html
  • tangle file
  • refresh/revert file
  • launch REPL
  • Go to Definition (VS)
  • Step Into
  • Step Out
  • Step Over
  • Toggle Breakpoint (VS)
  • Build Selection
  • Debug Start
  • Split Window (Dolphin)
  • Shell (Dolphin)
(global-set-key (kbd "<f2>") 'magit-status)
(global-set-key (kbd "<f3>") 'cider-browse-ns)
(global-set-key (kbd "<f4>") 'eshell)
(setq display-buffer-alist '(("\\`\\*e?shell" display-buffer-use-some-window)))
(global-set-key (kbd "<f5>") 'calendar)
(global-set-key (kbd "<f6>") 'org-babel-tangle)
(global-set-key (kbd "<f7>") 'org-html-export-to-html)
(global-set-key (kbd "<f8>") 'eshell)
(global-set-key (kbd "<f9>") 'cider-jack-in)
(global-set-key (kbd "<f10>") 'cider-xref-fn-refs)
(global-set-key (kbd "<f11>") 'cider-browse-ns-all)
(global-set-key (kbd "<f12>") ''cider-enlighten-mode)

Bookmarks

Live in ~/.emacs.d/bookmarks
Set bookmarks with =

  • C-x r m RET
  • C-x r m /bookmark/ RET

Jump to bookmark

  • C-x r b /bookmark/ RET

List bookmarks

  • C-x r l

Other notes

  • C-l recenter around point
  • show-trailing-whitespace - to display.. whitespace
  • delete-trailing-whitespace
  • display-time
  • display-battery-mode

"Save As"

  • C-x C-w

Autoreload files (like logs.. ones that change "under you")

Help

  • C-h C-h general help menu
  • C-h a /topic/ RET appropos
  • C-h i d m emacs RET i /topic/ RET index
  • C-h i d m emacs RET s /topic/ RET manual text
  • C-h C-f FAQ
  • [prefix] C-h completion options
  • C-h b all active bindings (minor/major modes)
  • C-h m major/minor modes
  • C-h d /topic/ RET apropos
  • C-h e Messages buffer
  • C-h h HELLO file + character set
  • C-h i Info + Manual
  • C-h k /key/ describe key
  • C-h o /symbol/ info on symbol

Dired

  • C-x C-d directory listing
  • C-u C-x C-d vebose directory listing
  • make-directory
  • delete-directory
  • q bury Dired buffer
  • SPC/n navigate dired up/down
  • j go to file
  • e/f RET extract file into buffer tar mode
  • d delete
  • m mark as file (with a *)
  • ** mark all executable files
  • u unmark
  • x execute pending operations
  • C /new/ RET like cp with new being the arg
  • D like rm removed marked files
  • R /new RET rename
  • H /new RET hard link
  • S /new RET soft link
  • M /new RET change mode
  • G /new RET new group
  • O /new RET new owner
  • T /new RET touch
  • Z compress (each into it's own archive)
  • c compress into one archive
  • :d decompress
  • :v verify signature
  • :s sign
  • :e encrypt
  • ! run shell command
  • & same but async
  • I insert file (empty)
  • ) Hide details (we turn on by default)
  • ~ go "up" a directory (like ..)
  • /host:filename
  • /user@host:filename
  • /user@host#port:filename
  • method:user@host#port:filename

GDB / GUD

  • M-x gdb runs the GDB graphical interface
  • M-x gud-gdb run GUD and GDB as a subprocess
  • C-x C-a C-b set breakpoint at current source line
  • C-c C-l display the last source line in another window
  • C-c C-s run next line ENTER function
  • C-c C-n STEPOVER
  • C-c C-i execute a simple machine instruction
  • C-c C-p evaluate expression at point - can mark a region
  • C-c C-r CONTINUE
  • C-c C-d delete breakpoint
  • C-c C-t temp breakpoint
  • C-c >/< up/down stack
  • C-c C-u CONTINUE to current line
  • C-c C-f run till stack frame is done
  • C-x C-a C-j jump to point
  • C-x k kill the GUD session and all associated buffers

Compiling

  • compile runs the compile command (default make -k) in the current buffer's directory
  • recompile reruns compile in the previous directory
  • C-x ` go to next error message
  • M-; go to next error