From 77bfd2c8f9aaa283d14f60e9016f7a36e9ab63cc Mon Sep 17 00:00:00 2001 From: Christopher James Hayward Date: Sun, 16 May 2021 10:41:09 -0400 Subject: [PATCH] editor -> (dired, evil, keys, magit) --- elisp/options.el | 2 +- modules/dired.org | 52 ++++++++ modules/editor.org | 320 --------------------------------------------- modules/evil.org | 62 +++++++++ modules/keys.org | 136 +++++++++++++++++++ modules/magit.org | 62 +++++++++ 6 files changed, 313 insertions(+), 321 deletions(-) create mode 100644 modules/dired.org delete mode 100644 modules/editor.org create mode 100644 modules/evil.org create mode 100644 modules/keys.org create mode 100644 modules/magit.org diff --git a/elisp/options.el b/elisp/options.el index 186d82a..66731cc 100644 --- a/elisp/options.el +++ b/elisp/options.el @@ -5,7 +5,7 @@ (defconst dotfiles/modules-p '(org trash - editor + keys evil dired magit shell email feeds diff --git a/modules/dired.org b/modules/dired.org new file mode 100644 index 0000000..fac631f --- /dev/null +++ b/modules/dired.org @@ -0,0 +1,52 @@ +#+TITLE: Dired +#+AUTHOR: Christopher James Hayward +#+EMAIL: chris@chrishayward.xyz + +#+PROPERTY: header-args:emacs-lisp :tangle dired.el :comments org +#+PROPERTY: header-args :results silent :eval no-export :comments org + +#+OPTIONS: num:nil toc:nil todo:nil tasks:nil tags:nil +#+OPTIONS: skip:nil author:nil email:nil creator:nil timestamp:nil + +Emacs has a built-in directory editor. + +* Config + +Emacs has some really amazing built-in packages, and ~dired~[fn:1] is one of them. It completly coveres everything you would expect from a file manager, but it's not perfect out of the box. + +** Current directory + +I don't want to press =RET= twice to navigate to the current directory. There's a way to get around this problem with ~jump~, included in the ~dired-x~ package, included with Emacs. + +#+begin_src emacs-lisp +(require 'dired-x) +#+end_src + +** Reusing the same buffer + +By default, ~dired~[fn:1] will create a new buffer each time you press =RET= over a directory. This leads to unwanted buffers all over the place. Avoid this behaviour with ~dired-single~[fn:2], reusing the same ~dired~ buffer. + ++ Move up a directory with =h= ++ Open a single buffer with =l= + +#+begin_src emacs-lisp +(use-package dired-single + :config (evil-collection-define-key 'normal 'dired-mode-map + "h" 'dired-single-up-directory + "l" 'dired-single-buffer)) +#+end_src + +* Shortcuts + +Open a new dired buffer using the ~jump~ command with =SPC d=. + +#+begin_src emacs-lisp +(dotfiles/leader + "d" '(dired-jump :which-key "Dired")) +#+end_src + +* Footnotes + +[fn:1] https://en.wikipedia.org/wiki/Dired + +[fn:2] https://github.com/crocket/dired-single diff --git a/modules/editor.org b/modules/editor.org deleted file mode 100644 index 2ff75a8..0000000 --- a/modules/editor.org +++ /dev/null @@ -1,320 +0,0 @@ -#+TITLE: Editor -#+AUTHOR: Christopher James Hayward -#+EMAIL: chris@chrishayward.xyz - -#+PROPERTY: header-args:emacs-lisp :tangle editor.el :comments org -#+PROPERTY: header-args :results silent :eval no-export :comments org - -#+OPTIONS: num:nil toc:nil todo:nil tasks:nil tags:nil -#+OPTIONS: skip:nil author:nil email:nil creator:nil timestamp:nil - -Configuration and imrpovements to the editor experience within Emacs. *Vim*[fn:1] user? This module extends the keybindings by implementing *Evil*[fn:2]. *Doom*[fn:3], *Spacemacs*[fn:4]? The all powerful leader key is also implemented right here! - -* Keybindings - -Offer =ESC= as an alternative to quit (most) prompts, instead of the default =C-g=. - -#+begin_src emacs-lisp -(global-set-key (kbd "") 'keyboard-escape-quit) -#+end_src - -** Completions - -Emacs has *a lot of keybindings*, sometimes it's useful to just start mashing keys and see what happens. This behaviour exists in a third-party package called *which-key*[fn:5]. It displays the current incomplete keybinding input in a mini-buffer, showing available completion options with their corresponding keybindings. - -#+begin_src emacs-lisp -(use-package which-key - :diminish which-key-mode - :custom (which-key-idle-delay dotfiles/idle) - :config (which-key-mode)) -#+end_src - -** Turn Emacs into Vim - -Emacs has *some strange default keybindings*, they're not like any other editor you've likely ever used. To overcome this nearly show-stopping hurdle, we turn Emacs into Vim[fn:1] with *Evil Mode - The Extensible VI Layer for Emacs*[fn:2]. - -#+begin_src emacs-lisp -(use-package evil - :custom (evil-want-integration t) ;; Required for `evil-collection'. - (evil-want-keybinding nil) ;; Same as above - :config (evil-mode +1)) -#+end_src - -While covering substantial ground towards our goal, the default keybindings implemented in *Evil*[fn:2] alone are *lacking* compared to what you would expect from *Vim*[fn:1]. There's, of course, a communicated curated package *evil-collection*[fn:6] that does a much better job implementing the proper keybindings. - -#+begin_src emacs-lisp -(use-package evil-collection - :after evil - :config (evil-collection-init)) -#+end_src - -*** Surround text - -Whether it's on purpose, or more likely, you forgot an opening brace; *evil-surround*[fn:7] surrounds highlighted blocks of text with functions, quotations, and any symbol you can input. - -#+begin_src emacs-lisp -(use-package evil-surround - :after evil - :config (global-evil-surround-mode 1)) -#+end_src - -*** Toggle comments - -When you're in deep with errors, or just trying some new code, it's useful to be able to toggle large comment sections in a language agnostic manner. In comes *evil-nerd-commentor*[fn:8], with a custom binding to =M-;=. What is =M-= ? Typically that refers to the =Alt= key, called the =Meta= key in Emacs. - -#+begin_src emacs-lisp -(use-package evil-nerd-commenter - :after evil - :bind ("M-;" . evilnc-comment-or-uncomment-lines)) -#+end_src - -** Implementing the leader key - -If you're like me and started with Emacs using a framework like *Doom*[fn:3] or *Spacemacs*[fn:4], you probably have a lot of muscle memory for using =SPC= as a leader key. This behaviour is actually not difficult to implement, especially when using *general.el*[fn:9]. - -+ =SPC= in most situations as a prefix key -+ =C-SPC= when using the [[file:desktop.org][Desktop]] module within an =X= buffer - -#+begin_src emacs-lisp -(use-package general - :after evil - :config - (general-create-definer dotfiles/leader - :states '(normal motion) - :keymaps 'override - :prefix dotfiles/leader-key - :global-prefix dotfiles/leader-key-global)) -#+end_src - -** Transient bindings - -Create transient keybindings with a shared prefix through *Hydra*[fn:10]. This is also used by a number of third-party packages as a completion system. An implementation example is available in the *Font* section of the [[file:interface.org][Interface]] module. - -+ Defer loading for performance - -#+begin_src emacs-lisp -(use-package hydra - :defer t) -#+end_src - -Place runtime tweaks behind =SPC t= - -#+begin_src emacs-lisp -(dotfiles/leader - "t" '(:ignore t :which-key "Tweaks")) -#+end_src - -** Cherry picked shortcuts - -Implement shortcut bindings, cherry picked from *Doom*[fn:3]. - -+ Close buffers with =SPC c= -+ Find files with =SPC , (comma)= - -#+begin_src emacs-lisp -(dotfiles/leader - "." '(find-file :which-key "Files") - "c" '(kill-buffer-and-window :which-key "Close")) -#+end_src - -*** Managing windows - -Window management with =SPC w= - -+ Swap with =w= -+ Close with =c= -+ Move with =h,j,k,l= -+ Split with =s - = - -#+begin_src emacs-lisp -(dotfiles/leader - "w" '(:ignore t :which-key "Window") - "ww" '(window-swap-states :which-key "Swap") - "wc" '(delete-window :which-key "Close") - "wh" '(windmove-left :which-key "Left") - "wj" '(windmove-down :which-key "Down") - "wk" '(windmove-up :which-key "Up") - "wl" '(windmove-right :which-key "Right") - "ws" '(:ignore t :which-key "Split") - "wsj" '(split-window-below :which-key "Down") - "wsl" '(split-window-right :which-key "Right")) -#+end_src - -*** Quitting Emacs - -Quit Emacs with =SPC q= - - + Save and quit =q= - + Quit without saving =w= - + Exit the Frame (daemon) =f= - -#+begin_src emacs-lisp -(dotfiles/leader - "q" '(:ignore t :which-key "Quit") - "qq" '(save-buffers-kill-emacs :which-key "Save") - "qw" '(kill-emacs :which-key "Now") - "qf" '(delete-frame :which-key "Frame")) -#+end_src - -* Helper functions - -Use the built-in ~describe-*~ functionality of Emacs to quickly access documentation for packages, variables, and functions. - -+ Run helper functions with =SPC h= - * Packages =p= - * Variables =v= - * Functions =f= - -#+begin_src emacs-lisp -(dotfiles/leader - "h" '(:ignore t :which-key "Help") - "hp" '(describe-package :which-key "Package") - "hv" '(describe-variable :which-key "Variable") - "hf" '(describe-function :which-key "Function")) -#+end_src - -* File navigation - -Emacs has some really cool built-in packages, *Dired*[fn:11] is one of them. It's not perfect out of the box though, there's work to do. - -** Navigating to the current directory - -I don't want to have to press =RET= twice to navigate to the current directory. Avoid this behaviour with ~jump~, included in the =dired-x= package that ships with *Dired*[fn:11]. - -+ Open a new dired buffer with =SPC d=. - -#+begin_src emacs-lisp -(require 'dired-x) -(dotfiles/leader - "d" '(dired-jump :which-key "Dired")) -#+end_src - -** Reusing the same buffer - -By default *Dired*[fn:11] will create a new buffer every time you press =RET= over a directory. This leads to unwanted buffers all over the place. Avoid this behaviour with *Dired Single*[fn:12], reusing the same dired buffer. - -+ Move up a directory with =h= -+ Open a single buffer with =l= - -#+begin_src emacs-lisp -(use-package dired-single - :config (evil-collection-define-key 'normal 'dired-mode-map - "h" 'dired-single-up-directory - "l" 'dired-single-buffer)) -#+end_src - -* Version control - -#+ATTR_ORG: :width 420px -#+ATTR_HTML: :width 420px -#+ATTR_LATEX: :width 420px -[[../docs/images/2021-02-13-example-magit.gif]] - -Yet another hallmark feature of Emacs: *Magit*[fn:13] with the *darling* name, the developer stresses it's supposed to be *Magic* but with *Git*[fn:14]. It's a complete *Git*[fn:14] porcelain within Emacs. - -#+begin_src emacs-lisp -(use-package magit - :commands magit-status - :custom (magit-display-buffer-function - #'magit-display-buffer-same-window-except-diff-v1)) -#+end_src - -Place keybindings for *magit*[fn:13] behind =SPC g=. - -+ Clone with =c= -+ Status with =g= - -#+begin_src emacs-lisp -(dotfiles/leader - "g" '(:ignore t :which-key "Magit") - "gc" '(magit-clone :which-key "Clone") - "gg" '(magit-status :which-key "Status")) -#+end_src - -** GitHub integration - -Interact with *Git*[fn:14] forges from *Magit*[fn:13] and Emacs using *Forge*[fn:15], requiring only a *GitHub*[fn:16] token to get started. If you're not sure what *GitHub*[fn:16] is, it's to *Git*[fn:14] what *Porn* is to *PornHub*. No citations! - -+ Requires a valid ~$GITHUB_TOKEN~ - -#+begin_src emacs-lisp -(use-package forge - :after magit) -#+end_src - -** Deploying the global config - -*Git*[fn:14] reads its global config from ~$HOME/.gitconfig~, create a link to the custom configuration. - -#+begin_src emacs-lisp -(dotfiles/symlink "~/.emacs.d/config/git" - "~/.gitconfig") -#+end_src - - -** Interactive terminal - -Sometimes *Eshell*[fn:17] just isn't enough. Going through [[https://chrishayward.xyz/notes/thinking-in-cpp/][Thinking in C++]] for one of my courses requires lots of terminal input which *Eshell*[fn:17] just doesn't handle. Prior to this I was dropping to another *TTY* interface, but that was cumbersome. *Vterm's*[fn:18] based on an external C library which is blazing fast. - -+ Always compile the module - -#+begin_src emacs-lisp -(use-package vterm - :commands (vterm-other-window) - :custom (vterm-always-compile-module t)) -#+end_src - -+ Open =vterm= buffer with =SPC v= - -#+begin_src emacs-lisp -(dotfiles/leader - "v" '(vterm-other-window :which-key "Terminal")) -#+end_src - -*** Installing dependencies - -Install dependencies on Debian/Ubuntu: - -#+begin_src shell -sudo apt install -y cmake \ - libtool \ - libtool-bin -#+end_src - -* Footnotes - -[fn:1] https://vim.org - -[fn:2] https://evil.readthedocs.io/en/latest/index.html - -[fn:3] https://github.com/hlissner/doom-emacs/ - -[fn:4] https://spacemacs.org - -[fn:5] https://github.com/justbur/emacs-which-key/ - -[fn:6] https://github.com/emacs-evil/evil-collection - -[fn:7] https://github.com/emacs-evil/evil-surround - -[fn:8] https://github.com/redguardtoo/evil-nerd-commenter - -[fn:9] https://github.com/noctuid/general.el - -[fn:10] https://github.com/abo-abo/hydra - -[fn:11] https://en.wikipedia.org/wiki/Dired - -[fn:12] https://github.com/crocket/dired-single - -[fn:13] https://github.com/magit/magit - -[fn:14] https://git-scm.com - -[fn:15] https://github.com/magit/forge - -[fn:16] https://github.com - -[fn:17] https://gnu.org/software/emacs/manual/html_mono/eshell.html - -[fn:18] https://github.com/akermu/emacs-libvterm diff --git a/modules/evil.org b/modules/evil.org new file mode 100644 index 0000000..b618e61 --- /dev/null +++ b/modules/evil.org @@ -0,0 +1,62 @@ +#+TITLE: Evil +#+AUTHOR: Christopher James Hayward +#+EMAIL: chris@chrishayward.xyz + +#+PROPERTY: header-args:emacs-lisp :tangle evil.el :comments org +#+PROPERTY: header-args :results silent :eval no-export :comments org + +#+OPTIONS: num:nil toc:nil todo:nil tasks:nil tags:nil +#+OPTIONS: skip:nil author:nil email:nil creator:nil timestamp:nil + +Transform Emacs into Vim. + +* Config + +Emacs has some strange default keybindings, which given the age of the project, is not surprising. To overcome this nearly show-stopping hurdle, we transform Emacs into Vim with ~evil~[fn:1], the Extensible VI Layer for Emacs. + +#+begin_src emacs-lisp +(use-package evil + :custom (evil-want-integration t) ;; Required for `evil-collection'. + (evil-want-keybinding nil) ;; Same as above + :config (evil-mode +1)) +#+end_src + +** Improvements + +While covering substantial ground towards the goal, the default keybindings implemented in ~evil~[fn:1] alone are lacking compared to what you would expect from Vim. There's a community curated package ~evil-collection~[fn:2] that does a much better job implementing the proper keybindings. + +#+begin_src emacs-lisp +(use-package evil-collection + :after evil + :config (evil-collection-init)) +#+end_src + +** Surround text + +Quickly and efficiently surround text with ~evil-surround~[fn:3]. Highlight blocks of text with function definitions, quotations, or any symbol you can input from your keyboard. + +#+begin_src emacs-lisp +(use-package evil-surround + :after evil + :config (global-evil-surround-mode 1)) +#+end_src + +** Toggle comments + +Toggle comments in a language agnostic manner with ~evil-nerd-commenter~[fn:4]. Add a custom binding to =M-;= to mimmic the behaviour in other popular Emacs configuration frameworks. What is =M-?= Called the *Meta* key in Emacs, it typically refers to =Alt=. + +#+begin_src emacs-lisp +(use-package evil-nerd-commenter + :after evil + :bind ("M-;" . evilnc-comment-or-uncomment-lines)) +#+end_src + +* Footnotes + +[fn:1] https://github.com/emacs-evil/evil-mode + +[fn:2] https://github.com/emacs-evil/evil-collection + +[fn:3] https://github.com/emacs-evil/evil-surround + +[fn:4] https://github.com/redguardtoo/evil-nerd-commenter diff --git a/modules/keys.org b/modules/keys.org new file mode 100644 index 0000000..f46fd33 --- /dev/null +++ b/modules/keys.org @@ -0,0 +1,136 @@ +#+TITLE: Keys +#+AUTHOR: Christopher James Hayward +#+EMAIL: chris@chrishayward.xyz + +#+PROPERTY: header-args:emacs-lisp :tangle keys.el :comments org +#+PROPERTY: header-args :results silent :eval no-export :comments org + +#+OPTIONS: num:nil toc:nil todo:nil tasks:nil tags:nil +#+OPTIONS: skip:nil author:nil email:nil creator:nil timestamp:nil + +Improve the keyboard experience within Emacs. + +* Config + +Some of the default keybindings in Emacs really do leave you wondering, for example, when you want to exit a prompt you have to use =C-g=. Offer =ESC= as an alternative to quit (most) prompts, which I have muscle memory for already from literally every program created since 1990. + +#+begin_src emacs-lisp +(global-set-key (kbd "") 'keyboard-escape-quit) +#+end_src + +** Hints + +Since Emacs is keyboard driven software, there are a lot of keybindings. Sometimes it's useful to start pressing well-known key combinations, and view the available completions. This behaviour is implemented in the third-party package ~which-key~[fn:1]. It displays the current incomplete keybinding input in a mini-buffer. It also works in the other direction, showing the corresponding keybindings for each command when you run =M-x=. + +#+begin_src emacs-lisp +(use-package which-key + :diminish which-key-mode + :custom (which-key-idle-delay dotfiles/idle) + :config (which-key-mode)) +#+end_src + +** Leader + +If like myself, you started using Emacs using a framework such as ~doom~[fn:2] or ~spacemacs~[fn:3], you probably have a considerable amount of muscle memory developed for using =SPC= as a leader key. In both of the previously mentioned frameworks, the package ~general.el~[fn:4] is used to implement this behaviour. It's a major improvement to the default way of creating custom keybindings in Emacs. + +#+begin_src emacs-lisp +(use-package general + :config + (general-create-definer dotfiles/leader + :states '(normal motion) + :keymaps 'override + :prefix dotfiles/leader-key + :global-prefix dotfiles/leader-key-global)) +#+end_src + +** Transient + +Create transient keybindings with a shared prefix through ~hydra~[fn:5]. This is also used by a number of third-party packages as a completion system. An implementation example is used to scale the font size. + +#+begin_src emacs-lisp +(use-package hydra + :defer t) +#+end_src + +* Shortcuts + +Implement some shortcut bindings, with a significant portion of them cherry picked from ~doom~[fn:2]: + ++ Close buffers with =SPC c= ++ Find files with =SPC . (period)= ++ Switch buffers with =SPC , (comma)= + +#+begin_src emacs-lisp +(dotfiles/leader + "." '(find-file :which-key "Files") + "," '(switch-buffer :which-key "Buffers") + "c" '(kill-buffer-and-window :which-key "Close")) +#+end_src + +** Managing windows + +Screen space is divided into Frames inside of Emacs, manage them behind =SPC w=: + ++ Swap with =w= ++ Close with =c= ++ Move with =h,j,k,l= ++ Split with =s - = + +#+begin_src emacs-lisp +(dotfiles/leader + "w" '(:ignore t :which-key "Window") + "ww" '(window-swap-states :which-key "Swap") + "wc" '(delete-window :which-key "Close") + "wh" '(windmove-left :which-key "Left") + "wj" '(windmove-down :which-key "Down") + "wk" '(windmove-up :which-key "Up") + "wl" '(windmove-right :which-key "Right") + "ws" '(:ignore t :which-key "Split") + "wsj" '(split-window-below :which-key "Down") + "wsl" '(split-window-right :which-key "Right")) +#+end_src + + +** Quitting Emacs + +Customize the behaviour of exiting emacs, with keybindings behind =SPC q=: + ++ Save and quit =q= ++ Quit without saving =w= ++ Exit the Frame (daemon) =f= + +#+begin_src emacs-lisp +(dotfiles/leader + "q" '(:ignore t :which-key "Quit") + "qq" '(save-buffers-kill-emacs :which-key "Save") + "qw" '(kill-emacs :which-key "Now") + "qf" '(delete-frame :which-key "Frame")) +#+end_src + +** Helper Functions + +Use the built-in ~describe-*~ functionality of Emacs to quickly access documentation for packages, variables, and functions. Run helper functions with =SPC h=: + ++ Packages =p= ++ Variables =v= ++ Functions =f= + +#+begin_src emacs-lisp +(dotfiles/leader + "h" '(:ignore t :which-key "Help") + "hp" '(describe-package :which-key "Package") + "hv" '(describe-variable :which-key "Variable") + "hf" '(describe-function :which-key "Function")) +#+end_src + +* Footnotes + +[fn:1] https://github.com/justbur/emacs-which-key/ + +[fn:2] https://github.com/hlissner/doom-emacs/ + +[fn:3] https://spacemacs.org + +[fn:4] https://github.com/noctuid/general.el + +[fn:5] https://github.com/abo-abo/hydra diff --git a/modules/magit.org b/modules/magit.org new file mode 100644 index 0000000..902c7ad --- /dev/null +++ b/modules/magit.org @@ -0,0 +1,62 @@ +#+TITLE: Magit +#+AUTHOR: Christopher James Hayward +#+EMAIL: chris@chrishayward.xyz + +#+PROPERTY: header-args:emacs-lisp :tangle magit.el :comments org +#+PROPERTY: header-args :results silent :eval no-export :comments org + +#+OPTIONS: num:nil toc:nil todo:nil tasks:nil tags:nil +#+OPTIONS: skip:nil author:nil email:nil creator:nil timestamp:nil + +Handle all of the git interactions inside of Emacs. + +* Setup + +Deploy the global configuration file for ~git~[fn:1], by default it reads from =$HOME/.gitconfig=. + +#+begin_src emacs-lisp +(dotfiles/symlink "~/.emacs.d/config/git" + "~/.gitconfig") +#+end_src + +* Config + +Another hallmark feature of Emacs is ~magit~[fn:2]. It's a complete git porcelain inside of Emacs. The developer has stressed that it's most unfortunate name was meant to be pronounced like *magic* but with *git*. Despire his best efforts a lot of people have taken to pronouncing it *maggot*, which keeps him awake at night (probably). + +#+begin_src emacs-lisp +(use-package magit + :commands magit-status + :custom (magit-display-buffer-function + #'magit-display-buffer-same-window-except-diff-v1)) +#+end_src + +** Forge integration + +It's possible to interact with forges from Github / Gitlab using ~forge~[fn:3]. It requires the respective =$TOKEN= to be configured, or an alternative form of authentication. This enables working with pull-requests, code-reviews, and issues from inside of Emacs. + +#+begin_src emacs-lisp +(use-package forge + :after magit) +#+end_src + +* Shortcuts + +Interact with ~magit~[fn:2] behind =SPC g=: + ++ Clone with =c= ++ Status with =g= + +#+begin_src emacs-lisp +(dotfiles/leader + "g" '(:ignore t :which-key "Magit") + "gc" '(magit-clone :which-key "Clone") + "gg" '(magit-status :which-key "Status")) +#+end_src + +* Footnotes + +[fn:1] https://git-scm.com + +[fn:2] https://github.com/magit/magit + +[fn:3] https://github.com/magit/forge