Merge branch 'develop' into add_shellcheck
This commit is contained in:
commit
e5e05f9d51
584 changed files with 32999 additions and 15507 deletions
174
modules/README.org
Normal file
174
modules/README.org
Normal file
|
@ -0,0 +1,174 @@
|
|||
#+TITLE: Doom Modules
|
||||
|
||||
* Table of Contents :TOC:noexport:
|
||||
- [[#feature][:feature]]
|
||||
- [[#completion][:completion]]
|
||||
- [[#ui][:ui]]
|
||||
- [[#editor][:editor]]
|
||||
- [[#emacs][:emacs]]
|
||||
- [[#tools][:tools]]
|
||||
- [[#lang][:lang]]
|
||||
- [[#app][:app]]
|
||||
- [[#collab][:collab]]
|
||||
- [[#config][:config]]
|
||||
|
||||
* :feature
|
||||
Broad modules that bring essential IDE functionality to Emacs.
|
||||
|
||||
+ debugger: A (nigh-)universal debugger in Emacs
|
||||
+ [[file:feature/eval/README.org][eval]]: REPL & code evaluation support for a variety of languages
|
||||
+ [[file:feature/evil/README.org][evil]] =+everywhere=: Vim in Emacs
|
||||
+ [[file:feature/file-templates/README.org][file-templates]]: Auto-inserted templates in blank new files
|
||||
+ [[file:feature/lookup/README.org][lookup]] =+docsets=: Universal jump-to & documentation lookup backend
|
||||
+ [[file:feature/snippets/README.org][snippets]]: A templating system for Emacs for lazy typers (aka programmers)
|
||||
+ [[file:feature/workspaces/README.org][workspaces]]: Isolated workspaces
|
||||
|
||||
* :completion
|
||||
Swappable completion modules for quickly narrowing down lists of candidates.
|
||||
|
||||
+ [[file:completion/company/README.org][company]] =+auto +childframe=: The ultimate code completion backend
|
||||
+ helm =+fuzzy +childframe=: *Another* search engine for love and life
|
||||
+ ido: The /other/ *other* search engine for love and life
|
||||
+ [[file:completion/ivy/README.org][ivy]] =+fuzzy +childframe=: /The/ search engine for love and life
|
||||
|
||||
* :ui
|
||||
Aesthetic modules that affect the Emacs interface or user experience.
|
||||
|
||||
+ [[file:ui/deft/README.org][deft]]:
|
||||
+ [[file:ui/doom/README.org][doom]]:
|
||||
+ [[file:ui/doom-dashboard/README.org][doom-dashboard]]:
|
||||
+ [[file:ui/doom-quit/README.org][doom-quit]]:
|
||||
+ fill-column:
|
||||
+ [[file:ui/hl-todo/README.org][hl-todo]]:
|
||||
+ indent-guides:
|
||||
+ [[file:ui/modeline/README.org][modeline]]:
|
||||
+ [[file:ui/nav-flash/README.org][nav-flash]]:
|
||||
+ [[file:ui/neotree/README.org][neotree]]:
|
||||
+ [[file:ui/ophints/README.org][ophints]]:
|
||||
+ [[file:ui/popup/README.org][popup]] =+all +defaults=: Makes temporary/disposable windows less intrusive
|
||||
+ pretty-code:
|
||||
+ [[file:ui/tabbar/README.org][tabbar]]:
|
||||
+ treemacs:
|
||||
+ [[file:ui/unicode/README.org][unicode]]:
|
||||
+ vc-gutter:
|
||||
+ vi-tilde-fringe:
|
||||
+ [[file:ui/window-select/README.org][window-select]]:
|
||||
|
||||
* :editor
|
||||
Modules that affect and augment your ability to write and edit text.
|
||||
|
||||
+ [[file:editor/fold/README.org][fold]]: universal code folding
|
||||
+ [[file:editor/format/README.org][format]] =+onsave=:
|
||||
+ [[file:editor/lispy/README.org][lispy]]:
|
||||
+ multiple-cursors:
|
||||
+ [[file:editor/parinfer/README.org][parinfer]]:
|
||||
+ rotate-text:
|
||||
|
||||
* :emacs
|
||||
Modules that reconfigure packages or features built into Emacs
|
||||
|
||||
+ dired =+ranger +icons=:
|
||||
+ electric:
|
||||
+ eshell:
|
||||
+ imenu:
|
||||
+ term:
|
||||
+ vc:
|
||||
|
||||
* :tools
|
||||
Small modules that give Emacs access to external tools & services.
|
||||
|
||||
+ ansible:
|
||||
+ docker:
|
||||
+ [[file:tools/editorconfig/README.org][editorconfig]]:
|
||||
+ [[file:tools/ein/README.org][ein]]:
|
||||
+ flycheck: Live error/warning highlights
|
||||
+ flyspell: Spell checking
|
||||
+ gist:
|
||||
+ [[file:tools/lsp/README.org][lsp]]:
|
||||
+ macos:
|
||||
+ magit:
|
||||
+ make:
|
||||
+ password-store:
|
||||
+ pdf:
|
||||
+ prodigy:
|
||||
+ rgb:
|
||||
+ terraform:
|
||||
+ tmux:
|
||||
+ upload:
|
||||
+ [[file:tools/wakatime/README.org][wakatime]]:
|
||||
+ vterm:
|
||||
|
||||
* :lang
|
||||
Modules that bring support for a language or group of languages to Emacs.
|
||||
|
||||
+ agda:
|
||||
+ assembly:
|
||||
+ [[file:lang/cc/README.org][cc]] =+lsp=:
|
||||
+ clojure:
|
||||
+ common-lisp:
|
||||
+ [[file:lang/coq/README.org][coq]]:
|
||||
+ crystal:
|
||||
+ [[file:lang/csharp/README.org][csharp]]:
|
||||
+ data:
|
||||
+ erlang:
|
||||
+ elixir:
|
||||
+ elm:
|
||||
+ emacs-lisp:
|
||||
+ [[file:lang/ess/README.org][ess]]:
|
||||
+ [[file:lang/go/README.org][go]] =+lsp=:
|
||||
+ [[file:lang/haskell/README.org][haskell]] =+intero +dante=:
|
||||
+ hy:
|
||||
+ [[file:lang/idris/README.org][idris]]:
|
||||
+ java =+meghanada=:
|
||||
+ [[file:lang/javascript/README.org][javascript]] =+lsp=:
|
||||
+ julia:
|
||||
+ kotlin:
|
||||
+ [[file:lang/latex/README.org][latex]]:
|
||||
+ ledger:
|
||||
+ lua:
|
||||
+ markdown:
|
||||
+ [[file:lang/nim/README.org][nim]]:
|
||||
+ nix:
|
||||
+ [[file:lang/ocaml/README.org][ocaml]] =+lsp=:
|
||||
+ [[file:lang/org/README.org][org]] =+attach +babel +capture +export +present +ipython=:
|
||||
+ [[file:lang/perl/README.org][perl]]:
|
||||
+ [[file:lang/php/README.org][php]] =+lsp=:
|
||||
+ plantuml:
|
||||
+ purescript:
|
||||
+ python =+lsp=:
|
||||
+ qt:
|
||||
+ racket:
|
||||
+ [[file:lang/rest/README.org][rest]]:
|
||||
+ ruby =+lsp=:
|
||||
+ [[file:lang/rust/README.org][rust]] =+lsp=:
|
||||
+ scala:
|
||||
+ [[file:lang/sh/README.org][sh]] =+fish +lsp=:
|
||||
+ [[file:lang/solidity/README.org][solidity]]:
|
||||
+ swift:
|
||||
+ terra:
|
||||
+ web =+lsp=:
|
||||
+ vala:
|
||||
|
||||
* :app
|
||||
Large, opinionated modules that transform and take over Emacs, i.e.
|
||||
Doom-specific porcelains.
|
||||
|
||||
+ calendar:
|
||||
+ [[file:app/email/README.org][email]] =+gmail=:
|
||||
+ [[file:app/irc/README.org][irc]]:
|
||||
+ rss =+org=:
|
||||
+ twitter:
|
||||
+ [[file:app/write/README.org][write]] =+wordnut +langtool=:
|
||||
|
||||
* :collab
|
||||
Modules that enable collaborative programming over the internet.
|
||||
|
||||
+ floobits:
|
||||
+ impatient-mode:
|
||||
|
||||
* :config
|
||||
Modules that configure Emacs one way or another, or focus on making it easier
|
||||
for you to customize it yourself.
|
||||
|
||||
+ literate:
|
||||
+ [[file:config/default/README.org][default]] =+bindings +smartparens=:
|
61
modules/app/calendar/autoload.el
Normal file
61
modules/app/calendar/autoload.el
Normal file
|
@ -0,0 +1,61 @@
|
|||
;;; app/calendar/autoload.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defvar +calendar--wconf nil)
|
||||
|
||||
(defun +calendar--init ()
|
||||
(if-let* ((win (cl-loop for win in (doom-visible-windows)
|
||||
if (string-match-p "^\\*cfw:" (buffer-name (window-buffer win)))
|
||||
return win)))
|
||||
(select-window win)
|
||||
(call-interactively +calendar-open-function)))
|
||||
|
||||
;;;###autoload
|
||||
(defun =calendar ()
|
||||
"Activate (or switch to) `calendar' in its workspace."
|
||||
(interactive)
|
||||
(if (featurep! :feature workspaces)
|
||||
(progn
|
||||
(+workspace-switch "Calendar" t)
|
||||
(doom/switch-to-scratch-buffer)
|
||||
(+calendar--init)
|
||||
(+workspace/display))
|
||||
(setq +calendar--wconf (current-window-configuration))
|
||||
(delete-other-windows)
|
||||
(switch-to-buffer (doom-fallback-buffer))
|
||||
(+calendar--init)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +calendar/quit ()
|
||||
"TODO"
|
||||
(interactive)
|
||||
(if (featurep! :feature workspaces)
|
||||
(+workspace/delete "Calendar")
|
||||
(doom-kill-matching-buffers "^\\*cfw:")
|
||||
(set-window-configuration +calendar--wconf)
|
||||
(setq +calendar--wconf nil)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +calendar/open-calendar ()
|
||||
"TODO"
|
||||
(interactive)
|
||||
(cfw:open-calendar-buffer
|
||||
;; :custom-map cfw:my-cal-map
|
||||
:contents-sources
|
||||
(list
|
||||
(cfw:org-create-source (doom-color 'fg)) ; orgmode source
|
||||
)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +calendar*cfw:render-button (title command &optional state)
|
||||
"render-button
|
||||
TITLE
|
||||
COMMAND
|
||||
STATE"
|
||||
(let ((text (concat " " title " "))
|
||||
(keymap (make-sparse-keymap)))
|
||||
(cfw:rt text (if state 'cfw:face-toolbar-button-on
|
||||
'cfw:face-toolbar-button-off))
|
||||
(define-key keymap [mouse-1] command)
|
||||
(cfw:tp text 'keymap keymap)
|
||||
(cfw:tp text 'mouse-face 'highlight)
|
||||
text))
|
56
modules/app/calendar/config.el
Normal file
56
modules/app/calendar/config.el
Normal file
|
@ -0,0 +1,56 @@
|
|||
;;; app/calendar/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defvar +calendar-org-gcal-secret-file
|
||||
(expand-file-name "private/org/secret.el" doom-modules-dir)
|
||||
"TODO")
|
||||
|
||||
(defvar +calendar-open-function #'+calendar/open-calendar
|
||||
"TODO")
|
||||
|
||||
|
||||
;;
|
||||
;; Packages
|
||||
|
||||
(def-package! calfw
|
||||
:commands (cfw:open-calendar-buffer)
|
||||
:config
|
||||
;; better frame for calendar
|
||||
(setq cfw:face-item-separator-color nil
|
||||
cfw:render-line-breaker 'cfw:render-line-breaker-none
|
||||
cfw:fchar-junction ?╋
|
||||
cfw:fchar-vertical-line ?┃
|
||||
cfw:fchar-horizontal-line ?━
|
||||
cfw:fchar-left-junction ?┣
|
||||
cfw:fchar-right-junction ?┫
|
||||
cfw:fchar-top-junction ?┯
|
||||
cfw:fchar-top-left-corner ?┏
|
||||
cfw:fchar-top-right-corner ?┓)
|
||||
|
||||
(define-key cfw:calendar-mode-map "q" #'+calendar/quit)
|
||||
|
||||
(add-hook 'cfw:calendar-mode-hook #'doom|mark-buffer-as-real)
|
||||
(add-hook 'cfw:calendar-mode-hook 'hide-mode-line-mode)
|
||||
|
||||
(advice-add #'cfw:render-button :override #'+calendar*cfw:render-button))
|
||||
|
||||
|
||||
(def-package! calfw-org
|
||||
:commands (cfw:open-org-calendar
|
||||
cfw:org-create-source
|
||||
cfw:open-org-calendar-withkevin
|
||||
my-open-calendar))
|
||||
|
||||
|
||||
(def-package! org-gcal
|
||||
:commands (org-gcal-sync
|
||||
org-gcal-fetch
|
||||
org-gcal-post-at-point
|
||||
org-gcal-delete-at-point)
|
||||
:config
|
||||
(load-file +calendar-org-gcal-secret-file)
|
||||
;; hack to avoid the deferred.el error
|
||||
(defun org-gcal--notify (title mes)
|
||||
(message "org-gcal::%s - %s" title mes)))
|
||||
|
||||
|
||||
;; (def-package! alert)
|
6
modules/app/calendar/packages.el
Normal file
6
modules/app/calendar/packages.el
Normal file
|
@ -0,0 +1,6 @@
|
|||
;; -*- no-byte-compile: t; -*-
|
||||
;;; app/calendar/packages.el
|
||||
|
||||
(package! calfw)
|
||||
(package! calfw-org)
|
||||
(package! org-gcal)
|
71
modules/app/calendar/readme.org
Normal file
71
modules/app/calendar/readme.org
Normal file
|
@ -0,0 +1,71 @@
|
|||
#+TITLE: `=Calendar App`
|
||||
* Setup sync between google calendar and org file
|
||||
:PROPERTIES:
|
||||
:ID: 5E190E8A-CA26-4679-B5F8-BF9CFD289271
|
||||
:END:
|
||||
- Checkout https://github.com/myuhe/org-gcal.el, put the following content in a file ~secret.el~ and set the variable `+calendar-org-gcal-secret-file` to the path of that file.
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
(setq org-gcal-client-id "your-id-foo.apps.googleusercontent.com"
|
||||
org-gcal-client-secret "your-secret"
|
||||
org-gcal-file-alist '(("your-mail@gmail.com" . "~/schedule.org")
|
||||
("another-mail@gmail.com" . "~/task.org")))
|
||||
#+END_SRC
|
||||
* Doom faces
|
||||
:PROPERTIES:
|
||||
:ID: 8223894E-EA68-4259-A2EA-AF7E3653C610
|
||||
:END:
|
||||
I'm using the following setting:
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
;; calfw
|
||||
(cfw:face-title :foreground blue :bold bold :height 2.0 :inherit 'variable-pitch)
|
||||
(cfw:face-header :foreground (doom-blend blue bg 0.8) :bold bold)
|
||||
(cfw:face-sunday :foreground (doom-blend red bg 0.8) :bold bold)
|
||||
(cfw:face-saturday :foreground (doom-blend red bg 0.8) :bold bold)
|
||||
(cfw:face-holiday :foreground nil :background bg-alt :bold bold)
|
||||
(cfw:face-grid :foreground vertical-bar)
|
||||
(cfw:face-periods :foreground yellow)
|
||||
(cfw:face-toolbar :foreground nil :background nil)
|
||||
(cfw:face-toolbar-button-off :foreground base6 :bold bold :inherit 'variable-pitch)
|
||||
(cfw:face-toolbar-button-on :foreground blue :bold bold :inherit 'variable-pitch)
|
||||
|
||||
(cfw:face-default-content :foreground fg)
|
||||
(cfw:face-day-title :foreground fg :bold bold)
|
||||
(cfw:face-today-title :foreground bg :background blue :bold bold)
|
||||
(cfw:face-default-day :bold bold)
|
||||
(cfw:face-today :foreground nil :background nil :bold bold)
|
||||
(cfw:face-annotation :foreground violet)
|
||||
(cfw:face-disable :foreground grey)
|
||||
(cfw:face-select :background region)
|
||||
#+END_SRC
|
||||
* Adjust calendar to be included
|
||||
:PROPERTIES:
|
||||
:ID: D734975C-4B49-4F66-A088-AB2707A77537
|
||||
:END:
|
||||
Checkout example from https://github.com/kiwanami/emacs-calfw
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
(defun my-open-calendar ()
|
||||
(interactive)
|
||||
(cfw:open-calendar-buffer
|
||||
:contents-sources
|
||||
(list
|
||||
(cfw:org-create-source "Green") ; orgmode source
|
||||
(cfw:howm-create-source "Blue") ; howm source
|
||||
(cfw:cal-create-source "Orange") ; diary source
|
||||
(cfw:ical-create-source "Moon" "~/moon.ics" "Gray") ; ICS source1
|
||||
(cfw:ical-create-source "gcal" "https://..../basic.ics" "IndianRed") ; google calendar ICS
|
||||
)))
|
||||
#+END_SRC
|
||||
Specifically, if you want to adjust the org files to be included, use a ~let~ binding to set the ~org-agenda-files~ like below:
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
;;;###autoload
|
||||
(defun cfw:open-org-calendar-with-cal1 ()
|
||||
(interactive)
|
||||
(let ((org-agenda-files '("/path/to/org/" "/path/to/cal1.org")))
|
||||
(call-interactively '+calendar/open-calendar)))
|
||||
|
||||
;;;###autoload
|
||||
(defun cfw:open-org-calendar-with-cal2 ()
|
||||
(interactive)
|
||||
(let ((org-agenda-files '("/path/to/org/" "/path/to/cal2.org")))
|
||||
(call-interactively '+calendar/open-calendar)))
|
||||
#+END_SRC
|
45
modules/app/email/+gmail.el
Normal file
45
modules/app/email/+gmail.el
Normal file
|
@ -0,0 +1,45 @@
|
|||
;;; app/email/+gmail.el -*- lexical-binding: t; -*-
|
||||
|
||||
(after! mu4e
|
||||
;; don't save message to Sent Messages, Gmail/IMAP takes care of this
|
||||
(setq mu4e-sent-messages-behavior 'delete
|
||||
|
||||
;; don't need to run cleanup after indexing for gmail
|
||||
mu4e-index-cleanup nil
|
||||
|
||||
;; because gmail uses labels as folders we can use lazy check since
|
||||
;; messages don't really "move"
|
||||
mu4e-index-lazy-check t)
|
||||
|
||||
;; In my workflow, emails won't be moved at all. Only their flags/labels are
|
||||
;; changed. Se we redefine the trash and refile marks not to do any moving.
|
||||
;; However, the real magic happens in `+email|gmail-fix-flags'.
|
||||
;;
|
||||
;; Gmail will handle the rest.
|
||||
(defun +email--mark-seen (docid msg target)
|
||||
(mu4e~proc-move docid (mu4e~mark-check-target target) "+S-u-N"))
|
||||
|
||||
(delq (assq 'delete mu4e-marks) mu4e-marks)
|
||||
(setf (alist-get 'trash mu4e-marks)
|
||||
(list :char '("d" . "▼")
|
||||
:prompt "dtrash"
|
||||
:dyn-target (lambda (_target msg) (mu4e-get-trash-folder msg))
|
||||
:action #'+email--mark-seen)
|
||||
;; Refile will be my "archive" function.
|
||||
(alist-get 'refile mu4e-marks)
|
||||
(list :char '("d" . "▼")
|
||||
:prompt "dtrash"
|
||||
:dyn-target (lambda (_target msg) (mu4e-get-trash-folder msg))
|
||||
:action #'+email--mark-seen))
|
||||
|
||||
;; This hook correctly modifies gmail flags on emails when they are marked.
|
||||
;; Without it, refiling (archiving), trashing, and flagging (starring) email
|
||||
;; won't properly result in the corresponding gmail action, since the marks
|
||||
;; are ineffectual otherwise.
|
||||
(defun +email|gmail-fix-flags (mark msg)
|
||||
(pcase mark
|
||||
(`trash (mu4e-action-retag-message msg "-\\Inbox,+\\Trash,-\\Draft"))
|
||||
(`refile (mu4e-action-retag-message msg "-\\Inbox"))
|
||||
(`flag (mu4e-action-retag-message msg "+\\Starred"))
|
||||
(`unflag (mu4e-action-retag-message msg "-\\Starred"))))
|
||||
(add-hook 'mu4e-mark-execute-pre-hook #'+email|gmail-fix-flags))
|
|
@ -1,42 +1,75 @@
|
|||
#+TITLE: :app email
|
||||
#+TITLE: app/email
|
||||
#+DATE: April 8, 2017
|
||||
#+SINCE: v2.0
|
||||
#+STARTUP: inlineimages
|
||||
|
||||
* Table of Contents :TOC:
|
||||
- [[Description][Description]]
|
||||
- [[Module Flags][Module Flags]]
|
||||
- [[Plugins][Plugins]]
|
||||
- [[Prerequisites][Prerequisites]]
|
||||
- [[MacOS][MacOS]]
|
||||
- [[Arch Linux][Arch Linux]]
|
||||
- [[Features][Features]]
|
||||
- [[Configuration][Configuration]]
|
||||
- [[offlineimap][offlineimap]]
|
||||
- [[mbsync][mbsync]]
|
||||
|
||||
* Description
|
||||
This module makes Emacs an email client, using ~mu4e~.
|
||||
|
||||
#+begin_quote
|
||||
I want to live in Emacs, but as we all know, living is incomplete without email. So I prayed to the text editor gods and they (I) answered. Emacs+evil's editing combined with org-mode for writing emails? /Yes please./
|
||||
I want to live in Emacs, but as we all know, living is incomplete without email.
|
||||
So I prayed to the text editor gods and they (I) answered. Emacs+evil's editing
|
||||
combined with org-mode for writing emails? /Yes please./
|
||||
|
||||
It uses ~mu4e~ to read my email, but depends on ~offlineimap~ (to sync my email via IMAP) and ~mu~ (to index my mail into a format ~mu4e~ can understand).
|
||||
|
||||
WARNING: my config is gmail/gsuite oriented, and since Google has its own opinions on the IMAP standard, it is unlikely to translate to other hosts.
|
||||
It uses ~mu4e~ to read my email, but depends on ~offlineimap~ (to sync my email
|
||||
via IMAP) and ~mu~ (to index my mail into a format ~mu4e~ can understand).
|
||||
#+end_quote
|
||||
|
||||
* Table of Contents :TOC:
|
||||
- [[#install][Install]]
|
||||
- [[#macos][MacOS]]
|
||||
- [[#arch-linux][Arch Linux]]
|
||||
- [[#dependencies][Dependencies]]
|
||||
** Module Flags
|
||||
+ ~+gmail~ Enables gmail-specific configuration.
|
||||
|
||||
* Install
|
||||
** Plugins
|
||||
+ [[https://github.com/agpchil/mu4e-maildirs-extension][mu4e-maildirs-extension]]
|
||||
|
||||
* Prerequisites
|
||||
This module requires:
|
||||
|
||||
+ ~offlineimap~ (to sync mail with)
|
||||
+ Either ~mbsync~ (default) or ~offlineimap~ (to sync mail with)
|
||||
+ ~mu~ (to index your downloaded messages)
|
||||
|
||||
** MacOS
|
||||
#+BEGIN_SRC sh :tangle (if (doom-system-os 'macos) "yes")
|
||||
#+BEGIN_SRC sh
|
||||
brew install mu --with-emacs
|
||||
# And one of the following
|
||||
brew install isync # mbsync
|
||||
brew install offlineimap
|
||||
#+END_SRC
|
||||
|
||||
** Arch Linux
|
||||
#+BEGIN_SRC sh :dir /sudo:: :tangle (if (doom-system-os 'arch) "yes")
|
||||
sudo pacman --noconfirm --needed -S offlineimap mu
|
||||
#+BEGIN_SRC sh
|
||||
sudo pacman --noconfirm --needed -S mu
|
||||
# And one of the following
|
||||
sudo pacman -S isync # mbsync
|
||||
sudo pacman -S offlineimap
|
||||
#+END_SRC
|
||||
|
||||
* Dependencies
|
||||
You need to do the following:
|
||||
* TODO Features
|
||||
|
||||
1. Write a ~\~/.offlineimaprc~. Mine can be found [[https://github.com/hlissner/dotfiles/tree/master/shell/+mu][in my dotfiles repository]]. It is configured to download mail to ~\~/.mail~. I use [[https://www.passwordstore.org/][unix pass]] to securely store my login credentials.
|
||||
* Configuration
|
||||
** offlineimap
|
||||
This module uses =mbsync= by default. To change this, change ~+email-backend~:
|
||||
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
(setq +email-backend 'offlineimap)
|
||||
#+END_SRC
|
||||
|
||||
Then you must set up offlineimap and index your mail:
|
||||
|
||||
1. Write a ~\~/.offlineimaprc~. Mine can be found [[https://github.com/hlissner/dotfiles/tree/master/shell/mu][in my dotfiles repository]]. It
|
||||
is configured to download mail to ~\~/.mail~. I use [[https://www.passwordstore.org/][unix pass]] to securely
|
||||
store my login credentials.
|
||||
2. Download your email: ~offlineimap -o~ (may take a while)
|
||||
3. Index it with mu: ~mu index --maildir ~/.mail~
|
||||
|
||||
|
@ -44,14 +77,15 @@ Then configure Emacs to use your email address:
|
|||
|
||||
#+BEGIN_SRC emacs-lisp :tangle no
|
||||
;; Each path is relative to `+email-mu4e-mail-path', which is ~/.mail by default
|
||||
(set! :email "Lissner.net"
|
||||
'((mu4e-sent-folder . "/Lissner.net/Sent Mail")
|
||||
(mu4e-drafts-folder . "/Lissner.net/Drafts")
|
||||
(mu4e-trash-folder . "/Lissner.net/Trash")
|
||||
(mu4e-refile-folder . "/Lissner.net/All Mail")
|
||||
(smtpmail-smtp-user . "henrik@lissner.net")
|
||||
(user-mail-address . "henrik@lissner.net")
|
||||
(mu4e-compose-signature . "---\nHenrik Lissner"))
|
||||
t)
|
||||
(set-email-account! "Lissner.net"
|
||||
'((mu4e-sent-folder . "/Lissner.net/Sent Mail")
|
||||
(mu4e-drafts-folder . "/Lissner.net/Drafts")
|
||||
(mu4e-trash-folder . "/Lissner.net/Trash")
|
||||
(mu4e-refile-folder . "/Lissner.net/All Mail")
|
||||
(smtpmail-smtp-user . "henrik@lissner.net")
|
||||
(user-mail-address . "henrik@lissner.net")
|
||||
(mu4e-compose-signature . "---\nHenrik Lissner"))
|
||||
t)
|
||||
#+END_SRC
|
||||
|
||||
** TODO mbsync
|
||||
|
|
|
@ -1,10 +1,62 @@
|
|||
;;; app/email/autoload/email.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autodef
|
||||
(defun set-email-account! (label letvars &optional default-p)
|
||||
"Registers an email address for mu4e. The LABEL is a string. LETVARS are a
|
||||
list of cons cells (VARIABLE . VALUE) -- you may want to modify:
|
||||
|
||||
+ `user-full-name' (this or the global `user-full-name' is required)
|
||||
+ `user-mail-address' (required)
|
||||
+ `smtpmail-smtp-user' (required for sending mail from Emacs)
|
||||
|
||||
OPTIONAL:
|
||||
+ `mu4e-sent-folder'
|
||||
+ `mu4e-drafts-folder'
|
||||
+ `mu4e-trash-folder'
|
||||
+ `mu4e-refile-folder'
|
||||
+ `mu4e-compose-signature'
|
||||
|
||||
DEFAULT-P is a boolean. If non-nil, it marks that email account as the
|
||||
default/fallback account."
|
||||
(after! mu4e
|
||||
(when-let* ((address (cdr (assq 'user-mail-address letvars))))
|
||||
(add-to-list 'mu4e-user-mail-address-list address))
|
||||
(setq mu4e-contexts
|
||||
(cl-loop for context in mu4e-contexts
|
||||
unless (string= (mu4e-context-name context) label)
|
||||
collect context))
|
||||
(let ((context (make-mu4e-context
|
||||
:name label
|
||||
:enter-func (lambda () (mu4e-message "Switched to %s" label))
|
||||
:leave-func #'mu4e-clear-caches
|
||||
:match-func
|
||||
(lambda (msg)
|
||||
(when msg
|
||||
(string-prefix-p (format "/%s" label)
|
||||
(mu4e-message-field msg :maildir))))
|
||||
:vars letvars)))
|
||||
(push context mu4e-contexts)
|
||||
(when default-p
|
||||
(setq-default mu4e-context-current context))
|
||||
context)))
|
||||
|
||||
|
||||
|
||||
(defvar +email-workspace-name "*mu4e*"
|
||||
"TODO")
|
||||
|
||||
(add-hook 'mu4e-main-mode-hook #'+email|init)
|
||||
|
||||
;;;###autoload
|
||||
(defun =email ()
|
||||
"Start email client."
|
||||
(interactive)
|
||||
(call-interactively #'mu4e))
|
||||
(require 'mu4e)
|
||||
(+workspace-switch +email-workspace-name t)
|
||||
(mu4e~start 'mu4e~main-view)
|
||||
;; (save-selected-window
|
||||
;; (prolusion-mail-show))
|
||||
)
|
||||
|
||||
;;;###autoload
|
||||
(defun +email/compose ()
|
||||
|
@ -13,3 +65,15 @@
|
|||
;; TODO Interactively select email account
|
||||
(call-interactively #'mu4e-compose-new))
|
||||
|
||||
|
||||
;;
|
||||
;; Hooks
|
||||
|
||||
(defun +email|init ()
|
||||
(add-hook 'kill-buffer-hook #'+email|kill-mu4e nil t))
|
||||
|
||||
(defun +email|kill-mu4e ()
|
||||
;; (prolusion-mail-hide)
|
||||
(when (+workspace-exists-p +email-workspace-name)
|
||||
(+workspace/delete +email-workspace-name)))
|
||||
|
||||
|
|
|
@ -4,88 +4,52 @@
|
|||
;; to give me the ability to read, search, write and send my email. It does so
|
||||
;; with `mu4e', and requires `offlineimap' and `mu' to be installed.
|
||||
|
||||
(defvar +email-mu4e-mail-path "~/.mail"
|
||||
"The directory path of mu's maildir.")
|
||||
(defvar +email-backend 'mbsync
|
||||
"Which backend to use. Can either be offlineimap, mbsync or nil (manual).")
|
||||
|
||||
|
||||
;;
|
||||
;; Config
|
||||
;;
|
||||
;; Packages
|
||||
|
||||
(def-setting! :email (label letvars &optional default-p)
|
||||
"Registers an email address for mu4e. The LABEL is a string. LETVARS are a
|
||||
list of cons cells (VARIABLE . VALUE) -- you may want to modify:
|
||||
(add-to-list 'auto-mode-alist '("\\.\\(?:offlineimap\\|mbsync\\)rc\\'" . conf-mode))
|
||||
|
||||
+ `user-full-name' (this or the global `user-full-name' is required)
|
||||
+ `user-mail-address' (required)
|
||||
+ `smtpmail-smtp-user' (required for sending mail from Emacs)
|
||||
|
||||
OPTIONAL:
|
||||
+ `mu4e-sent-folder'
|
||||
+ `mu4e-drafts-folder'
|
||||
+ `mu4e-trash-folder'
|
||||
+ `mu4e-refile-folder'
|
||||
+ `mu4e-compose-signature'
|
||||
|
||||
DEFAULT-P is a boolean. If non-nil, it marks that email account as the
|
||||
default/fallback account."
|
||||
`(after! mu4e
|
||||
(let ((account-vars ,letvars))
|
||||
(when-let* ((address (cdr (assq 'user-mail-address account-vars))))
|
||||
(cl-pushnew address mu4e-user-mail-address-list :test #'equal))
|
||||
(let ((context (make-mu4e-context
|
||||
:name ,label
|
||||
:enter-func (lambda () (mu4e-message "Switched to %s" ,label))
|
||||
:leave-func (lambda () (mu4e-clear-caches))
|
||||
:match-func
|
||||
(lambda (msg)
|
||||
(when msg
|
||||
(string-prefix-p (format "/%s" ,label)
|
||||
(mu4e-message-field msg :maildir))))
|
||||
:vars ,letvars)))
|
||||
(push context mu4e-contexts)
|
||||
,(when default-p
|
||||
`(setq-default mu4e-context-current context))))))
|
||||
|
||||
|
||||
;;
|
||||
;; Plugins
|
||||
;;
|
||||
|
||||
(def-package! mu4e
|
||||
:commands (mu4e mu4e-compose-new)
|
||||
:init
|
||||
(provide 'html2text) ; disable obsolete package
|
||||
(setq mu4e-maildir "~/.mail"
|
||||
mu4e-attachment-dir "~/.mail/.attachments"
|
||||
mu4e-user-mail-address-list nil)
|
||||
:config
|
||||
(setq mu4e-maildir +email-mu4e-mail-path
|
||||
mu4e-attachment-dir "~/Downloads"
|
||||
mu4e-user-mail-address-list nil
|
||||
mu4e-update-interval nil
|
||||
(pcase +email-backend
|
||||
(`mbsync
|
||||
(setq mu4e-get-mail-command "mbsync -a"
|
||||
mu4e-change-filenames-when-moving t))
|
||||
(`offlineimap
|
||||
(setq mu4e-get-mail-command "offlineimap -o -q")))
|
||||
|
||||
(setq mu4e-update-interval nil
|
||||
mu4e-compose-format-flowed t ; visual-line-mode + auto-fill upon sending
|
||||
mu4e-view-show-addresses t
|
||||
mu4e-sent-messages-behavior 'sent
|
||||
mu4e-hide-index-messages t
|
||||
;; try to show images
|
||||
mu4e-view-show-images t
|
||||
mu4e-view-image-max-width 800
|
||||
;; don't save message to Sent Messages, Gmail/IMAP takes care of this
|
||||
mu4e-sent-messages-behavior 'delete
|
||||
;; allow for updating mail using 'U' in the main view:
|
||||
;; for mbsync
|
||||
;; mu4e-headers-skip-duplicates t
|
||||
;; mu4e-change-filenames-when-moving nil
|
||||
;; mu4e-get-mail-command "mbsync -a"
|
||||
;; for offlineimap
|
||||
mu4e-get-mail-command "offlineimap -o -q"
|
||||
;; configuration for sending mail
|
||||
message-send-mail-function #'smtpmail-send-it
|
||||
smtpmail-stream-type 'starttls
|
||||
message-kill-buffer-on-exit t ; close after sending
|
||||
;; start with the first (default) context;
|
||||
mu4e-context-policy 'pick-first
|
||||
;; compose with the current context, or ask
|
||||
mu4e-compose-context-policy 'ask-if-none
|
||||
;; use helm/ivy
|
||||
mu4e-completing-read-function (cond ((featurep! :completion ivy) #'ivy-completing-read)
|
||||
((featurep! :completion helm) #'completing-read)
|
||||
(t #'ido-completing-read))
|
||||
;; close message after sending it
|
||||
message-kill-buffer-on-exit t
|
||||
mu4e-completing-read-function
|
||||
(cond ((featurep! :completion ivy) #'ivy-completing-read)
|
||||
((featurep! :completion helm) #'completing-read)
|
||||
(t #'ido-completing-read))
|
||||
;; no need to ask
|
||||
mu4e-confirm-quit nil
|
||||
;; remove 'lists' column
|
||||
|
@ -94,14 +58,28 @@ default/fallback account."
|
|||
(:human-date . 12)
|
||||
(:flags . 4)
|
||||
(:from . 25)
|
||||
(:subject))
|
||||
mu4e-bookmarks `(("\\\\Inbox" "Inbox" ?i)
|
||||
("\\\\Draft" "Drafts" ?d)
|
||||
("flag:unread AND \\\\Inbox" "Unread messages" ?u)
|
||||
("flag:flagged" "Starred messages" ?s)
|
||||
("date:today..now" "Today's messages" ?t)
|
||||
("date:7d..now" "Last 7 days" ?w)
|
||||
("mime:image/*" "Messages with images" ?p)))
|
||||
(:subject)))
|
||||
|
||||
;; set mail user agent
|
||||
(setq mail-user-agent 'mu4e-user-agent)
|
||||
|
||||
;; Use fancy icons
|
||||
(setq mu4e-headers-has-child-prefix '("+" . "")
|
||||
mu4e-headers-empty-parent-prefix '("-" . "")
|
||||
mu4e-headers-first-child-prefix '("\\" . "")
|
||||
mu4e-headers-duplicate-prefix '("=" . "")
|
||||
mu4e-headers-default-prefix '("|" . "")
|
||||
mu4e-headers-draft-mark '("D" . "")
|
||||
mu4e-headers-flagged-mark '("F" . "")
|
||||
mu4e-headers-new-mark '("N" . "")
|
||||
mu4e-headers-passed-mark '("P" . "")
|
||||
mu4e-headers-replied-mark '("R" . "")
|
||||
mu4e-headers-seen-mark '("S" . "")
|
||||
mu4e-headers-trashed-mark '("T" . "")
|
||||
mu4e-headers-attach-mark '("a" . "")
|
||||
mu4e-headers-encrypted-mark '("x" . "")
|
||||
mu4e-headers-signed-mark '("s" . "")
|
||||
mu4e-headers-unread-mark '("u" . ""))
|
||||
|
||||
;; Add a column to display what email account the email belongs to.
|
||||
(add-to-list 'mu4e-header-info-custom
|
||||
|
@ -114,152 +92,33 @@ default/fallback account."
|
|||
(let ((maildir (mu4e-message-field msg :maildir)))
|
||||
(format "%s" (substring maildir 1 (string-match-p "/" maildir 1)))))))
|
||||
|
||||
;; In my workflow, emails won't be moved at all. Only their flags/labels are
|
||||
;; changed. Se we redefine the trash and refile marks not to do any moving.
|
||||
;; However, the real magic happens in `+email|gmail-fix-flags'.
|
||||
;;
|
||||
;; Gmail will handle the rest.
|
||||
(setq mu4e-marks (assq-delete-all 'delete mu4e-marks))
|
||||
(setq mu4e-marks (assq-delete-all 'trash mu4e-marks))
|
||||
(push '(trash :char ("d" . "▼")
|
||||
:prompt "dtrash"
|
||||
:dyn-target (lambda (target msg) (mu4e-get-trash-folder msg))
|
||||
:action
|
||||
(lambda (docid msg target)
|
||||
(mu4e~proc-move docid (mu4e~mark-check-target target) "+S-u-N")))
|
||||
mu4e-marks)
|
||||
|
||||
;; Refile will be my "archive" function.
|
||||
(setq mu4e-marks (assq-delete-all 'refile mu4e-marks))
|
||||
(push '(refile :char ("r" . "▶")
|
||||
:prompt "refile"
|
||||
:show-target (lambda (target) "archive")
|
||||
:action
|
||||
(lambda (docid msg target)
|
||||
(mu4e~proc-move docid (mu4e~mark-check-target target) "+S-u-N")))
|
||||
mu4e-marks)
|
||||
|
||||
;; This hook correctly modifies gmail flags on emails when they are marked.
|
||||
;; Without it, refiling (archiving), trashing, and flagging (starring) email
|
||||
;; won't properly result in the corresponding gmail action, since the marks
|
||||
;; are ineffectual otherwise.
|
||||
(defun +email|gmail-fix-flags (mark msg)
|
||||
(cond ((eq mark 'trash) (mu4e-action-retag-message msg "-\\Inbox,+\\Trash,-\\Draft"))
|
||||
((eq mark 'refile) (mu4e-action-retag-message msg "-\\Inbox"))
|
||||
((eq mark 'flag) (mu4e-action-retag-message msg "+\\Starred"))
|
||||
((eq mark 'unflag) (mu4e-action-retag-message msg "-\\Starred"))))
|
||||
(add-hook 'mu4e-mark-execute-pre-hook #'+email|gmail-fix-flags)
|
||||
|
||||
;; Refresh the current view after marks are executed
|
||||
(defun +email*refresh (&rest _) (mu4e-headers-rerun-search))
|
||||
(advice-add #'mu4e-mark-execute-all :after #'+email*refresh)
|
||||
|
||||
(when (featurep! :feature spellcheck)
|
||||
(when (featurep! :tools flyspell)
|
||||
(add-hook 'mu4e-compose-mode-hook #'flyspell-mode))
|
||||
|
||||
;; Wrap text in messages
|
||||
(add-hook! 'mu4e-view-mode-hook
|
||||
(setq-local truncate-lines nil))
|
||||
(setq-hook! 'mu4e-view-mode-hook truncate-lines nil)
|
||||
|
||||
(when (fboundp 'imagemagick-register-types)
|
||||
(imagemagick-register-types))
|
||||
|
||||
(after! evil
|
||||
(cl-loop for str in '((mu4e-main-mode . normal)
|
||||
(mu4e-view-mode . normal)
|
||||
(mu4e-headers-mode . normal)
|
||||
(mu4e-compose-mode . normal)
|
||||
(mu4e~update-mail-mode . normal))
|
||||
do (evil-set-initial-state (car str) (cdr str)))
|
||||
|
||||
(setq mu4e-view-mode-map (make-sparse-keymap)
|
||||
;; mu4e-compose-mode-map (make-sparse-keymap)
|
||||
mu4e-headers-mode-map (make-sparse-keymap)
|
||||
mu4e-main-mode-map (make-sparse-keymap))
|
||||
|
||||
(map! (:map (mu4e-main-mode-map mu4e-view-mode-map)
|
||||
:leader
|
||||
:n "," #'mu4e-context-switch
|
||||
:n "." #'mu4e-headers-search-bookmark
|
||||
:n ">" #'mu4e-headers-search-bookmark-edit
|
||||
:n "/" #'mu4e~headers-jump-to-maildir)
|
||||
|
||||
(:map (mu4e-headers-mode-map mu4e-view-mode-map)
|
||||
:localleader
|
||||
:n "f" #'mu4e-compose-forward
|
||||
:n "r" #'mu4e-compose-reply
|
||||
:n "c" #'mu4e-compose-new
|
||||
:n "e" #'mu4e-compose-edit)
|
||||
|
||||
(:map mu4e-main-mode-map
|
||||
:n "q" #'mu4e-quit
|
||||
:n "u" #'mu4e-update-index
|
||||
:n "U" #'mu4e-update-mail-and-index
|
||||
:n "J" #'mu4e~headers-jump-to-maildir
|
||||
:n "c" #'+email/compose
|
||||
:n "b" #'mu4e-headers-search-bookmark)
|
||||
|
||||
(:map mu4e-headers-mode-map
|
||||
:n "q" #'mu4e~headers-quit-buffer
|
||||
:n "r" #'mu4e-compose-reply
|
||||
:n "c" #'mu4e-compose-edit
|
||||
:n "s" #'mu4e-headers-search-edit
|
||||
:n "S" #'mu4e-headers-search-narrow
|
||||
:n "RET" #'mu4e-headers-view-message
|
||||
:n "u" #'mu4e-headers-mark-for-unmark
|
||||
:n "U" #'mu4e-mark-unmark-all
|
||||
:n "v" #'evil-visual-line
|
||||
:nv "d" #'+email/mark
|
||||
:nv "=" #'+email/mark
|
||||
:nv "-" #'+email/mark
|
||||
:nv "+" #'+email/mark
|
||||
:nv "!" #'+email/mark
|
||||
:nv "?" #'+email/mark
|
||||
:nv "r" #'+email/mark
|
||||
:nv "m" #'+email/mark
|
||||
:n "x" #'mu4e-mark-execute-all
|
||||
|
||||
:n "]]" #'mu4e-headers-next-unread
|
||||
:n "[[" #'mu4e-headers-prev-unread
|
||||
|
||||
(:localleader
|
||||
:n "s" 'mu4e-headers-change-sorting
|
||||
:n "t" 'mu4e-headers-toggle-threading
|
||||
:n "r" 'mu4e-headers-toggle-include-related
|
||||
|
||||
:n "%" #'mu4e-headers-mark-pattern
|
||||
:n "t" #'mu4e-headers-mark-subthread
|
||||
:n "T" #'mu4e-headers-mark-thread))
|
||||
|
||||
(:map mu4e-view-mode-map
|
||||
:n "q" #'mu4e~view-quit-buffer
|
||||
:n "r" #'mu4e-compose-reply
|
||||
:n "c" #'mu4e-compose-edit
|
||||
:n "o" #'ace-link-mu4e
|
||||
|
||||
:n "<M-Left>" #'mu4e-view-headers-prev
|
||||
:n "<M-Right>" #'mu4e-view-headers-next
|
||||
:n "[m" #'mu4e-view-headers-prev
|
||||
:n "]m" #'mu4e-view-headers-next
|
||||
:n "[u" #'mu4e-view-headers-prev-unread
|
||||
:n "]u" #'mu4e-view-headers-next-unread
|
||||
|
||||
(:localleader
|
||||
:n "%" #'mu4e-view-mark-pattern
|
||||
:n "t" #'mu4e-view-mark-subthread
|
||||
:n "T" #'mu4e-view-mark-thread
|
||||
|
||||
:n "d" #'mu4e-view-mark-for-trash
|
||||
:n "r" #'mu4e-view-mark-for-refile
|
||||
:n "m" #'mu4e-view-mark-for-move))
|
||||
|
||||
(:map mu4e~update-mail-mode-map
|
||||
:n "q" #'mu4e-interrupt-update-mail))))
|
||||
(set-evil-initial-state!
|
||||
'(mu4e-main-mode
|
||||
mu4e-view-mode
|
||||
mu4e-headers-mode
|
||||
mu4e-compose-mode
|
||||
mu4e~update-mail-mode)
|
||||
'normal))
|
||||
|
||||
|
||||
(def-package! mu4e-maildirs-extension
|
||||
:after mu4e
|
||||
:config (mu4e-maildirs-extension-load))
|
||||
:config
|
||||
(mu4e-maildirs-extension)
|
||||
(setq mu4e-maildirs-extension-title nil))
|
||||
|
||||
|
||||
(def-package! org-mu4e
|
||||
|
@ -273,3 +132,8 @@ default/fallback account."
|
|||
(add-hook! 'message-send-hook
|
||||
(setq-local org-mu4e-convert-to-html nil)))
|
||||
|
||||
|
||||
;;
|
||||
;; Sub-modules
|
||||
|
||||
(if (featurep! +gmail) (load! "+gmail"))
|
||||
|
|
|
@ -1,59 +1,118 @@
|
|||
#+TITLE: :app irc
|
||||
|
||||
This module turns adds an IRC client to Emacs ([[https://github.com/jorgenschaefer/circe][~circe~)]] with native notifications ([[https://github.com/eqyiel/circe-notifications][circe-notifications]]).
|
||||
#+TITLE: app/irc
|
||||
#+DATE: June 11, 2017
|
||||
#+SINCE: v2.0.3
|
||||
#+STARTUP: inlineimages
|
||||
|
||||
* Table of Contents :TOC:
|
||||
- [[#dependencies][Dependencies]]
|
||||
- [[#configure][Configure]]
|
||||
- [[#pass-the-unix-password-manager][Pass: the unix password manager]]
|
||||
- [[#emacs-auth-source-api][Emacs' auth-source API]]
|
||||
- [[Description][Description]]
|
||||
- [[Module Flags][Module Flags]]
|
||||
- [[Plugins][Plugins]]
|
||||
- [[Dependencies][Dependencies]]
|
||||
- [[Prerequisites][Prerequisites]]
|
||||
- [[Features][Features]]
|
||||
- [[An IRC Client in Emacs][An IRC Client in Emacs]]
|
||||
- [[Configuration][Configuration]]
|
||||
- [[Pass: the unix password manager][Pass: the unix password manager]]
|
||||
- [[Emacs' auth-source API][Emacs' auth-source API]]
|
||||
- [[Troubleshooting][Troubleshooting]]
|
||||
|
||||
* Description
|
||||
This module turns adds an IRC client to Emacs with OS notifications.
|
||||
|
||||
** Module Flags
|
||||
This module provides no flags.
|
||||
|
||||
** Plugins
|
||||
+ [[https://github.com/jorgenschaefer/circe][circe]]
|
||||
+ [[https://github.com/eqyiel/circe-notifications][circe-notifications]]
|
||||
|
||||
* Dependencies
|
||||
This module has no dependencies, besides =gnutls-cli= or =openssl= for secure connections.
|
||||
This module requires =gnutls-cli= or =openssl= for secure connections.
|
||||
|
||||
* Configure
|
||||
Use the ~:irc~ setting to configure IRC servers. Its second argument (a plist) takes the same arguments as ~circe-network-options~.
|
||||
* Prerequisites
|
||||
This module has no direct prerequisites.
|
||||
|
||||
* Features
|
||||
** An IRC Client in Emacs
|
||||
To connect to IRC you can invoke the ~=irc~ function using =M-x= or your own
|
||||
custom keybinding.
|
||||
|
||||
| command | description |
|
||||
|---------+-------------------------------------------|
|
||||
| ~=irc~ | Connect to IRC and all configured servers |
|
||||
|
||||
When in a circe buffer these keybindings will be available.
|
||||
|
||||
| command | key | description |
|
||||
|-----------------------------+-----------+----------------------------------------------|
|
||||
| ~+irc/tracking-next-buffer~ | =SPC m a= | Switch to the next active buffer |
|
||||
| ~circe-command-JOIN~ | =SPC m j= | Join a channel |
|
||||
| ~+irc/send-message~ | =SPC m m= | Send a private message |
|
||||
| ~circe-command-NAMES~ | =SPC m n= | List the names of the current channel |
|
||||
| ~circe-command-PART~ | =SPC m p= | Part the current channel |
|
||||
| ~+irc/quit~ | =SPC m Q= | Kill the current circe session and workgroup |
|
||||
| ~circe-reconnect~ | =SPC m R= | Reconnect the current server |
|
||||
|
||||
* Configuration
|
||||
Use ~set-irc-server!~ to configure IRC servers. Its second argument (a plist)
|
||||
takes the same arguments as ~circe-network-options~.
|
||||
|
||||
#+BEGIN_SRC emacs-lisp :tangle no
|
||||
(set! :irc "chat.freenode.net"
|
||||
`(:tls t
|
||||
:nick "doom"
|
||||
:sasl-username "myusername"
|
||||
:sasl-password "mypassword"
|
||||
:channels ("#emacs")))
|
||||
(set-irc-server! "chat.freenode.net"
|
||||
`(:tls t
|
||||
:nick "doom"
|
||||
:sasl-username "myusername"
|
||||
:sasl-password "mypassword"
|
||||
:channels ("#emacs")))
|
||||
#+END_SRC
|
||||
|
||||
*It is a obviously a bad idea to store auth-details in plaintext,* so here are some ways to avoid that:
|
||||
*It is a obviously a bad idea to store auth-details in plaintext,* so here are
|
||||
some ways to avoid that:
|
||||
|
||||
** Pass: the unix password manager
|
||||
[[https://www.passwordstore.org/][Pass]] is my tool of choice. I use it to manage my passwords. If you activate the [[/modules/tools/password-store/README.org][:tools password-store]] module you get an elisp API through which to access your password store.
|
||||
[[https://www.passwordstore.org/][Pass]] is my tool of choice. I use it to manage my passwords. If you activate the
|
||||
[[../../../modules/tools/password-store/README.org][:tools password-store]] module you get an elisp API through which to access your
|
||||
password store.
|
||||
|
||||
~:irc~'s plist can use functions instead of strings. ~+pass-get-user~ and ~+pass-get-secret~ can help here:
|
||||
~set-irc-server!~ accepts a plist can use functions instead of strings.
|
||||
~+pass-get-user~ and ~+pass-get-secret~ can help here:
|
||||
|
||||
#+BEGIN_SRC emacs-lisp :tangle no
|
||||
(set! :irc "chat.freenode.net"
|
||||
`(:tls t
|
||||
:nick "doom"
|
||||
:sasl-username ,(+pass-get-user "irc/freenode.net")
|
||||
:sasl-password ,(+pass-get-secret "irc/freenode.net")
|
||||
:channels ("#emacs")))
|
||||
(set-irc-server! "chat.freenode.net"
|
||||
`(:tls t
|
||||
:nick "doom"
|
||||
:sasl-username ,(+pass-get-user "irc/freenode.net")
|
||||
:sasl-password ,(+pass-get-secret "irc/freenode.net")
|
||||
:channels ("#emacs")))
|
||||
#+END_SRC
|
||||
|
||||
But wait, there's more! This stores your password in a public variable which could be accessed or appear in backtraces. Not good! So we go a step further:
|
||||
But wait, there's more! This stores your password in a public variable which
|
||||
could be accessed or appear in backtraces. Not good! So we go a step further:
|
||||
|
||||
#+BEGIN_SRC emacs-lisp :tangle no
|
||||
(set! :irc "chat.freenode.net"
|
||||
`(:tls t
|
||||
:nick "doom"
|
||||
:sasl-username ,(+pass-get-user "irc/freenode.net")
|
||||
:sasl-password (lambda (&rest _) (+pass-get-secret "irc/freenode.net"))
|
||||
:channels ("#emacs")))
|
||||
(set-irc-server! "chat.freenode.net"
|
||||
`(:tls t
|
||||
:port 6697
|
||||
:nick "doom"
|
||||
:sasl-username ,(+pass-get-user "irc/freenode.net")
|
||||
:sasl-password (lambda (&rest _) (+pass-get-secret "irc/freenode.net"))
|
||||
:channels ("#emacs")))
|
||||
#+END_SRC
|
||||
|
||||
And you're good to go!
|
||||
|
||||
Note that =+pass-get-user= tries to find your username by looking for the fields
|
||||
listed in =+pass-user-fields= (by default =login=, =user==, =username== and
|
||||
=email=)=). An example configuration looks like
|
||||
|
||||
#+BEGIN_SRC txt :tangle no
|
||||
mysecretpassword
|
||||
username: myusername
|
||||
#+END_SRC
|
||||
|
||||
** Emacs' auth-source API
|
||||
~auth-source~ is built into Emacs. As suggested [[https://github.com/jorgenschaefer/circe/wiki/Configuration#safer-password-management][in the circe wiki]], you can store (and retrieve) encrypted passwords with it.
|
||||
~auth-source~ is built into Emacs. As suggested [[https://github.com/jorgenschaefer/circe/wiki/Configuration#safer-password-management][in the circe wiki]], you can store
|
||||
(and retrieve) encrypted passwords with it.
|
||||
|
||||
#+BEGIN_SRC emacs-lisp :tangle no
|
||||
(setq auth-sources '("~/.authinfo.gpg"))
|
||||
|
@ -71,10 +130,12 @@ And you're good to go!
|
|||
(defun my-nickserv-password (server)
|
||||
(my-fetch-password :user "forcer" :host "irc.freenode.net"))
|
||||
|
||||
(set! :irc "chat.freenode.net"
|
||||
'(:tls t
|
||||
:nick "doom"
|
||||
:sasl-password my-nickserver-password
|
||||
:channels ("#emacs")))
|
||||
(set-irc-server! "chat.freenode.net"
|
||||
'(:tls t
|
||||
:port 6697
|
||||
:nick "doom"
|
||||
:sasl-password my-nickserver-password
|
||||
:channels ("#emacs")))
|
||||
#+END_SRC
|
||||
|
||||
* TODO Troubleshooting
|
||||
|
|
|
@ -20,11 +20,17 @@
|
|||
If INHIBIT-WORKSPACE (the universal argument) is non-nil, don't spawn a new
|
||||
workspace for it."
|
||||
(interactive "P")
|
||||
(if (+workspace-exists-p +irc--workspace-name)
|
||||
(+workspace-switch +irc--workspace-name)
|
||||
(and (+irc-setup-wconf inhibit-workspace)
|
||||
(cl-loop for network in circe-network-options
|
||||
collect (circe (car network))))))
|
||||
(cond ((and (featurep! :feature workspaces)
|
||||
(+workspace-exists-p +irc--workspace-name))
|
||||
(+workspace-switch +irc--workspace-name))
|
||||
((not (+irc-setup-wconf inhibit-workspace))
|
||||
(user-error "Couldn't start up a workspace for IRC")))
|
||||
(if (doom-buffers-in-mode 'circe-mode (buffer-list) t)
|
||||
(message "Circe buffers are already open")
|
||||
(if circe-network-options
|
||||
(cl-loop for network in circe-network-options
|
||||
collect (circe (car network)))
|
||||
(call-interactively #'circe))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +irc/connect (&optional inhibit-workspace)
|
||||
|
@ -36,6 +42,12 @@ workspace for it."
|
|||
(and (+irc-setup-wconf inhibit-workspace)
|
||||
(call-interactively #'circe)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +irc/send-message (who what)
|
||||
"Send WHO a message containing WHAT."
|
||||
(interactive "sWho: \nsWhat: ")
|
||||
(circe-command-MSG who what))
|
||||
|
||||
;;;###autoload
|
||||
(defun +irc/quit ()
|
||||
"Kill current circe session and workgroup."
|
||||
|
@ -78,3 +90,10 @@ argument) is non-nil only show channels in current server."
|
|||
(defun +irc--ivy-switch-to-buffer-action (buffer)
|
||||
(when (stringp buffer)
|
||||
(ivy--switch-buffer-action (string-trim-left buffer))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +irc/tracking-next-buffer ()
|
||||
"Dissables switching to an unread buffer unless in the irc workspace."
|
||||
(interactive)
|
||||
(when (derived-mode-p 'circe-mode)
|
||||
(tracking-next-buffer)))
|
||||
|
|
9
modules/app/irc/autoload/settings.el
Normal file
9
modules/app/irc/autoload/settings.el
Normal file
|
@ -0,0 +1,9 @@
|
|||
;;; app/irc/autoload/settings.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autodef
|
||||
(defun set-irc-server! (server letvars)
|
||||
"Registers an irc SERVER for circe.
|
||||
|
||||
See `circe-network-options' for details."
|
||||
(after! circe
|
||||
(push (cons server letvars) circe-network-options)))
|
|
@ -1,7 +1,9 @@
|
|||
;;; app/irc/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defvar +irc-left-padding 13
|
||||
"TODO")
|
||||
"By how much spaces the left hand side of the line should be padded.
|
||||
Below a value of 12 this may result in uneven alignment between the various
|
||||
types of messages.")
|
||||
|
||||
(defvar +irc-truncate-nick-char ?…
|
||||
"Character to displayed when nick > `+irc-left-padding' in length.")
|
||||
|
@ -11,27 +13,29 @@
|
|||
"If these commands are called pre prompt the buffer will scroll to `point-max'.")
|
||||
|
||||
(defvar +irc-disconnect-hook nil
|
||||
"TODO")
|
||||
"Runs each hook when circe noticies the connection has been disconnected.
|
||||
Useful for scenarios where an instant reconnect will not be successful.")
|
||||
|
||||
(defvar +irc-bot-list '("fsbot" "rudybot")
|
||||
"TODO")
|
||||
"Nicks listed have `circe-fool-face' applied and will not be tracked.")
|
||||
|
||||
(defvar +irc-time-stamp-format "%H:%M"
|
||||
"TODO")
|
||||
"The format of time stamps.
|
||||
|
||||
See `format-time-string' for a full description of available
|
||||
formatting directives. ")
|
||||
|
||||
(defvar +irc-notifications-watch-strings nil
|
||||
"TODO")
|
||||
"A list of strings which can trigger a notification. You don't need to put
|
||||
your nick here.
|
||||
|
||||
See `circe-notifications-watch-strings'.")
|
||||
|
||||
(defvar +irc-defer-notifications nil
|
||||
"How long to defer enabling notifications, in seconds (e.g. 5min = 300).
|
||||
Useful for ZNC users who want to avoid the deluge of notifications during buffer
|
||||
playback.")
|
||||
|
||||
(def-setting! :irc (server letvars)
|
||||
"Registers an irc server for circe."
|
||||
`(after! circe
|
||||
(push (cons ,server ,letvars) circe-network-options)))
|
||||
|
||||
(defvar +irc--defer-timer nil)
|
||||
|
||||
(defsubst +irc--pad (left right)
|
||||
|
@ -40,8 +44,7 @@ playback.")
|
|||
|
||||
|
||||
;;
|
||||
;; Plugins
|
||||
;;
|
||||
;; Packages
|
||||
|
||||
(def-package! circe
|
||||
:commands (circe circe-server-buffers)
|
||||
|
@ -56,6 +59,10 @@ playback.")
|
|||
circe-format-self-say circe-format-say
|
||||
circe-format-action (format "{nick:+%ss} * {body}" +irc-left-padding)
|
||||
circe-format-self-action circe-format-action
|
||||
circe-format-server-notice
|
||||
(let ((left "-Server-")) (concat (make-string (- +irc-left-padding (length left)) ? )
|
||||
(concat left " _ {body}")))
|
||||
circe-format-notice (format "{nick:%ss} _ {body}" +irc-left-padding)
|
||||
circe-format-server-topic
|
||||
(+irc--pad "Topic" "{userhost}: {topic-diff}")
|
||||
circe-format-server-join-in-channel
|
||||
|
@ -70,6 +77,8 @@ playback.")
|
|||
(+irc--pad "Quit" "{nick} ({userhost}) left {channel}: {reason}]")
|
||||
circe-format-server-rejoin
|
||||
(+irc--pad "Re-join" "{nick} ({userhost}), left {departuredelta} ago")
|
||||
circe-format-server-netmerge
|
||||
(+irc--pad "Netmerge" "{split}, split {ago} ago (Use /WL to see who's still missing)")
|
||||
circe-format-server-nick-change
|
||||
(+irc--pad "Nick" "{old-nick} ({userhost}) is now known as {new-nick}")
|
||||
circe-format-server-nick-change-self
|
||||
|
@ -83,8 +92,9 @@ playback.")
|
|||
|
||||
(add-hook 'circe-channel-mode-hook #'turn-on-visual-line-mode)
|
||||
|
||||
;; Let `+irc/quit' and `circe' handle buffer cleanup
|
||||
(map! :map circe-mode-map [remap doom/kill-this-buffer] #'bury-buffer)
|
||||
(defun +irc*circe-disconnect-hook (&rest _)
|
||||
(run-hooks '+irc-disconnect-hook))
|
||||
(advice-add 'circe--irc-conn-disconnected :after #'+irc*circe-disconnect-hook)
|
||||
|
||||
(defun +irc*circe-truncate-nicks ()
|
||||
"Truncate long nicknames in chat output non-destructively."
|
||||
|
@ -98,15 +108,48 @@ playback.")
|
|||
+irc-truncate-nick-char)))))
|
||||
(add-hook 'lui-pre-output-hook #'+irc*circe-truncate-nicks)
|
||||
|
||||
(defun +circe-buffer-p (buf)
|
||||
"Return non-nil if BUF is a `circe-mode' buffer."
|
||||
(with-current-buffer buf
|
||||
(and (derived-mode-p 'circe-mode)
|
||||
(eq (safe-persp-name (get-current-persp))
|
||||
+irc--workspace-name))))
|
||||
(add-hook 'doom-real-buffer-functions #'+circe-buffer-p)
|
||||
|
||||
(defun +irc|circe-message-option-bot (nick &rest ignored)
|
||||
"Fontify known bots and mark them to not be tracked."
|
||||
(when (member nick +irc-bot-list)
|
||||
'((text-properties . (face circe-fool-face lui-do-not-track t)))))
|
||||
(add-hook 'circe-message-option-functions #'+irc|circe-message-option-bot)
|
||||
|
||||
(after! solaire-mode
|
||||
;; distinguish chat/channel buffers from server buffers.
|
||||
(add-hook 'circe-chat-mode-hook #'solaire-mode)))
|
||||
(defun +irc|add-circe-buffer-to-persp ()
|
||||
(let ((persp (get-current-persp))
|
||||
(buf (current-buffer)))
|
||||
;; Add a new circe buffer to irc workspace when we're in another workspace
|
||||
(unless (eq (safe-persp-name persp) +irc--workspace-name)
|
||||
;; Add new circe buffers to the persp containing circe buffers
|
||||
(persp-add-buffer buf (persp-get-by-name +irc--workspace-name))
|
||||
;; Remove new buffer from accidental workspace
|
||||
(persp-remove-buffer buf persp))))
|
||||
(add-hook 'circe-mode-hook #'+irc|add-circe-buffer-to-persp)
|
||||
|
||||
;; Let `+irc/quit' and `circe' handle buffer cleanup
|
||||
(define-key circe-mode-map [remap kill-buffer] #'bury-buffer)
|
||||
;; Fail gracefully if not in a circe buffer
|
||||
(global-set-key [remap tracking-next-buffer] #'+irc/tracking-next-buffer)
|
||||
|
||||
(map! :localleader
|
||||
(:map circe-mode-map
|
||||
"a" #'tracking-next-buffer
|
||||
"j" #'circe-command-JOIN
|
||||
"m" #'+irc/send-message
|
||||
"p" #'circe-command-PART
|
||||
"Q" #'+irc/quit
|
||||
"R" #'circe-reconnect
|
||||
(:when (featurep! :completion ivy)
|
||||
"c" #'+irc/ivy-jump-to-channel))
|
||||
(:map circe-channel-mode-map
|
||||
"n" #'circe-command-NAMES)))
|
||||
|
||||
|
||||
(def-package! circe-color-nicks
|
||||
|
@ -144,10 +187,11 @@ playback.")
|
|||
(def-package! lui
|
||||
:commands lui-mode
|
||||
:config
|
||||
(map! :map lui-mode-map "C-u" #'lui-kill-to-beginning-of-line)
|
||||
(when (featurep! :feature spellcheck)
|
||||
(setq lui-flyspell-p t
|
||||
lui-fill-type nil))
|
||||
(define-key lui-mode-map "\C-u" #'lui-kill-to-beginning-of-line)
|
||||
(setq lui-fill-type nil)
|
||||
|
||||
(when (featurep! :tools flyspell)
|
||||
(setq lui-flyspell-p t))
|
||||
|
||||
(after! evil
|
||||
(defun +irc|evil-insert ()
|
||||
|
@ -182,6 +226,9 @@ Courtesy of esh-mode.el"
|
|||
(add-hook! 'lui-mode-hook
|
||||
(add-hook 'pre-command-hook #'+irc|preinput-scroll-to-bottom nil t))
|
||||
|
||||
;; enable a horizontal line marking the last read message
|
||||
(add-hook! 'lui-mode-hook #'enable-lui-track-bar)
|
||||
|
||||
(defun +irc|init-lui-margins ()
|
||||
(setq lui-time-stamp-position 'right-margin
|
||||
lui-time-stamp-format +irc-time-stamp-format
|
||||
|
|
189
modules/app/notmuch/autoload.el
Normal file
189
modules/app/notmuch/autoload.el
Normal file
|
@ -0,0 +1,189 @@
|
|||
;;; app/notmuch/autoload.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autoload
|
||||
(defun =notmuch ()
|
||||
"Activate (or switch to) `notmuch' in its workspace."
|
||||
(interactive)
|
||||
(unless (featurep! :feature workspaces)
|
||||
(user-error ":feature workspaces is required, but disabled"))
|
||||
(condition-case-unless-debug e
|
||||
(progn
|
||||
(+workspace-switch "*MAIL*" t)
|
||||
(if-let* ((buf (cl-find-if (lambda (it) (string-match-p "^\\*notmuch" (buffer-name (window-buffer it))))
|
||||
(doom-visible-windows))))
|
||||
(select-window (get-buffer-window buf))
|
||||
(notmuch-search "tag:inbox"))
|
||||
(+workspace/display))
|
||||
('error
|
||||
(+notmuch/quit)
|
||||
(signal (car e) (cdr e)))))
|
||||
|
||||
|
||||
;;
|
||||
;; Commands
|
||||
|
||||
;;;###autoload
|
||||
(defun +notmuch/quit ()
|
||||
(interactive)
|
||||
;; (+popup/close (get-buffer-window "*notmuch-hello*"))
|
||||
(doom-kill-matching-buffers "^\\*notmuch")
|
||||
(+workspace/delete "*MAIL*"))
|
||||
|
||||
;;;###autoload
|
||||
(defun +notmuch/update ()
|
||||
(interactive)
|
||||
(start-process-shell-command
|
||||
"notmuch update" nil
|
||||
(pcase +notmuch-sync-backend
|
||||
(`gmi
|
||||
(concat "cd " +notmuch-mail-folder " && gmi push && gmi pull && notmuch new && afew -a -t"))
|
||||
(`mbsync
|
||||
"mbsync -a && notmuch new && afew -a -t")
|
||||
(`mbsync-xdg
|
||||
"mbsync -c \"$XDG_CONFIG_HOME\"/isync/mbsyncrc -a && notmuch new && afew -a -t")
|
||||
(`offlineimap
|
||||
"offlineimap && notmuch new && afew -a -t"))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +notmuch/search-delete ()
|
||||
(interactive)
|
||||
(notmuch-search-add-tag (list "+trash" "-inbox" "-unread"))
|
||||
(notmuch-tree-next-message))
|
||||
|
||||
;;;###autoload
|
||||
(defun +notmuch/tree-delete ()
|
||||
(interactive)
|
||||
(notmuch-tree-add-tag (list "+trash" "-inbox" "-unread"))
|
||||
(notmuch-tree-next-message))
|
||||
|
||||
;;;###autoload
|
||||
(defun +notmuch/search-spam ()
|
||||
(interactive)
|
||||
(notmuch-search-add-tag (list "+spam" "-inbox" "-unread"))
|
||||
(notmuch-search-next-thread))
|
||||
|
||||
;;;###autoload
|
||||
(defun +notmuch/tree-spam ()
|
||||
(interactive)
|
||||
(notmuch-tree-add-tag (list "+spam" "-inbox" "-unread"))
|
||||
(notmuch-tree-next-message))
|
||||
|
||||
;;;###autoload
|
||||
(defun +notmuch/open-message-with-mail-app-notmuch-tree ()
|
||||
(interactive)
|
||||
(let* ((msg-path (car (plist-get (notmuch-tree-get-message-properties) :filename)))
|
||||
(temp (make-temp-file "notmuch-message-" nil ".eml")))
|
||||
(shell-command-to-string (format "cp '%s' '%s'" msg-path temp))
|
||||
(start-process-shell-command "email" nil (format "xdg-open '%s'" temp))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +notmuch/open-message-with-mail-app-notmuch-show ()
|
||||
(interactive)
|
||||
(let* ((msg-path (car (plist-get (notmuch-show-get-message-properties) :filename)))
|
||||
(temp (make-temp-file "notmuch-message-" nil ".eml")))
|
||||
(shell-command-to-string (format "cp '%s' '%s'" msg-path temp))
|
||||
(start-process-shell-command "email" nil (format "xdg-open '%s'" temp))))
|
||||
|
||||
|
||||
;;
|
||||
;; Advice
|
||||
|
||||
;;;###autoload
|
||||
(defun +notmuch*dont-confirm-on-kill-process (orig-fn &rest args)
|
||||
"Don't prompt for confirmation when killing notmuch sentinel."
|
||||
(let (confirm-kill-processes)
|
||||
(apply orig-fn args)))
|
||||
|
||||
;; (defun +notmuch*hello-insert-searches (title query-list &rest options)
|
||||
;; (widget-insert (propertize title 'face 'org-agenda-structure))
|
||||
;; (if (and notmuch-hello-first-run (plist-get options :initially-hidden))
|
||||
;; (add-to-list 'notmuch-hello-hidden-sections title))
|
||||
;; (let ((is-hidden (member title notmuch-hello-hidden-sections))
|
||||
;; (widget-push-button-prefix "")
|
||||
;; (widget-push-button-suffix "")
|
||||
;; (start (point)))
|
||||
;; (if is-hidden
|
||||
;; (widget-create 'push-button
|
||||
;; :notify `(lambda (widget &rest ignore)
|
||||
;; (setq notmuch-hello-hidden-sections
|
||||
;; (delete ,title notmuch-hello-hidden-sections))
|
||||
;; (notmuch-hello-update))
|
||||
;; (propertize " +" 'face 'org-agenda-structure))
|
||||
;; (widget-create 'push-button
|
||||
;; :notify `(lambda (widget &rest ignore)
|
||||
;; (add-to-list 'notmuch-hello-hidden-sections
|
||||
;; ,title)
|
||||
;; (notmuch-hello-update))
|
||||
;; " -"))
|
||||
;; (widget-insert "\n")
|
||||
;; (when (not is-hidden)
|
||||
;; (let ((searches (apply 'notmuch-hello-query-counts query-list options)))
|
||||
;; (when (or (not (plist-get options :hide-if-empty))
|
||||
;; searches)
|
||||
;; (widget-insert "\n")
|
||||
;; (notmuch-hello-insert-buttons searches)
|
||||
;; (indent-rigidly start (point) notmuch-hello-indent))))))
|
||||
|
||||
;; (defun +notmuch*hello-insert-saved-searches ()
|
||||
;; "Insert the saved-searches section."
|
||||
;; (let ((searches (notmuch-hello-query-counts
|
||||
;; (if notmuch-saved-search-sort-function
|
||||
;; (funcall notmuch-saved-search-sort-function
|
||||
;; notmuch-saved-searches)
|
||||
;; notmuch-saved-searches)
|
||||
;; :show-empty-searches notmuch-show-empty-saved-searches)))
|
||||
;; (when searches
|
||||
;; (widget-insert (propertize "Notmuch" 'face 'org-agenda-date-today))
|
||||
;; (widget-insert "\n\n")
|
||||
;; (widget-insert (propertize "Saved searches" 'face 'org-agenda-structure))
|
||||
;; (widget-insert "\n\n")
|
||||
;; (let ((start (point)))
|
||||
;; (notmuch-hello-insert-buttons searches)
|
||||
;; (indent-rigidly start (point) notmuch-hello-indent)))))
|
||||
|
||||
;; (defun +notmuch*hello-insert-buttons (searches)
|
||||
;; (let* ((widest (notmuch-hello-longest-label searches))
|
||||
;; (tags-and-width (notmuch-hello-tags-per-line widest))
|
||||
;; (tags-per-line (car tags-and-width))
|
||||
;; (column-width (cdr tags-and-width))
|
||||
;; (column-indent 0)
|
||||
;; (count 0)
|
||||
;; (reordered-list (notmuch-hello-reflect searches tags-per-line))
|
||||
;; ;; Hack the display of the buttons used.
|
||||
;; (widget-push-button-prefix "")
|
||||
;; (widget-push-button-suffix ""))
|
||||
;; ;; dme: It feels as though there should be a better way to
|
||||
;; ;; implement this loop than using an incrementing counter.
|
||||
;; (mapc (lambda (elem)
|
||||
;; ;; (not elem) indicates an empty slot in the matrix.
|
||||
;; (when elem
|
||||
;; (if (> column-indent 0)
|
||||
;; (widget-insert (make-string column-indent ? )))
|
||||
;; (let* ((name (plist-get elem :name))
|
||||
;; (query (plist-get elem :query))
|
||||
;; (oldest-first (case (plist-get elem :sort-order)
|
||||
;; (newest-first nil)
|
||||
;; (oldest-first t)
|
||||
;; (otherwise notmuch-search-oldest-first)))
|
||||
;; (search-type (eq (plist-get elem :search-type) 'tree))
|
||||
;; (msg-count (plist-get elem :count)))
|
||||
;; (widget-insert (format "\n%5s "
|
||||
;; (notmuch-hello-nice-number msg-count)))
|
||||
;; (widget-create 'push-button
|
||||
;; :notify #'notmuch-hello-widget-search
|
||||
;; :notmuch-search-terms query
|
||||
;; :notmuch-search-oldest-first oldest-first
|
||||
;; :notmuch-search-type search-type
|
||||
;; name)
|
||||
;; (setq column-indent
|
||||
;; (1+ (max 0 (- column-width (length name)))))))
|
||||
;; (setq count (1+ count))
|
||||
;; (when (eq (% count tags-per-line) 0)
|
||||
;; (setq column-indent 0)
|
||||
;; (widget-insert "\n")))
|
||||
;; reordered-list)
|
||||
|
||||
;; ;; If the last line was not full (and hence did not include a
|
||||
;; ;; carriage return), insert one now.
|
||||
;; (unless (eq (% count tags-per-line) 0)
|
||||
;; (widget-insert "\n"))))
|
74
modules/app/notmuch/config.el
Normal file
74
modules/app/notmuch/config.el
Normal file
|
@ -0,0 +1,74 @@
|
|||
;;; app/notmuch/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
;; FIXME This module is a WIP!
|
||||
|
||||
(defvar +notmuch-sync-backend 'gmi
|
||||
"Which backend to use. Can be either gmi, mbsync, offlineimap or nil (manual).")
|
||||
|
||||
(defvar +notmuch-mail-folder "~/.mail/account.gmail"
|
||||
"Where your email folder is located (for use with gmailieer).")
|
||||
|
||||
(after! notmuch
|
||||
(set-company-backend! 'notmuch-message-mode
|
||||
'(notmuch-company (company-ispell :with company-yasnippet)))
|
||||
|
||||
(set-popup-rule! "^\\*notmuch-hello" :side 'left :size 30 :ttl 0)
|
||||
|
||||
(setq notmuch-fcc-dirs nil
|
||||
notmuch-show-logo nil
|
||||
notmuch-message-headers-visible nil
|
||||
message-kill-buffer-on-exit t
|
||||
message-send-mail-function 'message-send-mail-with-sendmail
|
||||
notmuch-search-oldest-first nil
|
||||
send-mail-function 'sendmail-send-it
|
||||
;; sendmail-program "/usr/local/bin/msmtp"
|
||||
notmuch-search-result-format
|
||||
'(("date" . "%12s ")
|
||||
("count" . "%-7s ")
|
||||
("authors" . "%-30s ")
|
||||
("subject" . "%-72s ")
|
||||
("tags" . "(%s)"))
|
||||
notmuch-tag-formats
|
||||
'(("unread" (propertize tag 'face 'notmuch-tag-unread)))
|
||||
notmuch-hello-sections
|
||||
'(notmuch-hello-insert-saved-searches
|
||||
notmuch-hello-insert-alltags)
|
||||
notmuch-saved-searches
|
||||
'((:name "inbox" :query "tag:inbox not tag:trash" :key "i")
|
||||
(:name "flagged" :query "tag:flagged" :key "f")
|
||||
(:name "sent" :query "tag:sent" :key "s")
|
||||
(:name "drafts" :query "tag:draft" :key "d"))
|
||||
notmuch-archive-tags '("-inbox" "-unread"))
|
||||
|
||||
;; (setq-hook! 'notmuch-show-mode-hook line-spacing 0)
|
||||
|
||||
(add-to-list 'doom-real-buffer-functions #'notmuch-interesting-buffer nil #'eq)
|
||||
|
||||
(advice-add #'notmuch-start-notmuch-sentinel :around #'+notmuch*dont-confirm-on-kill-process)
|
||||
|
||||
;; Visual enhancements
|
||||
(defun +notmuch|center-window ()
|
||||
(setq-local visual-fill-column-width 90)
|
||||
(visual-fill-column-mode))
|
||||
(add-hook 'notmuch-show-mode-hook #'+notmuch|center-window)
|
||||
|
||||
;; modeline doesn't have much use in these modes
|
||||
(add-hook! (notmuch-show-mode notmuch-tree-mode notmuch-search-mode)
|
||||
#'hide-mode-line-mode))
|
||||
|
||||
|
||||
(def-package! org-mime
|
||||
:after (org notmuch)
|
||||
:config (setq org-mime-library 'mml))
|
||||
|
||||
|
||||
(def-package! counsel-notmuch
|
||||
:when (featurep! :completion ivy)
|
||||
:commands counsel-notmuch
|
||||
:after notmuch)
|
||||
|
||||
(def-package! helm-notmuch
|
||||
:when (featurep! :completion helm)
|
||||
:commands helm-notmuch
|
||||
:after notmuch)
|
||||
|
9
modules/app/notmuch/packages.el
Normal file
9
modules/app/notmuch/packages.el
Normal file
|
@ -0,0 +1,9 @@
|
|||
;; -*- no-byte-compile: t; -*-
|
||||
;;; app/notmuch/packages.el
|
||||
|
||||
(package! notmuch)
|
||||
(package! org-mime)
|
||||
(when (featurep! :completion ivy)
|
||||
(package! counsel-notmuch))
|
||||
(when (featurep! :completion helm)
|
||||
(package! helm-notmuch))
|
|
@ -11,17 +11,17 @@
|
|||
(defface +regex-match-0-face
|
||||
`((t (:foreground "Black" :background ,(doom-color 'magenta) :bold t)))
|
||||
"TODO"
|
||||
:group 'doom)
|
||||
:group 'faces)
|
||||
|
||||
(defface +regex-match-1-face
|
||||
`((t (:foreground "Black" :background ,(doom-color 'blue) :bold t)))
|
||||
"TODO"
|
||||
:group 'doom)
|
||||
:group 'faces)
|
||||
|
||||
(defface +regex-match-2-face
|
||||
`((t (:foreground "Black" :background ,(doom-color 'green) :bold t)))
|
||||
"TODO"
|
||||
:group 'doom)
|
||||
:group 'faces)
|
||||
|
||||
(defvar +regex-faces
|
||||
'(+regex-match-0-face +regex-match-1-face +regex-match-2-face)
|
||||
|
@ -33,8 +33,6 @@
|
|||
(define-key map "\C-c\C-c" #'+regex-update-buffers)
|
||||
(define-key map "\C-c\C-r" #'=regex/replace)
|
||||
(define-key map "\C-c\C-k" #'+regex/quit)
|
||||
(define-key map [remap doom-kill-buffer] #'+regex/quit)
|
||||
(define-key map [remap doom/kill-this-buffer] #'+regex/quit)
|
||||
(define-key map [remap kill-this-buffer] #'+regex/quit)
|
||||
(define-key map [remap kill-buffer] #'+regex/quit)
|
||||
map)
|
||||
|
@ -66,8 +64,8 @@
|
|||
(switch-to-buffer +regex--text-buffer)
|
||||
(with-current-buffer +regex--text-buffer
|
||||
(insert +regex-dummy-text)))
|
||||
(doom-popup-buffer +regex--groups-buffer)
|
||||
(doom-popup-buffer +regex--expr-buffer)
|
||||
(pop-to-buffer +regex--groups-buffer)
|
||||
(pop-to-buffer +regex--expr-buffer)
|
||||
(with-current-buffer +regex--expr-buffer
|
||||
(conf-mode)
|
||||
(rainbow-delimiters-mode +1)
|
||||
|
|
|
@ -46,7 +46,7 @@ http://regexr.com/foo.html?q=bar
|
|||
https://mediatemple.net"
|
||||
"TODO")
|
||||
|
||||
(set! :popup
|
||||
'("*doom-regex*" :size 4 :select t :noesc t)
|
||||
'("*doom-regex-groups*" :align left :size 30 :noselect t :noesc t))
|
||||
(set-popup-rules!
|
||||
'(("^\\*doom-regex\\*$" :size 4 :quit nil)
|
||||
("^\\*doom-regex-groups" :side 'left :size 28 :select nil :quit nil)))
|
||||
|
||||
|
|
|
@ -1,47 +1,18 @@
|
|||
;;; app/rss/autoload.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autoload
|
||||
(defun =rss ()
|
||||
"Activate (or switch to) `elfeed' in its workspace."
|
||||
(interactive)
|
||||
(call-interactively 'elfeed))
|
||||
|
||||
;;;###autoload
|
||||
(defun +rss/quit ()
|
||||
(interactive)
|
||||
(doom-kill-matching-buffers "^\\*elfeed")
|
||||
(dolist (file +rss-elfeed-files)
|
||||
(when-let* ((buf (get-file-buffer (expand-file-name file +rss-org-dir))))
|
||||
(kill-buffer buf))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +rss|elfeed-wrap ()
|
||||
"Enhances an elfeed entry's readability by wrapping it to a width of
|
||||
`fill-column' and centering it with `visual-fill-column-mode'."
|
||||
(let ((inhibit-read-only t)
|
||||
(inhibit-modification-hooks t))
|
||||
(setq-local truncate-lines nil)
|
||||
(setq-local shr-width 85)
|
||||
(set-buffer-modified-p nil)))
|
||||
(defalias '=rss #'elfeed
|
||||
"Activate (or switch to) `elfeed' in its workspace.")
|
||||
|
||||
;;;###autoload
|
||||
(defun +rss/delete-pane ()
|
||||
"Delete the *elfeed-entry* split pane."
|
||||
(interactive)
|
||||
(let* ((buff (get-buffer "*elfeed-entry*"))
|
||||
(window (get-buffer-window buff)))
|
||||
(kill-buffer buff)
|
||||
(delete-window window)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +rss-popup-pane (buf)
|
||||
"Display BUF in a popup."
|
||||
(doom-popup-buffer buf
|
||||
'(:align +rss-split-direction
|
||||
:size 0.75
|
||||
:select t
|
||||
:autokill t
|
||||
:autoclose t)))
|
||||
(let* ((buf (get-buffer "*elfeed-entry*"))
|
||||
(window (get-buffer-window buf)))
|
||||
(delete-window window)
|
||||
(when (buffer-live-p buf)
|
||||
(kill-buffer buf))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +rss/open (entry)
|
||||
|
@ -70,6 +41,48 @@
|
|||
(forward-line -1)
|
||||
(call-interactively '+rss/open)))
|
||||
|
||||
|
||||
;;
|
||||
;; Hooks
|
||||
|
||||
;;;###autoload
|
||||
(defun +rss|elfeed-wrap ()
|
||||
"Enhances an elfeed entry's readability by wrapping it to a width of
|
||||
`fill-column'."
|
||||
(let ((inhibit-read-only t)
|
||||
(inhibit-modification-hooks t))
|
||||
(setq-local truncate-lines nil)
|
||||
(setq-local shr-use-fonts nil)
|
||||
(setq-local shr-width 85)
|
||||
(set-buffer-modified-p nil)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +rss|cleanup ()
|
||||
"Clean up after an elfeed session. Kills all elfeed and elfeed-org files."
|
||||
(interactive)
|
||||
;; `delete-file-projectile-remove-from-cache' slows down `elfeed-db-compact'
|
||||
;; tremendously, so we disable the projectile cache:
|
||||
(let (projectile-enable-caching)
|
||||
(elfeed-db-compact))
|
||||
(let ((buf (previous-buffer)))
|
||||
(when (or (null buf) (not (doom-real-buffer-p buf)))
|
||||
(switch-to-buffer (doom-fallback-buffer))))
|
||||
(let ((search-buffers (doom-buffers-in-mode 'elfeed-search-mode))
|
||||
(show-buffers (doom-buffers-in-mode 'elfeed-show-mode))
|
||||
kill-buffer-query-functions)
|
||||
(dolist (file +rss-elfeed-files)
|
||||
(when-let* ((buf (get-file-buffer (expand-file-name file org-directory))))
|
||||
(kill-buffer buf)))
|
||||
(dolist (b search-buffers)
|
||||
(with-current-buffer b
|
||||
(remove-hook 'kill-buffer-hook #'+rss|cleanup :local)
|
||||
(kill-buffer b)))
|
||||
(mapc #'kill-buffer show-buffers)))
|
||||
|
||||
|
||||
;;
|
||||
;; Functions
|
||||
|
||||
;;;###autoload
|
||||
(defun +rss-dead-feeds (&optional years)
|
||||
"Return a list of feeds that haven't posted anything in YEARS."
|
||||
|
@ -84,3 +97,21 @@
|
|||
(cl-loop for url in (elfeed-feed-list)
|
||||
unless (gethash url living-feeds)
|
||||
collect url)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +rss-put-sliced-image (spec alt &optional flags)
|
||||
"TODO"
|
||||
(cl-letf (((symbol-function #'insert-image)
|
||||
(lambda (image &optional alt _area _slice)
|
||||
(let ((height (cdr (image-size image t))))
|
||||
(insert-sliced-image image alt nil (max 1 (/ height 20.0)) 1)))))
|
||||
(shr-put-image spec alt flags)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +rss-render-image-tag-without-underline (dom &optional url)
|
||||
"TODO"
|
||||
(let ((start (point)))
|
||||
(shr-tag-img dom url)
|
||||
;; And remove underlines in case images are links, otherwise we get an
|
||||
;; underline beneath every slice.
|
||||
(put-text-property start (point) 'face '(:underline nil))))
|
||||
|
|
|
@ -4,16 +4,20 @@
|
|||
;; by apps Reeder and Readkit. It can be invoked via `=rss'. Otherwise, if you
|
||||
;; don't care for the UI you can invoke elfeed directly with `elfeed'.
|
||||
|
||||
(defvar +rss-elfeed-files (list "rss/elfeed.org")
|
||||
"The files that configure `elfeed's rss feeds.")
|
||||
(defvar +rss-elfeed-files (list "elfeed.org")
|
||||
"Where to look for elfeed.org files, relative to `org-directory'. Can be
|
||||
absolute paths.")
|
||||
|
||||
(defvar +rss-split-direction 'below
|
||||
"What direction to pop up the entry buffer in elfeed.")
|
||||
|
||||
(defvar +rss-enable-sliced-images t
|
||||
"Automatically slice images shown in elfeed-show-mode buffers, making them
|
||||
easier to scroll through.")
|
||||
|
||||
|
||||
;;
|
||||
;; Packages
|
||||
;;
|
||||
|
||||
(def-package! elfeed
|
||||
:commands elfeed
|
||||
|
@ -21,44 +25,50 @@
|
|||
(setq elfeed-search-filter "@2-week-ago "
|
||||
elfeed-db-directory (concat doom-local-dir "elfeed/db/")
|
||||
elfeed-enclosure-default-dir (concat doom-local-dir "elfeed/enclosures/")
|
||||
elfeed-show-entry-switch #'+rss-popup-pane
|
||||
elfeed-show-entry-switch #'pop-to-buffer
|
||||
elfeed-show-entry-delete #'+rss/delete-pane
|
||||
shr-max-image-proportion 0.6)
|
||||
shr-max-image-proportion 0.8)
|
||||
|
||||
(set-popup-rule! "^\\*elfeed-entry"
|
||||
:size 0.75 :actions '(display-buffer-below-selected)
|
||||
:select t :quit nil :ttl t)
|
||||
|
||||
(make-directory elfeed-db-directory t)
|
||||
|
||||
;; Ensure elfeed buffers are treated as real
|
||||
(push (lambda (buf) (string-match-p "^\\*elfeed" (buffer-name buf)))
|
||||
doom-real-buffer-functions)
|
||||
(defun +rss-buffer-p (buf)
|
||||
(string-match-p "^\\*elfeed" (buffer-name buf)))
|
||||
(add-to-list 'doom-real-buffer-functions #'+rss-buffer-p nil #'eq)
|
||||
|
||||
;; Enhance readability of a post
|
||||
(add-hook 'elfeed-show-mode-hook #'+rss|elfeed-wrap)
|
||||
(add-hook! 'elfeed-search-mode-hook
|
||||
(add-hook 'kill-buffer-hook #'+rss|cleanup nil t))
|
||||
|
||||
(map! (:map (elfeed-search-mode-map elfeed-show-mode-map)
|
||||
[remap doom/kill-this-buffer] "q"
|
||||
[remap kill-this-buffer] "q"
|
||||
[remap kill-buffer] "q")
|
||||
;; Large images are annoying to scroll through, because scrolling follows the
|
||||
;; cursor, so we force shr to insert images in slices.
|
||||
(when +rss-enable-sliced-images
|
||||
(setq-hook! 'elfeed-show-mode-hook
|
||||
shr-put-image-function #'+rss-put-sliced-image
|
||||
shr-external-rendering-functions '((img . +rss-render-image-tag-without-underline))))
|
||||
|
||||
(:map elfeed-search-mode-map
|
||||
:n "q" #'+rss/quit
|
||||
:n "r" #'elfeed-update
|
||||
:n "s" #'elfeed-search-live-filter
|
||||
:n "RET" #'elfeed-search-show-entry)
|
||||
|
||||
(:map elfeed-show-mode-map
|
||||
:n "q" #'elfeed-kill-buffer
|
||||
:m "j" #'evil-next-visual-line
|
||||
:m "k" #'evil-previous-visual-line
|
||||
[remap doom/next-buffer] #'+rss/next
|
||||
[remap doom/previous-buffer] #'+rss/previous
|
||||
[remap next-buffer] #'+rss/next
|
||||
[remap previous-buffer] #'+rss/previous)))
|
||||
;; Keybindings
|
||||
(after! elfeed-show
|
||||
(define-key! elfeed-show-mode-map
|
||||
[remap next-buffer] #'+rss/next
|
||||
[remap previous-buffer] #'+rss/previous))
|
||||
(when (featurep! :feature evil +everywhere)
|
||||
(evil-define-key 'normal elfeed-search-mode-map
|
||||
"q" #'elfeed-kill-buffer
|
||||
"r" #'elfeed-search-update--force
|
||||
(kbd "M-RET") #'elfeed-search-browse-url)))
|
||||
|
||||
|
||||
(def-package! elfeed-org
|
||||
:after (:all org elfeed)
|
||||
:when (featurep! +org)
|
||||
:after elfeed
|
||||
:config
|
||||
(setq rmh-elfeed-org-files
|
||||
(let ((default-directory +org-dir))
|
||||
(let ((default-directory org-directory))
|
||||
(mapcar #'expand-file-name +rss-elfeed-files)))
|
||||
(elfeed-org))
|
||||
|
|
|
@ -1,12 +1,39 @@
|
|||
;;; app/twitter/autoload.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defvar +twitter-workspace-name "*Twitter*"
|
||||
"The name to use for the twitter workspace.")
|
||||
|
||||
;;;###autoload
|
||||
(defun =twitter ()
|
||||
(interactive)
|
||||
(+workspace-switch "*Twitter*" t)
|
||||
(delete-other-windows)
|
||||
(condition-case _ex
|
||||
(defun +twitter-display-buffer (buf)
|
||||
"A replacement display-buffer command for `twittering-pop-to-buffer-function'
|
||||
that works with the feature/popup module."
|
||||
(let ((win (selected-window)))
|
||||
(display-buffer buf)
|
||||
;; This is required because the new window generated by `pop-to-buffer'
|
||||
;; may hide the region following the current position.
|
||||
(twittering-ensure-whole-of-status-is-visible win)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +twitter-buffer-p (buf)
|
||||
"Return non-nil if BUF is a `twittering-mode' buffer."
|
||||
(eq 'twittering-mode (buffer-local-value 'major-mode buf)))
|
||||
|
||||
|
||||
;;
|
||||
;; Commands
|
||||
|
||||
(defvar +twitter--old-wconf nil)
|
||||
;;;###autoload
|
||||
(defun =twitter (arg)
|
||||
"Opens a workspace dedicated to `twittering-mode'."
|
||||
(interactive "P")
|
||||
(condition-case _
|
||||
(progn
|
||||
(if (and (not arg) (featurep! :feature workspaces))
|
||||
(+workspace/new +twitter-workspace-name)
|
||||
(setq +twitter--old-wconf (current-window-configuration))
|
||||
(delete-other-windows)
|
||||
(switch-to-buffer (doom-fallback-buffer)))
|
||||
(call-interactively #'twit)
|
||||
(unless (get-buffer (car twittering-initial-timeline-spec-string))
|
||||
(error "Failed to open twitter"))
|
||||
|
@ -14,28 +41,62 @@
|
|||
(dolist (name (cdr twittering-initial-timeline-spec-string))
|
||||
(split-window-horizontally)
|
||||
(switch-to-buffer name))
|
||||
(balance-windows))
|
||||
('error
|
||||
(+twitter/quit-all))))
|
||||
(balance-windows)
|
||||
(call-interactively #'+twitter/rerender-all))
|
||||
('error (+twitter/quit-all))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +twitter/quit ()
|
||||
"Close the current `twitter-mode' buffer."
|
||||
(interactive)
|
||||
(when (eq major-mode 'twittering-mode)
|
||||
(twittering-kill-buffer)
|
||||
(+workspace/close-window-or-workspace)))
|
||||
(cond ((one-window-p) (+twitter/quit-all))
|
||||
((featurep! :feature workspaces)
|
||||
(+workspace/close-window-or-workspace))
|
||||
((delete-window)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +twitter/quit-all ()
|
||||
"Close all open `twitter-mode' buffers and the associated workspace, if any."
|
||||
(interactive)
|
||||
(+workspace/delete "Twitter")
|
||||
(dolist (buf (doom-buffers-in-mode 'twittering-mode))
|
||||
(with-current-buffer buf
|
||||
(twittering-kill-buffer))))
|
||||
(when (featurep! :feature workspaces)
|
||||
(+workspace/delete +twitter-workspace-name))
|
||||
(when +twitter--old-wconf
|
||||
(set-window-configuration +twitter--old-wconf)
|
||||
(setq +twitter--old-wconf nil))
|
||||
(dolist (buf (doom-buffers-in-mode 'twittering-mode (buffer-list) t))
|
||||
(twittering-kill-buffer buf)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +twitter/rerender-all ()
|
||||
"Rerender all `twittering-mode' buffers."
|
||||
(interactive)
|
||||
(dolist (buf (doom-buffers-in-mode 'twittering-mode))
|
||||
(dolist (buf (doom-buffers-in-mode 'twittering-mode (buffer-list) t))
|
||||
(with-current-buffer buf
|
||||
(twittering-rerender-timeline-all buf))))
|
||||
(twittering-rerender-timeline-all buf)
|
||||
(setq-local line-spacing 0.2)
|
||||
(goto-char (point-min)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +twitter/ace-link ()
|
||||
"Open a visible link, username or hashtag in a `twittering-mode' buffer."
|
||||
(interactive)
|
||||
(let ((pt (avy-with +twitter/ace-link
|
||||
(avy--process
|
||||
(+twitter--collect-links)
|
||||
(avy--style-fn avy-style)))))
|
||||
(when (number-or-marker-p pt)
|
||||
(goto-char pt)
|
||||
(let ((uri (get-text-property (point) 'uri)))
|
||||
(if uri (browse-url uri))))))
|
||||
|
||||
(defun +twitter--collect-links ()
|
||||
(let ((end (window-end))
|
||||
points)
|
||||
(save-excursion
|
||||
(goto-char (window-start))
|
||||
(while (and (< (point) end)
|
||||
(ignore-errors (twittering-goto-next-thing) t))
|
||||
(push (point) points))
|
||||
(nreverse points))))
|
||||
|
|
|
@ -3,34 +3,84 @@
|
|||
(def-package! twittering-mode
|
||||
:commands twit
|
||||
:config
|
||||
(setq twittering-use-master-password t
|
||||
twittering-icon-mode nil
|
||||
(setq twittering-private-info-file (expand-file-name "twittering-mode.gpg" doom-etc-dir)
|
||||
twittering-use-master-password t
|
||||
twittering-request-confirmation-on-posting t
|
||||
;; twittering-icon-mode t
|
||||
;; twittering-use-icon-storage t
|
||||
;; twittering-icon-storage-file (concat doom-cache-dir "twittering-mode-icons.gz")
|
||||
;; twittering-convert-fix-size 12
|
||||
twittering-timeline-header ""
|
||||
twittering-timeline-footer ""
|
||||
twittering-edit-skeleton 'inherit-any
|
||||
twittering-status-format
|
||||
"%RT{%FACE[bold]{RT }}%S (%FACE[bold]{@%s}), %@%r%R:\n%FOLD[ ]{%t %QT{\n+----\n%FOLD[|]{ %S (@%s), %@:\n%FOLD[ ]{%t}}\n+----}}\n "
|
||||
twittering-status-format "%FACE[font-lock-function-name-face]{ @%s} %FACE[italic]{%@} %FACE[error]{%FIELD-IF-NONZERO[❤ %d]{favorite_count}} %FACE[warning]{%FIELD-IF-NONZERO[↺ %d]{retweet_count}}
|
||||
%FOLD[ ]{%FILL{%t}%QT{
|
||||
%FOLD[ ]{%FACE[font-lock-function-name-face]{@%s}\t%FACE[shadow]{%@}
|
||||
%FOLD[ ]{%FILL{%t}}
|
||||
}}}
|
||||
|
||||
%FACE[twitter-divider]{ }
|
||||
"
|
||||
;; twittering-timeline-spec-alias '()
|
||||
twittering-initial-timeline-spec-string
|
||||
'(":home" ":mentions" ":direct_messages"))
|
||||
|
||||
(set! :popup "*twittering-edit*" :size 12 :select t)
|
||||
(set-popup-rule! "^\\*twittering-edit" :size 15 :ttl nil :quit nil :select t)
|
||||
|
||||
(add-hook! twittering-mode
|
||||
(setq header-line-format (or (doom-modeline 'twitter) mode-line-format)
|
||||
(defface twitter-divider
|
||||
'((((background dark)) (:underline (:color "#141519")))
|
||||
(((background light)) (:underline (:color "#d3d3d3"))))
|
||||
"The vertical divider between tweets."
|
||||
:group 'twittering-mode)
|
||||
|
||||
(add-hook 'doom-real-buffer-functions #'+twitter-buffer-p)
|
||||
(when (featurep! :ui popup)
|
||||
(setq twittering-pop-to-buffer-function #'+twitter-display-buffer))
|
||||
|
||||
(after! solaire-mode
|
||||
(add-hook 'twittering-mode-hook #'solaire-mode))
|
||||
|
||||
;; Custom header-line for twitter buffers
|
||||
(defun +twitter|switch-mode-and-header-line ()
|
||||
(setq header-line-format mode-line-format
|
||||
mode-line-format nil))
|
||||
(add-hook 'twittering-mode-hook #'+twitter|switch-mode-and-header-line)
|
||||
|
||||
(map! :map twittering-mode-map
|
||||
[remap twittering-kill-buffer] #'+twitter/quit
|
||||
"Q" #'+twitter/quit-all
|
||||
"o" #'ace-link-addr
|
||||
"j" #'evil-next-visual-line
|
||||
"k" #'evil-previous-visual-line
|
||||
"J" #'twittering-goto-next-status
|
||||
"K" #'twittering-goto-previous-status)
|
||||
(cond ((featurep! :ui doom-modeline +new)
|
||||
(setq-hook! 'twittering-mode-hook mode-line-format-right nil))
|
||||
((featurep! :ui doom-modeline)
|
||||
(def-modeline! 'twitter
|
||||
'(bar matches " %b " selection-info)
|
||||
'())
|
||||
(add-hook! 'twittering-mode-hook (doom-set-modeline 'twitter))))
|
||||
|
||||
(def-modeline! twitter
|
||||
(bar matches " %b " selection-info)
|
||||
()))
|
||||
;; `epa--decode-coding-string' isn't defined in later versions of Emacs 27
|
||||
(unless (fboundp 'epa--decode-coding-string)
|
||||
(defalias 'epa--decode-coding-string #'decode-coding-string))
|
||||
|
||||
(define-key! twittering-mode-map
|
||||
"q" #'+twitter/quit
|
||||
"Q" #'+twitter/quit-all
|
||||
[remap twittering-kill-buffer] #'+twitter/quit
|
||||
[remap delete-window] #'+twitter/quit
|
||||
[remap +workspace/close-window-or-workspace] #'+twitter/quit)
|
||||
(when (featurep! :feature evil +everywhere)
|
||||
(define-key! twittering-mode-map
|
||||
[remap evil-window-delete] #'+twitter/quit
|
||||
"f" #'twittering-favorite
|
||||
"F" #'twittering-unfavorite
|
||||
"\C-f" #'twittering-follow
|
||||
"\C-F" #'twittering-unfollow
|
||||
"d" #'twittering-delete-status
|
||||
"r" #'twittering-retweet
|
||||
"R" #'twittering-toggle-or-retrieve-replied-statuses
|
||||
"o" #'twittering-update-status-interactive
|
||||
"O" #'+twitter/ace-link
|
||||
"/" #'twittering-search
|
||||
"J" #'twittering-goto-next-status
|
||||
"K" #'twittering-goto-previous-status
|
||||
"g" nil
|
||||
"gg" #'twittering-goto-first-status
|
||||
"G" #'twittering-goto-last-status
|
||||
"gj" #'twittering-goto-next-status-of-user
|
||||
"gk" #'twittering-goto-previous-status-of-user)))
|
||||
|
|
113
modules/app/write/README.org
Normal file
113
modules/app/write/README.org
Normal file
|
@ -0,0 +1,113 @@
|
|||
#+TITLE: :app write
|
||||
|
||||
Adds word processing tools and the ~+write-mode~ minor mode, which converts
|
||||
Emacs into a more comfortable writing environment.
|
||||
|
||||
* Table of Contents :TOC:
|
||||
- [[Features][Features]]
|
||||
- [[~M-x +write-mode~][~M-x +write-mode~]]
|
||||
- [[Language Tool ~+langtool~][Language Tool ~+langtool~]]
|
||||
- [[Wordnut ~+wordnut~][Wordnut ~+wordnut~]]
|
||||
- [[Synosaurus][Synosaurus]]
|
||||
- [[Prerequisites][Prerequisites]]
|
||||
- [[Language Tool][Language Tool]]
|
||||
- [[Wordnut][Wordnut]]
|
||||
- [[Configuration][Configuration]]
|
||||
- [[mixed-pitch-mode][mixed-pitch-mode]]
|
||||
- [[Appendix][Appendix]]
|
||||
- [[Minor modes][Minor modes]]
|
||||
- [[Commands][Commands]]
|
||||
|
||||
* Features
|
||||
This module provides two module flags:
|
||||
|
||||
- ~+langtool~ Enables language tool integration.
|
||||
- ~+wordnut~ Enables wordnet integration.
|
||||
|
||||
** ~M-x +write-mode~
|
||||
Write mode makes Emacs a more comfortable writing environment by:
|
||||
|
||||
- Centering the buffer (with ~visual-fill-column-mode~), ala distraction-free
|
||||
mode from other text editors.
|
||||
- Soft-wrapping long text lines with ~visual-line-mode~.
|
||||
- Enabling ~mixed-pitch-mode~, allowing fixed-width and variable-pitch fonts to
|
||||
co-exist in one buffer. For example, a monospace font for SRC blocks and Arial
|
||||
for everything else.
|
||||
- In org-mode:
|
||||
- Turns on ~org-indent-mode~
|
||||
- Turns on ~+org-pretty-mode~
|
||||
|
||||
** Language Tool ~+langtool~
|
||||
[[https://www.languagetool.org/][Language Tool]] is a polyglot proofreader service that checks for grammar and
|
||||
stylistic issues in your writing. This requires Java 1.8+.
|
||||
|
||||
#+begin_quote
|
||||
This requires Java 1.8+
|
||||
#+end_quote
|
||||
|
||||
*** Commands
|
||||
- ~langtool-check~
|
||||
- ~langtool-correct-buffer~
|
||||
|
||||
** Wordnut ~+wordnut~
|
||||
Wordnut provides a searchable dictionary frontend for Emacs. This requires
|
||||
~wordnet~, which should be available in your OS's package manager.
|
||||
|
||||
*** Commands
|
||||
- ~wordnut-search~
|
||||
- ~wordnut-lookup-curent-word~
|
||||
|
||||
** Synosaurus
|
||||
Synosaurus provides a service for looking up synonyms. It requires an internet
|
||||
connection.
|
||||
|
||||
*** Commands
|
||||
- ~synosaurus-lookup~
|
||||
- ~synosaurus-choose-and-replace~
|
||||
|
||||
* Prerequisites
|
||||
** Language Tool
|
||||
Either download and deploy it from https://languagetool.org/ or install it
|
||||
through your OS package manager:
|
||||
|
||||
#+BEGIN_SRC sh
|
||||
# MacOS/Homebrew users:
|
||||
brew install languagetool
|
||||
|
||||
# Arch Linux users:
|
||||
sudo pacman -S languagetool
|
||||
#+END_SRC
|
||||
|
||||
This module tries to guess the location of languagetool-commandline.jar. If you
|
||||
get a warning that Doom =couldn't find languagetool-commandline.jar=, you will
|
||||
need to find langaugetool-commandline.jar and set ~langtool-language-tool-jar~
|
||||
to its path.
|
||||
|
||||
** Wordnut
|
||||
This requires =wordnet= to be installed, which should be available through your
|
||||
OS package manager:
|
||||
|
||||
#+BEGIN_SRC sh
|
||||
# MacOS/Homebrew users:
|
||||
brew install wordnet
|
||||
|
||||
# Arch Linux users:
|
||||
sudo pacaur -S wordnet # on the AUR
|
||||
#+END_SRC
|
||||
|
||||
* Configuration
|
||||
** mixed-pitch-mode
|
||||
To configure which faces are displayed with fixed-pitch fonts in
|
||||
~mixed-pitch-mode~, look into ~mixed-pitch-fixed-pitch-faces~.
|
||||
|
||||
* Appendix
|
||||
** Minor modes
|
||||
- ~+write-mode~
|
||||
- ~mixed-pitch-mode~
|
||||
** Commands
|
||||
- ~langtool-check~
|
||||
- ~langtool-correct-buffer~
|
||||
- ~synosaurus-choose-and-replace~
|
||||
- ~synosaurus-lookup~
|
||||
- ~wordnut-lookup-curent-word~
|
||||
- ~wordnut-search~
|
|
@ -1,18 +1,43 @@
|
|||
;;; app/write/autoload.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autoload
|
||||
(define-minor-mode +write-mode
|
||||
"TODO"
|
||||
:init-value nil
|
||||
:keymap nil
|
||||
(let ((arg (if +write-mode +1 -1))
|
||||
(iarg (if +write-mode -1 +1)))
|
||||
(text-scale-set (if +write-mode 2 0))
|
||||
(doom/toggle-line-numbers iarg)
|
||||
(setq-local visual-fill-column-center-text +write-mode)
|
||||
(visual-fill-column-mode arg)
|
||||
(visual-line-mode arg)
|
||||
(when (eq major-mode 'org-mode)
|
||||
(+org-pretty-mode arg))
|
||||
(setq line-spacing (if +write-mode 4))))
|
||||
(defvar +write-mode-map (make-sparse-keymap)
|
||||
"TODO")
|
||||
|
||||
;;;###autoload
|
||||
(define-minor-mode +write-mode
|
||||
"Turns Emacs into a more comfortable writing environment and word processor."
|
||||
:init-value nil
|
||||
:keymap +write-mode-map
|
||||
(setq-local visual-fill-column-center-text t)
|
||||
(when +write-text-scale
|
||||
(text-scale-set (if +write-mode 2 0)))
|
||||
(when +write-line-spacing
|
||||
(setq-local line-spacing +write-line-spacing)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +write|init-org-mode ()
|
||||
"Initializes `org-mode' specific settings for `+write-mode'."
|
||||
(when (eq major-mode 'org-mode)
|
||||
(+org-pretty-mode (if +write-mode +1 -1))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +write|init-line-numbers ()
|
||||
(display-line-numbers-mode (if +write-mode +1 -1)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +write|init-mixed-pitch ()
|
||||
(mixed-pitch-mode (if +write-mode +1 -1)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +write|init-visual-fill-column ()
|
||||
(visual-fill-column-mode (if +write-mode +1 -1)))
|
||||
|
||||
;;;###autoload
|
||||
(add-hook! '+write-mode-hook
|
||||
#'(flyspell-mode
|
||||
visual-line-mode
|
||||
+write|init-mixed-pitch
|
||||
+write|init-visual-fill-column
|
||||
+write|init-line-numbers
|
||||
+write|init-org-mode))
|
||||
|
|
56
modules/app/write/config.el
Normal file
56
modules/app/write/config.el
Normal file
|
@ -0,0 +1,56 @@
|
|||
;;; app/write/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defvar +write-text-scale nil
|
||||
"What to scale the text up to in `+write-mode'. Uses `text-scale-set'.")
|
||||
|
||||
(defvar +write-line-spacing nil
|
||||
"What to set `line-spacing' in `+write-mode'.")
|
||||
|
||||
;;
|
||||
;; Packages
|
||||
|
||||
(def-package! langtool
|
||||
:when (featurep! +langtool)
|
||||
:commands (langtool-check
|
||||
langtool-check-done
|
||||
langtool-show-message-at-point
|
||||
langtool-correct-buffer)
|
||||
:init (setq langtool-default-language "en-US")
|
||||
:config
|
||||
(unless langtool-language-tool-jar
|
||||
(setq langtool-language-tool-jar
|
||||
(cond (IS-MAC
|
||||
(locate-file "libexec/languagetool-commandline.jar"
|
||||
(doom-files-in "/usr/local/Cellar/languagetool"
|
||||
:type 'dirs
|
||||
:depth 1)))
|
||||
(IS-LINUX
|
||||
"/usr/share/java/languagetool/languagetool-commandline.jar")))))
|
||||
|
||||
|
||||
;; `synosaurus'
|
||||
(setq synosaurus-choose-method 'default)
|
||||
|
||||
|
||||
;; `mixed-pitch'
|
||||
(after! mixed-pitch
|
||||
(setq mixed-pitch-fixed-pitch-faces
|
||||
(append mixed-pitch-fixed-pitch-faces
|
||||
'(org-todo-keyword-todo
|
||||
org-todo-keyword-habt
|
||||
org-todo-keyword-done
|
||||
org-todo-keyword-wait
|
||||
org-todo-keyword-kill
|
||||
org-todo-keyword-outd
|
||||
org-todo
|
||||
org-indent
|
||||
line-number
|
||||
line-number-current-line
|
||||
org-special-keyword
|
||||
org-date
|
||||
org-property-value
|
||||
org-special-keyword
|
||||
org-property-value
|
||||
org-ref-cite-face
|
||||
org-tag
|
||||
font-lock-comment-face))))
|
7
modules/app/write/doctor.el
Normal file
7
modules/app/write/doctor.el
Normal file
|
@ -0,0 +1,7 @@
|
|||
;; -*- lexical-binding: t; no-byte-compile: t; -*-
|
||||
;;; app/write/doctor.el
|
||||
|
||||
(when (featurep! +langtool)
|
||||
(require 'langtool)
|
||||
(unless (file-exists-p langtool-language-tool-jar)
|
||||
(warn! "Couldn't find languagetool-commandline.jar")))
|
11
modules/app/write/packages.el
Normal file
11
modules/app/write/packages.el
Normal file
|
@ -0,0 +1,11 @@
|
|||
;; -*- no-byte-compile: t; -*-
|
||||
;;; app/write/packages.el
|
||||
|
||||
(package! synosaurus)
|
||||
(package! mixed-pitch)
|
||||
|
||||
(when (featurep! +langtool)
|
||||
(package! langtool))
|
||||
(when (featurep! +wordnut)
|
||||
(package! wordnut))
|
||||
|
4
modules/collab/floobits/packages.el
Normal file
4
modules/collab/floobits/packages.el
Normal file
|
@ -0,0 +1,4 @@
|
|||
;; -*- no-byte-compile: t; -*-
|
||||
;;; collab/foobits/packages.el
|
||||
|
||||
(package! floobits)
|
|
@ -1,10 +1,9 @@
|
|||
;;; tools/impatient-mode/autoload.el -*- lexical-binding: t; -*-
|
||||
;;; collab/impatient-mode/autoload.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autoload
|
||||
(defun +impatient-mode/toggle ()
|
||||
"TODO"
|
||||
"Toggle `impatient-mode' in the current buffer."
|
||||
(interactive)
|
||||
(require 'simple-httpd)
|
||||
(unless (process-status "httpd")
|
||||
(httpd-start))
|
||||
(impatient-mode)
|
|
@ -1,5 +1,5 @@
|
|||
;; -*- no-byte-compile: t; -*-
|
||||
;;; tools/impatient-mode/packages.el
|
||||
;;; collab/impatient-mode/packages.el
|
||||
|
||||
(package! htmlize)
|
||||
(package! impatient-mode)
|
|
@ -1,48 +1,150 @@
|
|||
#+TITLE: :completion company
|
||||
#+TITLE: completion/company
|
||||
#+DATE: February 19, 2017
|
||||
#+SINCE: v2.0
|
||||
#+STARTUP: inlineimages
|
||||
|
||||
This module adds code-completion support, powered by [[https://github.com/company-mode/company-mode][company]].
|
||||
* Table of Contents :TOC_3:noexport:
|
||||
- [[Description][Description]]
|
||||
- [[Module Flags][Module Flags]]
|
||||
- [[Plugins][Plugins]]
|
||||
- [[Prerequisites][Prerequisites]]
|
||||
- [[Features][Features]]
|
||||
- [[Code completion][Code completion]]
|
||||
- [[Vim-esque omni-completion prefix (C-x)][Vim-esque omni-completion prefix (C-x)]]
|
||||
- [[Configuration][Configuration]]
|
||||
- [[Enable as-you-type code completion][Enable as-you-type code completion]]
|
||||
- [[Enable company backend(s) in certain modes][Enable company backend(s) in certain modes]]
|
||||
- [[Troubleshooting][Troubleshooting]]
|
||||
- [[Code-completion doesn't pop up automatically.][Code-completion doesn't pop up automatically.]]
|
||||
- [[X-mode doesn't have code completion support or requires extra setup.][X-mode doesn't have code completion support or requires extra setup.]]
|
||||
- [[No backends (or the incorrect ones) have been registered for X-mode.][No backends (or the incorrect ones) have been registered for X-mode.]]
|
||||
|
||||
+ Uses ~company-quickhelp~ for documentation tooltips
|
||||
+ Uses ~company-statistics~ to order results by usage frequency
|
||||
* Description
|
||||
This module provides code completion, powered by [[https://github.com/company-mode/company-mode][company-mode]]. It is required
|
||||
for code completion in many of Doom's :lang modules.
|
||||
|
||||
[[/../screenshots/company.png]]
|
||||
https://assets.doomemacs.org/completion/company/overlay.png
|
||||
|
||||
* Table of Contents :TOC:
|
||||
- [[#install][Install]]
|
||||
- [[#configure][Configure]]
|
||||
- [[#auto-completion][Auto-completion]]
|
||||
- [[#troubleshooting][Troubleshooting]]
|
||||
** Module Flags
|
||||
+ =+auto= Enables as-you-type completion.
|
||||
+ =+childframe= Enables displaying completion candidates in a child frame,
|
||||
rather than an overlay or tooltip (among with other UI enhancements). *This
|
||||
requires GUI Emacs 26.1+.*
|
||||
+ =+tng= Enables completion using only ~TAB~. Pressing ~TAB~ will select the
|
||||
next completion suggestion, while ~S-TAB~ will select the previous one.
|
||||
|
||||
* Install
|
||||
Some languages require additional setup, and some languages may have no
|
||||
completion support at all.
|
||||
** Plugins
|
||||
+ [[https://github.com/company-mode/company-mode][company-mode]]
|
||||
+ [[https://github.com/hlissner/emacs-company-dict][company-dict]]
|
||||
+ [[https://github.com/raxod502/prescient.el][company-prescient]]
|
||||
+ [[https://github.com/sebastiencs/company-box][company-box]]
|
||||
|
||||
Check the README.org in that language's module for details.
|
||||
* Prerequisites
|
||||
This module has no direct prerequisites.
|
||||
|
||||
* Configure
|
||||
** Auto-completion
|
||||
By default, I've disabled auto-completion. This is my preference. I prefer to
|
||||
invoke company when I need it by calling ~company-complete~ manually (typically,
|
||||
bound to =C-SPC= in insert mode). However, some may not share my preference.
|
||||
However, some major modes may require additional setup for code completion to
|
||||
work in them. Some major modes may have no completion support at all. Check that
|
||||
major mode's module's documentation for details.
|
||||
|
||||
To enable auto-completion you must:
|
||||
* Features
|
||||
** Code completion
|
||||
Ccompletion must be triggered manually with the =C-SPC= key. If you want
|
||||
as-you-type code completion, the ~+auto~ module flag will enable it.
|
||||
|
||||
1. Load ~company~,
|
||||
2. and change ~company-idle-delay~ to a non-nil float (the default is 0.5)
|
||||
| Keybind | Description |
|
||||
|---------+------------------------------------------|
|
||||
| =C-SPC= | Invoke code completion manually |
|
||||
| =C-n= | Go to next candidate |
|
||||
| =C-p= | Go to previous candidate |
|
||||
| =C-j= | (evil) Go to next candidate |
|
||||
| =C-k= | (evil) Go to previous candidate |
|
||||
| =C-h= | Display documentation (if available) |
|
||||
| =C-u= | Move to previous page of candidates |
|
||||
| =C-d= | Move to next page of candidates |
|
||||
| =C-s= | Filter candidates |
|
||||
| =C-S-s= | Search candidates with helm/ivy |
|
||||
| =C-SPC= | Complete common |
|
||||
| =TAB= | Complete common or select next candidate |
|
||||
| =S-TAB= | Select previous candidate |
|
||||
|
||||
For example:
|
||||
** Vim-esque omni-completion prefix (C-x)
|
||||
In the spirit of Vim's omni-completion, the following insert mode keybinds are
|
||||
available to evil users to access specific company backends:
|
||||
|
||||
| Keybind | Description |
|
||||
|-----------+-----------------------------------|
|
||||
| =C-x C-]= | Complete etags |
|
||||
| =C-x C-f= | Complete file path |
|
||||
| =C-x C-k= | Complete from dictionary/keyword |
|
||||
| =C-x C-l= | Complete full line |
|
||||
| =C-x C-o= | Invoke complete-at-point function |
|
||||
| =C-x C-n= | Complete next symbol at point |
|
||||
| =C-x C-p= | Complete previous symbol at point |
|
||||
| =C-x C-s= | Complete snippet |
|
||||
| =C-x s= | Complete spelling suggestions |
|
||||
|
||||
* Configuration
|
||||
** Enable as-you-type code completion
|
||||
The =+auto= module flag enables this. You may customize ~company-idle-delay~ to
|
||||
control how quickly the popup should appear.
|
||||
|
||||
The ~+company/toggle-auto-completion~ command is also available to toggle this
|
||||
interactively.
|
||||
|
||||
** Enable company backend(s) in certain modes
|
||||
The ~set-company-backend!~ function exists for setting ~company-backends~
|
||||
buffer-locally in MODES, which is either a major-mode symbol, a minor-mode
|
||||
symbol, or a list of either. BACKENDS are prepended to ~company-backends~ for
|
||||
those modes.
|
||||
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
(require 'company)
|
||||
(setq company-idle-delay 0.2
|
||||
company-minimum-prefix-length 3)
|
||||
(after! js2-mode
|
||||
(set-company-backend! 'js2-mode 'company-tide 'company-yasnippet))
|
||||
|
||||
(after! sh-script
|
||||
(set-company-backend! 'sh-mode
|
||||
'(company-shell :with company-yasnippet)))
|
||||
|
||||
(after! cc-mode
|
||||
(set-company-backend! 'c-mode
|
||||
'(:separate company-irony-c-headers company-irony)))
|
||||
#+END_SRC
|
||||
|
||||
To unset the backends for a particular mode, pass ~nil~ to it:
|
||||
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
(after! sh-script
|
||||
(set-company-backend! 'sh-mode nil))
|
||||
#+END_SRC
|
||||
|
||||
* Troubleshooting
|
||||
If completion isn't working for you, please consider the following before
|
||||
posting a bug report:
|
||||
If code completion isn't working for you, consider the following common causes
|
||||
before you file a bug report:
|
||||
|
||||
+ If what you are expecting is popup-as-you-type completion (which is disabled
|
||||
by default), see the "Configure > Auto-completion" section above, which will
|
||||
instruct you on how to enable this.
|
||||
+ Some languages don't have any auto-completion support at all.
|
||||
** Code-completion doesn't pop up automatically.
|
||||
This is by design. The expectation is that you invoke completion manually with
|
||||
=C-SPC=. This was decided because code-completion backends can be slow, some
|
||||
dreadfully so, and invoking them every time you move your cursor can add pauses
|
||||
and delays while editing.
|
||||
|
||||
If, despite that, you still want this functionality, use the =+auto= flag to
|
||||
enable it.
|
||||
|
||||
** X-mode doesn't have code completion support or requires extra setup.
|
||||
There is no guarantee your language mode will have completion support.
|
||||
|
||||
Some, like ~lua-mode~, don't have completion support in Emacs at all. Others may
|
||||
requires additional setup to get code completion working. For instance,
|
||||
~go-mode~ requires ~guru~ to be installed on your system, and ~enh-ruby-mode~
|
||||
requires that you have a Robe server running (~M-x robe-start~).
|
||||
|
||||
Check the relevant module's documentation for this kind of information.
|
||||
|
||||
** No backends (or the incorrect ones) have been registered for X-mode.
|
||||
Doom expects every mode to have an explicit list of company-backends (and as
|
||||
short a list as possible). This may mean you aren't getting all the completion
|
||||
you want or any at all.
|
||||
|
||||
Check the value of ~company-backends~ (=SPC h v company-backends=) from that
|
||||
mode to see what backends are available. Check the [[*Assigning company backend(s) to modes][Configuration section]] for
|
||||
details on changing what backends are available for that mode.
|
||||
|
|
|
@ -1,14 +1,123 @@
|
|||
;;; completion/company/autoload.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autoload
|
||||
(defvar +company-backend-alist
|
||||
'((text-mode :derived (company-dabbrev company-yasnippet company-ispell))
|
||||
(prog-mode :derived (:separate company-capf company-yasnippet))
|
||||
(conf-mode :derived company-capf company-dabbrev-code company-yasnippet))
|
||||
"An alist matching modes to company backends. The backends for any mode is
|
||||
built from this.")
|
||||
|
||||
;;;###autodef
|
||||
(defun set-company-backend! (modes &rest backends)
|
||||
"Prepends BACKENDS (in order) to `company-backends' in MODES.
|
||||
|
||||
MODES should be one symbol or a list of them, representing major or minor modes.
|
||||
This will overwrite backends for MODES on consecutive uses.
|
||||
|
||||
If the car of BACKENDS is nil, unset the backends for MODES.
|
||||
|
||||
Examples:
|
||||
|
||||
(set-company-backend! 'js2-mode
|
||||
'company-tide 'company-yasnippet)
|
||||
|
||||
(set-company-backend! 'sh-mode
|
||||
'(company-shell :with company-yasnippet))
|
||||
|
||||
(set-company-backend! '(c-mode c++-mode)
|
||||
'(:separate company-irony-c-headers company-irony))
|
||||
|
||||
(set-company-backend! 'sh-mode nil) ; unsets backends for sh-mode
|
||||
|
||||
To have BACKENDS apply to any mode that is a parent of MODES, set MODES to
|
||||
:derived, e.g.
|
||||
|
||||
(set-company-backend! :derived 'text-mode 'company-dabbrev 'company-yasnippet)"
|
||||
(declare (indent defun))
|
||||
(let ((type :exact))
|
||||
(when (eq modes :derived)
|
||||
(setq type :derived
|
||||
modes (pop backends)))
|
||||
(dolist (mode (doom-enlist modes))
|
||||
(if (null (car backends))
|
||||
(setq +company-backend-alist
|
||||
(delq (assq mode +company-backend-alist)
|
||||
+company-backend-alist))
|
||||
(setf (alist-get mode +company-backend-alist)
|
||||
(cons type backends))))))
|
||||
|
||||
|
||||
;;
|
||||
;;; Library
|
||||
|
||||
(defun +company--backends ()
|
||||
(append (cl-loop for (mode . rest) in +company-backend-alist
|
||||
for type = (car rest)
|
||||
for backends = (cdr rest)
|
||||
if (or (and (eq type :derived) (derived-mode-p mode)) ; parent modes
|
||||
(and (eq type :exact)
|
||||
(or (eq major-mode mode) ; major modes
|
||||
(and (boundp mode)
|
||||
(symbol-value mode))))) ; minor modes
|
||||
append backends)
|
||||
(default-value 'company-backends)))
|
||||
|
||||
|
||||
;;
|
||||
;;; Hooks
|
||||
|
||||
;;;###autoload
|
||||
(defun +company|init-backends ()
|
||||
"Set `company-backends' for the current buffer."
|
||||
(unless (eq major-mode 'fundamental-mode)
|
||||
(set (make-local-variable 'company-backends) (+company--backends)))
|
||||
(add-hook 'after-change-major-mode-hook #'+company|init-backends nil 'local))
|
||||
|
||||
(put '+company|init-backends 'permanent-local-hook t)
|
||||
|
||||
|
||||
;;
|
||||
;;; Commands
|
||||
|
||||
;;;###autoload
|
||||
(defun +company-has-completion-p ()
|
||||
"Return non-nil if a completion candidate exists at point."
|
||||
(and (company-manual-begin)
|
||||
(= company-candidates-length 1)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +company/toggle-auto-completion ()
|
||||
"Toggle as-you-type code completion."
|
||||
(interactive)
|
||||
(require 'company)
|
||||
(setq company-idle-delay (unless company-idle-delay 0.2))
|
||||
(message "Auto completion %s"
|
||||
(if company-idle-delay "enabled" "disabled")))
|
||||
|
||||
;;;###autoload
|
||||
(defun +company/complete ()
|
||||
"Bring up the completion popup. If only one result, complete it."
|
||||
(interactive)
|
||||
(require 'company)
|
||||
(when (ignore-errors
|
||||
(/= (point)
|
||||
(cdr (bounds-of-thing-at-point 'symbol))))
|
||||
(save-excursion (insert " ")))
|
||||
(when (and (company-manual-begin)
|
||||
(= company-candidates-length 1))
|
||||
(company-complete-common)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +company/dabbrev ()
|
||||
"Invokes `company-dabbrev-code' in prog-mode buffers and `company-dabbrev'
|
||||
everywhere else."
|
||||
(interactive)
|
||||
(call-interactively
|
||||
(if (derived-mode-p 'prog-mode)
|
||||
#'company-dabbrev-code
|
||||
#'company-dabbrev)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +company/whole-lines (command &optional arg &rest ignored)
|
||||
"`company-mode' completion backend that completes whole-lines, akin to vim's
|
||||
|
@ -16,9 +125,9 @@ C-x C-l."
|
|||
(interactive (list 'interactive))
|
||||
(require 'company)
|
||||
(pcase command
|
||||
('interactive (company-begin-backend '+company/whole-lines))
|
||||
('prefix (company-grab-line "^[\t\s]*\\(.+\\)" 1))
|
||||
('candidates
|
||||
(`interactive (company-begin-backend '+company/whole-lines))
|
||||
(`prefix (company-grab-line "^[\t\s]*\\(.+\\)" 1))
|
||||
(`candidates
|
||||
(all-completions
|
||||
arg
|
||||
(split-string
|
||||
|
@ -35,12 +144,13 @@ C-x C-l."
|
|||
(require 'company-dict)
|
||||
(require 'company-keywords)
|
||||
(let ((company-backends '((company-keywords company-dict))))
|
||||
(call-interactively 'company-complete)))
|
||||
(call-interactively #'company-complete)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +company/dabbrev-code-previous ()
|
||||
"TODO"
|
||||
(interactive)
|
||||
(require 'company-dabbrev)
|
||||
(let ((company-selection-wrap-around t))
|
||||
(call-interactively #'company-dabbrev-code)
|
||||
(call-interactively #'+company/dabbrev)
|
||||
(company-select-previous-or-abort)))
|
||||
|
|
|
@ -1,86 +1,124 @@
|
|||
;;; completion/company/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
(def-setting! :company-backend (modes &rest backends)
|
||||
"Prepends BACKENDS to `company-backends' in major MODES.
|
||||
|
||||
MODES should be one major-mode symbol or a list of them."
|
||||
`(progn
|
||||
,@(cl-loop for mode in (doom-enlist (doom-unquote modes))
|
||||
for def-name = (intern (format "doom--init-company-%s" mode))
|
||||
collect
|
||||
`(defun ,def-name ()
|
||||
(when (and (eq major-mode ',mode)
|
||||
,(not (eq backends '(nil))))
|
||||
(require 'company)
|
||||
(make-variable-buffer-local 'company-backends)
|
||||
(dolist (backend (list ,@(reverse backends)))
|
||||
(cl-pushnew backend company-backends :test #'equal))))
|
||||
collect `(add-hook! ,mode #',def-name))))
|
||||
|
||||
|
||||
;;
|
||||
;; Packages
|
||||
;;
|
||||
|
||||
(def-package! company
|
||||
:commands (company-mode global-company-mode company-complete
|
||||
company-complete-common company-manual-begin company-grab-line)
|
||||
:config
|
||||
:commands (company-complete-common company-manual-begin company-grab-line)
|
||||
:init
|
||||
(setq company-idle-delay nil
|
||||
company-tooltip-limit 10
|
||||
company-tooltip-limit 14
|
||||
company-dabbrev-downcase nil
|
||||
company-dabbrev-ignore-case nil
|
||||
company-dabbrev-code-other-buffers t
|
||||
company-tooltip-align-annotations t
|
||||
company-require-match 'never
|
||||
company-global-modes '(not eshell-mode comint-mode erc-mode message-mode help-mode gud-mode)
|
||||
company-frontends '(company-pseudo-tooltip-frontend company-echo-metadata-frontend)
|
||||
company-backends '(company-capf company-dabbrev company-ispell)
|
||||
company-transformers '(company-sort-by-occurrence))
|
||||
|
||||
(after! yasnippet
|
||||
(nconc company-backends '(company-yasnippet)))
|
||||
|
||||
company-global-modes
|
||||
'(not erc-mode message-mode help-mode gud-mode eshell-mode)
|
||||
company-backends '(company-capf)
|
||||
company-frontends
|
||||
'(company-pseudo-tooltip-frontend
|
||||
company-echo-metadata-frontend))
|
||||
:config
|
||||
(add-hook 'company-mode-hook #'+company|init-backends)
|
||||
(when (featurep! :feature evil)
|
||||
(add-hook 'company-mode-hook #'evil-normalize-keymaps))
|
||||
(global-company-mode +1))
|
||||
|
||||
|
||||
(def-package! company-statistics
|
||||
:after company
|
||||
:config
|
||||
(setq company-statistics-file (concat doom-cache-dir "company-stats-cache.el"))
|
||||
(quiet! (company-statistics-mode +1)))
|
||||
(def-package! company
|
||||
:when (featurep! +auto)
|
||||
:defer 2
|
||||
:after-call post-self-insert-hook
|
||||
:config (setq company-idle-delay 0.1))
|
||||
|
||||
|
||||
;; Looks ugly on OSX without emacs-mac build
|
||||
(def-package! company-quickhelp
|
||||
:after company
|
||||
(def-package! company-tng
|
||||
:when (featurep! +tng)
|
||||
:defer 2
|
||||
:after-call post-self-insert-hook
|
||||
:config
|
||||
(setq company-quickhelp-delay nil)
|
||||
(company-quickhelp-mode +1))
|
||||
(add-to-list 'company-frontends 'company-tng-frontend)
|
||||
(define-key! company-active-map
|
||||
"RET" nil
|
||||
[return] nil
|
||||
"TAB" #'company-select-next
|
||||
[tab] #'company-select-next
|
||||
[backtab] #'company-select-previous))
|
||||
|
||||
|
||||
;;
|
||||
;; Packages
|
||||
|
||||
(def-package! company-prescient
|
||||
:hook (company-mode . company-prescient-mode)
|
||||
:config
|
||||
(setq prescient-save-file (concat doom-cache-dir "prescient-save.el"))
|
||||
(prescient-persist-mode +1))
|
||||
|
||||
|
||||
(def-package! company-box
|
||||
:when (and EMACS26+ (featurep! +childframe))
|
||||
:hook (company-mode . company-box-mode)
|
||||
:config
|
||||
(setq company-box-show-single-candidate t
|
||||
company-box-backends-colors nil
|
||||
company-box-max-candidates 50
|
||||
company-box-icons-alist 'company-box-icons-all-the-icons
|
||||
company-box-icons-functions
|
||||
'(+company-box-icons--yasnippet company-box-icons--lsp +company-box-icons--elisp company-box-icons--acphp)
|
||||
company-box-icons-all-the-icons
|
||||
`((Unknown . ,(all-the-icons-material "find_in_page" :height 0.8 :face 'all-the-icons-purple))
|
||||
(Text . ,(all-the-icons-material "text_fields" :height 0.8 :face 'all-the-icons-green))
|
||||
(Method . ,(all-the-icons-material "functions" :height 0.8 :face 'all-the-icons-red))
|
||||
(Function . ,(all-the-icons-material "functions" :height 0.8 :face 'all-the-icons-red))
|
||||
(Constructor . ,(all-the-icons-material "functions" :height 0.8 :face 'all-the-icons-red))
|
||||
(Field . ,(all-the-icons-material "functions" :height 0.8 :face 'all-the-icons-red))
|
||||
(Variable . ,(all-the-icons-material "adjust" :height 0.8 :face 'all-the-icons-blue))
|
||||
(Class . ,(all-the-icons-material "class" :height 0.8 :face 'all-the-icons-red))
|
||||
(Interface . ,(all-the-icons-material "settings_input_component" :height 0.8 :face 'all-the-icons-red))
|
||||
(Module . ,(all-the-icons-material "view_module" :height 0.8 :face 'all-the-icons-red))
|
||||
(Property . ,(all-the-icons-material "settings" :height 0.8 :face 'all-the-icons-red))
|
||||
(Unit . ,(all-the-icons-material "straighten" :height 0.8 :face 'all-the-icons-red))
|
||||
(Value . ,(all-the-icons-material "filter_1" :height 0.8 :face 'all-the-icons-red))
|
||||
(Enum . ,(all-the-icons-material "plus_one" :height 0.8 :face 'all-the-icons-red))
|
||||
(Keyword . ,(all-the-icons-material "filter_center_focus" :height 0.8 :face 'all-the-icons-red))
|
||||
(Snippet . ,(all-the-icons-material "short_text" :height 0.8 :face 'all-the-icons-red))
|
||||
(Color . ,(all-the-icons-material "color_lens" :height 0.8 :face 'all-the-icons-red))
|
||||
(File . ,(all-the-icons-material "insert_drive_file" :height 0.8 :face 'all-the-icons-red))
|
||||
(Reference . ,(all-the-icons-material "collections_bookmark" :height 0.8 :face 'all-the-icons-red))
|
||||
(Folder . ,(all-the-icons-material "folder" :height 0.8 :face 'all-the-icons-red))
|
||||
(EnumMember . ,(all-the-icons-material "people" :height 0.8 :face 'all-the-icons-red))
|
||||
(Constant . ,(all-the-icons-material "pause_circle_filled" :height 0.8 :face 'all-the-icons-red))
|
||||
(Struct . ,(all-the-icons-material "streetview" :height 0.8 :face 'all-the-icons-red))
|
||||
(Event . ,(all-the-icons-material "event" :height 0.8 :face 'all-the-icons-red))
|
||||
(Operator . ,(all-the-icons-material "control_point" :height 0.8 :face 'all-the-icons-red))
|
||||
(TypeParameter . ,(all-the-icons-material "class" :height 0.8 :face 'all-the-icons-red))
|
||||
;; (Template . ,(company-box-icons-image "Template.png"))))
|
||||
(Yasnippet . ,(all-the-icons-material "short_text" :height 0.8 :face 'all-the-icons-green))
|
||||
(ElispFunction . ,(all-the-icons-material "functions" :height 0.8 :face 'all-the-icons-red))
|
||||
(ElispVariable . ,(all-the-icons-material "check_circle" :height 0.8 :face 'all-the-icons-blue))
|
||||
(ElispFeature . ,(all-the-icons-material "stars" :height 0.8 :face 'all-the-icons-orange))
|
||||
(ElispFace . ,(all-the-icons-material "format_paint" :height 0.8 :face 'all-the-icons-pink))))
|
||||
|
||||
(defun +company-box-icons--yasnippet (candidate)
|
||||
(when (get-text-property 0 'yas-annotation candidate)
|
||||
'Yasnippet))
|
||||
|
||||
(defun +company-box-icons--elisp (candidate)
|
||||
(when (derived-mode-p 'emacs-lisp-mode)
|
||||
(let ((sym (intern candidate)))
|
||||
(cond ((fboundp sym) 'ElispFunction)
|
||||
((boundp sym) 'ElispVariable)
|
||||
((featurep sym) 'ElispFeature)
|
||||
((facep sym) 'ElispFace))))))
|
||||
|
||||
|
||||
(def-package! company-dict
|
||||
:commands company-dict
|
||||
:defer t
|
||||
:config
|
||||
(setq company-dict-dir (expand-file-name "dicts" doom-private-dir))
|
||||
(defun +company|enable-project-dicts (mode &rest _)
|
||||
"Enable per-project dictionaries."
|
||||
(if (symbol-value mode)
|
||||
(cl-pushnew mode company-dict-minor-mode-list :test #'eq)
|
||||
(add-to-list 'company-dict-minor-mode-list mode nil #'eq)
|
||||
(setq company-dict-minor-mode-list (delq mode company-dict-minor-mode-list))))
|
||||
(add-hook 'doom-project-hook #'+company|enable-project-dicts))
|
||||
|
||||
|
||||
;;
|
||||
;; Autoloads
|
||||
;;
|
||||
|
||||
(autoload 'company-capf "company-capf")
|
||||
(autoload 'company-yasnippet "company-yasnippet")
|
||||
(autoload 'company-dabbrev "company-dabbrev")
|
||||
(autoload 'company-dabbrev-code "company-dabbrev-code")
|
||||
(autoload 'company-etags "company-etags")
|
||||
(autoload 'company-elisp "company-elisp")
|
||||
(autoload 'company-files "company-files")
|
||||
(autoload 'company-gtags "company-gtags")
|
||||
(autoload 'company-ispell "company-ispell")
|
||||
|
||||
|
|
|
@ -3,5 +3,6 @@
|
|||
|
||||
(package! company)
|
||||
(package! company-dict)
|
||||
(package! company-quickhelp)
|
||||
(package! company-statistics)
|
||||
(package! company-prescient)
|
||||
(when (and EMACS26+ (featurep! +childframe))
|
||||
(package! company-box))
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
;; -*- lexical-binding: t; no-byte-compile: t; -*-
|
||||
;;; completion/company/test/company.el
|
||||
|
||||
(require! :completion company)
|
||||
(require 'company)
|
||||
|
||||
;;
|
||||
(def-test! set-company-backend
|
||||
:minor-mode company-mode
|
||||
(let ((company-backends '(default)))
|
||||
(set! :company-backend 'emacs-lisp-mode '(backend-1))
|
||||
(set! :company-backend 'lisp-interaction-mode 'backend-1 'backend-2)
|
||||
(set! :company-backend 'text-mode 'backend-1)
|
||||
(with-temp-buffer
|
||||
(emacs-lisp-mode)
|
||||
(should (equal company-backends '((backend-1) default))))
|
||||
(with-temp-buffer
|
||||
(lisp-interaction-mode)
|
||||
(should (equal company-backends '(backend-1 backend-2 default))))
|
||||
(with-temp-buffer
|
||||
(text-mode)
|
||||
(should (equal company-backends '(backend-1 default))))
|
||||
;; global backends shouldn't be affected
|
||||
(should (equal company-backends '(default)))))
|
75
modules/completion/company/test/test-company.el
Normal file
75
modules/completion/company/test/test-company.el
Normal file
|
@ -0,0 +1,75 @@
|
|||
;; -*- lexical-binding: t; no-byte-compile: t; -*-
|
||||
;;; completion/company/test/test-company.el
|
||||
|
||||
(describe "completion/company"
|
||||
(before-all
|
||||
(load! "../autoload"))
|
||||
|
||||
(describe ":company-backend"
|
||||
:var (a +company-backend-alist backends)
|
||||
(before-each
|
||||
(setq-default company-backends '(t))
|
||||
(setq +company-backend-alist nil
|
||||
a (get-buffer-create "x"))
|
||||
(fset 'backends
|
||||
(lambda (mode)
|
||||
(let ((major-mode mode))
|
||||
(+company--backends))))
|
||||
(set-buffer a)
|
||||
(spy-on 'require))
|
||||
(after-each
|
||||
(kill-buffer a))
|
||||
|
||||
;;
|
||||
(it "sets backends for a major mode"
|
||||
(set-company-backend! 'text-mode 'a)
|
||||
(expect (backends 'text-mode) :to-equal '(a t)))
|
||||
|
||||
(it "sets backends for a derived-mode"
|
||||
(set-company-backend! :derived 'prog-mode 'a)
|
||||
(expect (backends 'prog-mode) :to-equal '(a t))
|
||||
(expect (backends 'emacs-lisp-mode) :to-equal '(a t)))
|
||||
|
||||
(it "sets multiple backends for exact major modes"
|
||||
(set-company-backend! '(text-mode emacs-lisp-mode) 'a 'b)
|
||||
(expect (backends 'text-mode) :to-equal (backends 'emacs-lisp-mode)))
|
||||
|
||||
(it "sets cumulative backends"
|
||||
(set-company-backend! :derived 'prog-mode '(a b c))
|
||||
(set-company-backend! 'emacs-lisp-mode 'd 'e)
|
||||
(expect (backends 'emacs-lisp-mode) :to-equal '(d e (a b c) t)))
|
||||
|
||||
(it "sets cumulative backends with a minor mode"
|
||||
(set-company-backend! :derived 'prog-mode '(a b c))
|
||||
(set-company-backend! 'emacs-lisp-mode 'd 'e)
|
||||
(set-company-backend! 'some-minor-mode 'x 'y)
|
||||
(setq-local some-minor-mode t)
|
||||
(expect (backends 'emacs-lisp-mode) :to-equal '(x y d e (a b c) t)))
|
||||
|
||||
(it "overwrites past backends"
|
||||
(set-company-backend! 'text-mode 'old 'backends)
|
||||
(set-company-backend! 'text-mode 'new 'backends)
|
||||
(expect (backends 'text-mode) :to-equal '(new backends t)))
|
||||
|
||||
(it "unsets past backends"
|
||||
(set-company-backend! 'text-mode 'old)
|
||||
(set-company-backend! 'text-mode nil)
|
||||
(expect (backends 'text-mode) :to-equal (default-value 'company-backends)))
|
||||
|
||||
(it "unsets past parent backends"
|
||||
(set-company-backend! :derived 'prog-mode 'old)
|
||||
(set-company-backend! 'emacs-lisp-mode 'child)
|
||||
(set-company-backend! :derived 'prog-mode nil)
|
||||
(expect (backends 'emacs-lisp-mode) :to-equal '(child t)))
|
||||
|
||||
(it "overwrites past cumulative backends"
|
||||
(set-company-backend! :derived 'prog-mode 'base)
|
||||
(set-company-backend! 'emacs-lisp-mode 'old)
|
||||
(set-company-backend! 'emacs-lisp-mode 'new)
|
||||
(expect (backends 'emacs-lisp-mode) :to-equal '(new base t)))
|
||||
|
||||
(it "overwrites past parent backends"
|
||||
(set-company-backend! :derived 'prog-mode 'base)
|
||||
(set-company-backend! 'emacs-lisp-mode 'child)
|
||||
(set-company-backend! :derived 'prog-mode 'new)
|
||||
(expect (backends 'emacs-lisp-mode) :to-equal '(child new t)))))
|
|
@ -1,60 +1,83 @@
|
|||
;;; completion/helm/autoload/evil.el -*- lexical-binding: t; -*-
|
||||
;;;###if (featurep! :feature evil)
|
||||
|
||||
;;;###autoload (autoload '+helm:swoop "completion/helm/autoload/evil" nil t)
|
||||
(evil-define-command +helm:swoop (&optional search bang)
|
||||
"Invoke `swoop' with SEARCH. If BANG, do multiline search."
|
||||
(interactive "<a><!>")
|
||||
(helm-swoop :$query search :$multiline bang))
|
||||
;;
|
||||
;; Project searching
|
||||
|
||||
(defun +helm--file-search (beg end query &optional directory options)
|
||||
(require 'helm-ag)
|
||||
(helm-ag--init-state)
|
||||
(let ((helm-ag--default-directory (or directory (doom-project-root)))
|
||||
(query (or query
|
||||
(if (evil-visual-state-p)
|
||||
(and beg end
|
||||
(> (abs (- end beg)) 1)
|
||||
(rxt-quote-pcre (buffer-substring-no-properties beg end)))
|
||||
+helm--file-last-query)
|
||||
+helm--file-last-query))
|
||||
(helm-ag-command-option (concat helm-ag-command-option " " (string-join options " "))))
|
||||
(setq helm-ag--last-query query)
|
||||
(helm-attrset 'search-this-file nil helm-ag-source)
|
||||
(helm-attrset 'name (helm-ag--helm-header helm-ag--default-directory) helm-ag-source)
|
||||
(helm :sources '(helm-ag-source)
|
||||
:input query
|
||||
:buffer "*helm-ag*"
|
||||
:keymap helm-ag-map
|
||||
:history 'helm-ag--helm-history)))
|
||||
;;;###autoload (autoload '+helm:pt "completion/helm/autoload/evil" nil t)
|
||||
(evil-define-command +helm:pt (all-files-p query)
|
||||
"Ex interface for `+helm/pt'"
|
||||
(interactive "<!><a>")
|
||||
(+helm/pt all-files-p query))
|
||||
|
||||
;;;###autoload (autoload '+helm:grep "completion/helm/autoload/evil" nil t)
|
||||
(evil-define-command +helm:grep (all-files-p query)
|
||||
"Ex interface for `+helm/grep'"
|
||||
(interactive "<!><a>")
|
||||
(+helm/grep all-files-p query))
|
||||
|
||||
(defvar +helm--file-last-search nil)
|
||||
;;;###autoload (autoload '+helm:ag "completion/helm/autoload/evil" nil t)
|
||||
(evil-define-command +helm:ag (beg end query &optional bang)
|
||||
"TODO"
|
||||
(interactive "<r><a><!>")
|
||||
(+helm--file-search beg end query nil
|
||||
(if bang (list "-a" "--hidden"))))
|
||||
|
||||
;;;###autoload (autoload '+helm:ag-cwd "completion/helm/autoload/evil" nil t)
|
||||
(evil-define-command +helm:ag-cwd (beg end query &optional bang)
|
||||
"TODO"
|
||||
(interactive "<r><a><!>")
|
||||
(+helm--file-search beg end query default-directory
|
||||
(list "-n" (if bang "-a"))))
|
||||
(evil-define-command +helm:ag (all-files-p query)
|
||||
"Ex interface for `+helm/ag'"
|
||||
(interactive "<!><a>")
|
||||
(+helm/ag all-files-p query))
|
||||
|
||||
;;;###autoload (autoload '+helm:rg "completion/helm/autoload/evil" nil t)
|
||||
(evil-define-command +helm:rg (beg end query &optional bang)
|
||||
"TODO"
|
||||
(interactive "<r><a><!>")
|
||||
(let ((helm-ag-base-command "rg --no-heading"))
|
||||
(+helm--file-search beg end query nil
|
||||
(if bang (list "-uu")))))
|
||||
(evil-define-command +helm:rg (all-files-p query)
|
||||
"Ex interface for `+helm/rg'"
|
||||
(interactive "<!><a>")
|
||||
(+helm/rg all-files-p query))
|
||||
|
||||
;;;###autoload (autoload '+helm:rg-cwd "completion/helm/autoload/evil" nil t)
|
||||
(evil-define-command +helm:rg-cwd (beg end query &optional bang)
|
||||
|
||||
;;;###autoload (autoload '+helm:pt-from-cwd "completion/helm/autoload/evil" nil t)
|
||||
(evil-define-command +helm:pt-from-cwd (query &optional recurse-p)
|
||||
"Ex interface for `+helm/pt-from-cwd'."
|
||||
(interactive "<a><!>")
|
||||
(+helm/pt-from-cwd (not recurse-p) query))
|
||||
|
||||
;;;###autoload (autoload '+helm:grep-from-cwd "completion/helm/autoload/evil" nil t)
|
||||
(evil-define-command +helm:grep-from-cwd (query &optional recurse-p)
|
||||
"Ex interface for `+helm/grep-from-cwd'."
|
||||
(interactive "<a><!>")
|
||||
(+helm/grep-from-cwd (not recurse-p) query))
|
||||
|
||||
;;;###autoload (autoload '+helm:ag-from-cwd "completion/helm/autoload/evil" nil t)
|
||||
(evil-define-command +helm:ag-from-cwd (query &optional recurse-p)
|
||||
"Ex interface for `+helm/ag-from-cwd'."
|
||||
(interactive "<a><!>")
|
||||
(+helm/ag-from-cwd (not recurse-p) query))
|
||||
|
||||
;;;###autoload (autoload '+helm:rg-from-cwd "completion/helm/autoload/evil" nil t)
|
||||
(evil-define-command +helm:rg-from-cwd (query &optional recurse-p)
|
||||
"Ex interface for `+helm/rg-from-cwd'."
|
||||
(interactive "<a><!>")
|
||||
(+helm/rg-from-cwd (not recurse-p) query))
|
||||
|
||||
|
||||
;;;###autoload
|
||||
(defun +helm--set-prompt-display (pos)
|
||||
"TODO"
|
||||
(interactive "<r><a><!>")
|
||||
(let ((helm-ag-base-command "rg --no-heading --maxdepth 1"))
|
||||
(+helm--file-search beg end query default-directory
|
||||
(if bang (list "-uu")))))
|
||||
(let (beg state region-active m)
|
||||
(with-selected-window (minibuffer-window)
|
||||
(setq beg (save-excursion (vertical-motion 0 (helm-window)) (point))
|
||||
state evil-state
|
||||
region-active (region-active-p)
|
||||
m (mark t)))
|
||||
(when region-active
|
||||
(setq m (- m beg))
|
||||
;; Increment pos to handle the space before prompt (i.e `pref').
|
||||
(put-text-property (1+ (min m pos)) (+ 2 (max m pos))
|
||||
'face
|
||||
(list :background (face-background 'region))
|
||||
header-line-format))
|
||||
(put-text-property
|
||||
;; Increment pos to handle the space before prompt (i.e `pref').
|
||||
(+ 1 pos) (+ 2 pos)
|
||||
'face
|
||||
(if (eq state 'insert)
|
||||
'underline
|
||||
;; Don't just use 'cursor, this can hide the current character.
|
||||
(list :inverse-video t
|
||||
:foreground (face-background 'cursor)
|
||||
:background (face-background 'default)))
|
||||
header-line-format)))
|
||||
|
|
249
modules/completion/helm/autoload/helm.el
Normal file
249
modules/completion/helm/autoload/helm.el
Normal file
|
@ -0,0 +1,249 @@
|
|||
;;; completion/helm/autoload/helm.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autoload
|
||||
(defun +helm/tasks (&optional _arg)
|
||||
(interactive "P")
|
||||
;; TODO Implement `+helm/tasks'
|
||||
(error "Not implemented yet"))
|
||||
|
||||
;;;###autoload
|
||||
(defun +helm/projectile-find-file ()
|
||||
"Call `helm-find-files' if called from HOME, otherwise
|
||||
`helm-projectile-find-file'."
|
||||
(interactive)
|
||||
(call-interactively
|
||||
(if (or (file-equal-p default-directory "~")
|
||||
(if-let* ((proot (doom-project-root)))
|
||||
(file-equal-p proot "~")
|
||||
t))
|
||||
#'helm-find-files
|
||||
#'helm-projectile-find-file)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +helm/workspace-buffer-list ()
|
||||
"A version of `helm-buffers-list' with its buffer list restricted to the
|
||||
current workspace."
|
||||
(interactive)
|
||||
(unless (featurep! :feature workspaces)
|
||||
(user-error "This command requires the :feature workspaces module"))
|
||||
(with-no-warnings
|
||||
(with-persp-buffer-list nil (helm-buffers-list))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +helm/workspace-mini ()
|
||||
"A version of `helm-mini' with its buffer list restricted to the current
|
||||
workspace."
|
||||
(interactive)
|
||||
(unless (featurep! :feature workspaces)
|
||||
(user-error "This command requires the :feature workspaces module"))
|
||||
(with-no-warnings
|
||||
(with-persp-buffer-list nil (helm-mini))))
|
||||
|
||||
|
||||
;;
|
||||
;; Project search
|
||||
|
||||
(defun +helm-ag-search-args (all-files-p recursive-p)
|
||||
(list (concat "ag " (if IS-WINDOWS "--vimgrep" "--nocolor --nogroup"))
|
||||
"-S"
|
||||
(if all-files-p "-z -a")
|
||||
(unless recursive-p "--depth 1")))
|
||||
|
||||
(defun +helm-rg-search-args (all-files-p recursive-p)
|
||||
(list "rg --no-heading --line-number --color never"
|
||||
"-S"
|
||||
(when all-files-p "-z -uu")
|
||||
(unless recursive-p "--maxdepth 1")))
|
||||
|
||||
(defun +helm-pt-search-args (all-files-p recursive-p)
|
||||
(list "pt --nocolor --nogroup -e"
|
||||
"-S"
|
||||
(if all-files-p "-z -a")
|
||||
(unless recursive-p "--depth 1")))
|
||||
|
||||
;;
|
||||
(defun +helm--grep-source ()
|
||||
(require 'helm-projectile)
|
||||
(helm-build-async-source (capitalize (helm-grep-command t))
|
||||
:header-name (lambda (_name) "Helm Projectile Grep (C-c ? Help)")
|
||||
:candidates-process #'helm-grep-collect-candidates
|
||||
:filter-one-by-one #'helm-grep-filter-one-by-one
|
||||
:candidate-number-limit 9999
|
||||
:nohighlight t
|
||||
:keymap helm-grep-map
|
||||
:history 'helm-grep-history
|
||||
:action (apply #'helm-make-actions helm-projectile-grep-or-ack-actions)
|
||||
:persistent-action 'helm-grep-persistent-action
|
||||
:persistent-help "Jump to line (`C-u' Record in mark ring)"
|
||||
:requires-pattern 2))
|
||||
|
||||
(defun +helm--grep-search (directory query prompt &optional all-files-p recursive-p)
|
||||
(let* ((default-directory directory)
|
||||
(helm-ff-default-directory directory)
|
||||
(helm-grep-in-recurse recursive-p)
|
||||
(helm-grep-ignored-files
|
||||
(unless all-files-p
|
||||
(cl-union (projectile-ignored-files-rel) grep-find-ignored-files)))
|
||||
(helm-grep-ignored-directories
|
||||
(unless all-files-p
|
||||
(cl-union (mapcar 'directory-file-name (projectile-ignored-directories-rel))
|
||||
grep-find-ignored-directories)))
|
||||
(helm-grep-default-command
|
||||
(if (and nil (eq (projectile-project-vcs) 'git))
|
||||
(format "git --no-pager grep --no-color -n%%c -e %%p %s -- %%f"
|
||||
(if recursive-p "" "--max-depth 1 "))
|
||||
(format "grep -si -a%s %%e -n%%cH -e %%p %%f %s"
|
||||
(if recursive-p " -R" "")
|
||||
(if recursive-p "." "./*"))))
|
||||
(helm-grep-default-recurse-command helm-grep-default-command))
|
||||
(setq helm-source-grep (+helm--grep-source))
|
||||
(helm :sources 'helm-source-grep
|
||||
:input query
|
||||
:prompt prompt
|
||||
:buffer "*helm grep*"
|
||||
:default-directory directory
|
||||
:keymap helm-grep-map
|
||||
:history 'helm-grep-history
|
||||
:truncate-lines helm-grep-truncate-lines)))
|
||||
|
||||
;;;###autoload
|
||||
(cl-defun +helm-file-search (engine &key query in all-files (recursive t))
|
||||
"Conduct a file search using ENGINE, which can be any of: rg, ag, pt, and
|
||||
grep. If omitted, ENGINE will default to the first one it detects, in that
|
||||
order.
|
||||
|
||||
:query STRING
|
||||
Determines the initial input to search for.
|
||||
:in PATH
|
||||
Sets what directory to base the search out of. Defaults to the current
|
||||
project's root.
|
||||
:recursive BOOL
|
||||
Whether or not to search files recursively from the base directory."
|
||||
(declare (indent defun))
|
||||
(require 'helm-ag)
|
||||
(helm-ag--init-state)
|
||||
(let* ((project-root (or (doom-project-root) default-directory))
|
||||
(directory (or in project-root))
|
||||
(default-directory directory)
|
||||
(helm-ag--default-directory directory)
|
||||
(helm-ag--default-target (list directory))
|
||||
(engine (or engine
|
||||
(cl-find-if #'executable-find +helm-project-search-engines
|
||||
:key #'symbol-name)
|
||||
(and (or (executable-find "grep")
|
||||
(executable-find "git"))
|
||||
'grep)
|
||||
(user-error "No search engine specified (is ag, rg, pt or git installed?)")))
|
||||
(query (or query
|
||||
(when (use-region-p)
|
||||
(let ((beg (or (bound-and-true-p evil-visual-beginning) (region-beginning)))
|
||||
(end (or (bound-and-true-p evil-visual-end) (region-end))))
|
||||
(when (> (abs (- end beg)) 1)
|
||||
(rxt-quote-pcre (buffer-substring-no-properties beg end)))))
|
||||
""))
|
||||
(prompt (format "[%s %s] "
|
||||
(symbol-name engine)
|
||||
(cond ((file-equal-p directory project-root)
|
||||
(projectile-project-name))
|
||||
((file-equal-p directory default-directory)
|
||||
"./")
|
||||
((file-relative-name directory project-root)))))
|
||||
(command
|
||||
(pcase engine
|
||||
(`ag (+helm-ag-search-args all-files recursive))
|
||||
(`rg (+helm-rg-search-args all-files recursive))
|
||||
(`pt (+helm-pt-search-args all-files recursive))
|
||||
('grep (+helm--grep-search directory query prompt all-files recursive)
|
||||
(cl-return t))))
|
||||
(helm-ag-base-command (string-join command " ")))
|
||||
;; TODO Define our own sources instead
|
||||
(helm-attrset 'name (format "[%s %s] Searching %s"
|
||||
engine
|
||||
(string-join (delq nil (cdr command)) " ")
|
||||
(abbreviate-file-name directory))
|
||||
helm-source-do-ag)
|
||||
(helm-attrset '+helm-command command helm-source-do-ag)
|
||||
(cl-letf (((symbol-function 'helm-do-ag--helm)
|
||||
(lambda () (helm :sources '(helm-source-do-ag)
|
||||
:prompt prompt
|
||||
:buffer "*helm-ag*"
|
||||
:keymap helm-do-ag-map
|
||||
:input query
|
||||
:history 'helm-ag--helm-history))))
|
||||
(helm-do-ag directory))))
|
||||
|
||||
(defun +helm--get-command (format)
|
||||
(cl-loop for tool in (cl-remove-duplicates +helm-project-search-engines :from-end t)
|
||||
if (executable-find (symbol-name tool))
|
||||
return (intern (format format tool))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +helm/project-search (&optional arg initial-query directory)
|
||||
"Performs a project search from the project root.
|
||||
|
||||
Uses the first available search backend from `+helm-project-search-engines'. If
|
||||
ARG (universal argument), include all files, even hidden or compressed ones, in
|
||||
the search."
|
||||
(interactive "P")
|
||||
(funcall (or (+helm--get-command "+helm/%s")
|
||||
#'+helm/grep)
|
||||
arg
|
||||
initial-query
|
||||
directory))
|
||||
|
||||
;;;###autoload
|
||||
(defun +helm/project-search-from-cwd (&optional arg initial-query)
|
||||
"Performs a project search recursively from the current directory.
|
||||
|
||||
Uses the first available search backend from `+helm-project-search-engines'. If
|
||||
ARG (universal argument), include all files, even hidden or compressed ones."
|
||||
(interactive "P")
|
||||
(funcall (or (+helm--get-command "+helm/%s-from-cwd")
|
||||
#'+helm/grep-from-cwd)
|
||||
arg
|
||||
initial-query))
|
||||
|
||||
|
||||
;;;###autoload (autoload '+helm/rg "completion/helm/autoload/helm")
|
||||
;;;###autoload (autoload '+helm/rg-from-cwd "completion/helm/autoload/helm")
|
||||
;;;###autoload (autoload '+helm/ag "completion/helm/autoload/helm")
|
||||
;;;###autoload (autoload '+helm/ag-from-cwd "completion/helm/autoload/helm")
|
||||
;;;###autoload (autoload '+helm/pt "completion/helm/autoload/helm")
|
||||
;;;###autoload (autoload '+helm/pt-from-cwd "completion/helm/autoload/helm")
|
||||
;;;###autoload (autoload '+helm/grep "completion/helm/autoload/helm")
|
||||
;;;###autoload (autoload '+helm/grep-from-cwd "completion/helm/autoload/helm")
|
||||
|
||||
(dolist (engine `(,@(cl-remove-duplicates +helm-project-search-engines :from-end t) grep))
|
||||
(defalias (intern (format "+helm/%s" engine))
|
||||
(lambda (arg &optional query directory)
|
||||
(interactive "P")
|
||||
(+helm-file-search engine
|
||||
:query query
|
||||
:in directory
|
||||
:all-files (and (not (null arg))
|
||||
(listp arg))))
|
||||
(format "Perform a project file search using %s.
|
||||
|
||||
QUERY is a regexp. If omitted, the current selection is used. If no selection is
|
||||
active, the last known search is used.
|
||||
|
||||
ARG is the universal argument. If a number is passed through it, e.g. C-u 3, then
|
||||
|
||||
If ALL-FILES-P, search compressed and hidden files as well."
|
||||
engine))
|
||||
|
||||
(defalias (intern (format "+helm/%s-from-cwd" engine))
|
||||
(lambda (arg &optional query)
|
||||
(interactive "P")
|
||||
(+helm-file-search engine
|
||||
:query query
|
||||
:in default-directory
|
||||
:all-files (and (not (null arg))
|
||||
(listp arg))))
|
||||
(format "Perform a project file search from the current directory using %s.
|
||||
|
||||
QUERY is a regexp. If omitted, the current selection is used. If no selection is
|
||||
active, the last known search is used.
|
||||
|
||||
If ALL-FILES-P, search compressed and hidden files as well."
|
||||
engine)))
|
66
modules/completion/helm/autoload/posframe.el
Normal file
66
modules/completion/helm/autoload/posframe.el
Normal file
|
@ -0,0 +1,66 @@
|
|||
;;; completion/helm/autoload/posframe.el -*- lexical-binding: t; -*-
|
||||
|
||||
(add-hook 'helm-cleanup-hook #'+helm|posframe-cleanup)
|
||||
|
||||
;;;###autoload
|
||||
(defun +helm-poshandler-frame-center-near-bottom (info)
|
||||
"Display the child frame in the center of the frame, slightly closer to the
|
||||
bottom, which is easier on the eyes on big displays."
|
||||
(let ((parent-frame (plist-get info :parent-frame))
|
||||
(pos (posframe-poshandler-frame-center info)))
|
||||
(cons (car pos)
|
||||
(truncate (/ (frame-pixel-height parent-frame)
|
||||
2)))))
|
||||
|
||||
(defvar +helm--posframe-buffer nil)
|
||||
;;;###autoload
|
||||
(defun +helm-posframe-display (buffer &optional _resume)
|
||||
"TODO"
|
||||
(setq helm--buffer-in-new-frame-p t)
|
||||
(let ((solaire-p (bound-and-true-p solaire-mode))
|
||||
(params (copy-sequence +helm-posframe-parameters)))
|
||||
(let-alist params
|
||||
(require 'posframe)
|
||||
(posframe-show
|
||||
(setq +helm--posframe-buffer buffer)
|
||||
:position (point)
|
||||
:poshandler +helm-posframe-handler
|
||||
:width
|
||||
(max (cl-typecase .width
|
||||
(integer .width)
|
||||
(float (truncate (* (frame-width) .width)))
|
||||
(function (funcall .width))
|
||||
(t 0))
|
||||
.min-width)
|
||||
:height
|
||||
(max (cl-typecase .height
|
||||
(integer .height)
|
||||
(float (truncate (* (frame-height) .height)))
|
||||
(function (funcall .height))
|
||||
(t 0))
|
||||
.min-height)
|
||||
:override-parameters
|
||||
(dolist (p '(width height min-width min-height) params)
|
||||
(setq params (delq (assq p params) params)))))
|
||||
;;
|
||||
(unless (or (null +helm-posframe-text-scale)
|
||||
(= +helm-posframe-text-scale 0))
|
||||
(with-current-buffer buffer
|
||||
(when (and (featurep 'solaire-mode)
|
||||
(not solaire-p))
|
||||
(solaire-mode +1))
|
||||
(text-scale-set +helm-posframe-text-scale)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +helm|posframe-cleanup ()
|
||||
"TODO"
|
||||
;; Ensure focus is properly returned to the underlying window, by forcing a
|
||||
;; chance in buffer/window focus. This gives the modeline a chance to refresh.
|
||||
(switch-to-buffer +helm--posframe-buffer t)
|
||||
;;
|
||||
(posframe-delete +helm--posframe-buffer))
|
||||
|
||||
|
||||
;;;###autoload
|
||||
(defun +helm*fix-get-font-height (orig-fn position)
|
||||
(ignore-errors (funcall orig-fn position)))
|
|
@ -1,124 +1,183 @@
|
|||
;;; completion/helm/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
;; Warning: since I don't use helm, this may be out of date.
|
||||
(defvar +helm-project-search-engines '(rg ag pt)
|
||||
"What search tools for `+helm/project-search' (and `+helm-file-search' when no
|
||||
ENGINE is specified) to try, and in what order.
|
||||
|
||||
(defvar +helm-global-prompt "››› "
|
||||
"The helm text prompt prefix string is globally replaced with this string.")
|
||||
To disable a particular tool, remove it from this list. To prioritize a tool
|
||||
over others, move it to the front of the list. Later duplicates in this list are
|
||||
silently ignored.
|
||||
|
||||
This falls back to git-grep (then grep) if none of these available.")
|
||||
|
||||
;; Posframe (requires +childframe)
|
||||
(defvar +helm-posframe-handler
|
||||
#'+helm-poshandler-frame-center-near-bottom
|
||||
"The function that determines the location of the childframe. It should return
|
||||
a cons cell representing the X and Y coordinates. See
|
||||
`posframe-poshandler-frame-center' as a reference.")
|
||||
|
||||
(defvar +helm-posframe-text-scale 1
|
||||
"The text-scale to use in the helm childframe. Set to nil for no scaling. Can
|
||||
be negative.")
|
||||
|
||||
(defvar +helm-posframe-parameters
|
||||
'((internal-border-width . 8)
|
||||
(width . 0.5)
|
||||
(height . 0.35)
|
||||
(min-width . 80)
|
||||
(min-height . 16))
|
||||
"TODO")
|
||||
|
||||
|
||||
;;
|
||||
;; Packages
|
||||
;;
|
||||
|
||||
(def-package! helm-mode
|
||||
:defer t
|
||||
:after-call pre-command-hook
|
||||
:init
|
||||
(map! [remap apropos] #'helm-apropos
|
||||
[remap find-library] #'helm-locate-library
|
||||
[remap bookmark-jump] #'helm-bookmarks
|
||||
[remap execute-extended-command] #'helm-M-x
|
||||
[remap find-file] #'helm-find-files
|
||||
[remap imenu-anywhere] #'helm-imenu-anywhere
|
||||
[remap imenu] #'helm-semantic-or-imenu
|
||||
[remap noop-show-kill-ring] #'helm-show-kill-ring
|
||||
[remap persp-switch-to-buffer] #'+helm/workspace-mini
|
||||
[remap switch-to-buffer] #'helm-buffers-list
|
||||
[remap projectile-find-file] #'+helm/projectile-find-file
|
||||
[remap projectile-recentf] #'helm-projectile-recentf
|
||||
[remap projectile-switch-project] #'helm-projectile-switch-project
|
||||
[remap projectile-switch-to-buffer] #'helm-projectile-switch-to-buffer
|
||||
[remap recentf-open-files] #'helm-recentf
|
||||
[remap yank-pop] #'helm-show-kill-ring)
|
||||
:config
|
||||
(helm-mode +1)
|
||||
;; helm is too heavy for `find-file-at-point'
|
||||
(add-to-list 'helm-completing-read-handlers-alist (cons #'find-file-at-point nil)))
|
||||
|
||||
|
||||
(def-package! helm
|
||||
:init
|
||||
(setq helm-quick-update t
|
||||
;; Speedier without fuzzy matching
|
||||
helm-mode-fuzzy-match nil
|
||||
helm-buffers-fuzzy-matching nil
|
||||
helm-apropos-fuzzy-match nil
|
||||
helm-M-x-fuzzy-match nil
|
||||
helm-recentf-fuzzy-match nil
|
||||
helm-projectile-fuzzy-match nil
|
||||
;; Display extraineous helm UI elements
|
||||
:after helm-mode
|
||||
:preface
|
||||
(setq helm-candidate-number-limit 50
|
||||
;; Remove extraineous helm UI elements
|
||||
helm-display-header-line nil
|
||||
helm-mode-line-string nil
|
||||
helm-ff-auto-update-initial-value nil
|
||||
helm-find-files-doc-header nil
|
||||
;; Don't override evil-ex's completion
|
||||
helm-mode-handle-completion-in-region nil
|
||||
helm-candidate-number-limit 50
|
||||
;; Don't wrap item cycling
|
||||
helm-move-to-line-cycle-in-source t)
|
||||
;; Default helm window sizes
|
||||
helm-display-buffer-default-width nil
|
||||
helm-display-buffer-default-height 0.25
|
||||
;; When calling `helm-semantic-or-imenu', don't immediately jump to
|
||||
;; symbol at point
|
||||
helm-imenu-execute-action-at-once-if-one nil
|
||||
;; disable special behavior for left/right, M-left/right keys.
|
||||
helm-ff-lynx-style-map nil)
|
||||
|
||||
(when (featurep! :feature evil +everywhere)
|
||||
(setq helm-default-prompt-display-function #'+helm--set-prompt-display))
|
||||
|
||||
:init
|
||||
(when (and EMACS26+ (featurep! +childframe))
|
||||
(setq helm-display-function #'+helm-posframe-display)
|
||||
;; Fix "Specified window is not displaying the current buffer" error
|
||||
(advice-add #'posframe--get-font-height :around #'+helm*fix-get-font-height))
|
||||
|
||||
(let ((fuzzy (featurep! +fuzzy)))
|
||||
(setq helm-M-x-fuzzy-match fuzzy
|
||||
helm-ag-fuzzy-match fuzzy
|
||||
helm-apropos-fuzzy-match fuzzy
|
||||
helm-apropos-fuzzy-match fuzzy
|
||||
helm-bookmark-show-location fuzzy
|
||||
helm-buffers-fuzzy-matching fuzzy
|
||||
helm-completion-in-region-fuzzy-match fuzzy
|
||||
helm-completion-in-region-fuzzy-match fuzzy
|
||||
helm-ff-fuzzy-matching fuzzy
|
||||
helm-file-cache-fuzzy-match fuzzy
|
||||
helm-flx-for-helm-locate fuzzy
|
||||
helm-imenu-fuzzy-match fuzzy
|
||||
helm-lisp-fuzzy-completion fuzzy
|
||||
helm-locate-fuzzy-match fuzzy
|
||||
helm-mode-fuzzy-match fuzzy
|
||||
helm-projectile-fuzzy-match fuzzy
|
||||
helm-recentf-fuzzy-match fuzzy
|
||||
helm-semantic-fuzzy-match fuzzy))
|
||||
|
||||
:config
|
||||
(load "helm-autoloads" nil t)
|
||||
(add-hook 'doom-init-hook #'helm-mode)
|
||||
(set-popup-rule! "^\\*helm" :vslot -100 :size 0.22 :ttl nil)
|
||||
|
||||
(defvar helm-projectile-find-file-map (make-sparse-keymap))
|
||||
(require 'helm-projectile)
|
||||
(set-keymap-parent helm-projectile-find-file-map helm-map)
|
||||
;; Hide the modeline
|
||||
(defun +helm|hide-mode-line (&rest _)
|
||||
(with-current-buffer (helm-buffer-get)
|
||||
(unless helm-mode-line-string
|
||||
(hide-mode-line-mode +1))))
|
||||
(add-hook 'helm-after-initialize-hook #'+helm|hide-mode-line)
|
||||
(advice-add #'helm-display-mode-line :override #'+helm|hide-mode-line)
|
||||
(advice-add #'helm-ag-show-status-default-mode-line :override #'ignore)
|
||||
|
||||
;; helm is too heavy for find-file-at-point
|
||||
(after! helm-mode
|
||||
(add-to-list 'helm-completing-read-handlers-alist '(find-file-at-point . nil)))
|
||||
|
||||
(set! :popup "\\` ?\\*[hH]elm.*?\\*\\'" :size 14 :regexp t)
|
||||
(setq projectile-completion-system 'helm)
|
||||
|
||||
;;; Helm hacks
|
||||
(defun +helm*replace-prompt (plist)
|
||||
"Globally replace helm prompts with `+helm-global-prompt'."
|
||||
(if (keywordp (car plist))
|
||||
(plist-put plist :prompt +helm-global-prompt)
|
||||
(setf (nth 2 plist) +helm-global-prompt)
|
||||
plist))
|
||||
(advice-add #'helm :filter-args #'+helm*replace-prompt)
|
||||
|
||||
(defun +helm*hide-header (&rest _)
|
||||
"Hide header-line & mode-line in helm windows."
|
||||
(setq mode-line-format nil))
|
||||
(advice-add #'helm-display-mode-line :override #'+helm*hide-header)
|
||||
|
||||
(map! :map global-map
|
||||
[remap apropos] #'helm-apropos
|
||||
[remap find-file] #'helm-find-files
|
||||
[remap recentf-open-files] #'helm-recentf
|
||||
[remap projectile-switch-to-buffer] #'helm-projectile-switch-to-buffer
|
||||
[remap projectile-recentf] #'helm-projectile-recentf
|
||||
[remap projectile-find-file] #'helm-projectile-find-file
|
||||
[remap imenu] #'helm-semantic-or-imenu
|
||||
[remap bookmark-jump] #'helm-bookmarks
|
||||
[remap noop-show-kill-ring] #'helm-show-kill-ring
|
||||
[remap projectile-switch-project] #'helm-projectile-switch-project
|
||||
[remap projectile-find-file] #'helm-projectile-find-file
|
||||
[remap imenu-anywhere] #'helm-imenu-anywhere
|
||||
[remap execute-extended-command] #'helm-M-x))
|
||||
;; TODO Find a better way
|
||||
(defun +helm*use-helpful (orig-fn arg)
|
||||
(cl-letf (((symbol-function #'describe-function)
|
||||
(symbol-function #'helpful-callable))
|
||||
((symbol-function #'describe-variable)
|
||||
(symbol-function #'helpful-variable)))
|
||||
(funcall orig-fn arg)))
|
||||
(advice-add #'helm-describe-variable :around #'+helm*use-helpful)
|
||||
(advice-add #'helm-describe-function :around #'+helm*use-helpful))
|
||||
|
||||
|
||||
(def-package! helm-locate
|
||||
:defer t
|
||||
:init (defvar helm-generic-files-map (make-sparse-keymap))
|
||||
:config (set-keymap-parent helm-generic-files-map helm-map))
|
||||
(def-package! helm-flx
|
||||
:when (featurep! +fuzzy)
|
||||
:hook (helm-mode . helm-flx-mode)
|
||||
:config (helm-flx-mode +1))
|
||||
|
||||
|
||||
(def-package! helm-bookmark
|
||||
:commands helm-bookmark
|
||||
:config (setq-default helm-bookmark-show-location t))
|
||||
;; `helm-ag'
|
||||
(after! helm-ag
|
||||
(map! :map helm-ag-edit-map :n "RET" #'compile-goto-error)
|
||||
(define-key helm-ag-edit-map [remap quit-window] #'helm-ag--edit-abort)
|
||||
(set-popup-rule! "^\\*helm-ag-edit" :size 0.35 :ttl 0 :quit nil)
|
||||
;; Recenter after jumping to match
|
||||
(advice-add #'helm-ag--find-file-action :after-while #'doom*recenter))
|
||||
|
||||
|
||||
(def-package! helm-files
|
||||
:defer t
|
||||
:config
|
||||
;; `helm-bookmark'
|
||||
(setq helm-bookmark-show-location t)
|
||||
|
||||
|
||||
;; `helm-files'
|
||||
(after! helm-files
|
||||
(setq helm-boring-file-regexp-list
|
||||
(append (list "\\.projects$" "\\.DS_Store$")
|
||||
helm-boring-file-regexp-list)))
|
||||
|
||||
|
||||
(def-package! helm-ag
|
||||
:defer t
|
||||
;; `helm-locate'
|
||||
(defvar helm-generic-files-map (make-sparse-keymap))
|
||||
(after! helm-locate (set-keymap-parent helm-generic-files-map helm-map))
|
||||
|
||||
|
||||
;; `helm-projectile'
|
||||
(def-package! helm-projectile
|
||||
:commands (helm-projectile-find-file
|
||||
helm-projectile-recentf
|
||||
helm-projectile-switch-project
|
||||
helm-projectile-switch-to-buffer)
|
||||
:init
|
||||
(setq projectile-completion-system 'helm)
|
||||
(defvar helm-projectile-find-file-map (make-sparse-keymap))
|
||||
:config
|
||||
(map! :map helm-ag-edit-map
|
||||
[remap doom/kill-this-buffer] #'helm-ag--edit-abort
|
||||
[remap quit-window] #'helm-ag--edit-abort))
|
||||
(set-keymap-parent helm-projectile-find-file-map helm-map))
|
||||
|
||||
|
||||
(def-package! helm-css-scss ; https://github.com/ShingoFukuyama/helm-css-scss
|
||||
:commands (helm-css-scss
|
||||
helm-css-scss-multi
|
||||
helm-css-scss-insert-close-comment)
|
||||
:config
|
||||
(setq helm-css-scss-split-direction #'split-window-vertically
|
||||
helm-css-scss-split-with-multiple-windows t))
|
||||
|
||||
|
||||
(def-package! helm-swoop ; https://github.com/ShingoFukuyama/helm-swoop
|
||||
:commands (helm-swoop helm-multi-swoop helm-multi-swoop-all)
|
||||
:config
|
||||
(setq helm-swoop-use-line-number-face t
|
||||
helm-swoop-candidate-number-limit 200
|
||||
helm-swoop-speed-or-color t
|
||||
helm-swoop-pre-input-function (lambda () "")))
|
||||
|
||||
|
||||
(def-package! helm-describe-modes :commands helm-describe-modes)
|
||||
|
||||
;; `swiper-helm'
|
||||
(after! swiper-helm
|
||||
(setq swiper-helm-display-function
|
||||
(lambda (buf &optional _resume) (pop-to-buffer buf)))
|
||||
(global-set-key [remap swiper] #'swiper-helm)
|
||||
(add-to-list 'swiper-font-lock-exclude #'+doom-dashboard-mode nil #'eq))
|
||||
|
|
|
@ -5,7 +5,10 @@
|
|||
(package! helm-ag)
|
||||
(package! helm-c-yasnippet)
|
||||
(package! helm-company)
|
||||
(package! helm-css-scss)
|
||||
(package! helm-describe-modes :recipe (:fetcher github :repo "emacs-helm/helm-describe-modes"))
|
||||
(package! helm-projectile)
|
||||
(package! helm-swoop)
|
||||
(package! swiper-helm)
|
||||
(when (featurep! +fuzzy)
|
||||
(package! helm-flx))
|
||||
(when (and EMACS26+ (featurep! +childframe))
|
||||
(package! posframe))
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
;;; completion/ido/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
(def-package! ido
|
||||
:config
|
||||
(defun +ido|init ()
|
||||
(setq ido-ignore-buffers
|
||||
'("\\` " "^\\*ESS\\*" "^\\*Messages\\*" "^\\*Help\\*" "^\\*Buffer"
|
||||
"^\\*.*Completions\\*$" "^\\*Ediff" "^\\*tramp" "^\\*cvs-"
|
||||
|
@ -16,29 +15,18 @@
|
|||
ido-enable-last-directory-history t
|
||||
ido-save-directory-list-file (concat doom-cache-dir "ido.last"))
|
||||
|
||||
(push "\\`.DS_Store$" ido-ignore-files)
|
||||
(push "Icon\\?$" ido-ignore-files)
|
||||
(unless (member "\\`.DS_Store$" ido-ignore-files)
|
||||
(push "\\`.DS_Store$" ido-ignore-files)
|
||||
(push "Icon\\?$" ido-ignore-files))
|
||||
|
||||
(ido-mode 1)
|
||||
(ido-everywhere 1)
|
||||
(require 'ido-ubiquitous)
|
||||
(ido-ubiquitous-mode 1)
|
||||
|
||||
(defun +ido|init ()
|
||||
(require 'ido-vertical-mode)
|
||||
(ido-vertical-mode 1)
|
||||
|
||||
(require 'flx-ido)
|
||||
(flx-ido-mode +1)
|
||||
|
||||
(require 'crm-custom)
|
||||
(crm-custom-mode +1)
|
||||
|
||||
(map! :map (ido-common-completion-map ido-completion-map ido-file-completion-map)
|
||||
"C-n" #'ido-next-match
|
||||
"C-p" #'ido-prev-match
|
||||
"C-w" #'ido-delete-backward-word-updir))
|
||||
(add-hook 'ido-setup-hook #'+ido|init)
|
||||
(define-key! (ido-common-completion-map ido-completion-map ido-file-completion-map)
|
||||
"\C-n" #'ido-next-match
|
||||
"\C-p" #'ido-prev-match
|
||||
"\C-w" #'ido-delete-backward-word-updir
|
||||
;; Go to $HOME with ~
|
||||
"~" (λ! (if (looking-back "/" (point-min))
|
||||
(insert "~/")
|
||||
(call-interactively #'self-insert-command))))
|
||||
|
||||
(defun +ido*sort-mtime ()
|
||||
"Sort ido filelist by mtime instead of alphabetically."
|
||||
|
@ -55,10 +43,16 @@
|
|||
(advice-add #'ido-sort-mtime :override #'+ido*sort-mtime)
|
||||
(add-hook! (ido-make-file-list ido-make-dir-list) #'+ido*sort-mtime)
|
||||
|
||||
(defun +ido|setup-home-keybind ()
|
||||
"Go to $HOME with ~"
|
||||
(define-key ido-file-completion-map (kbd "~")
|
||||
(λ! (if (looking-back "/" (point-min))
|
||||
(insert "~/")
|
||||
(call-interactively #'self-insert-command)))))
|
||||
(add-hook 'ido-setup-hook #'+ido|setup-home-keybind))
|
||||
;;
|
||||
(ido-mode 1)
|
||||
(ido-everywhere 1)
|
||||
(ido-ubiquitous-mode 1)
|
||||
(ido-vertical-mode 1)
|
||||
(flx-ido-mode +1)
|
||||
(crm-custom-mode +1)
|
||||
|
||||
;;
|
||||
(remove-hook 'ido-setup-hook #'+ido|init))
|
||||
|
||||
;;
|
||||
(add-hook 'ido-setup-hook #'+ido|init)
|
||||
|
|
|
@ -2,6 +2,6 @@
|
|||
;;; completion/ido/packages.el
|
||||
|
||||
(package! flx-ido)
|
||||
(package! ido-ubiquitous)
|
||||
(package! ido-completing-read+)
|
||||
(package! ido-vertical-mode)
|
||||
(package! crm-custom)
|
||||
|
|
|
@ -1,128 +1,57 @@
|
|||
#+TITLE: :completion ivy
|
||||
#+TITLE: completion/ivy
|
||||
#+DATE: February 13, 2017
|
||||
#+SINCE: v2.0
|
||||
#+STARTUP: inlineimages
|
||||
|
||||
This module adds Ivy, a completion backend.
|
||||
* Table of Contents :TOC_3:noexport:
|
||||
- [[Description][Description]]
|
||||
- [[Module Flags][Module Flags]]
|
||||
- [[Plugins][Plugins]]
|
||||
- [[Hacks][Hacks]]
|
||||
- [[Prerequisites][Prerequisites]]
|
||||
- [[Install][Install]]
|
||||
- [[MacOS][MacOS]]
|
||||
- [[Arch Linux][Arch Linux]]
|
||||
- [[Features][Features]]
|
||||
- [[Jump-to-file project navigation][Jump-to-file project navigation]]
|
||||
- [[Project search & replace][Project search & replace]]
|
||||
- [[In-buffer searching][In-buffer searching]]
|
||||
- [[Task lookup][Task lookup]]
|
||||
- [[Ivy integration for various completing commands][Ivy integration for various completing commands]]
|
||||
- [[General][General]]
|
||||
- [[Jump to files, buffers or projects)][Jump to files, buffers or projects)]]
|
||||
- [[Search][Search]]
|
||||
- [[Configuration][Configuration]]
|
||||
- [[Enable fuzzy/non-fuzzy search for specific commands][Enable fuzzy/non-fuzzy search for specific commands]]
|
||||
- [[Change the position of the ivy childframe][Change the position of the ivy childframe]]
|
||||
- [[Troubleshooting][Troubleshooting]]
|
||||
|
||||
* Description
|
||||
This module provides Ivy integration for a variety of Emacs commands, as well as
|
||||
a unified interface for project search and replace, powered by ag, rg, pt,
|
||||
git-grep & grep (whichever is available).
|
||||
|
||||
#+begin_quote
|
||||
I prefer ivy over ido for its flexibility. I prefer ivy over helm because it's
|
||||
lighter.
|
||||
lighter, simpler and faster in many cases.
|
||||
#+end_quote
|
||||
|
||||
+ Project-wide search & replace powered by ~rg~ or ~ag~
|
||||
+ Project jump-to navigation ala Command-T, Sublime Text's Jump-to-anywhere or
|
||||
Vim's CtrlP plugin.
|
||||
+ Ivy integration for ~M-x~, ~imenu~, ~recentf~ and others.
|
||||
+ A powerful, interactive in-buffer search using ~swiper~.
|
||||
+ Ivy-powered TODO/FIXME navigation
|
||||
** Module Flags
|
||||
+ =+fuzzy= Enables the fuzzy method for ivy searches.
|
||||
+ =+childframe= Causes Ivy to display in a floating child frame, above Emacs.
|
||||
*This requires GUI Emacs 26.1+*
|
||||
|
||||
* Table of Contents :TOC:
|
||||
- [[#install][Install]]
|
||||
- [[#macos][MacOS]]
|
||||
- [[#arch-linux][Arch Linux]]
|
||||
- [[#usage][Usage]]
|
||||
- [[#project-search--replace][Project search & replace]]
|
||||
- [[#jump-to-file-project-navigation][Jump-to-file project navigation]]
|
||||
- [[#in-buffer-searching][In-buffer searching]]
|
||||
- [[#task-lookup][Task lookup]]
|
||||
- [[#appendix][Appendix]]
|
||||
- [[#commands][Commands]]
|
||||
- [[#hacks][Hacks]]
|
||||
|
||||
* Install
|
||||
This module optionally depends on [[https://github.com/BurntSushi/ripgrep][ripgrep]] and [[https://github.com/ggreer/the_silver_searcher][the_silver_searcher]].
|
||||
|
||||
~rg~ is faster, but its results aren't deterministic, neither does it support
|
||||
multiline search or full PCRE (at the time of writing), that's where ~ag~ is
|
||||
useful.
|
||||
|
||||
** MacOS
|
||||
#+BEGIN_SRC sh :tangle (if (doom-system-os 'macos) "yes")
|
||||
brew install ripgrep the_silver_searcher
|
||||
#+END_SRC
|
||||
|
||||
** Arch Linux
|
||||
#+BEGIN_SRC sh :dir /sudo:: :tangle (if (doom-system-os 'arch) "yes")
|
||||
sudo pacman --needed --noconfirm -S ripgrep the_silver_searcher
|
||||
#+END_SRC
|
||||
|
||||
* Usage
|
||||
Here is some insight into how I use this module.
|
||||
|
||||
** Project search & replace
|
||||
There are four Ex interfaces for the silver searcher and ripgrep. They are:
|
||||
|
||||
+ ~:ag[!]~
|
||||
+ ~:agcwd[!]~
|
||||
+ ~:rg[!]~
|
||||
+ ~:rgcwd[!]~
|
||||
|
||||
The optional BANG tells ag/rg to include ignored files in the search. And the
|
||||
\*cwd variant of each command will only search in the current directory
|
||||
(non-recursively).
|
||||
|
||||
[[/../screenshots/modules/completion/ivy/ivy-search.gif]]
|
||||
|
||||
Now, how do we do text replacements? With the ivy popup open you can press
|
||||
=S+Tab= to create an wgrep buffer out of the results.
|
||||
|
||||
[[/../screenshots/modules/completion/ivy/ivy-search-replace.gif]]
|
||||
|
||||
Make your modifications and press =C-c C-c= to commit them, or =C-c C-k= to
|
||||
abort.
|
||||
|
||||
** Jump-to-file project navigation
|
||||
Inspired by Sublime Text's jump-to-anywhere, Vim's CtrlP/Unite plugins, and
|
||||
Textmate's Command-T, a marriage of ~projectile~ and ~ivy~ makes this available
|
||||
in Emacs.
|
||||
|
||||
Invoke it with =SPC f /=, =SPC SPC= or ~M-x counsel-projectile-find-file~.
|
||||
|
||||
[[/../screenshots/modules/completion/ivy/ivy-projectile.gif]]
|
||||
|
||||
** In-buffer searching
|
||||
I use ~evil-search~ (invoked by pressing =/= in normal mode) when jumping
|
||||
small/moderate (or predictable) distances. However, there are occasions where I
|
||||
need more feedback, so I turn to ~swiper~ (available directly with =M-x swiper
|
||||
RET=, or via ~:sw[iper]~).
|
||||
|
||||
[[/../screenshots/modules/completion/ivy/ivy-swiper.gif]]
|
||||
|
||||
** Task lookup
|
||||
I sprinkle my projects with TODO's & FIXME's. You can navigate to and peruse
|
||||
them via ~M-x +ivy/tasks~ or ~:todo[!]~ (ex command).
|
||||
|
||||
[[/../screenshots/modules/completion/ivy/ivy-todo.gif]]
|
||||
|
||||
* Appendix
|
||||
** Commands
|
||||
Here is a list of my commonly used commands, their default keybinds (defined in
|
||||
[[../../private/default/+bindings.el][private/default/+bindings.el]]), and their corresponding ex command (defined in
|
||||
[[../../private/default/+evil-commands.el][private/default/+evil-commands.el]]).
|
||||
|
||||
| command | key / ex command | description |
|
||||
|-------------------------------------+------------------------+------------------------------------------------------------------|
|
||||
| ~counsel-M-x~ | =M-x= | Smarter, smex-powered M-x |
|
||||
| ~counsel-bookmark~ | =SPC RET= | Find bookmark |
|
||||
| ~counsel-find-file~ | =SPC f .= or =SPC .= | Browse from current directory |
|
||||
| ~counsel-projectile-find-file~ | =SPC f /= or =SPC SPC= | Find file in project |
|
||||
| ~counsel-projectile-switch-project~ | =SPC p p= | Open another project |
|
||||
| ~counsel-recentf~ | =SPC f r= | Find recently opened file |
|
||||
| ~ivy-switch-buffer~ | =SPC b b= | Jump to buffer in current workspace |
|
||||
| ~+ivy/switch-workspace-buffer~ | =SPC b B= | Jump to buffer across workspaces |
|
||||
| ~+ivy:ag~ | ~:ag[!] [QUERY]~ | Search project (BANG = ignore gitignore) |
|
||||
| ~+ivy:ag-cwd~ | ~:agcwd[!] [QUERY]~ | Search this directory (BANG = don't recurse into subdirectories) |
|
||||
| ~+ivy:rg~ | ~:rg[!] [QUERY]~ | Search project (if BANG, ignore gitignore) |
|
||||
| ~+ivy:rg-cwd~ | ~:rgcwd[!] [QUERY]~ | Search this directory (BANG = don't recurse into subdirectories) |
|
||||
| ~+ivy:swiper~ | ~:sw[iper] [QUERY]~ | Search current buffer |
|
||||
| ~+ivy:todo~ | ~:todo[!]~ | List all TODO/FIXMEs in project (or current file if BANG) |
|
||||
|
||||
While in a search (e.g. invoked from ~+ivy:ag~ or ~+ivy:rg~), these new
|
||||
keybindings are available to you:
|
||||
|
||||
| key | description |
|
||||
|-------------+--------------------------------------------------------------------------------|
|
||||
| =<backtab>= | Perform search/replace on the search results (open occur buffer in wgrep mode) |
|
||||
| =C-SPC= | Preview the current candidate |
|
||||
| =M-RET= | Open the selected candidate in other-window |
|
||||
** Plugins
|
||||
+ [[https://github.com/abo-abo/swiper][ivy]]
|
||||
+ [[https://github.com/abo-abo/swiper][counsel]]
|
||||
+ [[https://github.com/ericdanan/counsel-projectile][counsel-projectile]]
|
||||
+ [[https://github.com/abo-abo/swiper][swiper]]
|
||||
+ [[https://github.com/abo-abo/swiper][ivy-hydra]]
|
||||
+ [[https://github.com/yevgnen/ivy-rich][ivy-rich]]
|
||||
+ [[https://github.com/mhayashi1120/Emacs-wgrep][wgrep]]
|
||||
+ [[https://github.com/DarwinAwardWinner/amx][amx]]
|
||||
+ [[https://github.com/lewang/flx][flx]]* (=+fuzzy=)
|
||||
+ [[https://github.com/tumashu/ivy-posframe][ivy-posframe]]* (=+childframe=)
|
||||
|
||||
** Hacks
|
||||
+ Functions with ivy/counsel equivalents have been globally remapped (like
|
||||
|
@ -131,4 +60,161 @@ keybindings are available to you:
|
|||
+ ~counsel-[arp]g~'s 3-character limit was reduced to 1 (mainly for the ex
|
||||
command)
|
||||
|
||||
* Prerequisites
|
||||
This module optionally depends on one of:
|
||||
|
||||
+ [[https://github.com/BurntSushi/ripgrep][ripgrep]] (rg)
|
||||
+ [[https://github.com/ggreer/the_silver_searcher][the_silver_searcher]] (ag)
|
||||
+ [[https://github.com/monochromegane/the_platinum_searcher][the_platinum_searcher]] (pt)
|
||||
|
||||
Ripgrep is recommended, but the order of its results aren't deterministic and it
|
||||
doesn't support full PCRE (at the time of writing). The_silver_searcher is a
|
||||
good alternative if either of these bother you.
|
||||
|
||||
If none of these are installed, file search commands will use git-grep (falling
|
||||
back to grep, otherwise).
|
||||
|
||||
** Install
|
||||
*** MacOS
|
||||
#+BEGIN_SRC sh
|
||||
brew install ripgrep the_silver_searcher
|
||||
#+END_SRC
|
||||
|
||||
*** Arch Linux
|
||||
#+BEGIN_SRC sh :dir /sudo::
|
||||
sudo pacman --needed --noconfirm -S ripgrep the_silver_searcher
|
||||
#+END_SRC
|
||||
|
||||
* Features
|
||||
Ivy and its ilk are large plugins. Covering everything about them is outside of
|
||||
this documentation's scope, so only Doom-specific Ivy features are listed here:
|
||||
|
||||
** Jump-to-file project navigation
|
||||
Inspired by Sublime Text's jump-to-anywhere, CtrlP/Unite in Vim, and Textmate's
|
||||
Command-T, this module provides similar functionality by bringing ~projectile~
|
||||
and ~ivy~ together.
|
||||
|
||||
https://assets.doomemacs.org/completion/ivy/projectile.png
|
||||
|
||||
| Keybind | Description |
|
||||
|----------------------+-------------------------------------|
|
||||
| =SPC f /=, =SPC SPC= | Jump to file in project |
|
||||
| =SPC f .=, =SPC .= | Jump to file from current directory |
|
||||
|
||||
** Project search & replace
|
||||
This module provides interactive text search and replace using the first search
|
||||
program available on your system (rg, ag, pt, git-grep or grep).
|
||||
|
||||
| Keybind | Description |
|
||||
|----------------------+-------------------------------------|
|
||||
| =SPC / b=, =M-f= | Search the current buffer |
|
||||
| =SPC / p= | Search project |
|
||||
| =SPC / d= | Search this directory |
|
||||
| =SPC p t= | List all TODO/FIXMEs in project |
|
||||
|
||||
https://assets.doomemacs.org/completion/ivy/search.png
|
||||
|
||||
The ~+ivy-project-search-engines~ variable is consulted to determine which
|
||||
underlying program to check for (and in what order). It's default value is ~'(rg
|
||||
ag pt)~. If none of these are available, it will resort to =git-grep= (falling
|
||||
back to =grep= after that).
|
||||
|
||||
To use a specific program, the following engine-specific commands are available
|
||||
(but not bound to any key by default) for searching from the project root or the
|
||||
current directory (recursively), respectively:
|
||||
|
||||
+ ~+ivy/ag~ / ~+ivy/ag-from-cwd~
|
||||
+ ~+ivy/rg~ / ~+ivy/rg-from-cwd~
|
||||
+ ~+ivy/pt~ / ~+ivy/pt-from-cwd~
|
||||
+ ~+ivy/grep~ / ~+ivy/grep-from-cwd~
|
||||
|
||||
The universal argument (=SPC u= for evil users; =C-u= otherwise) changes the
|
||||
behavior of these commands, instructing the underlying search engine to include
|
||||
ignored files.
|
||||
|
||||
This module also provides Ex Commands for evil users:
|
||||
|
||||
| Ex command | Description |
|
||||
|-----------------------+------------------------------------------------|
|
||||
| ~:ag[!] [QUERY]~ | Search project w/ ag[fn:1] |
|
||||
| ~:rg[!] [QUERY]~ | Search project w/ rg[fn:1] |
|
||||
| ~:pt[!] [QUERY]~ | Search project w/ pt[fn:1] |
|
||||
| ~:grep[!] [QUERY]~ | Search project w/ git-grep/grep[fn:1] |
|
||||
| ~:agcwd[!] [QUERY]~ | Search this directory w/ the_silver_searcher |
|
||||
| ~:rgcwd[!] [QUERY]~ | Search this directory w/ ripgrep |
|
||||
| ~:ptcwd[!] [QUERY]~ | Search this directory w/ the_platinum_searcher |
|
||||
| ~:grepcwd[!] [QUERY]~ | Search this directory w/ git-grep/grep |
|
||||
|
||||
The optional BANG functions is equivalent to the universal argument for the
|
||||
previous commands.
|
||||
|
||||
-----
|
||||
|
||||
While in a search (e.g. invoked from ~+ivy:ag~ or ~:rg~), these extra
|
||||
keybindings are available to you:
|
||||
|
||||
| Keybind | Description |
|
||||
|---------+------------------------------------------------|
|
||||
| =S-TAB= | Open a writable buffer of your search results |
|
||||
| =C-SPC= | Preview the current candidate |
|
||||
| =M-RET= | Open the selected candidate in other-window |
|
||||
|
||||
Changes to the resulting wgrep buffer (opened by =S-TAB=) can be committed with
|
||||
=C-c C-c= and aborted with =C-c C-k=.
|
||||
|
||||
https://assets.doomemacs.org/completion/ivy/search-replace.png
|
||||
|
||||
** In-buffer searching
|
||||
The =swiper= package provides an interactive buffer search powered by ivy. It
|
||||
can be invoked with:
|
||||
|
||||
+ =SPC / b=
|
||||
+ =M-f=
|
||||
+ ~:sw[iper] [QUERY]~
|
||||
|
||||
https://assets.doomemacs.org/completion/ivy/swiper.png
|
||||
|
||||
A wgrep buffer can be opened from swiper with =S-TAB=.
|
||||
|
||||
** Task lookup
|
||||
Some projects have TODO's and FIXME's littered across them. The ~+ivy/tasks~
|
||||
command allows you to search and jump to them. It can be invoked with:
|
||||
|
||||
+ =SPC p t= (C-u = restrict search to current file)
|
||||
+ ~:todo[!]~ (BANG = restrict search to current file)
|
||||
|
||||
https://assets.doomemacs.org/completion/ivy/todo.png
|
||||
|
||||
** Ivy integration for various completing commands
|
||||
*** General
|
||||
| Keybind | Description |
|
||||
|----------------+---------------------------|
|
||||
| =M-x=, =SPC := | Smarter, smex-powered M-x |
|
||||
| =SPC '= | Resume last ivy session |
|
||||
|
||||
*** Jump to files, buffers or projects)
|
||||
| Keybind | Description |
|
||||
|---------------------------------+---------------------------------------|
|
||||
| =SPC RET= | Find bookmark |
|
||||
| =SPC f .=, =SPC .= | Browse from current directory |
|
||||
| =SPC f /=, =SPC p /=, =SPC SPC= | Find file in project |
|
||||
| =SPC f r= | Find recently opened file |
|
||||
| =SPC p p= | Open another project |
|
||||
| =SPC b b=, =SPC ,= | Switch to buffer in current workspace |
|
||||
| =SPC b B=, =SPC <= | Switch to buffer |
|
||||
|
||||
*** Search
|
||||
| Keybind | Description |
|
||||
|------------------+------------------------------------------|
|
||||
| =SPC / i= | Search for symbol in current buffer |
|
||||
| =SPC / I= | Search for symbol in all similar buffers |
|
||||
| =SPC / b=, =M-f= | Search the current buffer |
|
||||
| =SPC / p= | Search project |
|
||||
| =SPC / d= | Search this directory |
|
||||
| =SPC p t= | List all TODO/FIXMEs in project |
|
||||
|
||||
* Configuration
|
||||
** TODO Enable fuzzy/non-fuzzy search for specific commands
|
||||
** TODO Change the position of the ivy childframe
|
||||
|
||||
* TODO Troubleshooting
|
||||
|
|
|
@ -14,94 +14,55 @@
|
|||
(+ivy/tasks bang))
|
||||
|
||||
|
||||
;; --- file searching ---------------------
|
||||
;;
|
||||
;; Project searching
|
||||
|
||||
(defvar +ivy--file-last-search nil)
|
||||
(defvar +ivy--file-search-recursion-p t)
|
||||
(defvar +ivy--file-search-all-files-p nil)
|
||||
;;;###autoload (autoload '+ivy:pt "completion/ivy/autoload/evil" nil t)
|
||||
(evil-define-command +ivy:pt (all-files-p query)
|
||||
"Ex interface for `+ivy/pt'"
|
||||
(interactive "<!><a>")
|
||||
(+ivy/pt all-files-p query))
|
||||
|
||||
(defun +ivy--file-search (engine beg end query &optional directory)
|
||||
(let* ((project-root (doom-project-root))
|
||||
(directory (or directory project-root))
|
||||
(recursion-p +ivy--file-search-recursion-p)
|
||||
(all-files-p +ivy--file-search-all-files-p)
|
||||
(engine (or engine
|
||||
(and (executable-find "rg") 'rg)
|
||||
(and (executable-find "ag") 'ag)))
|
||||
(query
|
||||
(or query
|
||||
(if (evil-visual-state-p)
|
||||
(and beg end
|
||||
(> (abs (- end beg)) 1)
|
||||
(rxt-quote-pcre (buffer-substring-no-properties beg end)))
|
||||
+ivy--file-last-search)
|
||||
+ivy--file-last-search))
|
||||
(prompt
|
||||
(format "%s%%s %s"
|
||||
(symbol-name engine)
|
||||
(cond ((equal directory default-directory)
|
||||
"./")
|
||||
((equal directory project-root)
|
||||
(projectile-project-name))
|
||||
(t
|
||||
(file-relative-name directory project-root))))))
|
||||
(setq +ivy--file-last-search query)
|
||||
(pcase engine
|
||||
('ag
|
||||
(let ((args (concat
|
||||
(if all-files-p " -a")
|
||||
(unless recursion-p " -n"))))
|
||||
(counsel-ag query directory args (format prompt args))))
|
||||
('rg
|
||||
;; smart-case instead of case-insensitive flag
|
||||
(let ((counsel-rg-base-command
|
||||
(replace-regexp-in-string " -i " " -S " counsel-rg-base-command))
|
||||
(args (concat
|
||||
(if all-files-p " -uu")
|
||||
(unless recursion-p " --maxdepth 1"))))
|
||||
(counsel-rg query directory args (format prompt args))))
|
||||
('pt) ;; TODO pt search engine (necessary?)
|
||||
(_ (error "No search engine specified")))))
|
||||
;;;###autoload (autoload '+ivy:grep "completion/ivy/autoload/evil" nil t)
|
||||
(evil-define-command +ivy:grep (all-files-p query)
|
||||
"Ex interface for `+ivy/grep'"
|
||||
(interactive "<!><a>")
|
||||
(+ivy/grep all-files-p query))
|
||||
|
||||
;;;###autoload (autoload '+ivy:ag "completion/ivy/autoload/evil" nil t)
|
||||
(evil-define-operator +ivy:ag (beg end query &optional all-files-p directory)
|
||||
"Perform a project file search using the silver search. QUERY is a pcre
|
||||
regexp. If omitted, the current selection is used. If no selection is active,
|
||||
the last known search is used.
|
||||
|
||||
If ALL-FILES-P, don't respect .gitignore files and search everything."
|
||||
(interactive "<r><a><!>")
|
||||
(let ((+ivy--file-search-all-files-p all-files-p))
|
||||
(+ivy--file-search 'ag beg end query directory)))
|
||||
(evil-define-command +ivy:ag (all-files-p query)
|
||||
"Ex interface for `+ivy/ag'"
|
||||
(interactive "<!><a>")
|
||||
(+ivy/ag all-files-p query))
|
||||
|
||||
;;;###autoload (autoload '+ivy:rg "completion/ivy/autoload/evil" nil t)
|
||||
(evil-define-operator +ivy:rg (beg end query &optional all-files-p directory)
|
||||
"Perform a project file search using ripgrep. QUERY is a regexp. If omitted,
|
||||
the current selection is used. If no selection is active, the last known search
|
||||
is used.
|
||||
|
||||
If ALL-FILES-P, don't respect .gitignore files and search everything.
|
||||
|
||||
NOTE: ripgrep doesn't support multiline searches (yet)."
|
||||
(interactive "<r><a><!>")
|
||||
(let ((+ivy--file-search-all-files-p all-files-p))
|
||||
(+ivy--file-search 'rg beg end query directory)))
|
||||
(evil-define-command +ivy:rg (all-files-p query)
|
||||
"Ex interface for `+ivy/rg'"
|
||||
(interactive "<!><a>")
|
||||
(+ivy/rg all-files-p query))
|
||||
|
||||
|
||||
;;;###autoload (autoload '+ivy:ag-cwd "completion/ivy/autoload/evil" nil t)
|
||||
(evil-define-operator +ivy:ag-cwd (beg end query &optional bang)
|
||||
"The same as :ag, but searches the current directory. If BANG, don't recurse
|
||||
into sub-directories."
|
||||
(interactive "<r><a><!>")
|
||||
(let ((+ivy--file-search-recursion-p (not bang)))
|
||||
(+ivy:ag beg end query t default-directory)))
|
||||
;;;###autoload (autoload '+ivy:pt-from-cwd "completion/ivy/autoload/evil" nil t)
|
||||
(evil-define-command +ivy:pt-from-cwd (query &optional recurse-p)
|
||||
"Ex interface for `+ivy/pt-from-cwd'."
|
||||
(interactive "<a><!>")
|
||||
(+ivy/pt-from-cwd (not recurse-p) query))
|
||||
|
||||
;;;###autoload (autoload '+ivy:rg-cwd "completion/ivy/autoload/evil" nil t)
|
||||
(evil-define-operator +ivy:rg-cwd (beg end query &optional bang)
|
||||
"The same as :rg, but only searches the current directory. If BANG, don't
|
||||
recurse into sub-directories.
|
||||
;;;###autoload (autoload '+ivy:grep-from-cwd "completion/ivy/autoload/evil" nil t)
|
||||
(evil-define-command +ivy:grep-from-cwd (query &optional recurse-p)
|
||||
"Ex interface for `+ivy/grep-from-cwd'."
|
||||
(interactive "<a><!>")
|
||||
(+ivy/grep-from-cwd (not recurse-p) query))
|
||||
|
||||
;;;###autoload (autoload '+ivy:ag-from-cwd "completion/ivy/autoload/evil" nil t)
|
||||
(evil-define-command +ivy:ag-from-cwd (query &optional recurse-p)
|
||||
"Ex interface for `+ivy/ag-from-cwd'."
|
||||
(interactive "<a><!>")
|
||||
(+ivy/ag-from-cwd (not recurse-p) query))
|
||||
|
||||
;;;###autoload (autoload '+ivy:rg-from-cwd "completion/ivy/autoload/evil" nil t)
|
||||
(evil-define-command +ivy:rg-from-cwd (query &optional recurse-p)
|
||||
"Ex interface for `+ivy/rg-from-cwd'."
|
||||
(interactive "<a><!>")
|
||||
(+ivy/rg-from-cwd (not recurse-p) query))
|
||||
|
||||
NOTE: ripgrep doesn't support multiline searches (yet)."
|
||||
(interactive "<r><a><!>")
|
||||
(let ((+ivy--file-search-recursion-p (not bang)))
|
||||
(+ivy:rg beg end query t default-directory)))
|
||||
|
|
31
modules/completion/ivy/autoload/hydras.el
Normal file
31
modules/completion/ivy/autoload/hydras.el
Normal file
|
@ -0,0 +1,31 @@
|
|||
;;; completion/ivy/autoload/hydras.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autoload
|
||||
(after! ivy-hydra
|
||||
(defhydra+ hydra-ivy (:hint nil :color pink)
|
||||
"
|
||||
Move ^^^^^^^^^^ | Call ^^^^ | Cancel^^ | Options^^ | Action _w_/_s_/_a_: %s(ivy-action-name)
|
||||
----------^^^^^^^^^^-+--------------^^^^-+-------^^-+--------^^-+---------------------------------
|
||||
_g_ ^ ^ _k_ ^ ^ _u_ | _f_orward _o_ccur | _i_nsert | _c_alling: %-7s(if ivy-calling \"on\" \"off\") _C_ase-fold: %-10`ivy-case-fold-search
|
||||
^↨^ _h_ ^+^ _l_ ^↕^ | _RET_ done ^^ | _q_uit | _m_atcher: %-7s(ivy--matcher-desc) _t_runcate: %-11`truncate-lines
|
||||
_G_ ^ ^ _j_ ^ ^ _d_ | _TAB_ alt-done ^^ | ^ ^ | _<_/_>_: shrink/grow
|
||||
"
|
||||
;; arrows
|
||||
("l" ivy-alt-done)
|
||||
("h" ivy-backward-delete-char)
|
||||
("g" ivy-beginning-of-buffer)
|
||||
("G" ivy-end-of-buffer)
|
||||
("d" ivy-scroll-up-command)
|
||||
("u" ivy-scroll-down-command)
|
||||
("e" ivy-scroll-down-command)
|
||||
;; actions
|
||||
("q" keyboard-escape-quit :exit t)
|
||||
("<escape>" keyboard-escape-quit :exit t)
|
||||
("TAB" ivy-alt-done :exit nil)
|
||||
("RET" ivy-done :exit t)
|
||||
("C-SPC" ivy-call-and-recenter :exit nil)
|
||||
("f" ivy-call)
|
||||
("c" ivy-toggle-calling)
|
||||
("m" ivy-toggle-fuzzy)
|
||||
("t" (setq truncate-lines (not truncate-lines)))
|
||||
("o" ivy-occur :exit t)))
|
|
@ -1,36 +1,91 @@
|
|||
;;; completion/ivy/autoload/ivy.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defsubst +ivy--icon-for-mode (mode)
|
||||
"Apply `all-the-icons-for-mode' on MODE but either return an icon or nil."
|
||||
(let ((icon (all-the-icons-icon-for-mode mode)))
|
||||
(unless (symbolp icon) icon)))
|
||||
(defun +ivy--is-workspace-buffer-p (buffer)
|
||||
(let ((buffer (car buffer)))
|
||||
(when (stringp buffer)
|
||||
(setq buffer (get-buffer buffer)))
|
||||
(+workspace-contains-buffer-p buffer)))
|
||||
|
||||
(defun +ivy--is-workspace-other-buffer-p (buffer)
|
||||
(let ((buffer (car buffer)))
|
||||
(when (stringp buffer)
|
||||
(setq buffer (get-buffer buffer)))
|
||||
(and (not (eq buffer (current-buffer)))
|
||||
(+workspace-contains-buffer-p buffer))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +ivy-buffer-transformer (str)
|
||||
(let* ((buf (get-buffer str))
|
||||
(path (buffer-file-name buf))
|
||||
(mode (buffer-local-value 'major-mode buf))
|
||||
(faces
|
||||
(with-current-buffer buf
|
||||
(cond ((string-match-p "^ ?\\*" (buffer-name buf))
|
||||
'font-lock-comment-face)
|
||||
((buffer-modified-p buf)
|
||||
'doom-modeline-buffer-modified)
|
||||
(buffer-read-only
|
||||
'error)))))
|
||||
(propertize
|
||||
(format "%-40s %s%-20s %s"
|
||||
str
|
||||
(if +ivy-buffer-icons
|
||||
(concat (propertize " " 'display
|
||||
(or (+ivy--icon-for-mode mode)
|
||||
(+ivy--icon-for-mode (get mode 'derived-mode-parent))))
|
||||
"\t")
|
||||
"")
|
||||
mode
|
||||
(or (and path (abbreviate-file-name (file-name-directory (file-truename path))))
|
||||
""))
|
||||
'face faces)))
|
||||
(defun +ivy-rich-buffer-name (candidate)
|
||||
"Display the buffer name.
|
||||
|
||||
Buffers that are considered unreal (see `doom-real-buffer-p') are dimmed with
|
||||
`+ivy-buffer-unreal-face'."
|
||||
(let ((b (get-buffer candidate)))
|
||||
(cond ((ignore-errors
|
||||
(file-remote-p
|
||||
(buffer-local-value 'default-directory b)))
|
||||
(ivy-append-face candidate 'ivy-remote))
|
||||
((doom-unreal-buffer-p b)
|
||||
(ivy-append-face candidate +ivy-buffer-unreal-face))
|
||||
((not (buffer-file-name b))
|
||||
(ivy-append-face candidate 'ivy-subdir))
|
||||
((buffer-modified-p b)
|
||||
(ivy-append-face candidate 'ivy-modified-buffer))
|
||||
(candidate))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +ivy-rich-buffer-icon (candidate)
|
||||
"Display the icon for CANDIDATE buffer.
|
||||
|
||||
Otherwise show the fundamental-mode icon."
|
||||
(with-current-buffer candidate
|
||||
(let ((icon (all-the-icons-icon-for-mode major-mode)))
|
||||
(if (symbolp icon)
|
||||
(all-the-icons-icon-for-mode 'fundamental-mode)
|
||||
icon))))
|
||||
|
||||
|
||||
;;
|
||||
;; Library
|
||||
|
||||
(defun +ivy--switch-buffer-preview ()
|
||||
(let (ivy-use-virtual-buffers ivy--virtual-buffers)
|
||||
(counsel--switch-buffer-update-fn)))
|
||||
|
||||
(defalias '+ivy--switch-buffer-preview-all #'counsel--switch-buffer-update-fn)
|
||||
(defalias '+ivy--switch-buffer-unwind #'counsel--switch-buffer-unwind)
|
||||
|
||||
(defun +ivy--switch-buffer (workspace other)
|
||||
(let ((current (not other))
|
||||
prompt action filter update unwind)
|
||||
(cond ((and workspace current)
|
||||
(setq prompt "Switch to workspace buffer: "
|
||||
action #'ivy--switch-buffer-action
|
||||
filter #'+ivy--is-workspace-other-buffer-p))
|
||||
(workspace
|
||||
(setq prompt "Switch to workspace buffer in other window: "
|
||||
action #'ivy--switch-buffer-other-window-action
|
||||
filter #'+ivy--is-workspace-buffer-p))
|
||||
(current
|
||||
(setq prompt "Switch to buffer: "
|
||||
action #'ivy--switch-buffer-action))
|
||||
((setq prompt "Switch to buffer in other window: "
|
||||
action #'ivy--switch-buffer-other-window-action)))
|
||||
(when +ivy-buffer-preview
|
||||
(cond ((not (and ivy-use-virtual-buffers
|
||||
(eq +ivy-buffer-preview 'everything)))
|
||||
(setq update #'+ivy--switch-buffer-preview
|
||||
unwind #'+ivy--switch-buffer-unwind))
|
||||
((setq update #'+ivy--switch-buffer-preview-all
|
||||
unwind #'+ivy--switch-buffer-unwind))))
|
||||
(ivy-read prompt 'internal-complete-buffer
|
||||
:action action
|
||||
:predicate filter
|
||||
:update-fn update
|
||||
:unwind unwind
|
||||
:preselect (buffer-name (other-buffer (current-buffer)))
|
||||
:matcher #'ivy--switch-buffer-matcher
|
||||
:keymap ivy-switch-buffer-map
|
||||
:caller #'+ivy--switch-buffer)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +ivy/switch-workspace-buffer (&optional arg)
|
||||
|
@ -38,14 +93,25 @@
|
|||
|
||||
If ARG (universal argument), open selection in other-window."
|
||||
(interactive "P")
|
||||
(ivy-read "Switch to workspace buffer: "
|
||||
(mapcar #'buffer-name (delq (current-buffer) (doom-buffer-list)))
|
||||
:action (if arg
|
||||
#'ivy--switch-buffer-other-window-action
|
||||
#'ivy--switch-buffer-action)
|
||||
:matcher #'ivy--switch-buffer-matcher
|
||||
:keymap ivy-switch-buffer-map
|
||||
:caller #'+ivy/switch-workspace-buffer))
|
||||
(+ivy--switch-buffer t arg))
|
||||
|
||||
;;;###autoload
|
||||
(defun +ivy/switch-workspace-buffer-other-window ()
|
||||
"Switch another window to a buffer within the current workspace."
|
||||
(interactive)
|
||||
(+ivy--switch-buffer t t))
|
||||
|
||||
;;;###autoload
|
||||
(defun +ivy/switch-buffer ()
|
||||
"Switch to another buffer."
|
||||
(interactive)
|
||||
(+ivy--switch-buffer nil nil))
|
||||
|
||||
;;;###autoload
|
||||
(defun +ivy/switch-buffer-other-window ()
|
||||
"Switch to another buffer in another window."
|
||||
(interactive)
|
||||
(+ivy--switch-buffer nil t))
|
||||
|
||||
(defun +ivy--tasks-candidates (tasks)
|
||||
"Generate a list of task tags (specified by `+ivy-task-tags') for
|
||||
|
@ -93,9 +159,9 @@ If ARG (universal argument), open selection in other-window."
|
|||
"\\):?\\s-*\\(.+\\)")
|
||||
x)
|
||||
(error
|
||||
(message! (red "Error matching task in file: (%s) %s"
|
||||
(error-message-string ex)
|
||||
(car (split-string x ":"))))
|
||||
(print! (red "Error matching task in file: (%s) %s")
|
||||
(error-message-string ex)
|
||||
(car (split-string x ":")))
|
||||
nil))
|
||||
collect `((type . ,(match-string 3 x))
|
||||
(desc . ,(match-string 4 x))
|
||||
|
@ -124,44 +190,13 @@ search current file. See `+ivy-task-tags' to customize what this searches for."
|
|||
(if arg
|
||||
(concat "in: " (file-relative-name buffer-file-name))
|
||||
"project"))
|
||||
(+ivy--tasks-candidates
|
||||
(+ivy--tasks (if arg buffer-file-name (doom-project-root))))
|
||||
(let ((tasks (+ivy--tasks (if arg buffer-file-name (doom-project-root)))))
|
||||
(unless tasks
|
||||
(user-error "No tasks in your project! Good job!"))
|
||||
(+ivy--tasks-candidates tasks))
|
||||
:action #'+ivy--tasks-open-action
|
||||
:caller '+ivy/tasks))
|
||||
|
||||
;;;###autoload
|
||||
(defun +ivy*counsel-ag-function (string base-cmd extra-ag-args)
|
||||
"Advice to 1) get rid of the character limit from `counsel-ag-function' and 2)
|
||||
disable ivy's over-zealous parentheses quoting behavior (if i want literal
|
||||
parentheses, I'll escape them myself).
|
||||
|
||||
NOTE This may need to be updated frequently, to meet changes upstream (in
|
||||
counsel-rg)."
|
||||
(when (null extra-ag-args)
|
||||
(setq extra-ag-args ""))
|
||||
(if (< (length string) 1) ;; #1
|
||||
(counsel-more-chars 1)
|
||||
(let ((default-directory counsel--git-dir)
|
||||
(regex (counsel-unquote-regex-parens
|
||||
(setq ivy--old-re
|
||||
(ivy--regex string)))))
|
||||
(let* ((args-end (string-match " -- " extra-ag-args))
|
||||
(file (if args-end
|
||||
(substring-no-properties extra-ag-args (+ args-end 3))
|
||||
""))
|
||||
(extra-ag-args (if args-end
|
||||
(substring-no-properties extra-ag-args 0 args-end)
|
||||
extra-ag-args))
|
||||
(ag-cmd (format base-cmd
|
||||
(concat extra-ag-args
|
||||
" -- "
|
||||
(shell-quote-argument regex)
|
||||
file))))
|
||||
(if (file-remote-p default-directory)
|
||||
(split-string (shell-command-to-string ag-cmd) "\n" t)
|
||||
(counsel--async-command ag-cmd)
|
||||
nil)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +ivy/wgrep-occur ()
|
||||
"Invoke the search+replace wgrep buffer on the current ag/rg search results."
|
||||
|
@ -200,7 +235,7 @@ counsel-rg)."
|
|||
(with-ivy-window
|
||||
(let ((file-name (match-string-no-properties 1 x))
|
||||
(line-number (match-string-no-properties 2 x)))
|
||||
(find-file-other-window (expand-file-name file-name counsel--git-dir))
|
||||
(find-file-other-window (expand-file-name file-name (ivy-state-directory ivy-last)))
|
||||
(goto-char (point-min))
|
||||
(forward-line (1- (string-to-number line-number)))
|
||||
(re-search-forward (ivy--regex ivy-text t) (line-end-position) t)
|
||||
|
@ -208,10 +243,169 @@ counsel-rg)."
|
|||
(selected-window))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +ivy-quit-and-resume ()
|
||||
"Close the current popup window and resume ivy."
|
||||
(interactive)
|
||||
(when (doom-popup-p)
|
||||
(doom/popup-close))
|
||||
(ivy-resume))
|
||||
(defun +ivy-confirm-delete-file (x)
|
||||
(dired-delete-file x 'confirm-each-subdirectory))
|
||||
|
||||
|
||||
;;
|
||||
;; File searching
|
||||
|
||||
;;;###autoload
|
||||
(defun +ivy/projectile-find-file ()
|
||||
"A more sensible `counsel-projectile-find-file', which will revert to
|
||||
`counsel-find-file' if invoked from $HOME, `counsel-file-jump' if invoked from a
|
||||
non-project, `projectile-find-file' if in a big project (more than
|
||||
`ivy-sort-max-size' files), or `counsel-projectile-find-file' otherwise.
|
||||
|
||||
The point of this is to avoid Emacs locking up indexing massive file trees."
|
||||
(interactive)
|
||||
(call-interactively
|
||||
(cond ((or (file-equal-p default-directory "~")
|
||||
(when-let* ((proot (doom-project-root)))
|
||||
(file-equal-p proot "~")))
|
||||
#'counsel-find-file)
|
||||
|
||||
((doom-project-p)
|
||||
(let ((files (projectile-current-project-files)))
|
||||
(if (<= (length files) ivy-sort-max-size)
|
||||
#'counsel-projectile-find-file
|
||||
#'projectile-find-file)))
|
||||
|
||||
(#'counsel-file-jump))))
|
||||
|
||||
;;;###autoload
|
||||
(cl-defun +ivy-file-search (engine &key query in all-files (recursive t))
|
||||
"Conduct a file search using ENGINE, which can be any of: rg, ag, pt, and
|
||||
grep. If omitted, ENGINE will default to the first one it detects, in that
|
||||
order.
|
||||
|
||||
:query STRING
|
||||
Determines the initial input to search for.
|
||||
:in PATH
|
||||
Sets what directory to base the search out of. Defaults to the current
|
||||
project's root.
|
||||
:recursive BOOL
|
||||
Whether or not to search files recursively from the base directory."
|
||||
(declare (indent defun))
|
||||
(let* ((project-root (or (doom-project-root) default-directory))
|
||||
(directory (or in project-root))
|
||||
(default-directory directory)
|
||||
(engine (or engine
|
||||
(cl-loop for tool in +ivy-project-search-engines
|
||||
if (executable-find (symbol-name tool))
|
||||
return tool)
|
||||
(and (or (executable-find "grep")
|
||||
(executable-find "git"))
|
||||
'grep)
|
||||
(error "No search engine specified (is ag, rg, pt or git installed?)")))
|
||||
(query
|
||||
(or (if query (rxt-quote-pcre query))
|
||||
(when (use-region-p)
|
||||
(let ((beg (or (bound-and-true-p evil-visual-beginning) (region-beginning)))
|
||||
(end (or (bound-and-true-p evil-visual-end) (region-end))))
|
||||
(when (> (abs (- end beg)) 1)
|
||||
(rxt-quote-pcre (buffer-substring-no-properties beg end)))))))
|
||||
(prompt
|
||||
(format "%s%%s %s"
|
||||
(symbol-name engine)
|
||||
(cond ((equal directory default-directory)
|
||||
"./")
|
||||
((equal directory project-root)
|
||||
(projectile-project-name))
|
||||
((file-relative-name directory project-root))))))
|
||||
(require 'counsel)
|
||||
(let ((ivy-more-chars-alist
|
||||
(if query '((t . 1)) ivy-more-chars-alist)))
|
||||
(pcase engine
|
||||
('grep
|
||||
(let ((args (if recursive " -R"))
|
||||
(counsel-projectile-grep-initial-input query))
|
||||
(if all-files
|
||||
(cl-letf (((symbol-function #'projectile-ignored-directories-rel)
|
||||
(symbol-function #'ignore))
|
||||
((symbol-function #'projectile-ignored-files-rel)
|
||||
(symbol-function #'ignore)))
|
||||
(counsel-projectile-grep args))
|
||||
(counsel-projectile-grep args))))
|
||||
('ag
|
||||
(let ((args (concat (if all-files " -a")
|
||||
(unless recursive " --depth 1"))))
|
||||
(counsel-ag query directory args (format prompt args))))
|
||||
('rg
|
||||
(let ((args (concat (if all-files " -uu")
|
||||
(unless recursive " --maxdepth 1"))))
|
||||
(counsel-rg query directory args (format prompt args))))
|
||||
('pt
|
||||
(let ((counsel-pt-base-command
|
||||
(concat counsel-pt-base-command
|
||||
(if all-files " -U")
|
||||
(unless recursive " --depth=1")))
|
||||
(default-directory directory))
|
||||
(counsel-pt query)))
|
||||
(_ (error "No search engine specified"))))))
|
||||
|
||||
(defun +ivy--get-command (format)
|
||||
(cl-loop for tool in (cl-remove-duplicates +ivy-project-search-engines :from-end t)
|
||||
if (executable-find (symbol-name tool))
|
||||
return (intern (format format tool))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +ivy/project-search (&optional arg initial-query directory)
|
||||
"Performs a project search from the project root.
|
||||
|
||||
Uses the first available search backend from `+ivy-project-search-engines'. If
|
||||
ARG (universal argument), include all files, even hidden or compressed ones, in
|
||||
the search."
|
||||
(interactive "P")
|
||||
(funcall (or (+ivy--get-command "+ivy/%s")
|
||||
#'+ivy/grep)
|
||||
arg
|
||||
initial-query
|
||||
directory))
|
||||
|
||||
;;;###autoload
|
||||
(defun +ivy/project-search-from-cwd (&optional arg initial-query)
|
||||
"Performs a project search recursively from the current directory.
|
||||
|
||||
Uses the first available search backend from `+ivy-project-search-engines'. If
|
||||
ARG (universal argument), include all files, even hidden or compressed ones."
|
||||
(interactive "P")
|
||||
(funcall (or (+ivy--get-command "+ivy/%s-from-cwd")
|
||||
#'+ivy/grep-from-cwd)
|
||||
arg
|
||||
initial-query))
|
||||
|
||||
|
||||
;;;###autoload (autoload '+ivy/rg "completion/ivy/autoload/ivy")
|
||||
;;;###autoload (autoload '+ivy/rg-from-cwd "completion/ivy/autoload/ivy")
|
||||
;;;###autoload (autoload '+ivy/ag "completion/ivy/autoload/ivy")
|
||||
;;;###autoload (autoload '+ivy/ag-from-cwd "completion/ivy/autoload/ivy")
|
||||
;;;###autoload (autoload '+ivy/pt "completion/ivy/autoload/ivy")
|
||||
;;;###autoload (autoload '+ivy/pt-from-cwd "completion/ivy/autoload/ivy")
|
||||
;;;###autoload (autoload '+ivy/grep "completion/ivy/autoload/ivy")
|
||||
;;;###autoload (autoload '+ivy/grep-from-cwd "completion/ivy/autoload/ivy")
|
||||
|
||||
(dolist (engine `(,@(cl-remove-duplicates +ivy-project-search-engines :from-end t) grep))
|
||||
(defalias (intern (format "+ivy/%s" engine))
|
||||
(lambda (all-files-p &optional query directory)
|
||||
(interactive "P")
|
||||
(+ivy-file-search engine :query query :in directory :all-files all-files-p))
|
||||
(format "Perform a project file search using %s.
|
||||
|
||||
QUERY is a regexp. If omitted, the current selection is used. If no selection is
|
||||
active, the last known search is used.
|
||||
|
||||
If ALL-FILES-P, search compressed and hidden files as well."
|
||||
engine))
|
||||
|
||||
(defalias (intern (format "+ivy/%s-from-cwd" engine))
|
||||
(lambda (all-files-p &optional query)
|
||||
(interactive "P")
|
||||
(+ivy-file-search engine :query query :in default-directory :all-files all-files-p))
|
||||
(format "Perform a project file search from the current directory using %s.
|
||||
|
||||
QUERY is a regexp. If omitted, the current selection is used. If no selection is
|
||||
active, the last known search is used.
|
||||
|
||||
If ALL-FILES-P, search compressed and hidden files as well."
|
||||
engine)))
|
||||
|
|
16
modules/completion/ivy/autoload/posframe.el
Normal file
16
modules/completion/ivy/autoload/posframe.el
Normal file
|
@ -0,0 +1,16 @@
|
|||
;;; completion/ivy/autoload/posframe.el -*- lexical-binding: t; -*-
|
||||
;;;###if (featurep! +childframe)
|
||||
|
||||
;;;###autoload
|
||||
(defun +ivy-display-at-frame-center-near-bottom (str)
|
||||
"TODO"
|
||||
(ivy-posframe--display str #'+ivy-poshandler-frame-center-near-bottom))
|
||||
|
||||
;;;###autoload
|
||||
(defun +ivy-poshandler-frame-center-near-bottom (info)
|
||||
"TODO"
|
||||
(let ((parent-frame (plist-get info :parent-frame))
|
||||
(pos (posframe-poshandler-frame-center info)))
|
||||
(cons (car pos)
|
||||
(truncate (/ (frame-pixel-height parent-frame) 2)))))
|
||||
|
|
@ -3,12 +3,32 @@
|
|||
(defvar +ivy-buffer-icons nil
|
||||
"If non-nil, show buffer mode icons in `ivy-switch-buffer' and the like.")
|
||||
|
||||
(defvar +ivy-buffer-preview nil
|
||||
"If non-nil, preview buffers while switching, à la `counsel-switch-buffer'.
|
||||
|
||||
When nil, don't preview anything.
|
||||
When non-nil, preview non-virtual buffers.
|
||||
When 'everything, also preview virtual buffers")
|
||||
|
||||
(defvar +ivy-task-tags
|
||||
'(("TODO" . warning)
|
||||
("FIXME" . error))
|
||||
"An alist of tags for `+ivy/tasks' to include in its search, whose CDR is the
|
||||
face to render it with.")
|
||||
|
||||
(defvar +ivy-project-search-engines '(rg ag pt)
|
||||
"What search tools for `+ivy/project-search' (and `+ivy-file-search' when no
|
||||
ENGINE is specified) to try, and in what order.
|
||||
|
||||
To disable a particular tool, remove it from this list. To prioritize a tool
|
||||
over others, move it to the front of the list. Later duplicates in this list are
|
||||
silently ignored.
|
||||
|
||||
If you want to already use git-grep or grep, set this to nil.")
|
||||
|
||||
(defvar +ivy-buffer-unreal-face 'font-lock-comment-face
|
||||
"The face for unreal buffers in `ivy-switch-to-buffer'.")
|
||||
|
||||
(defmacro +ivy-do-action! (action)
|
||||
"Returns an interactive lambda that sets the current ivy action and
|
||||
immediately runs it on the current candidate (ending the ivy session)."
|
||||
|
@ -20,133 +40,274 @@ immediately runs it on the current candidate (ending the ivy session)."
|
|||
|
||||
|
||||
;;
|
||||
;; Packages
|
||||
;;
|
||||
;;; Packages
|
||||
|
||||
(def-package! ivy
|
||||
:init
|
||||
(add-hook 'doom-post-init-hook #'ivy-mode)
|
||||
:defer 1
|
||||
:after-call pre-command-hook
|
||||
:config
|
||||
(setq ivy-height 12
|
||||
ivy-do-completion-in-region nil
|
||||
(setq ivy-height 15
|
||||
ivy-wrap t
|
||||
ivy-fixed-height-minibuffer t
|
||||
projectile-completion-system 'ivy
|
||||
smex-completion-method 'ivy
|
||||
;; Don't use ^ as initial input
|
||||
ivy-initial-inputs-alist nil
|
||||
;; highlight til EOL
|
||||
ivy-format-function #'ivy-format-function-line
|
||||
;; disable magic slash on non-match
|
||||
ivy-magic-slash-non-match-action nil)
|
||||
ivy-magic-slash-non-match-action nil
|
||||
;; don't show recent files in switch-buffer
|
||||
ivy-use-virtual-buffers nil
|
||||
;; ...but if that ever changes, show their full path
|
||||
ivy-virtual-abbreviate 'full
|
||||
;; don't quit minibuffer on delete-error
|
||||
ivy-on-del-error-function nil
|
||||
;; enable ability to select prompt (alternative to `ivy-immediate-done')
|
||||
ivy-use-selectable-prompt t)
|
||||
|
||||
(after! magit (setq magit-completing-read-function #'ivy-completing-read))
|
||||
(after! yasnippet (push #'+ivy-yas-prompt yas-prompt-functions))
|
||||
(after! yasnippet
|
||||
(add-to-list 'yas-prompt-functions #'+ivy-yas-prompt nil #'eq))
|
||||
|
||||
(map! [remap apropos] #'counsel-apropos
|
||||
[remap describe-face] #'counsel-describe-face
|
||||
[remap find-file] #'counsel-find-file
|
||||
[remap switch-to-buffer] #'ivy-switch-buffer
|
||||
[remap persp-switch-to-buffer] #'+ivy/switch-workspace-buffer
|
||||
[remap recentf-open-files] #'counsel-recentf
|
||||
[remap imenu] #'counsel-imenu
|
||||
[remap bookmark-jump] #'counsel-bookmark
|
||||
[remap projectile-find-file] #'counsel-projectile-find-file
|
||||
[remap imenu-anywhere] #'ivy-imenu-anywhere
|
||||
[remap execute-extended-command] #'counsel-M-x
|
||||
[remap describe-face] #'counsel-describe-face)
|
||||
(define-key! ivy-mode-map
|
||||
[remap switch-to-buffer] #'+ivy/switch-buffer
|
||||
[remap switch-to-buffer-other-window] #'+ivy/switch-buffer-other-window
|
||||
[remap persp-switch-to-buffer] #'+ivy/switch-workspace-buffer
|
||||
[remap imenu-anywhere] #'ivy-imenu-anywhere)
|
||||
|
||||
;; Show more buffer information in switch-buffer commands
|
||||
(ivy-set-display-transformer #'ivy-switch-buffer #'+ivy-buffer-transformer)
|
||||
(ivy-set-display-transformer #'ivy-switch-buffer-other-window #'+ivy-buffer-transformer)
|
||||
(ivy-set-display-transformer #'+ivy/switch-workspace-buffer #'+ivy-buffer-transformer)
|
||||
(ivy-set-display-transformer #'counsel-recentf #'abbreviate-file-name)
|
||||
(ivy-mode +1)
|
||||
|
||||
(nconc ivy-sort-functions-alist
|
||||
'((persp-kill-buffer . nil)
|
||||
(persp-remove-buffer . nil)
|
||||
(persp-add-buffer . nil)
|
||||
(persp-switch . nil)
|
||||
(persp-window-switch . nil)
|
||||
(persp-frame-switch . nil)
|
||||
(+workspace/switch-to . nil)
|
||||
(+workspace/delete . nil))))
|
||||
(def-package! ivy-hydra
|
||||
:commands (ivy-dispatching-done-hydra ivy--matcher-desc ivy-hydra/body)
|
||||
:init
|
||||
(define-key! ivy-minibuffer-map
|
||||
"C-o" #'ivy-dispatching-done-hydra
|
||||
"M-o" #'hydra-ivy/body)
|
||||
:config
|
||||
;; ivy-hydra rebinds this, so we have to do so again
|
||||
(define-key ivy-minibuffer-map (kbd "M-o") #'hydra-ivy/body)))
|
||||
|
||||
|
||||
(def-package! swiper :commands (swiper swiper-all))
|
||||
(def-package! ivy-rich
|
||||
:hook (ivy-mode . ivy-rich-mode)
|
||||
:config
|
||||
(when +ivy-buffer-icons
|
||||
(cl-pushnew '(+ivy-rich-buffer-icon (:width 2 :align right))
|
||||
(cadr (plist-get ivy-rich-display-transformers-list
|
||||
'ivy-switch-buffer)))
|
||||
(after! counsel-projectile
|
||||
(setq ivy-rich-display-transformers-list
|
||||
(plist-put ivy-rich-display-transformers-list
|
||||
'counsel-projectile-switch-project
|
||||
'(:columns
|
||||
(((lambda (_) (all-the-icons-octicon "file-directory"))
|
||||
(:width 2 :align right))
|
||||
(ivy-rich-candidate)))))
|
||||
(setq ivy-rich-display-transformers-list
|
||||
(plist-put ivy-rich-display-transformers-list
|
||||
'counsel-projectile-find-file
|
||||
'(:columns
|
||||
((all-the-icons-icon-for-file (:width 2 :align right))
|
||||
(ivy-rich-candidate)))))))
|
||||
|
||||
;; Remove built-in coloring of buffer list; we do our own
|
||||
(setq ivy-switch-buffer-faces-alist nil)
|
||||
(ivy-set-display-transformer 'internal-complete-buffer nil)
|
||||
|
||||
;; Highlight buffers differently based on whether they're in the same project
|
||||
;; as the current project or not.
|
||||
(let* ((plist (plist-get ivy-rich-display-transformers-list 'ivy-switch-buffer))
|
||||
(switch-buffer-alist (assq 'ivy-rich-candidate (plist-get plist :columns))))
|
||||
(when switch-buffer-alist
|
||||
(setcar switch-buffer-alist '+ivy-rich-buffer-name)))
|
||||
|
||||
;; Allow these transformers to apply to more switch-buffer commands
|
||||
(let ((ivy-switch-buffer-transformer (plist-get ivy-rich-display-transformers-list 'ivy-switch-buffer)))
|
||||
(dolist (cmd '(+ivy--switch-buffer counsel-projectile-switch-to-buffer))
|
||||
(setq ivy-rich-display-transformers-list
|
||||
(plist-put ivy-rich-display-transformers-list
|
||||
cmd ivy-switch-buffer-transformer)))))
|
||||
|
||||
|
||||
(def-package! counsel
|
||||
:requires ivy
|
||||
:commands counsel-describe-face
|
||||
:init
|
||||
(map! [remap apropos] #'counsel-apropos
|
||||
[remap bookmark-jump] #'counsel-bookmark
|
||||
[remap describe-face] #'counsel-faces
|
||||
[remap describe-function] #'counsel-describe-function
|
||||
[remap describe-variable] #'counsel-describe-variable
|
||||
[remap describe-bindings] #'counsel-descbinds
|
||||
[remap set-variable] #'counsel-set-variable
|
||||
[remap execute-extended-command] #'counsel-M-x
|
||||
[remap find-file] #'counsel-find-file
|
||||
[remap find-library] #'counsel-find-library
|
||||
[remap info-lookup-symbol] #'counsel-info-lookup-symbol
|
||||
[remap imenu] #'counsel-imenu
|
||||
[remap recentf-open-files] #'counsel-recentf
|
||||
[remap org-capture] #'counsel-org-capture
|
||||
[remap swiper] #'counsel-grep-or-swiper
|
||||
[remap evil-ex-registers] #'counsel-evil-registers
|
||||
[remap yank-pop] #'counsel-yank-pop)
|
||||
:config
|
||||
(require 'counsel-projectile)
|
||||
(setq counsel-find-file-ignore-regexp "\\(?:^[#.]\\)\\|\\(?:[#~]$\\)\\|\\(?:^Icon?\\)")
|
||||
(set-popup-rule! "^\\*ivy-occur" :size 0.35 :ttl 0 :quit nil)
|
||||
|
||||
;; Configure `counsel-rg', `counsel-ag' & `counsel-pt'
|
||||
(set! :popup 'ivy-occur-grep-mode :size (+ 2 ivy-height) :regexp t :autokill t)
|
||||
(dolist (cmd '(counsel-ag counsel-rg counsel-pt))
|
||||
(ivy-add-actions
|
||||
cmd
|
||||
'(("O" +ivy-git-grep-other-window-action "open in other window"))))
|
||||
(setq counsel-find-file-ignore-regexp "\\(?:^[#.]\\)\\|\\(?:[#~]$\\)\\|\\(?:^Icon?\\)"
|
||||
counsel-describe-function-function #'helpful-callable
|
||||
counsel-describe-variable-function #'helpful-variable
|
||||
;; Add smart-casing (-S) to default command arguments:
|
||||
counsel-rg-base-command "rg -S --no-heading --line-number --color never %s ."
|
||||
counsel-ag-base-command "ag -S --nocolor --nogroup %s"
|
||||
counsel-pt-base-command "pt -S --nocolor --nogroup -e %s")
|
||||
|
||||
;; 1. Remove character limit from `counsel-ag-function'
|
||||
;; 2. This may need to be updated frequently, to meet changes upstream
|
||||
;; 3. counsel-ag, counsel-rg and counsel-pt all use this function
|
||||
(advice-add #'counsel-ag-function :override #'+ivy*counsel-ag-function))
|
||||
(add-to-list 'swiper-font-lock-exclude #'+doom-dashboard-mode nil #'eq)
|
||||
|
||||
;; Factories
|
||||
(defun +ivy-action-reloading (cmd)
|
||||
(lambda (x)
|
||||
(funcall cmd x)
|
||||
(ivy--reset-state ivy-last)))
|
||||
|
||||
(defun +ivy-action-given-file (cmd prompt)
|
||||
(lambda (source)
|
||||
(let* ((enable-recursive-minibuffers t)
|
||||
(target (read-file-name (format "%s %s to:" prompt source))))
|
||||
(funcall cmd source target 1))))
|
||||
|
||||
;; Configure `counsel-find-file'
|
||||
(ivy-add-actions
|
||||
'counsel-find-file
|
||||
`(("b" counsel-find-file-cd-bookmark-action "cd bookmark")
|
||||
("s" counsel-find-file-as-root "open as root")
|
||||
("m" counsel-find-file-mkdir-action "mkdir")
|
||||
("c" ,(+ivy-action-given-file #'copy-file "Copy file") "copy file")
|
||||
("d" ,(+ivy-action-reloading #'+ivy-confirm-delete-file) "delete")
|
||||
("r" (lambda (path) (rename-file path (read-string "New name: "))) "rename")
|
||||
("R" ,(+ivy-action-reloading (+ivy-action-given-file #'rename-file "Move")) "move")
|
||||
("f" find-file-other-window "other window")
|
||||
("F" find-file-other-frame "other frame")
|
||||
("p" (lambda (path) (with-ivy-window (insert (file-relative-name path default-directory)))) "insert relative path")
|
||||
("P" (lambda (path) (with-ivy-window (insert path))) "insert absolute path")
|
||||
("l" (lambda (path) "Insert org-link with relative path"
|
||||
(with-ivy-window (insert (format "[[./%s]]" (file-relative-name path default-directory))))) "insert org-link (rel. path)")
|
||||
("L" (lambda (path) "Insert org-link with absolute path"
|
||||
(with-ivy-window (insert (format "[[%s]]" path)))) "insert org-link (abs. path)")))
|
||||
|
||||
(ivy-add-actions
|
||||
'counsel-ag ; also applies to `counsel-rg' & `counsel-pt'
|
||||
'(("O" +ivy-git-grep-other-window-action "open in other window"))))
|
||||
|
||||
|
||||
(def-package! counsel-projectile
|
||||
:commands (counsel-projectile-find-file counsel-projectile-find-dir counsel-projectile-switch-to-buffer
|
||||
counsel-projectile-grep counsel-projectile-ag counsel-projectile-switch-project)
|
||||
:init
|
||||
(map! [remap projectile-find-file] #'+ivy/projectile-find-file
|
||||
[remap projectile-find-dir] #'counsel-projectile-find-dir
|
||||
[remap projectile-switch-to-buffer] #'counsel-projectile-switch-to-buffer
|
||||
[remap projectile-grep] #'counsel-projectile-grep
|
||||
[remap projectile-ag] #'counsel-projectile-ag
|
||||
[remap projectile-switch-project] #'counsel-projectile-switch-project)
|
||||
:config
|
||||
;; no highlighting visited files; slows down the filtering
|
||||
(ivy-set-display-transformer #'counsel-projectile-find-file nil))
|
||||
|
||||
|
||||
(def-package! wgrep
|
||||
:commands wgrep-change-to-wgrep-mode
|
||||
:config (setq wgrep-auto-save-buffer t))
|
||||
|
||||
|
||||
(def-package! ivy-posframe
|
||||
:when (and EMACS26+ (featurep! +childframe))
|
||||
:hook (ivy-mode . ivy-posframe-enable)
|
||||
:preface
|
||||
;; This function searches the entire `obarray' just to populate
|
||||
;; `ivy-display-functions-props'. There are 15k entries in mine! This is
|
||||
;; wasteful, so...
|
||||
(advice-add #'ivy-posframe-setup :override #'ignore)
|
||||
:config
|
||||
(setq ivy-fixed-height-minibuffer nil
|
||||
ivy-posframe-parameters
|
||||
`((min-width . 90)
|
||||
(min-height . ,ivy-height)
|
||||
(internal-border-width . 10)))
|
||||
|
||||
;; ... let's do it manually instead
|
||||
(unless (assq 'ivy-posframe-display-at-frame-bottom-left ivy-display-functions-props)
|
||||
(dolist (fn (list 'ivy-posframe-display-at-frame-bottom-left
|
||||
'ivy-posframe-display-at-frame-center
|
||||
'ivy-posframe-display-at-point
|
||||
'ivy-posframe-display-at-frame-bottom-window-center
|
||||
'ivy-posframe-display
|
||||
'ivy-posframe-display-at-window-bottom-left
|
||||
'ivy-posframe-display-at-window-center
|
||||
'+ivy-display-at-frame-center-near-bottom))
|
||||
(push (cons fn '(:cleanup ivy-posframe-cleanup)) ivy-display-functions-props)))
|
||||
;; default to posframe display function
|
||||
(setf (alist-get t ivy-display-functions-alist) #'+ivy-display-at-frame-center-near-bottom)
|
||||
|
||||
;; Fix #1017: stop session persistence from restoring a broken posframe
|
||||
(defun +workspace|delete-all-posframes (&rest _) (posframe-delete-all))
|
||||
(add-hook 'persp-after-load-state-functions #'+workspace|delete-all-posframes)
|
||||
|
||||
;; posframe doesn't work well with async sources
|
||||
(dolist (fn '(swiper counsel-ag counsel-grep counsel-git-grep))
|
||||
(setf (alist-get fn ivy-display-functions-alist) #'ivy-display-function-fallback)))
|
||||
|
||||
|
||||
(def-package! flx
|
||||
:when (featurep! +fuzzy)
|
||||
:defer t ; is loaded by ivy
|
||||
:init
|
||||
(setq ivy-re-builders-alist
|
||||
'((counsel-ag . ivy--regex-plus)
|
||||
(counsel-rg . ivy--regex-plus)
|
||||
(counsel-grep . ivy--regex-plus)
|
||||
(swiper . ivy--regex-plus)
|
||||
(swiper-isearch . ivy--regex-plus)
|
||||
(t . ivy--regex-fuzzy))
|
||||
ivy-initial-inputs-alist nil))
|
||||
|
||||
|
||||
;; Used by `counsel-M-x'
|
||||
(def-package! smex
|
||||
:commands (smex smex-major-mode-commands)
|
||||
:config
|
||||
(setq smex-save-file (concat doom-cache-dir "/smex-items"))
|
||||
(smex-initialize))
|
||||
(setq amx-save-file (concat doom-cache-dir "amx-items"))
|
||||
|
||||
|
||||
(def-package! ivy-hydra
|
||||
:commands (+ivy@coo/body ivy-dispatching-done-hydra)
|
||||
:init
|
||||
(map! :map ivy-minibuffer-map
|
||||
"C-o" #'+ivy@coo/body
|
||||
"M-o" #'ivy-dispatching-done-hydra)
|
||||
:config
|
||||
(def-hydra! +ivy@coo (:hint nil :color pink)
|
||||
"
|
||||
Move ^^^^^^^^^^ | Call ^^^^ | Cancel^^ | Options^^ | Action _w_/_s_/_a_: %s(ivy-action-name)
|
||||
----------^^^^^^^^^^-+--------------^^^^-+-------^^-+--------^^-+---------------------------------
|
||||
_g_ ^ ^ _k_ ^ ^ _u_ | _f_orward _o_ccur | _i_nsert | _c_alling: %-7s(if ivy-calling \"on\" \"off\") _C_ase-fold: %-10`ivy-case-fold-search
|
||||
^↨^ _h_ ^+^ _l_ ^↕^ | _RET_ done ^^ | _q_uit | _m_atcher: %-7s(ivy--matcher-desc) _t_runcate: %-11`truncate-lines
|
||||
_G_ ^ ^ _j_ ^ ^ _d_ | _TAB_ alt-done ^^ | ^ ^ | _<_/_>_: shrink/grow
|
||||
"
|
||||
;; arrows
|
||||
("j" ivy-next-line)
|
||||
("k" ivy-previous-line)
|
||||
("l" ivy-alt-done)
|
||||
("h" ivy-backward-delete-char)
|
||||
("g" ivy-beginning-of-buffer)
|
||||
("G" ivy-end-of-buffer)
|
||||
("d" ivy-scroll-up-command)
|
||||
("u" ivy-scroll-down-command)
|
||||
("e" ivy-scroll-down-command)
|
||||
;; actions
|
||||
("q" keyboard-escape-quit :exit t)
|
||||
("C-g" keyboard-escape-quit :exit t)
|
||||
("<escape>" keyboard-escape-quit :exit t)
|
||||
("C-o" nil)
|
||||
("i" nil)
|
||||
("TAB" ivy-alt-done :exit nil)
|
||||
("C-j" ivy-alt-done :exit nil)
|
||||
("RET" ivy-done :exit t)
|
||||
("C-m" ivy-done :exit t)
|
||||
("C-SPC" ivy-call-and-recenter :exit nil)
|
||||
("f" ivy-call)
|
||||
("c" ivy-toggle-calling)
|
||||
("m" ivy-toggle-fuzzy)
|
||||
(">" ivy-minibuffer-grow)
|
||||
("<" ivy-minibuffer-shrink)
|
||||
("w" ivy-prev-action)
|
||||
("s" ivy-next-action)
|
||||
("a" ivy-read-action)
|
||||
("t" (setq truncate-lines (not truncate-lines)))
|
||||
("C" ivy-toggle-case-fold)
|
||||
("o" ivy-occur :exit t)))
|
||||
;;
|
||||
;; Evil key fixes
|
||||
|
||||
(map! :when (featurep! :feature evil +everywhere)
|
||||
:after ivy
|
||||
:map (ivy-occur-mode-map ivy-occur-grep-mode-map)
|
||||
:m "j" #'ivy-occur-next-line
|
||||
:m "k" #'ivy-occur-previous-line
|
||||
:m "h" #'evil-backward-char
|
||||
:m "l" #'evil-forward-char
|
||||
:m "g" nil
|
||||
:m "gg" #'evil-goto-first-line
|
||||
:map ivy-occur-mode-map
|
||||
:n [mouse-1] #'ivy-occur-click
|
||||
:n [return] #'ivy-occur-press-and-switch
|
||||
:n "gf" #'ivy-occur-press
|
||||
:n "ga" #'ivy-occur-read-action
|
||||
:n "go" #'ivy-occur-dispatch
|
||||
:n "gc" #'ivy-occur-toggle-calling
|
||||
:n "gr" #'ivy-occur-revert-buffer
|
||||
:n "q" #'quit-window
|
||||
:map ivy-occur-grep-mode-map
|
||||
:v "j" #'evil-next-line
|
||||
:v "k" #'evil-previous-line
|
||||
:n "D" #'ivy-occur-delete-candidate
|
||||
:n "C-d" #'evil-scroll-down
|
||||
:n "d" #'ivy-occur-delete-candidate
|
||||
:n "C-x C-q" #'ivy-wgrep-change-to-wgrep-mode
|
||||
:n "i" #'ivy-wgrep-change-to-wgrep-mode
|
||||
:n "gd" #'ivy-occur-delete-candidate
|
||||
:n [mouse-1] #'ivy-occur-click
|
||||
:n [return] #'ivy-occur-press-and-switch
|
||||
:n "gf" #'ivy-occur-press
|
||||
:n "gr" #'ivy-occur-revert-buffer
|
||||
:n "ga" #'ivy-occur-read-action
|
||||
:n "go" #'ivy-occur-dispatch
|
||||
:n "gc" #'ivy-occur-toggle-calling
|
||||
:n "q" #'quit-window)
|
||||
|
|
5
modules/completion/ivy/doctor.el
Normal file
5
modules/completion/ivy/doctor.el
Normal file
|
@ -0,0 +1,5 @@
|
|||
;; -*- lexical-binding: t; no-byte-compile: t; -*-
|
||||
;;; completion/ivy/doctor.el
|
||||
|
||||
(when (and (not EMACS26+) (featurep! +childframe))
|
||||
(error! "The +childframe feature requires Emacs 26+"))
|
|
@ -1,9 +1,17 @@
|
|||
;; -*- no-byte-compile: t; -*-
|
||||
;;; completion/ivy/packages.el
|
||||
|
||||
(package! amx)
|
||||
(package! ivy)
|
||||
(package! counsel)
|
||||
(package! counsel-projectile)
|
||||
(package! smex)
|
||||
(package! swiper)
|
||||
(package! ivy-hydra)
|
||||
(package! ivy-rich)
|
||||
(package! wgrep)
|
||||
|
||||
(when (featurep! +fuzzy)
|
||||
(package! flx))
|
||||
|
||||
(when (and EMACS26+ (featurep! +childframe))
|
||||
(package! ivy-posframe))
|
||||
|
|
353
modules/config/default/+emacs-bindings.el
Normal file
353
modules/config/default/+emacs-bindings.el
Normal file
|
@ -0,0 +1,353 @@
|
|||
;;; config/default/+emacs-bindings.el -*- lexical-binding: t; -*-
|
||||
|
||||
;; Sensible deafult key bindings for non-evil users
|
||||
(setq doom-leader-alt-key "C-c"
|
||||
doom-localleader-alt-key "C-c l")
|
||||
|
||||
;; persp-mode and projectile in different prefixes
|
||||
(setq persp-keymap-prefix (kbd "C-c w"))
|
||||
(after! projectile
|
||||
(define-key projectile-mode-map (kbd "C-c p") 'projectile-command-map))
|
||||
|
||||
(after! which-key
|
||||
(which-key-add-key-based-replacements "C-c !" "checking")
|
||||
(which-key-add-key-based-replacements "C-c l" "<localleader>"))
|
||||
|
||||
|
||||
;;
|
||||
;;; Global keybinds
|
||||
|
||||
(map! "C-'" #'imenu
|
||||
;; Text scaling
|
||||
"<C-mouse-4>" #'text-scale-increase
|
||||
"<C-mouse-5>" #'text-scale-decrease
|
||||
"<C-down-mouse-2>" (λ! (text-scale-set 0))
|
||||
"M-+" (λ! (text-scale-set 0))
|
||||
"M-=" #'text-scale-increase
|
||||
"M--" #'text-scale-decrease
|
||||
;; Editor related bindings
|
||||
[remap newline] #'newline-and-indent
|
||||
"C-j" #'+default/newline
|
||||
(:when (featurep! :completion ivy)
|
||||
"C-S-s" #'swiper
|
||||
"C-S-r" #'ivy-resume)
|
||||
(:when (featurep! :completion helm)
|
||||
"C-S-s" #'swiper-helm
|
||||
"C-S-r" #'helm-resume)
|
||||
;; Buffer related bindings
|
||||
"C-x b" #'persp-switch-to-buffer
|
||||
(:when (featurep! :completion ivy)
|
||||
"C-x 4 b" #'+ivy/switch-workspace-buffer-other-window)
|
||||
"C-x C-b" #'ibuffer-list-buffers
|
||||
"C-x B" #'switch-to-buffer
|
||||
"C-x 4 B" #'switch-to-buffer-other-window
|
||||
"C-x k" #'doom/kill-this-buffer-in-all-windows
|
||||
;; Popup bindigns
|
||||
"C-x p" #'+popup/other
|
||||
"C-`" #'+popup/toggle
|
||||
"C-~" #'+popup/raise)
|
||||
|
||||
|
||||
;;
|
||||
;;; Leader keys
|
||||
|
||||
(map! :leader
|
||||
:desc "Find file in project" "C-f" #'projectile-find-file
|
||||
:desc "Evaluate line/region" "e" #'+eval/line-or-region
|
||||
:desc "Open scratch buffer" "x" #'doom/open-scratch-buffer
|
||||
:desc "Open project scratch buffer" "X" #'doom/open-project-scratch-buffer
|
||||
|
||||
(:when (featurep! :emacs term)
|
||||
:desc "Terminal" "`" #'+term/open
|
||||
:desc "Terminal in popup" "~" #'+term/open-popup-in-project)
|
||||
(:when (featurep! :tools vterm)
|
||||
:desc "Terminal" "`" #'+vterm/open
|
||||
:desc "Terminal in popup" "~" #'+vterm/open-popup-in-project)
|
||||
(:when (featurep! :emacs eshell)
|
||||
:desc "Eshell" "`" #'+eshell/open
|
||||
:desc "Eshell in popup" "~" #'+eshell/open-popup)
|
||||
|
||||
;; Add labels to prefixes defined elsewhere
|
||||
:desc "project" "p" nil
|
||||
|
||||
(:prefix ("f" . "file")
|
||||
:desc "Find other file" "a" #'projectile-find-other-file
|
||||
:desc "Browse private config" "c" #'doom/open-private-config
|
||||
:desc "Find file in private config" "C" #'doom/find-file-in-private-config
|
||||
:desc "Open project editorconfig" "." #'editorconfig-find-current-editorconfig
|
||||
:desc "Find directory" "d" #'dired
|
||||
:desc "Find file in emacs.d" "e" #'+default/find-in-emacsd
|
||||
:desc "Browse emacs.d" "E" #'+default/browse-emacsd
|
||||
:desc "Find file from here" "f" (if (fboundp 'counsel-file-jump) #'counsel-file-jump #'find-file)
|
||||
:desc "Find file in other project" "F" #'doom/browse-in-other-project
|
||||
:desc "Find file in project" "p" #'projectile-find-file
|
||||
:desc "Find file in other project" "P" #'doom/find-file-in-other-project
|
||||
:desc "Recent files" "r" #'recentf-open-files
|
||||
:desc "Recent project files" "R" #'projectile-recentf
|
||||
:desc "Sudo this file" "s" #'doom/sudo-this-file
|
||||
:desc "Sudo find file" "S" #'doom/sudo-find-file
|
||||
:desc "Delete this file" "X" #'doom/delete-this-file
|
||||
:desc "Yank filename" "y" #'+default/yank-buffer-filename)
|
||||
|
||||
"o" nil ; we need to unbind it first as Org claims this
|
||||
(:prefix ("o". "org")
|
||||
(:prefix ("a" . "org agenda")
|
||||
:desc "Agenda" "a" #'org-agenda
|
||||
:desc "Todo list" "t" #'org-todo-list
|
||||
:desc "Tags view" "m" #'org-tags-view
|
||||
:desc "View search" "v" #'org-search-view)
|
||||
:desc "Switch org buffers" "b" #'org-switchb
|
||||
:desc "Capture" "c" #'org-capture
|
||||
:desc "Goto capture" "C" (λ! (require 'org-capture) (call-interactively #'org-capture-goto-target))
|
||||
:desc "Link store" "l" #'org-store-link
|
||||
:desc "Sync org caldav" "s" #'org-caldav-sync)
|
||||
|
||||
(:prefix ("q" . "quit/restart")
|
||||
:desc "Quit Emacs" "q" #'kill-emacs
|
||||
:desc "Save and quit Emacs" "Q" #'save-buffers-kill-terminal
|
||||
(:when (featurep! :feature workspaces)
|
||||
:desc "Quit Emacs & forget session" "X" #'+workspace/kill-session-and-quit)
|
||||
:desc "Restart & restore Emacs" "r" #'doom/restart-and-restore
|
||||
:desc "Restart Emacs" "R" #'doom/restart)
|
||||
|
||||
(:prefix ("&" . "snippets")
|
||||
:desc "New snippet" "n" #'yas-new-snippet
|
||||
:desc "Insert snippet" "i" #'yas-insert-snippet
|
||||
:desc "Find global snippet" "/" #'yas-visit-snippet-file
|
||||
:desc "Reload snippets" "r" #'yas-reload-all
|
||||
:desc "Create Temp Template" "c" #'aya-create
|
||||
:desc "Use Temp Template" "e" #'aya-expand)
|
||||
|
||||
(:prefix ("v" . "versioning")
|
||||
:desc "Git revert file" "R" #'vc-revert
|
||||
(:when (featurep! :ui vc-gutter)
|
||||
:desc "Git revert hunk" "r" #'git-gutter:revert-hunk
|
||||
:desc "Git stage hunk" "s" #'git-gutter:stage-hunk
|
||||
:desc "Git time machine" "t" #'git-timemachine-toggle
|
||||
:desc "Jump to next hunk" "n" #'git-gutter:next-hunk
|
||||
:desc "Jump to previous hunk" "p" #'git-gutter:previous-hunk)
|
||||
(:when (featurep! :tools magit)
|
||||
:desc "Magit dispatch" "/" #'magit-dispatch
|
||||
:desc "Forge dispatch" "'" #'forge-dispatch
|
||||
:desc "Magit status" "g" #'magit-status
|
||||
:desc "Magit file delete" "x" #'magit-file-delete
|
||||
:desc "Magit blame" "B" #'magit-blame-addition
|
||||
:desc "Magit clone" "C" #'+magit/clone
|
||||
:desc "Magit fetch" "F" #'magit-fetch
|
||||
:desc "Magit buffer log" "L" #'magit-log
|
||||
:desc "Git stage file" "S" #'magit-stage-file
|
||||
:desc "Git unstage file" "U" #'magit-unstage-file
|
||||
(:prefix ("f" . "find")
|
||||
:desc "Find file" "f" #'magit-find-file
|
||||
:desc "Find gitconfig file" "g" #'magit-find-git-config-file
|
||||
:desc "Find commit" "c" #'magit-show-commit
|
||||
:desc "Find issue" "i" #'forge-visit-issue
|
||||
:desc "Find pull request" "p" #'forge-visit-pullreq)
|
||||
(:prefix ("o" . "open in browser")
|
||||
:desc "Browse region or line" "." #'+vc/git-browse-region-or-line
|
||||
:desc "Browse remote" "r" #'forge-browse-remote
|
||||
:desc "Browse commit" "c" #'forge-browse-commit
|
||||
:desc "Browse an issue" "i" #'forge-browse-issue
|
||||
:desc "Browse a pull request" "p" #'forge-browse-pullreq
|
||||
:desc "Browse issues" "I" #'forge-browse-issues
|
||||
:desc "Browse pull requests" "P" #'forge-browse-pullreqs)
|
||||
(:prefix ("l" . "list")
|
||||
(:when (featurep! :tools gist)
|
||||
:desc "List gists" "g" #'+gist:list)
|
||||
:desc "List repositories" "r" #'magit-list-repositories
|
||||
:desc "List submodules" "s" #'magit-list-submodules
|
||||
:desc "List issues" "i" #'forge-list-issues
|
||||
:desc "List pull requests" "p" #'forge-list-pullreqs
|
||||
:desc "List notifications" "n" #'forge-list-notifications)
|
||||
(:prefix ("c" . "create")
|
||||
:desc "Initialize repo" "r" #'magit-init
|
||||
:desc "Clone repo" "R" #'+magit/clone
|
||||
:desc "Commit" "c" #'magit-commit-create
|
||||
:desc "Issue" "i" #'forge-create-issue
|
||||
:desc "Pull request" "p" #'forge-create-pullreq)))
|
||||
|
||||
(:prefix ("w" . "workspaces/windows")
|
||||
:desc "Autosave session" "a" #'doom/quicksave-session
|
||||
:desc "Display workspaces" "d" #'+workspace/display
|
||||
:desc "Rename workspace" "r" #'+workspace/rename
|
||||
:desc "Create workspace" "c" #'+workspace/new
|
||||
:desc "Delete workspace" "k" #'+workspace/delete
|
||||
:desc "Save session" "s" #'doom/save-session
|
||||
:desc "Save workspace" "S" #'+workspace/save
|
||||
:desc "Load session" "l" #'doom/load-session
|
||||
:desc "Load last autosaved session" "L" #'doom/quickload-session
|
||||
:desc "Kill other buffers" "o" #'doom/kill-other-buffers
|
||||
:desc "Undo window config" "u" #'winner-undo
|
||||
:desc "Redo window config" "U" #'winner-redo
|
||||
:desc "Switch to left workspace" "p" #'+workspace/switch-left
|
||||
:desc "Switch to right workspace" "n" #'+workspace/switch-right
|
||||
:desc "Switch to" "w" #'+workspace/switch-to
|
||||
:desc "Switch to workspace 1" "1" (λ! (+workspace/switch-to 0))
|
||||
:desc "Switch to workspace 2" "2" (λ! (+workspace/switch-to 1))
|
||||
:desc "Switch to workspace 3" "3" (λ! (+workspace/switch-to 2))
|
||||
:desc "Switch to workspace 4" "4" (λ! (+workspace/switch-to 3))
|
||||
:desc "Switch to workspace 5" "5" (λ! (+workspace/switch-to 4))
|
||||
:desc "Switch to workspace 6" "6" (λ! (+workspace/switch-to 5))
|
||||
:desc "Switch to workspace 7" "7" (λ! (+workspace/switch-to 6))
|
||||
:desc "Switch to workspace 8" "8" (λ! (+workspace/switch-to 7))
|
||||
:desc "Switch to workspace 9" "9" (λ! (+workspace/switch-to 8))
|
||||
:desc "Switch to last workspace" "0" #'+workspace/switch-to-last)
|
||||
|
||||
(:when (featurep! :editor multiple-cursors)
|
||||
(:prefix ("m" . "multiple cursors")
|
||||
:desc "Edit lines" "l" #'mc/edit-lines
|
||||
:desc "Mark next" "n" #'mc/mark-next-like-this
|
||||
:desc "Unmark next" "N" #'mc/unmark-next-like-this
|
||||
:desc "Mark previous" "p" #'mc/mark-previous-like-this
|
||||
:desc "Unmark previous" "P" #'mc/unmark-previous-like-this
|
||||
:desc "Mark all" "t" #'mc/mark-all-like-this
|
||||
:desc "Mark all DWIM" "m" #'mc/mark-all-like-this-dwim
|
||||
:desc "Edit line endings" "e" #'mc/edit-ends-of-lines
|
||||
:desc "Edit line starts" "a" #'mc/edit-beginnings-of-lines
|
||||
:desc "Mark tag" "s" #'mc/mark-sgml-tag-pair
|
||||
:desc "Mark in defun" "d" #'mc/mark-all-like-this-in-defun
|
||||
:desc "Add cursor w/mouse" "<mouse-1>" #'mc/add-cursor-on-click))
|
||||
|
||||
;; APPs
|
||||
(:when (featurep! :app email)
|
||||
(:prefix ("M" . "email")
|
||||
:desc "Open email app" "M" #'=email
|
||||
:desc "Compose email" "c" #'+email/compose))
|
||||
|
||||
(:when (featurep! :app irc)
|
||||
(:prefix ("I" . "irc")
|
||||
:desc "Open irc app" "I" #'=irc
|
||||
:desc "Next unread buffer" "a" #'tracking-next-buffer
|
||||
:desc "Quit irc" "q" #'+irc/quit
|
||||
:desc "Reconnect all" "r" #'circe-reconnect-all
|
||||
:desc "Send message" "s" #'+irc/send-message
|
||||
(:when (featurep! :completion ivy)
|
||||
:desc "Jump to channel" "j" #'irc/ivy-jump-to-channel)))
|
||||
|
||||
(:when (featurep! :app twitter)
|
||||
(:prefix ("T" . "twitter")
|
||||
:desc "Open twitter app" "T" #'=twitter
|
||||
:desc "Quit twitter" "q" #'+twitter/quit
|
||||
:desc "Rerender twits" "r" #'+twitter/rerender-all
|
||||
:desc "Ace link" "l" #'+twitter/ace-link)))
|
||||
|
||||
|
||||
;;
|
||||
;;; Plugins
|
||||
|
||||
(map! "C-=" #'er/expand-region
|
||||
"C--" #'er/contract-region
|
||||
(:when (featurep! :ui neotree)
|
||||
"<f9>" #'+neotree/open
|
||||
"<F-f9>" #'+neotree/find-this-file)
|
||||
(:when (featurep! :ui treemacs)
|
||||
"<f9>" #'+treemacs/open
|
||||
"<F-f9>" #'+treemacs/find-file)
|
||||
;; smartparens
|
||||
(:after smartparens
|
||||
:map smartparens-mode-map
|
||||
"C-M-a" #'sp-beginning-of-sexp
|
||||
"C-M-e" #'sp-end-of-sexp
|
||||
"C-M-f" #'sp-forward-sexp
|
||||
"C-M-b" #'sp-backward-sexp
|
||||
"C-M-d" #'sp-splice-sexp
|
||||
"C-M-k" #'sp-kill-sexp
|
||||
"C-M-t" #'sp-transpose-sexp
|
||||
"C-<right>" #'sp-forward-slurp-sexp
|
||||
"M-<right>" #'sp-forward-barf-sexp
|
||||
"C-<left>" #'sp-backward-slurp-sexp
|
||||
"M-<left>" #'sp-backward-barf-sexp)
|
||||
;; company mode
|
||||
"C-;" #'+company/complete
|
||||
;; Counsel
|
||||
(:when (featurep! :completion ivy)
|
||||
(:after counsel
|
||||
:map counsel-ag-map
|
||||
"C-c C-e" #'+ivy/wgrep-occur ; search/replace on results
|
||||
[backtab] #'+ivy/wgrep-occur ; search/replace on results
|
||||
"C-SPC" #'ivy-call-and-recenter ; preview
|
||||
"M-RET" (+ivy-do-action! #'+ivy-git-grep-other-window-action))
|
||||
"C-M-y" #'counsel-yank-pop)
|
||||
;; repl toggle
|
||||
"C-c C-z" #'+eval/open-repl-other-window
|
||||
;; company mode
|
||||
(:after company
|
||||
:map company-active-map
|
||||
"C-o" #'company-search-kill-others
|
||||
"C-n" #'company-select-next
|
||||
"C-p" #'company-select-previous
|
||||
"C-h" #'company-quickhelp-manual-begin
|
||||
"C-S-h" #'company-show-doc-buffer
|
||||
"C-s" #'company-search-candidates
|
||||
"M-s" #'company-filter-candidates
|
||||
"<C-tab>" #'company-complete-common-or-cycle
|
||||
[tab] #'company-complete-common-or-cycle
|
||||
[backtab] #'company-select-previous
|
||||
"C-RET" #'counsel-company
|
||||
:map company-search-map
|
||||
"C-n" #'company-search-repeat-forward
|
||||
"C-p" #'company-search-repeat-backward
|
||||
"C-s" (λ! (company-search-abort) (company-filter-candidates)))
|
||||
;; neotree bindings
|
||||
(:after neotree
|
||||
:map neotree-mode-map
|
||||
"q" #'neotree-hide
|
||||
"RET" #'neotree-enter
|
||||
"SPC" #'neotree-quick-look
|
||||
"v" #'neotree-enter-vertical-split
|
||||
"s" #'neotree-enter-horizontal-split
|
||||
"c" #'neotree-create-node
|
||||
"D" #'neotree-delete-node
|
||||
"g" #'neotree-refresh
|
||||
"r" #'neotree-rename-node
|
||||
"R" #'neotree-refresh
|
||||
"h" #'+neotree/collapse-or-up
|
||||
"l" #'+neotree/expand-or-open
|
||||
"n" #'neotree-next-line
|
||||
"p" #'neotree-previous-line
|
||||
"N" #'neotree-select-next-sibling-node
|
||||
"P" #'neotree-select-previous-sibling-node)
|
||||
;; help and info
|
||||
(:after help-mode
|
||||
:map help-mode-map
|
||||
"o" #'ace-link-help
|
||||
">" #'help-go-forward
|
||||
"<" #'help-go-back
|
||||
"n" #'forward-button
|
||||
"p" #'backward-button)
|
||||
(:after helpful
|
||||
:map helpful-mode-map
|
||||
"o" #'ace-link-help)
|
||||
(:after apropos
|
||||
:map apropos-mode-map
|
||||
"o" #'ace-link-help
|
||||
"n" #'forward-button
|
||||
"p" #'backward-button)
|
||||
(:after info
|
||||
:map Info-mode-map
|
||||
"o" #'ace-link-info)
|
||||
;; yasnippet
|
||||
(:after yasnippet
|
||||
;; keymap while editing an inserted snippet
|
||||
:map yas-keymap
|
||||
"C-e" #'+snippets/goto-end-of-field
|
||||
"C-a" #'+snippets/goto-start-of-field
|
||||
"<S-tab>" #'yas-prev-field
|
||||
"<M-backspace>" #'+snippets/delete-to-start-of-field
|
||||
[backspace] #'+snippets/delete-backward-char
|
||||
[delete] #'+snippets/delete-forward-char-or-field)
|
||||
;; flycheck
|
||||
(:after flycheck
|
||||
:map flycheck-error-list-mode-map
|
||||
"C-n" #'flycheck-error-list-next-error
|
||||
"C-p" #'flycheck-error-list-previous-error
|
||||
"RET" #'flycheck-error-list-goto-error)
|
||||
;; ivy
|
||||
(:after ivy
|
||||
:map ivy-minibuffer-map
|
||||
"TAB" #'ivy-alt-done
|
||||
"C-g" #'keyboard-escape-quit)
|
||||
;; ein notebokks
|
||||
(:after ein:notebook-multilang
|
||||
:map ein:notebook-multilang-mode-map
|
||||
"C-c h" #'+ein/hydra/body))
|
32
modules/config/default/+emacs.el
Normal file
32
modules/config/default/+emacs.el
Normal file
|
@ -0,0 +1,32 @@
|
|||
;;; config/default/+emacs.el -*- lexical-binding: t; -*-
|
||||
|
||||
(require 'projectile) ; we need its keybinds immediately
|
||||
|
||||
|
||||
;;
|
||||
;;; Reasonable defaults
|
||||
|
||||
(setq shift-select-mode t)
|
||||
(delete-selection-mode +1)
|
||||
|
||||
(def-package! expand-region
|
||||
:commands (er/contract-region er/mark-symbol er/mark-word)
|
||||
:config
|
||||
(defun doom*quit-expand-region ()
|
||||
"Properly abort an expand-region region."
|
||||
(when (memq last-command '(er/expand-region er/contract-region))
|
||||
(er/contract-region 0)))
|
||||
(advice-add #'evil-escape :before #'doom*quit-expand-region)
|
||||
(advice-add #'doom/escape :before #'doom*quit-expand-region))
|
||||
|
||||
|
||||
(def-package! winum
|
||||
:after-call (doom-switch-window-hook)
|
||||
:config (winum-mode +1))
|
||||
|
||||
|
||||
;;
|
||||
;;; Keybinds
|
||||
|
||||
(when (featurep! +bindings)
|
||||
(load! "+emacs-bindings"))
|
871
modules/config/default/+evil-bindings.el
Normal file
871
modules/config/default/+evil-bindings.el
Normal file
|
@ -0,0 +1,871 @@
|
|||
;;; config/default/+bindings.el -*- lexical-binding: t; -*-
|
||||
|
||||
;; This file defines a Spacemacs-esque keybinding scheme
|
||||
|
||||
;; Don't let evil-collection interfere with certain keys
|
||||
(setq evil-collection-key-blacklist
|
||||
(list "C-j" "C-k" "gd" "gf" "K" "[" "]" "gz"
|
||||
doom-leader-key doom-localleader-key
|
||||
doom-leader-alt-key doom-localleader-alt-key))
|
||||
|
||||
|
||||
;;
|
||||
;;; Global keybindings
|
||||
|
||||
(map! (:map override
|
||||
;; A little sandbox to run code in
|
||||
"M-;" #'eval-expression
|
||||
"A-;" #'eval-expression)
|
||||
|
||||
;; Smart tab
|
||||
:i [tab] (general-predicate-dispatch nil ; fall back to nearest keymap
|
||||
(and (featurep! :feature snippets)
|
||||
(bound-and-true-p yas-minor-mode)
|
||||
(yas-maybe-expand-abbrev-key-filter 'yas-expand))
|
||||
'yas-expand
|
||||
(and (featurep! :completion company +tng)
|
||||
(+company-has-completion-p))
|
||||
'+company/complete)
|
||||
:n [tab] (general-predicate-dispatch nil
|
||||
(and (featurep! :editor fold)
|
||||
(save-excursion (end-of-line) (invisible-p (point))))
|
||||
'+fold/toggle
|
||||
(fboundp 'evilmi-jump-items)
|
||||
'evilmi-jump-items)
|
||||
:v [tab] (general-predicate-dispatch nil
|
||||
(and (bound-and-true-p yas-minor-mode)
|
||||
(or (eq evil-visual-selection 'line)
|
||||
(and (fboundp 'evilmi-jump-items)
|
||||
(save-excursion
|
||||
(/= (point)
|
||||
(progn (evilmi-jump-items nil)
|
||||
(point)))))))
|
||||
'yas-insert-snippet
|
||||
(fboundp 'evilmi-jump-items)
|
||||
'evilmi-jump-items)
|
||||
|
||||
;; Smarter newlines
|
||||
:i [remap newline] #'newline-and-indent ; auto-indent on newline
|
||||
:i "C-j" #'+default/newline ; default behavior
|
||||
|
||||
(:after vc-annotate
|
||||
:map vc-annotate-mode-map
|
||||
[remap quit-window] #'kill-this-buffer)
|
||||
|
||||
(:map (help-mode-map helpful-mode-map)
|
||||
:n "o" 'ace-link-help)
|
||||
|
||||
;; misc
|
||||
:n "C-S-f" #'toggle-frame-fullscreen
|
||||
|
||||
;; Global evil keybinds
|
||||
:m "]a" #'evil-forward-arg
|
||||
:m "[a" #'evil-backward-arg
|
||||
:m "]o" #'outline-next-visible-heading
|
||||
:m "[o" #'outline-previous-visible-heading
|
||||
:n "]b" #'next-buffer
|
||||
:n "[b" #'previous-buffer
|
||||
:n "zx" #'kill-this-buffer
|
||||
:n "ZX" #'bury-buffer
|
||||
:n "gp" #'+evil/reselect-paste
|
||||
:n "g=" #'widen
|
||||
:v "g=" #'+evil:narrow-buffer
|
||||
:nv "z=" #'flyspell-correct-word-generic
|
||||
:nv "g@" #'+evil:apply-macro
|
||||
:nv "gc" #'evil-commentary
|
||||
:nv "gx" #'evil-exchange
|
||||
:nv "C-a" #'evil-numbers/inc-at-pt
|
||||
:nv "C-S-a" #'evil-numbers/dec-at-pt
|
||||
:v "gp" #'+evil/paste-preserve-register
|
||||
:v "@" #'+evil:apply-macro
|
||||
;; repeat in visual mode (FIXME buggy)
|
||||
:v "." #'+evil:apply-macro
|
||||
;; don't leave visual mode after shifting
|
||||
:v "<" #'+evil/visual-dedent ; vnoremap < <gv
|
||||
:v ">" #'+evil/visual-indent ; vnoremap > >gv
|
||||
|
||||
;; window management (prefix "C-w")
|
||||
(:map evil-window-map
|
||||
;; Navigation
|
||||
"C-h" #'evil-window-left
|
||||
"C-j" #'evil-window-down
|
||||
"C-k" #'evil-window-up
|
||||
"C-l" #'evil-window-right
|
||||
"C-w" #'other-window
|
||||
;; Swapping windows
|
||||
"H" #'+evil/window-move-left
|
||||
"J" #'+evil/window-move-down
|
||||
"K" #'+evil/window-move-up
|
||||
"L" #'+evil/window-move-right
|
||||
"C-S-w" #'ace-swap-window
|
||||
;; Window undo/redo
|
||||
(:prefix "m"
|
||||
"m" #'doom/window-maximize-buffer
|
||||
"v" #'doom/window-maximize-vertically
|
||||
"s" #'doom/window-maximize-horizontally)
|
||||
"u" #'winner-undo
|
||||
"C-u" #'winner-undo
|
||||
"C-r" #'winner-redo
|
||||
"o" #'doom/window-enlargen
|
||||
;; Delete window
|
||||
"c" #'+workspace/close-window-or-workspace
|
||||
"C-C" #'ace-delete-window)
|
||||
|
||||
;; Plugins
|
||||
;; evil-easymotion
|
||||
:m "gs" #'+evil/easymotion ; lazy-load `evil-easymotion'
|
||||
(:after evil-easymotion
|
||||
:map evilem-map
|
||||
"a" (evilem-create #'evil-forward-arg)
|
||||
"A" (evilem-create #'evil-backward-arg)
|
||||
"s" (evilem-create #'evil-snipe-repeat
|
||||
:name 'evil-easymotion-snipe-forward
|
||||
:pre-hook (save-excursion (call-interactively #'evil-snipe-s))
|
||||
:bind ((evil-snipe-scope 'buffer)
|
||||
(evil-snipe-enable-highlight)
|
||||
(evil-snipe-enable-incremental-highlight)))
|
||||
"S" (evilem-create #'evil-snipe-repeat
|
||||
:name 'evil-easymotion-snipe-backward
|
||||
:pre-hook (save-excursion (call-interactively #'evil-snipe-S))
|
||||
:bind ((evil-snipe-scope 'buffer)
|
||||
(evil-snipe-enable-highlight)
|
||||
(evil-snipe-enable-incremental-highlight)))
|
||||
"SPC" #'avy-goto-char-timer
|
||||
"/" (evilem-create #'evil-ex-search-next
|
||||
:pre-hook (save-excursion (call-interactively #'evil-ex-search-forward))
|
||||
:bind ((evil-search-wrap)))
|
||||
"?" (evilem-create #'evil-ex-search-previous
|
||||
:pre-hook (save-excursion (call-interactively #'evil-ex-search-backward))
|
||||
:bind ((evil-search-wrap))))
|
||||
|
||||
;; text object plugins
|
||||
:textobj "x" #'evil-inner-xml-attr #'evil-outer-xml-attr
|
||||
:textobj "a" #'evil-inner-arg #'evil-outer-arg
|
||||
:textobj "B" #'evil-textobj-anyblock-inner-block #'evil-textobj-anyblock-a-block
|
||||
:textobj "i" #'evil-indent-plus-i-indent #'evil-indent-plus-a-indent
|
||||
:textobj "k" #'evil-indent-plus-i-indent-up #'evil-indent-plus-a-indent-up
|
||||
:textobj "j" #'evil-indent-plus-i-indent-up-down #'evil-indent-plus-a-indent-up-down
|
||||
|
||||
;; evil-snipe
|
||||
(:after evil-snipe
|
||||
:map evil-snipe-parent-transient-map
|
||||
"C-;" (λ! (require 'evil-easymotion)
|
||||
(call-interactively
|
||||
(evilem-create #'evil-snipe-repeat
|
||||
:bind ((evil-snipe-scope 'whole-buffer)
|
||||
(evil-snipe-enable-highlight)
|
||||
(evil-snipe-enable-incremental-highlight))))))
|
||||
|
||||
;; evil-surround
|
||||
:v "S" #'evil-surround-region
|
||||
:o "s" #'evil-surround-edit
|
||||
:o "S" #'evil-Surround-edit)
|
||||
|
||||
|
||||
;;
|
||||
;;; Module keybinds
|
||||
|
||||
;;; :feature
|
||||
(map! (:when (featurep! :feature debugger)
|
||||
:after realgud
|
||||
:map realgud:shortkey-mode-map
|
||||
:n "j" #'evil-next-line
|
||||
:n "k" #'evil-previous-line
|
||||
:n "h" #'evil-backward-char
|
||||
:n "l" #'evil-forward-char
|
||||
:n "c" #'realgud:cmd-continue
|
||||
:m "n" #'realgud:cmd-next
|
||||
:m "b" #'realgud:cmd-break
|
||||
:m "B" #'realgud:cmd-clear)
|
||||
|
||||
(:when (featurep! :feature eval)
|
||||
:g "M-r" #'+eval/buffer
|
||||
:nv "gr" #'+eval:region
|
||||
:n "gR" #'+eval/buffer
|
||||
:v "gR" #'+eval:replace-region)
|
||||
|
||||
(:when (featurep! :feature lookup)
|
||||
:nv "K" #'+lookup/documentation
|
||||
:nv "gd" #'+lookup/definition
|
||||
:nv "gD" #'+lookup/references
|
||||
:nv "gf" #'+lookup/file)
|
||||
|
||||
(:when (featurep! :feature snippets)
|
||||
;; auto-yasnippet
|
||||
:i [C-tab] #'aya-expand
|
||||
:nv [C-tab] #'aya-create
|
||||
;; yasnippet
|
||||
(:after yasnippet
|
||||
(:map yas-keymap
|
||||
"C-e" #'+snippets/goto-end-of-field
|
||||
"C-a" #'+snippets/goto-start-of-field
|
||||
[M-right] #'+snippets/goto-end-of-field
|
||||
[M-left] #'+snippets/goto-start-of-field
|
||||
[M-backspace] #'+snippets/delete-to-start-of-field
|
||||
[backspace] #'+snippets/delete-backward-char
|
||||
[delete] #'+snippets/delete-forward-char-or-field)))
|
||||
|
||||
(:when (featurep! :tools flyspell)
|
||||
;; Keybinds that have no Emacs+evil analogues (i.e. don't exist):
|
||||
;; zq - mark word at point as good word
|
||||
;; zw - mark word at point as bad
|
||||
;; zu{q,w} - undo last marking
|
||||
;; Keybinds that evil define:
|
||||
;; z= - correct flyspell word at point
|
||||
;; ]s - jump to previous spelling error
|
||||
;; [s - jump to next spelling error
|
||||
(:map flyspell-mouse-map
|
||||
"RET" #'flyspell-correct-word-generic
|
||||
[return] #'flyspell-correct-word-generic
|
||||
[mouse-1] #'flyspell-correct-word-generic))
|
||||
|
||||
(:when (featurep! :tools flycheck)
|
||||
:m "]e" #'next-error
|
||||
:m "[e" #'previous-error
|
||||
(:after flycheck
|
||||
:map flycheck-error-list-mode-map
|
||||
:n "C-n" #'flycheck-error-list-next-error
|
||||
:n "C-p" #'flycheck-error-list-previous-error
|
||||
:n "j" #'flycheck-error-list-next-error
|
||||
:n "k" #'flycheck-error-list-previous-error
|
||||
:n "RET" #'flycheck-error-list-goto-error
|
||||
:n [return] #'flycheck-error-list-goto-error))
|
||||
|
||||
(:when (featurep! :feature workspaces)
|
||||
:n "gt" #'+workspace/switch-right
|
||||
:n "gT" #'+workspace/switch-left
|
||||
:n "]w" #'+workspace/switch-right
|
||||
:n "[w" #'+workspace/switch-left
|
||||
:g "M-1" (λ! (+workspace/switch-to 0))
|
||||
:g "M-2" (λ! (+workspace/switch-to 1))
|
||||
:g "M-3" (λ! (+workspace/switch-to 2))
|
||||
:g "M-4" (λ! (+workspace/switch-to 3))
|
||||
:g "M-5" (λ! (+workspace/switch-to 4))
|
||||
:g "M-6" (λ! (+workspace/switch-to 5))
|
||||
:g "M-7" (λ! (+workspace/switch-to 6))
|
||||
:g "M-8" (λ! (+workspace/switch-to 7))
|
||||
:g "M-9" (λ! (+workspace/switch-to 8))
|
||||
:g "M-0" #'+workspace/switch-to-last
|
||||
:g "M-t" #'+workspace/new
|
||||
:g "M-T" #'+workspace/display))
|
||||
|
||||
;;; :completion
|
||||
(map! (:when (featurep! :completion company)
|
||||
:i "C-@" #'+company/complete
|
||||
:i "C-SPC" #'+company/complete
|
||||
(:prefix "C-x"
|
||||
:i "C-l" #'+company/whole-lines
|
||||
:i "C-k" #'+company/dict-or-keywords
|
||||
:i "C-f" #'company-files
|
||||
:i "C-]" #'company-etags
|
||||
:i "s" #'company-ispell
|
||||
:i "C-s" #'company-yasnippet
|
||||
:i "C-o" #'company-capf
|
||||
:i "C-n" #'+company/dabbrev
|
||||
:i "C-p" #'+company/dabbrev-code-previous)
|
||||
(:after company
|
||||
(:map company-active-map
|
||||
"C-w" nil ; don't interfere with `evil-delete-backward-word'
|
||||
"C-n" #'company-select-next
|
||||
"C-p" #'company-select-previous
|
||||
"C-j" #'company-select-next
|
||||
"C-k" #'company-select-previous
|
||||
"C-h" #'company-show-doc-buffer
|
||||
"C-u" #'company-previous-page
|
||||
"C-d" #'company-next-page
|
||||
"C-s" #'company-filter-candidates
|
||||
"C-S-s" (cond ((featurep! :completion helm) #'helm-company)
|
||||
((featurep! :completion ivy) #'counsel-company))
|
||||
"C-SPC" #'company-complete-common
|
||||
"TAB" #'company-complete-common-or-cycle
|
||||
[tab] #'company-complete-common-or-cycle
|
||||
[backtab] #'company-select-previous)
|
||||
(:map company-search-map ; applies to `company-filter-map' too
|
||||
"C-n" #'company-select-next-or-abort
|
||||
"C-p" #'company-select-previous-or-abort
|
||||
"C-j" #'company-select-next-or-abort
|
||||
"C-k" #'company-select-previous-or-abort
|
||||
"C-s" (λ! (company-search-abort) (company-filter-candidates))
|
||||
"ESC" #'company-search-abort)
|
||||
;; TAB auto-completion in term buffers
|
||||
(:map comint-mode-map
|
||||
"TAB" #'company-complete
|
||||
[tab] #'company-complete)))
|
||||
|
||||
(:when (featurep! :completion ivy)
|
||||
(:map (help-mode-map helpful-mode-map)
|
||||
:n "Q" #'ivy-resume)
|
||||
(:after ivy
|
||||
:map ivy-minibuffer-map
|
||||
"C-SPC" #'ivy-call-and-recenter ; preview file
|
||||
"C-l" #'ivy-alt-done
|
||||
"C-v" #'yank)
|
||||
(:after counsel
|
||||
:map counsel-ag-map
|
||||
"C-SPC" #'ivy-call-and-recenter ; preview
|
||||
"C-l" #'ivy-done
|
||||
"C-c C-e" #'+ivy/wgrep-occur ; search/replace on results
|
||||
[backtab] #'+ivy/wgrep-occur ; search/replace on results
|
||||
[C-return] (+ivy-do-action! #'+ivy-git-grep-other-window-action))
|
||||
(:after swiper
|
||||
:map swiper-map
|
||||
[backtab] #'+ivy/wgrep-occur))
|
||||
|
||||
(:when (featurep! :completion helm)
|
||||
(:after helm
|
||||
(:map helm-map
|
||||
[left] #'left-char
|
||||
[right] #'right-char
|
||||
"C-S-n" #'helm-next-source
|
||||
"C-S-p" #'helm-previous-source
|
||||
"C-j" #'helm-next-line
|
||||
"C-k" #'helm-previous-line
|
||||
"C-S-j" #'helm-next-source
|
||||
"C-S-k" #'helm-previous-source
|
||||
"C-f" #'helm-next-page
|
||||
"C-S-f" #'helm-previous-page
|
||||
"C-u" #'helm-delete-minibuffer-contents
|
||||
"C-w" #'backward-kill-word
|
||||
"C-r" #'evil-paste-from-register ; Evil registers in helm! Glorious!
|
||||
"C-s" #'helm-minibuffer-history
|
||||
"C-b" #'backward-word
|
||||
;; Swap TAB and C-z
|
||||
"TAB" #'helm-execute-persistent-action
|
||||
[tab] #'helm-execute-persistent-action
|
||||
"C-z" #'helm-select-action)
|
||||
(:after swiper-helm
|
||||
:map swiper-helm-keymap [backtab] #'helm-ag-edit)
|
||||
(:after helm-ag
|
||||
:map helm-ag-map
|
||||
"C--" #'+helm-do-ag-decrease-context
|
||||
"C-=" #'+helm-do-ag-increase-context
|
||||
[backtab] #'helm-ag-edit
|
||||
[left] nil
|
||||
[right] nil)
|
||||
(:after helm-files
|
||||
:map (helm-find-files-map helm-read-file-map)
|
||||
[C-return] #'helm-ff-run-switch-other-window
|
||||
"C-w" #'helm-find-files-up-one-level)
|
||||
(:after helm-locate
|
||||
:map helm-generic-files-map
|
||||
[C-return] #'helm-ff-run-switch-other-window)
|
||||
(:after helm-buffers
|
||||
:map helm-buffer-map
|
||||
[C-return] #'helm-buffer-switch-other-window)
|
||||
(:after helm-occur
|
||||
:map helm-occur-map
|
||||
[C-return] #'helm-occur-run-goto-line-ow)
|
||||
(:after helm-grep
|
||||
:map helm-grep-map
|
||||
[C-return] #'helm-grep-run-other-window-action))))
|
||||
|
||||
;;; :ui
|
||||
(map! (:when (featurep! :ui hl-todo)
|
||||
:m "]t" #'hl-todo-next
|
||||
:m "[t" #'hl-todo-previous)
|
||||
|
||||
(:when (featurep! :ui neotree)
|
||||
:after neotree
|
||||
:map neotree-mode-map
|
||||
:n "g" nil
|
||||
:n "TAB" #'neotree-quick-look
|
||||
:n "RET" #'neotree-enter
|
||||
:n [tab] #'neotree-quick-look
|
||||
:n [return] #'neotree-enter
|
||||
:n "DEL" #'evil-window-prev
|
||||
:n "c" #'neotree-create-node
|
||||
:n "r" #'neotree-rename-node
|
||||
:n "d" #'neotree-delete-node
|
||||
:n "j" #'neotree-next-line
|
||||
:n "k" #'neotree-previous-line
|
||||
:n "n" #'neotree-next-line
|
||||
:n "p" #'neotree-previous-line
|
||||
:n "h" #'+neotree/collapse-or-up
|
||||
:n "l" #'+neotree/expand-or-open
|
||||
:n "J" #'neotree-select-next-sibling-node
|
||||
:n "K" #'neotree-select-previous-sibling-node
|
||||
:n "H" #'neotree-select-up-node
|
||||
:n "L" #'neotree-select-down-node
|
||||
:n "G" #'evil-goto-line
|
||||
:n "gg" #'evil-goto-first-line
|
||||
:n "v" #'neotree-enter-vertical-split
|
||||
:n "s" #'neotree-enter-horizontal-split
|
||||
:n "q" #'neotree-hide
|
||||
:n "R" #'neotree-refresh)
|
||||
|
||||
(:when (featurep! :ui popup)
|
||||
:n "C-`" #'+popup/toggle
|
||||
:n "C-~" #'+popup/raise
|
||||
:g "C-x p" #'+popup/other)
|
||||
|
||||
(:when (featurep! :ui vc-gutter)
|
||||
:m "]d" #'git-gutter:next-hunk
|
||||
:m "[d" #'git-gutter:previous-hunk))
|
||||
|
||||
;;; :editor
|
||||
(map! (:when (featurep! :editor fold)
|
||||
:nv "C-SPC" #'+fold/toggle)
|
||||
|
||||
(:when (featurep! :editor format)
|
||||
:n "gQ" #'+format:region)
|
||||
|
||||
(:when (featurep! :editor multiple-cursors)
|
||||
;; evil-mc
|
||||
(:prefix "gz"
|
||||
:nv "d" #'evil-mc-make-and-goto-next-match
|
||||
:nv "D" #'evil-mc-make-and-goto-prev-match
|
||||
:nv "j" #'evil-mc-make-cursor-move-next-line
|
||||
:nv "k" #'evil-mc-make-cursor-move-prev-line
|
||||
:nv "m" #'evil-mc-make-all-cursors
|
||||
:nv "n" #'evil-mc-make-and-goto-next-cursor
|
||||
:nv "N" #'evil-mc-make-and-goto-last-cursor
|
||||
:nv "p" #'evil-mc-make-and-goto-prev-cursor
|
||||
:nv "P" #'evil-mc-make-and-goto-first-cursor
|
||||
:nv "q" #'evil-mc-undo-all-cursors
|
||||
:nv "t" #'+multiple-cursors/evil-mc-toggle-cursors
|
||||
:nv "u" #'evil-mc-undo-last-added-cursor
|
||||
:nv "z" #'+multiple-cursors/evil-mc-make-cursor-here)
|
||||
(:after evil-mc
|
||||
:map evil-mc-key-map
|
||||
:nv "C-n" #'evil-mc-make-and-goto-next-cursor
|
||||
:nv "C-N" #'evil-mc-make-and-goto-last-cursor
|
||||
:nv "C-p" #'evil-mc-make-and-goto-prev-cursor
|
||||
:nv "C-P" #'evil-mc-make-and-goto-first-cursor)
|
||||
;; evil-multiedit
|
||||
:v "R" #'evil-multiedit-match-all
|
||||
:n "M-d" #'evil-multiedit-match-symbol-and-next
|
||||
:n "M-D" #'evil-multiedit-match-symbol-and-prev
|
||||
:v "M-d" #'evil-multiedit-match-and-next
|
||||
:v "M-D" #'evil-multiedit-match-and-prev
|
||||
:nv "C-M-d" #'evil-multiedit-restore
|
||||
(:after evil-multiedit
|
||||
(:map evil-multiedit-state-map
|
||||
"M-d" #'evil-multiedit-match-and-next
|
||||
"M-D" #'evil-multiedit-match-and-prev
|
||||
"RET" #'evil-multiedit-toggle-or-restrict-region
|
||||
[return] #'evil-multiedit-toggle-or-restrict-region)
|
||||
(:map (evil-multiedit-state-map evil-multiedit-insert-state-map)
|
||||
"C-n" #'evil-multiedit-next
|
||||
"C-p" #'evil-multiedit-prev)))
|
||||
|
||||
(:when (featurep! :editor rotate-text)
|
||||
:n "!" #'rotate-text))
|
||||
|
||||
;;; :emacs
|
||||
(map! (:when (featurep! :emacs vc)
|
||||
:after git-timemachine
|
||||
:map git-timemachine-mode-map
|
||||
:n "C-p" #'git-timemachine-show-previous-revision
|
||||
:n "C-n" #'git-timemachine-show-next-revision
|
||||
:n "[[" #'git-timemachine-show-previous-revision
|
||||
:n "]]" #'git-timemachine-show-next-revision
|
||||
:n "q" #'git-timemachine-quit
|
||||
:n "gb" #'git-timemachine-blame))
|
||||
|
||||
;;; :tools
|
||||
(map! (:when (featurep! :tools magit)
|
||||
(:after evil-magit
|
||||
;; fix conflicts with private bindings
|
||||
:map (magit-status-mode-map magit-revision-mode-map)
|
||||
"C-j" nil
|
||||
"C-k" nil)
|
||||
(:map transient-map
|
||||
"q" #'transient-quit-one))
|
||||
|
||||
(:when (featurep! :tools gist)
|
||||
:after gist
|
||||
:map gist-list-menu-mode-map
|
||||
:n "go" #'gist-browse-current-url
|
||||
:n "gr" #'gist-list-reload
|
||||
:n "c" #'gist-add-buffer
|
||||
:n "d" #'gist-kill-current
|
||||
:n "e" #'gist-edit-current-description
|
||||
:n "f" #'gist-fork
|
||||
:n "q" #'kill-this-buffer
|
||||
:n "s" #'gist-star
|
||||
:n "S" #'gist-unstar
|
||||
:n "y" #'gist-print-current-url))
|
||||
|
||||
;;; :lang
|
||||
(map! (:when (featurep! :lang markdown)
|
||||
:after markdown-mode
|
||||
:map markdown-mode-map
|
||||
;; fix conflicts with private bindings
|
||||
[backspace] nil))
|
||||
|
||||
|
||||
;;
|
||||
;;; <leader>
|
||||
|
||||
(map! :leader
|
||||
:desc "Eval expression" ";" #'eval-expression
|
||||
:desc "M-x" ":" #'execute-extended-command
|
||||
:desc "Pop up scratch buffer" "x" #'doom/open-scratch-buffer
|
||||
:desc "Org Capture" "X" #'org-capture
|
||||
|
||||
;; C-u is used by evil
|
||||
:desc "Universal argument" "u" #'universal-argument
|
||||
:desc "window" "w" evil-window-map
|
||||
:desc "help" "h" help-map
|
||||
|
||||
:desc "Toggle last popup" "~" #'+popup/toggle
|
||||
:desc "Find file" "." #'find-file
|
||||
|
||||
:desc "Switch buffer" "," #'switch-to-buffer
|
||||
(:when (featurep! :feature workspaces)
|
||||
:desc "Switch workspace buffer" "," #'persp-switch-to-buffer
|
||||
:desc "Switch buffer" "<" #'switch-to-buffer)
|
||||
|
||||
:desc "Resume last search" "'"
|
||||
(cond ((featurep! :completion ivy) #'ivy-resume)
|
||||
((featurep! :completion helm) #'helm-resume))
|
||||
|
||||
:desc "Search for symbol in project" "*" #'+default/search-project-for-symbol-at-point
|
||||
|
||||
:desc "Find file in project" "SPC" #'projectile-find-file
|
||||
:desc "Blink cursor line" "DEL" #'+nav-flash/blink-cursor
|
||||
:desc "Jump to bookmark" "RET" #'bookmark-jump
|
||||
|
||||
;; Prefixed key groups
|
||||
(:prefix ("/" . "search")
|
||||
:desc "Search buffer" "b" #'swiper
|
||||
:desc "Search current directory" "d" #'+default/search-from-cwd
|
||||
:desc "Jump to symbol" "i" #'imenu
|
||||
:desc "Jump to symbol across buffers" "I" #'imenu-anywhere
|
||||
:desc "Jump to link" "l" #'ace-link
|
||||
:desc "Look up online" "o" #'+lookup/online-select
|
||||
:desc "Search project" "p" #'+default/search-project)
|
||||
|
||||
(:when (featurep! :feature workspaces)
|
||||
(:prefix ("TAB" . "workspace")
|
||||
:desc "Display tab bar" "TAB" #'+workspace/display
|
||||
:desc "Switch workspace" "." #'+workspace/switch-to
|
||||
:desc "New workspace" "n" #'+workspace/new
|
||||
:desc "Load workspace from file" "l" #'+workspace/load
|
||||
:desc "Save workspace to file" "s" #'+workspace/save
|
||||
:desc "Delete session" "x" #'+workspace/kill-session
|
||||
:desc "Delete this workspace" "d" #'+workspace/delete
|
||||
:desc "Rename workspace" "r" #'+workspace/rename
|
||||
:desc "Restore last session" "R" #'+workspace/restore-last-session
|
||||
:desc "Next workspace" "]" #'+workspace/switch-right
|
||||
:desc "Previous workspace" "[" #'+workspace/switch-left
|
||||
:desc "Switch to 1st workspace" "1" (λ! (+workspace/switch-to 0))
|
||||
:desc "Switch to 2nd workspace" "2" (λ! (+workspace/switch-to 1))
|
||||
:desc "Switch to 3rd workspace" "3" (λ! (+workspace/switch-to 2))
|
||||
:desc "Switch to 4th workspace" "4" (λ! (+workspace/switch-to 3))
|
||||
:desc "Switch to 5th workspace" "5" (λ! (+workspace/switch-to 4))
|
||||
:desc "Switch to 6th workspace" "6" (λ! (+workspace/switch-to 5))
|
||||
:desc "Switch to 7th workspace" "7" (λ! (+workspace/switch-to 6))
|
||||
:desc "Switch to 8th workspace" "8" (λ! (+workspace/switch-to 7))
|
||||
:desc "Switch to 9th workspace" "9" (λ! (+workspace/switch-to 8))
|
||||
:desc "Switch to last workspace" "0" #'+workspace/switch-to-last))
|
||||
|
||||
(:prefix ("b" . "buffer")
|
||||
:desc "Toggle narrowing" "-" #'doom/clone-and-narrow-buffer
|
||||
:desc "Previous buffer" "[" #'previous-buffer
|
||||
:desc "Next buffer" "]" #'next-buffer
|
||||
(:when (featurep! :feature workspaces)
|
||||
:desc "Switch workspace buffer" "b" #'persp-switch-to-buffer
|
||||
:desc "Switch buffer" "B" #'switch-to-buffer)
|
||||
(:unless (featurep! :feature workspaces)
|
||||
:desc "Switch buffer" "b" #'switch-to-buffer)
|
||||
:desc "Kill buffer" "k" #'kill-this-buffer
|
||||
:desc "Next buffer" "n" #'next-buffer
|
||||
:desc "New empty buffer" "N" #'evil-buffer-new
|
||||
:desc "Kill other buffers" "o" #'doom/kill-other-buffers
|
||||
:desc "Previous buffer" "p" #'previous-buffer
|
||||
:desc "Save buffer" "s" #'save-buffer
|
||||
:desc "Sudo edit this file" "S" #'doom/sudo-this-file
|
||||
:desc "Pop scratch buffer" "x" #'doom/open-scratch-buffer
|
||||
:desc "Bury buffer" "z" #'bury-buffer)
|
||||
|
||||
(:prefix ("c" . "code")
|
||||
:desc "Compile" "c" #'compile
|
||||
:desc "Jump to definition" "d" #'+lookup/definition
|
||||
:desc "Jump to references" "D" #'+lookup/references
|
||||
:desc "Evaluate buffer/region" "e" #'+eval/buffer-or-region
|
||||
:desc "Evaluate & replace region" "E" #'+eval:replace-region
|
||||
:desc "Format buffer/region" "f" #'+format/region-or-buffer
|
||||
:desc "Open REPL" "r" #'+eval/open-repl-other-window
|
||||
:desc "Delete trailing whitespace" "w" #'delete-trailing-whitespace
|
||||
:desc "Delete trailing newlines" "W" #'doom/delete-trailing-newlines
|
||||
:desc "List errors" "x" #'flycheck-list-errors)
|
||||
|
||||
(:prefix ("f" . "file")
|
||||
:desc "Find file" "." #'find-file
|
||||
:desc "Find file from here" "/"
|
||||
(if (featurep! :completion ivy)
|
||||
#'counsel-file-jump
|
||||
(λ! (doom-project-find-file default-directory)))
|
||||
:desc "Open project editorconfig" "c" #'editorconfig-find-current-editorconfig
|
||||
:desc "Find directory" "d" #'dired
|
||||
:desc "Find file in emacs.d" "e" #'+default/find-in-emacsd
|
||||
:desc "Browse emacs.d" "E" #'+default/browse-emacsd
|
||||
:desc "Find file from here" "f" #'find-file
|
||||
:desc "Find file in private config" "p" #'doom/find-file-in-private-config
|
||||
:desc "Browse private config" "P" #'doom/open-private-config
|
||||
:desc "Recent files" "r" #'recentf-open-files
|
||||
:desc "Recent project files" "R" #'projectile-recentf
|
||||
:desc "Save file" "s" #'save-buffer
|
||||
:desc "Sudo find file" "S" #'doom/sudo-find-file
|
||||
:desc "Delete this file" "X" #'doom/delete-this-file
|
||||
:desc "Yank filename" "y" #'+default/yank-buffer-filename)
|
||||
|
||||
(:prefix ("g" . "git")
|
||||
:desc "Git revert file" "R" #'vc-revert
|
||||
(:when (featurep! :ui vc-gutter)
|
||||
:desc "Git revert hunk" "r" #'git-gutter:revert-hunk
|
||||
:desc "Git stage hunk" "s" #'git-gutter:stage-hunk
|
||||
:desc "Git time machine" "t" #'git-timemachine-toggle
|
||||
:desc "Jump to next hunk" "]" #'git-gutter:next-hunk
|
||||
:desc "Jump to previous hunk" "[" #'git-gutter:previous-hunk)
|
||||
(:when (featurep! :tools magit)
|
||||
:desc "Magit dispatch" "/" #'magit-dispatch
|
||||
:desc "Forge dispatch" "'" #'forge-dispatch
|
||||
:desc "Magit status" "g" #'magit-status
|
||||
:desc "Magit file delete" "x" #'magit-file-delete
|
||||
:desc "Magit blame" "B" #'magit-blame-addition
|
||||
:desc "Magit clone" "C" #'+magit/clone
|
||||
:desc "Magit fetch" "F" #'magit-fetch
|
||||
:desc "Magit buffer log" "L" #'magit-log
|
||||
:desc "Git stage file" "S" #'magit-stage-file
|
||||
:desc "Git unstage file" "U" #'magit-unstage-file
|
||||
(:prefix ("f" . "find")
|
||||
:desc "Find file" "f" #'magit-find-file
|
||||
:desc "Find gitconfig file" "g" #'magit-find-git-config-file
|
||||
:desc "Find commit" "c" #'magit-show-commit
|
||||
:desc "Find issue" "i" #'forge-visit-issue
|
||||
:desc "Find pull request" "p" #'forge-visit-pullreq)
|
||||
(:prefix ("o" . "open in browser")
|
||||
:desc "Browse region or line" "." #'+vc/git-browse-region-or-line
|
||||
:desc "Browse remote" "r" #'forge-browse-remote
|
||||
:desc "Browse commit" "c" #'forge-browse-commit
|
||||
:desc "Browse an issue" "i" #'forge-browse-issue
|
||||
:desc "Browse a pull request" "p" #'forge-browse-pullreq
|
||||
:desc "Browse issues" "I" #'forge-browse-issues
|
||||
:desc "Browse pull requests" "P" #'forge-browse-pullreqs)
|
||||
(:prefix ("l" . "list")
|
||||
(:when (featurep! :tools gist)
|
||||
:desc "List gists" "g" #'+gist:list)
|
||||
:desc "List repositories" "r" #'magit-list-repositories
|
||||
:desc "List submodules" "s" #'magit-list-submodules
|
||||
:desc "List issues" "i" #'forge-list-issues
|
||||
:desc "List pull requests" "p" #'forge-list-pullreqs
|
||||
:desc "List notifications" "n" #'forge-list-notifications)
|
||||
(:prefix ("c" . "create")
|
||||
:desc "Initialize repo" "r" #'magit-init
|
||||
:desc "Clone repo" "R" #'+magit/clone
|
||||
:desc "Commit" "c" #'magit-commit-create
|
||||
:desc "Issue" "i" #'forge-create-issue
|
||||
:desc "Pull request" "p" #'forge-create-pullreq)))
|
||||
|
||||
(:prefix ("i" . "insert")
|
||||
:desc "Insert from clipboard" "y" #'+default/yank-pop
|
||||
:desc "Insert from evil register" "r" #'evil-ex-registers
|
||||
:desc "Insert snippet" "s" #'yas-insert-snippet)
|
||||
|
||||
(:prefix ("n" . "notes")
|
||||
:desc "Open deft" "d" #'deft
|
||||
:desc "Find file in notes" "n" #'+default/find-in-notes
|
||||
:desc "Browse notes" "N" #'+default/browse-notes
|
||||
:desc "Pop scratch buffer" "s" #'doom/open-scratch-buffer
|
||||
:desc "Org capture" "x" #'org-capture
|
||||
:desc "Org store link" "l" #'org-store-link)
|
||||
|
||||
(:prefix ("o" . "open")
|
||||
:desc "Org agenda" "A" #'org-agenda
|
||||
(:prefix ("a" . "org agenda")
|
||||
:desc "Agenda" "a" #'org-agenda
|
||||
:desc "Todo list" "t" #'org-todo-list
|
||||
:desc "Tags search" "m" #'org-tags-view
|
||||
:desc "View search" "v" #'org-search-view)
|
||||
:desc "Default browser" "b" #'browse-url-of-file
|
||||
:desc "Debugger" "d" #'+debug/open
|
||||
:desc "REPL" "r" #'+eval/open-repl-other-window
|
||||
:desc "REPL (same window)" "R" #'+eval/open-repl-same-window
|
||||
:desc "Dired" "-" #'dired-jump
|
||||
(:when (featurep! :ui neotree)
|
||||
:desc "Project sidebar" "p" #'+neotree/open
|
||||
:desc "Find file in project sidebar" "P" #'+neotree/find-this-file)
|
||||
(:when (featurep! :ui treemacs)
|
||||
:desc "Project sidebar" "p" #'+treemacs/toggle
|
||||
:desc "Find file in project sidebar" "P" #'+treemacs/find-file)
|
||||
(:when (featurep! :emacs imenu)
|
||||
:desc "Imenu sidebar" "i" #'imenu-list-smart-toggle)
|
||||
(:when (featurep! :emacs term)
|
||||
:desc "Terminal" "t" #'+term/open
|
||||
:desc "Terminal in popup" "T" #'+term/open-popup-in-project)
|
||||
(:when (featurep! :tools vterm)
|
||||
:desc "Terminal" "t" #'+vterm/open
|
||||
:desc "Terminal in popup" "T" #'+vterm/open-popup-in-project)
|
||||
(:when (featurep! :emacs eshell)
|
||||
:desc "Eshell" "e" #'+eshell/open
|
||||
:desc "Eshell in popup" "E" #'+eshell/open-popup)
|
||||
(:when (featurep! :collab floobits)
|
||||
(:prefix ("f" . "floobits")
|
||||
"c" #'floobits-clear-highlights
|
||||
"f" #'floobits-follow-user
|
||||
"j" #'floobits-join-workspace
|
||||
"l" #'floobits-leave-workspace
|
||||
"R" #'floobits-share-dir-private
|
||||
"s" #'floobits-summon
|
||||
"t" #'floobits-follow-mode-toggle
|
||||
"U" #'floobits-share-dir-public))
|
||||
(:when (featurep! :tools macos)
|
||||
:desc "Reveal in Finder" "o" #'+macos/reveal-in-finder
|
||||
:desc "Reveal project in Finder" "O" #'+macos/reveal-project-in-finder
|
||||
:desc "Send to Transmit" "u" #'+macos/send-to-transmit
|
||||
:desc "Send project to Transmit" "U" #'+macos/send-project-to-transmit
|
||||
:desc "Send to Launchbar" "l" #'+macos/send-to-launchbar
|
||||
:desc "Send project to Launchbar" "L" #'+macos/send-project-to-launchbar)
|
||||
(:when (featurep! :tools docker)
|
||||
:desc "Docker" "D" #'docker))
|
||||
|
||||
(:prefix ("p" . "project")
|
||||
:desc "Browse project" "." #'+default/browse-project
|
||||
:desc "Find file in other project" ">" #'doom/find-file-in-other-project
|
||||
:desc "Find file in project" "/" #'projectile-find-file
|
||||
:desc "Browse other project" "?" #'doom/browse-in-other-project
|
||||
:desc "Run cmd in project root" "!" #'projectile-run-shell-command-in-root
|
||||
:desc "Add new project" "a" #'projectile-add-known-project
|
||||
:desc "Switch to project buffer" "b" #'projectile-switch-to-buffer
|
||||
:desc "Compile in project" "c" #'projectile-compile-project
|
||||
:desc "Remove known project" "d" #'projectile-remove-known-project
|
||||
:desc "Kill project buffers" "k" #'projectile-kill-buffers
|
||||
:desc "Invalidate project cache" "i" #'projectile-invalidate-cache
|
||||
:desc "Find other file" "o" #'projectile-find-other-file
|
||||
:desc "Switch project" "p" #'projectile-switch-project
|
||||
:desc "Find recent project files" "r" #'projectile-recentf
|
||||
:desc "Scratch buffer" "s" #'doom/open-project-scratch-buffer
|
||||
:desc "List project tasks" "t" #'+default/project-tasks
|
||||
(:prefix ("x" . "terminal")
|
||||
:desc "Open eshell in project" "e" #'projectile-run-eshell
|
||||
:desc "Open ielm in project" "i" #'projectile-run-ielm
|
||||
:desc "Open term in project" "t" #'projectile-run-term
|
||||
:desc "Open shell in project" "s" #'projectile-run-shell))
|
||||
|
||||
(:prefix ("q" . "session")
|
||||
:desc "Quit Emacs" "q" #'save-buffers-kill-terminal
|
||||
:desc "Quit Emacs without saving" "Q" #'evil-quit-all-with-error-code
|
||||
:desc "Quick save current session" "s" #'doom/quicksave-session
|
||||
:desc "Restore last session" "l" #'doom/quickload-session
|
||||
:desc "Save session to file" "S" #'doom/save-session
|
||||
:desc "Restore session from file" "L" #'doom/load-session
|
||||
:desc "Restart & restore Emacs" "r" #'doom/restart-and-restore
|
||||
:desc "Restart Emacs" "R" #'doom/restart)
|
||||
|
||||
(:when (featurep! :tools upload)
|
||||
(:prefix ("r" . "remote")
|
||||
:desc "Upload local" "u" #'ssh-deploy-upload-handler
|
||||
:desc "Upload local (force)" "U" #'ssh-deploy-upload-handler-forced
|
||||
:desc "Download remote" "d" #'ssh-deploy-download-handler
|
||||
:desc "Diff local & remote" "D" #'ssh-deploy-diff-handler
|
||||
:desc "Browse remote files" "." #'ssh-deploy-browse-remote-handler
|
||||
:desc "Detect remote changes" ">" #'ssh-deploy-remote-changes-handler))
|
||||
|
||||
(:when (featurep! :feature snippets)
|
||||
(:prefix ("s" . "snippets")
|
||||
:desc "New snippet" "n" #'yas-new-snippet
|
||||
:desc "Insert snippet" "i" #'yas-insert-snippet
|
||||
:desc "Jump to mode snippet" "/" #'yas-visit-snippet-file
|
||||
:desc "Jump to snippet" "s" #'+snippets/find-file
|
||||
:desc "Browse snippets" "S" #'+snippets/browse
|
||||
:desc "Reload snippets" "r" #'yas-reload-all
|
||||
:desc "Create temporary snippet" "c" #'aya-create
|
||||
:desc "Use temporary snippet" "e" #'aya-expand))
|
||||
|
||||
(:prefix ("t" . "toggle")
|
||||
:desc "Flyspell" "s" #'flyspell-mode
|
||||
:desc "Flycheck" "f" #'flycheck-mode
|
||||
:desc "Line numbers" "l" #'doom/toggle-line-numbers
|
||||
:desc "Frame fullscreen" "F" #'toggle-frame-fullscreen
|
||||
:desc "Indent guides" "i" #'highlight-indent-guides-mode
|
||||
:desc "Impatient mode" "h" #'+impatient-mode/toggle
|
||||
:desc "Big mode" "b" #'doom-big-font-mode
|
||||
:desc "Evil goggles" "g" #'evil-goggles-mode
|
||||
:desc "org-tree-slide mode" "p" #'+org-present/start))
|
||||
|
||||
|
||||
;;
|
||||
;;; Universal motion repeating keys
|
||||
|
||||
(defvar +default-repeat-keys (cons ";" ",")
|
||||
"The keys to use for repeating motions.
|
||||
|
||||
This is a cons cell whose CAR is the key for repeating a motion forward, and
|
||||
whose CDR is for repeating backward. They should both be kbd-able strings.")
|
||||
|
||||
(when +default-repeat-keys
|
||||
(defmacro do-repeat! (command next-func prev-func)
|
||||
"Makes ; and , the universal repeat-keys in evil-mode.
|
||||
To change these keys see `+default-repeat-keys'."
|
||||
(let ((fn-sym (intern (format "+default*repeat-%s" (doom-unquote command)))))
|
||||
`(progn
|
||||
(defun ,fn-sym (&rest _)
|
||||
(define-key! :states 'motion
|
||||
(car +default-repeat-keys) #',next-func
|
||||
(cdr +default-repeat-keys) #',prev-func))
|
||||
(advice-add #',command :before #',fn-sym))))
|
||||
|
||||
;; n/N
|
||||
(do-repeat! evil-ex-search-next evil-ex-search-next evil-ex-search-previous)
|
||||
(do-repeat! evil-ex-search-previous evil-ex-search-next evil-ex-search-previous)
|
||||
(do-repeat! evil-ex-search-forward evil-ex-search-next evil-ex-search-previous)
|
||||
(do-repeat! evil-ex-search-backward evil-ex-search-next evil-ex-search-previous)
|
||||
|
||||
;; f/F/t/T/s/S
|
||||
(setq evil-snipe-repeat-keys nil
|
||||
evil-snipe-override-evil-repeat-keys nil) ; causes problems with remapped ;
|
||||
(do-repeat! evil-snipe-f evil-snipe-repeat evil-snipe-repeat-reverse)
|
||||
(do-repeat! evil-snipe-F evil-snipe-repeat evil-snipe-repeat-reverse)
|
||||
(do-repeat! evil-snipe-t evil-snipe-repeat evil-snipe-repeat-reverse)
|
||||
(do-repeat! evil-snipe-T evil-snipe-repeat evil-snipe-repeat-reverse)
|
||||
(do-repeat! evil-snipe-s evil-snipe-repeat evil-snipe-repeat-reverse)
|
||||
(do-repeat! evil-snipe-S evil-snipe-repeat evil-snipe-repeat-reverse)
|
||||
(do-repeat! evil-snipe-x evil-snipe-repeat evil-snipe-repeat-reverse)
|
||||
(do-repeat! evil-snipe-X evil-snipe-repeat evil-snipe-repeat-reverse)
|
||||
|
||||
;; */#
|
||||
(do-repeat! evil-visualstar/begin-search-forward
|
||||
evil-ex-search-next evil-ex-search-previous)
|
||||
(do-repeat! evil-visualstar/begin-search-backward
|
||||
evil-ex-search-previous evil-ex-search-next))
|
||||
|
||||
|
||||
;;
|
||||
;;; Universal evil integration
|
||||
|
||||
(when (featurep! :feature evil +everywhere)
|
||||
;; Have C-u behave similarly to `doom/backward-to-bol-or-indent'.
|
||||
;; NOTE SPC u replaces C-u as the universal argument.
|
||||
(map! :gi "C-u" #'doom/backward-kill-to-bol-and-indent
|
||||
:gi "C-w" #'backward-kill-word
|
||||
;; Vimmish ex motion keys
|
||||
:gi "C-b" #'backward-word
|
||||
:gi "C-f" #'forward-word)
|
||||
|
||||
(after! view
|
||||
(define-key view-mode-map [escape] #'View-quit-all))
|
||||
(after! man
|
||||
(evil-define-key* 'normal Man-mode-map "q" #'kill-this-buffer))
|
||||
|
||||
;; Minibuffer
|
||||
(define-key! evil-ex-completion-map
|
||||
"C-a" #'move-beginning-of-line
|
||||
"C-b" #'backward-word
|
||||
"C-s" (if (featurep! :completion ivy)
|
||||
#'counsel-minibuffer-history
|
||||
#'helm-minibuffer-history))
|
||||
|
||||
(define-key! :keymaps +default-minibuffer-maps
|
||||
[escape] #'abort-recursive-edit
|
||||
"C-v" #'yank
|
||||
"C-z" (λ! (ignore-errors (call-interactively #'undo)))
|
||||
"C-a" #'move-beginning-of-line
|
||||
"C-b" #'backward-word
|
||||
"C-r" #'evil-paste-from-register
|
||||
;; Scrolling lines
|
||||
"C-j" #'next-line
|
||||
"C-k" #'previous-line
|
||||
"C-S-j" #'scroll-up-command
|
||||
"C-S-k" #'scroll-down-command))
|
21
modules/config/default/+evil.el
Normal file
21
modules/config/default/+evil.el
Normal file
|
@ -0,0 +1,21 @@
|
|||
;;; config/default/+evil.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defun +default|disable-delete-selection-mode ()
|
||||
(delete-selection-mode -1))
|
||||
(add-hook 'evil-insert-state-entry-hook #'delete-selection-mode)
|
||||
(add-hook 'evil-insert-state-exit-hook #'+default|disable-delete-selection-mode)
|
||||
|
||||
|
||||
;;
|
||||
;;; Keybindings
|
||||
|
||||
;; This section is dedicated to "fixing" certain keys so that they behave
|
||||
;; sensibly (and consistently with similar contexts).
|
||||
|
||||
;; Make SPC u SPC u [...] possible (#747)
|
||||
(map! :map universal-argument-map
|
||||
:prefix doom-leader-key "u" #'universal-argument-more
|
||||
:prefix doom-leader-alt-key "u" #'universal-argument-more)
|
||||
|
||||
(when (featurep! +bindings)
|
||||
(load! "+evil-bindings"))
|
60
modules/config/default/README.org
Normal file
60
modules/config/default/README.org
Normal file
|
@ -0,0 +1,60 @@
|
|||
#+TITLE: :config default
|
||||
|
||||
This module provides a set of reasonable defaults, including:
|
||||
|
||||
+ A Spacemacs-esque keybinding scheme
|
||||
+ Extra Ex commands for evil-mode users
|
||||
+ A yasnippet snippets library tailored to Doom emacs
|
||||
+ A configuration for (almost) universally repeating searches with =;= and =,=
|
||||
|
||||
#+begin_quote
|
||||
The defaults module is intended as a "reasonable-defaults" module, but also as a
|
||||
reference for your own private modules. You'll find [[https://github.com/hlissner/doom-emacs-private][my private module in a
|
||||
separate repo]].
|
||||
|
||||
Refer to the [[https://github.com/hlissner/doom-emacs/wiki/Customization][Customization page]] on the wiki for details on starting your own
|
||||
private module.
|
||||
#+end_quote
|
||||
|
||||
* Table of Contents :TOC:
|
||||
- [[#install][Install]]
|
||||
- [[#configuration][Configuration]]
|
||||
- [[#using-another-snippets-library][Using another snippets library]]
|
||||
- [[#im-not-an-evil-user][I'm not an evil user...]]
|
||||
- [[#appendix][Appendix]]
|
||||
- [[#commands][Commands]]
|
||||
- [[#hacks][Hacks]]
|
||||
|
||||
* Install
|
||||
This module has no external dependencies.
|
||||
|
||||
* Configuration
|
||||
** Using another snippets library
|
||||
Don't want to use provided one? Then add this to your private module,
|
||||
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
;; in config/$USER/packages.el
|
||||
(package! emacs-snippets :ignore t)
|
||||
|
||||
;; in config/$USER/config.el
|
||||
(def-package-hook! emacs-snippets :disabled t)
|
||||
(after! yasnippet
|
||||
(push "~/path/to/my/private/snippets" yas-snippet-dirs))
|
||||
#+END_SRC
|
||||
|
||||
** I'm not an evil user...
|
||||
That's fine. All evil configuration is ignored if =:feature evil= is disabled.
|
||||
|
||||
* Appendix
|
||||
** Commands
|
||||
+ ~+default/browse-project~
|
||||
+ ~+default/browse-templates~
|
||||
+ ~+default/find-in-templates~
|
||||
+ ~+default/browse-emacsd~
|
||||
+ ~+default/find-in-emacsd~
|
||||
+ ~+default/browse-notes~
|
||||
+ ~+default/find-in-notes~
|
||||
+ ~+default/find-in-snippets~
|
||||
** Hacks
|
||||
+ ~epa-pinentry-mode~ is set to ~'loopback~, forcing gpg-agent to use the Emacs
|
||||
minibuffer when prompting for your passphrase. *Only works with GPG 2.1+!*
|
271
modules/config/default/autoload/default.el
Normal file
271
modules/config/default/autoload/default.el
Normal file
|
@ -0,0 +1,271 @@
|
|||
;; config/default/autoload/default.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autoload
|
||||
(defun +default/yank-buffer-filename ()
|
||||
"Copy the current buffer's path to the kill ring."
|
||||
(interactive)
|
||||
(if-let* ((filename (or buffer-file-name (bound-and-true-p list-buffers-directory))))
|
||||
(message (kill-new (abbreviate-file-name filename)))
|
||||
(error "Couldn't find filename in current buffer")))
|
||||
|
||||
;;;###autoload
|
||||
(defun +default/browse-project ()
|
||||
(interactive) (doom-project-browse (doom-project-root)))
|
||||
;; NOTE No need for find-in-project, use `projectile-find-file'
|
||||
|
||||
;;;###autoload
|
||||
(defun +default/browse-templates ()
|
||||
(interactive) (doom-project-browse +file-templates-dir))
|
||||
;;;###autoload
|
||||
(defun +default/find-in-templates ()
|
||||
(interactive) (doom-project-find-file +file-templates-dir))
|
||||
|
||||
;;;###autoload
|
||||
(defun +default/browse-emacsd ()
|
||||
(interactive) (doom-project-browse doom-emacs-dir))
|
||||
;;;###autoload
|
||||
(defun +default/find-in-emacsd ()
|
||||
(interactive) (doom-project-find-file doom-emacs-dir))
|
||||
|
||||
;;;###autoload
|
||||
(defun +default/browse-notes ()
|
||||
(interactive) (doom-project-browse org-directory))
|
||||
;;;###autoload
|
||||
(defun +default/find-in-notes ()
|
||||
(interactive) (doom-project-find-file org-directory))
|
||||
|
||||
;;;###autoload
|
||||
(defun +default/compile (arg)
|
||||
"Runs `compile' from the root of the current project.
|
||||
|
||||
If a compilation window is already open, recompile that instead.
|
||||
|
||||
If ARG (universal argument), runs `compile' from the current directory."
|
||||
(interactive "P")
|
||||
(if (and (bound-and-true-p compilation-in-progress)
|
||||
(buffer-live-p compilation-last-buffer))
|
||||
(recompile)
|
||||
(call-interactively
|
||||
(if arg
|
||||
#'projectile-compile-project
|
||||
#'compile))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +default/man-or-woman ()
|
||||
"Invoke `man' if man is installed, otherwise use `woman'."
|
||||
(interactive)
|
||||
(call-interactively
|
||||
(if (executable-find "man")
|
||||
#'man
|
||||
#'woman)))
|
||||
|
||||
;;;###autoload
|
||||
(defalias '+default/newline #'newline)
|
||||
|
||||
;;;###autoload
|
||||
(defun +default/new-buffer ()
|
||||
"TODO"
|
||||
(interactive)
|
||||
(if (featurep! 'evil)
|
||||
(call-interactively #'evil-buffer-new)
|
||||
(let ((buffer (generate-new-buffer "*new*")))
|
||||
(set-window-buffer nil buffer)
|
||||
(with-current-buffer buffer
|
||||
(funcall (default-value 'major-mode))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +default/project-tasks ()
|
||||
"Invokes `+ivy/tasks' or `+helm/tasks', depending on which is available."
|
||||
(interactive)
|
||||
(cond ((featurep! :completion ivy) (+ivy/tasks))
|
||||
((featurep! :completion helm) (+helm/tasks))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +default/newline-above ()
|
||||
"Insert an indented new line before the current one."
|
||||
(interactive)
|
||||
(if (featurep 'evil)
|
||||
(call-interactively 'evil-open-above)
|
||||
(beginning-of-line)
|
||||
(save-excursion (newline))
|
||||
(indent-according-to-mode)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +default/newline-below ()
|
||||
"Insert an indented new line after the current one."
|
||||
(interactive)
|
||||
(if (featurep 'evil)
|
||||
(call-interactively 'evil-open-below)
|
||||
(end-of-line)
|
||||
(newline-and-indent)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +default/yank-pop ()
|
||||
"Interactively select what text to insert from the kill ring."
|
||||
(interactive)
|
||||
(call-interactively
|
||||
(cond ((fboundp 'counsel-yank-pop) #'counsel-yank-pop)
|
||||
((fboundp 'helm-show-kill-ring) #'helm-show-kill-ring)
|
||||
((error "No kill-ring search backend available. Enable ivy or helm!")))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +default*newline-indent-and-continue-comments (_orig-fn)
|
||||
"Inserts a newline and possibly indents it. Also continues comments if
|
||||
executed from a commented line; handling special cases for certain languages
|
||||
with weak native support."
|
||||
(interactive)
|
||||
(cond ((sp-point-in-string) (newline))
|
||||
((and (sp-point-in-comment)
|
||||
comment-line-break-function)
|
||||
(funcall comment-line-break-function))
|
||||
(t
|
||||
(newline nil t)
|
||||
(indent-according-to-mode))))
|
||||
|
||||
(defun doom--backward-delete-whitespace-to-column ()
|
||||
"Delete back to the previous column of whitespace, or as much whitespace as
|
||||
possible, or just one char if that's not possible."
|
||||
(interactive)
|
||||
(let* ((context (ignore-errors (sp-get-thing)))
|
||||
(op (plist-get context :op))
|
||||
(cl (plist-get context :cl))
|
||||
open-len close-len)
|
||||
(cond ;; When in strings (sp acts weird with quotes; this is the fix)
|
||||
;; Also, skip closing delimiters
|
||||
((and op cl
|
||||
(string= op cl)
|
||||
(and (string= (char-to-string (or (char-before) 0)) op)
|
||||
(setq open-len (length op)))
|
||||
(and (string= (char-to-string (or (char-after) 0)) cl)
|
||||
(setq close-len (length cl))))
|
||||
(delete-char (- open-len))
|
||||
(delete-char close-len))
|
||||
|
||||
;; Delete up to the nearest tab column IF only whitespace between
|
||||
;; point and bol.
|
||||
((and (not indent-tabs-mode)
|
||||
(not (bolp))
|
||||
(not (sp-point-in-string))
|
||||
(save-excursion (>= (- (skip-chars-backward " \t")) tab-width)))
|
||||
(let ((movement (% (current-column) tab-width)))
|
||||
(when (= movement 0)
|
||||
(setq movement tab-width))
|
||||
(delete-char (- movement)))
|
||||
(unless (memq (char-before) (list ?\n ?\ ))
|
||||
(insert " ")))
|
||||
|
||||
;; Otherwise do a regular delete
|
||||
((delete-char -1)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +default*delete-backward-char (n &optional killflag)
|
||||
"Same as `delete-backward-char', but preforms these additional checks:
|
||||
|
||||
+ If point is surrounded by (balanced) whitespace and a brace delimiter ({} []
|
||||
()), delete a space on either side of the cursor.
|
||||
+ If point is at BOL and surrounded by braces on adjacent lines, collapse
|
||||
newlines:
|
||||
{
|
||||
|
|
||||
} => {|}
|
||||
+ Otherwise, resort to `doom--backward-delete-whitespace-to-column'.
|
||||
+ Resorts to `delete-char' if n > 1"
|
||||
(interactive "p\nP")
|
||||
(or (integerp n)
|
||||
(signal 'wrong-type-argument (list 'integerp n)))
|
||||
(cond ((and (use-region-p)
|
||||
delete-active-region
|
||||
(= n 1))
|
||||
;; If a region is active, kill or delete it.
|
||||
(if (eq delete-active-region 'kill)
|
||||
(kill-region (region-beginning) (region-end) 'region)
|
||||
(funcall region-extract-function 'delete-only)))
|
||||
;; In Overwrite mode, maybe untabify while deleting
|
||||
((null (or (null overwrite-mode)
|
||||
(<= n 0)
|
||||
(memq (char-before) '(?\t ?\n))
|
||||
(eobp)
|
||||
(eq (char-after) ?\n)))
|
||||
(let ((ocol (current-column)))
|
||||
(delete-char (- n) killflag)
|
||||
(save-excursion
|
||||
(insert-char ?\s (- ocol (current-column)) nil))))
|
||||
;;
|
||||
((and (= n 1) (bound-and-true-p smartparens-mode))
|
||||
(cond ((and (memq (char-before) (list ?\ ?\t))
|
||||
(save-excursion
|
||||
(and (/= (skip-chars-backward " \t" (line-beginning-position)) 0)
|
||||
(bolp))))
|
||||
(doom--backward-delete-whitespace-to-column))
|
||||
((let* ((pair (ignore-errors (sp-get-thing)))
|
||||
(op (plist-get pair :op))
|
||||
(cl (plist-get pair :cl))
|
||||
(beg (plist-get pair :beg))
|
||||
(end (plist-get pair :end)))
|
||||
(cond ((and end beg (= end (+ beg (length op) (length cl))))
|
||||
(sp-backward-delete-char 1))
|
||||
((doom-surrounded-p pair 'inline 'balanced)
|
||||
(delete-char -1 killflag)
|
||||
(delete-char 1)
|
||||
(when (= (point) (+ (length cl) beg))
|
||||
(sp-backward-delete-char 1)
|
||||
(sp-insert-pair op)))
|
||||
((and (bolp) (doom-surrounded-p pair nil 'balanced))
|
||||
(delete-region beg end)
|
||||
(sp-insert-pair op)
|
||||
t)
|
||||
((run-hook-with-args-until-success 'doom-delete-backward-functions))
|
||||
((doom--backward-delete-whitespace-to-column)))))))
|
||||
;; Otherwise, do simple deletion.
|
||||
((delete-char (- n) killflag))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +default/search-from-cwd (&optional arg)
|
||||
"Conduct a text search in files under the current folder.
|
||||
If prefix ARG is set, prompt for a directory to search from."
|
||||
(interactive "P")
|
||||
(let ((default-directory
|
||||
(if arg
|
||||
(read-directory-name "Switch to project: " default-directory)
|
||||
default-directory)))
|
||||
(call-interactively
|
||||
(cond ((featurep! :completion ivy) #'+ivy/project-search-from-cwd)
|
||||
((featurep! :completion helm) #'+helm/project-search-from-cwd)
|
||||
(#'projectile-grep)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +default/search-project (&optional arg)
|
||||
"Conduct a text search in the current project root.
|
||||
If prefix ARG is set, prompt for a known project to search from."
|
||||
(interactive "P")
|
||||
(let ((default-directory
|
||||
(if arg
|
||||
(if-let* ((projects (projectile-relevant-known-projects)))
|
||||
(completing-read "Switch to project: " projects
|
||||
nil t nil nil (doom-project-root))
|
||||
(user-error "There are no known projects"))
|
||||
default-directory)))
|
||||
(call-interactively
|
||||
(cond ((featurep! :completion ivy) #'+ivy/project-search)
|
||||
((featurep! :completion helm) #'+helm/project-search)
|
||||
(#'rgrep)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +default/search-project-for-symbol-at-point (&optional arg symbol)
|
||||
"Conduct a text search in the current project for symbol at point.
|
||||
If prefix ARG is set, prompt for a known project to search from."
|
||||
(interactive
|
||||
(list current-prefix-arg
|
||||
(thing-at-point 'symbol t)))
|
||||
(let ((default-directory
|
||||
(if arg
|
||||
(if-let* ((projects (projectile-relevant-known-projects)))
|
||||
(completing-read "Switch to project: " projects
|
||||
nil t nil nil (doom-project-root))
|
||||
(user-error "There are no known projects"))
|
||||
default-directory)))
|
||||
(cond ((featurep! :completion ivy)
|
||||
(+ivy/project-search nil (rxt-quote-pcre symbol)))
|
||||
((featurep! :completion helm)
|
||||
(+helm/project-search nil (rxt-quote-pcre symbol)))
|
||||
((rgrep (regexp-quote symbol))))))
|
265
modules/config/default/config.el
Normal file
265
modules/config/default/config.el
Normal file
|
@ -0,0 +1,265 @@
|
|||
;;; config/default/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defvar +default-minibuffer-maps
|
||||
`(minibuffer-local-map
|
||||
minibuffer-local-ns-map
|
||||
minibuffer-local-completion-map
|
||||
minibuffer-local-must-match-map
|
||||
minibuffer-local-isearch-map
|
||||
read-expression-map
|
||||
,@(if (featurep! :completion ivy) '(ivy-minibuffer-map)))
|
||||
"A list of all the keymaps used for the minibuffer.")
|
||||
|
||||
|
||||
;;
|
||||
;;; Reasonable defaults
|
||||
|
||||
(after! epa
|
||||
(setq epa-file-encrypt-to
|
||||
(or epa-file-encrypt-to
|
||||
;; Collect all public key IDs with your username
|
||||
(unless (string-empty-p user-full-name)
|
||||
(cl-loop for key in (ignore-errors (epg-list-keys (epg-make-context) user-full-name))
|
||||
collect (epg-sub-key-id (car (epg-key-sub-key-list key)))))
|
||||
user-mail-address)
|
||||
;; With GPG 2.1, this forces gpg-agent to use the Emacs minibuffer to
|
||||
;; prompt for the key passphrase.
|
||||
epa-pinentry-mode 'loopback))
|
||||
|
||||
|
||||
;;
|
||||
;;; Smartparens config
|
||||
|
||||
(when (featurep! +smartparens)
|
||||
;; You can disable :unless predicates with (sp-pair "'" nil :unless nil)
|
||||
;; And disable :post-handlers with (sp-pair "{" nil :post-handlers nil)
|
||||
;; or specific :post-handlers with:
|
||||
;; (sp-pair "{" nil :post-handlers '(:rem ("| " "SPC")))
|
||||
(after! smartparens
|
||||
;; Autopair quotes more conservatively; if I'm next to a word/before another
|
||||
;; quote, I likely don't want to open a new pair.
|
||||
(let ((unless-list '(sp-point-before-word-p
|
||||
sp-point-after-word-p
|
||||
sp-point-before-same-p)))
|
||||
(sp-pair "'" nil :unless unless-list)
|
||||
(sp-pair "\"" nil :unless unless-list))
|
||||
|
||||
;; Expand {|} => { | }
|
||||
;; Expand {|} => {
|
||||
;; |
|
||||
;; }
|
||||
(dolist (brace '("(" "{" "["))
|
||||
(sp-pair brace nil
|
||||
:post-handlers '(("||\n[i]" "RET") ("| " "SPC"))
|
||||
;; I likely don't want a new pair if adjacent to a word or opening brace
|
||||
:unless '(sp-point-before-word-p sp-point-before-same-p)))
|
||||
|
||||
;; Major-mode specific fixes
|
||||
(sp-local-pair '(ruby-mode enh-ruby-mode) "{" "}"
|
||||
:pre-handlers '(:rem sp-ruby-pre-handler)
|
||||
:post-handlers '(:rem sp-ruby-post-handler))
|
||||
|
||||
;; Don't do square-bracket space-expansion where it doesn't make sense to
|
||||
(sp-local-pair '(emacs-lisp-mode org-mode markdown-mode gfm-mode)
|
||||
"[" nil :post-handlers '(:rem ("| " "SPC")))
|
||||
|
||||
;; Reasonable default pairs for HTML-style comments
|
||||
(sp-local-pair (append sp--html-modes '(markdown-mode gfm-mode))
|
||||
"<!--" "-->"
|
||||
:unless '(sp-point-before-word-p sp-point-before-same-p)
|
||||
:actions '(insert) :post-handlers '(("| " "SPC")))
|
||||
|
||||
;; Disable electric keys in C modes because it interferes with smartparens
|
||||
;; and custom bindings. We'll do it ourselves (mostly).
|
||||
(after! cc-mode
|
||||
(c-toggle-electric-state -1)
|
||||
(c-toggle-auto-newline -1)
|
||||
(setq c-electric-flag nil)
|
||||
(dolist (key '("#" "{" "}" "/" "*" ";" "," ":" "(" ")" "\177"))
|
||||
(define-key c-mode-base-map key nil)))
|
||||
|
||||
;; Expand C-style doc comment blocks. Must be done manually because some of
|
||||
;; these languages use specialized (and deferred) parsers, whose state we
|
||||
;; can't access while smartparens is doing its thing.
|
||||
(defun +default-expand-doc-comment-block (&rest _ignored)
|
||||
(let ((indent (current-indentation)))
|
||||
(newline-and-indent)
|
||||
(save-excursion
|
||||
(newline)
|
||||
(insert (make-string indent 32) " */")
|
||||
(delete-char 2))))
|
||||
(sp-local-pair
|
||||
'(js2-mode typescript-mode rjsx-mode rust-mode c-mode c++-mode objc-mode
|
||||
csharp-mode java-mode php-mode css-mode scss-mode less-css-mode
|
||||
stylus-mode)
|
||||
"/*" "*/"
|
||||
:actions '(insert)
|
||||
:post-handlers '(("| " "SPC") ("|\n*/[i][d-2]" "RET") (+default-expand-doc-comment-block "*")))
|
||||
|
||||
;; Highjacks backspace to:
|
||||
;; a) balance spaces inside brackets/parentheses ( | ) -> (|)
|
||||
;; b) delete space-indented `tab-width' steps at a time
|
||||
;; c) close empty multiline brace blocks in one step:
|
||||
;; {
|
||||
;; |
|
||||
;; }
|
||||
;; becomes {|}
|
||||
;; d) refresh smartparens' :post-handlers, so SPC and RET expansions work
|
||||
;; even after a backspace.
|
||||
;; e) properly delete smartparen pairs when they are encountered, without
|
||||
;; the need for strict mode.
|
||||
;; f) do none of this when inside a string
|
||||
(advice-add #'delete-backward-char :override #'+default*delete-backward-char)
|
||||
|
||||
;; Makes `newline-and-indent' continue comments (and more reliably)
|
||||
(advice-add #'newline-and-indent :around #'+default*newline-indent-and-continue-comments)))
|
||||
|
||||
|
||||
;;
|
||||
;;; Keybinding fixes
|
||||
|
||||
;; This section is dedicated to "fixing" certain keys so that they behave
|
||||
;; sensibly (and consistently with similar contexts).
|
||||
|
||||
;; Consistently use q to quit windows
|
||||
(after! tabulated-list
|
||||
(define-key tabulated-list-mode-map "q" #'quit-window))
|
||||
|
||||
;; OS specific fixes
|
||||
(when IS-MAC
|
||||
;; Fix MacOS shift+tab
|
||||
(define-key input-decode-map [S-iso-lefttab] [backtab])
|
||||
;; Fix conventional OS keys in Emacs
|
||||
(map! "s-`" #'other-frame ; fix frame-switching
|
||||
;; fix OS window/frame navigation/manipulation keys
|
||||
"s-w" #'delete-window
|
||||
"s-W" #'delete-frame
|
||||
"s-n" #'+default/new-buffer
|
||||
"s-N" #'make-frame
|
||||
"s-q" (if (daemonp) #'delete-frame #'save-buffers-kill-terminal)
|
||||
"C-s-f" #'toggle-frame-fullscreen
|
||||
;; Restore somewhat common navigation
|
||||
"s-l" #'goto-line
|
||||
;; Restore OS undo, save, copy, & paste keys (without cua-mode, because
|
||||
;; it imposes some other functionality and overhead we don't need)
|
||||
"s-f" #'swiper
|
||||
"s-z" #'undo
|
||||
"s-Z" #'redo
|
||||
"s-c" (if (featurep 'evil) #'evil-yank #'copy-region-as-kill)
|
||||
"s-v" #'yank
|
||||
"s-s" #'save-buffer
|
||||
;; Buffer-local font scaling
|
||||
"s-+" (λ! (text-scale-set 0))
|
||||
"s-=" #'text-scale-increase
|
||||
"s--" #'text-scale-decrease
|
||||
;; Conventional text-editing keys & motions
|
||||
"s-a" #'mark-whole-buffer
|
||||
:g "s-/" (λ! (save-excursion (comment-line 1)))
|
||||
:n "s-/" #'evil-commentary-line
|
||||
:v "s-/" #'evil-commentary
|
||||
:gni [s-return] #'+default/newline-below
|
||||
:gni [S-s-return] #'+default/newline-above
|
||||
:gi [s-backspace] #'doom/backward-kill-to-bol-and-indent
|
||||
:gi [s-left] #'doom/backward-to-bol-or-indent
|
||||
:gi [s-right] #'doom/forward-to-last-non-comment-or-eol
|
||||
:gi [M-backspace] #'backward-kill-word
|
||||
:gi [M-left] #'backward-word
|
||||
:gi [M-right] #'forward-word))
|
||||
|
||||
|
||||
;;
|
||||
;;; Keybind schemes
|
||||
|
||||
;; Custom help keys -- these aren't under `+bindings' because they ought to be
|
||||
;; universal.
|
||||
(define-key! help-map
|
||||
;; new keybinds
|
||||
"'" #'describe-char
|
||||
"A" #'doom/describe-autodefs
|
||||
"B" #'doom/open-bug-report
|
||||
"D" #'doom/open-manual
|
||||
"E" #'doom/open-vanilla-sandbox
|
||||
"M" #'doom/describe-active-minor-mode
|
||||
"O" #'+lookup/online
|
||||
"T" #'doom/toggle-profiler
|
||||
"V" #'set-variable
|
||||
"W" #'+default/man-or-woman
|
||||
"C-k" #'describe-key-briefly
|
||||
"C-l" #'describe-language-environment
|
||||
"C-m" #'info-emacs-manual
|
||||
"C-v" #'doom/version
|
||||
|
||||
;; Unbind `help-for-help'. Conflicts with which-key's help command for the
|
||||
;; <leader> h prefix. It's already on ? and F1 anyway.
|
||||
"C-h" nil
|
||||
|
||||
;; replacement keybinds
|
||||
;; replaces `info-emacs-manual' b/c it's on C-m now
|
||||
"r" nil
|
||||
"rr" #'doom/reload
|
||||
"rt" #'doom/reload-theme
|
||||
"rp" #'doom/reload-packages
|
||||
"rf" #'doom/reload-font
|
||||
"re" #'doom/reload-env
|
||||
|
||||
;; replaces `apropos-command'
|
||||
"a" #'apropos
|
||||
;; replaces `describe-copying' b/c not useful
|
||||
"C-c" #'describe-coding-system
|
||||
;; replaces `apropos-documentation' b/c `apropos' covers this
|
||||
"d" #'doom/describe-module
|
||||
;; replaces `Info-got-emacs-command-node' b/c redundant w/ `Info-goto-node'
|
||||
"F" #'describe-face
|
||||
;; replaces `view-hello-file' b/c annoying
|
||||
"h" #'doom/describe-symbol
|
||||
;; replaces `describe-language-environment' b/c remapped to C-l
|
||||
"L" #'global-command-log-mode
|
||||
;; replaces `view-emacs-news' b/c it's on C-n too
|
||||
"n" #'doom/open-news
|
||||
;; replaces `finder-by-keyword'
|
||||
;; "p" #'doom/describe-package
|
||||
;; replaces `describe-package' b/c redundant w/ `doom/describe-package'
|
||||
"P" #'find-library)
|
||||
|
||||
(after! which-key
|
||||
(which-key-add-key-based-replacements doom-leader-key "<leader>")
|
||||
(which-key-add-key-based-replacements doom-localleader-key "<localleader>")
|
||||
|
||||
(which-key-add-key-based-replacements "C-h r" "reload")
|
||||
(when (featurep 'evil)
|
||||
(which-key-add-key-based-replacements (concat doom-leader-key " r") "reload")
|
||||
(which-key-add-key-based-replacements (concat doom-leader-alt-key " r") "reload")))
|
||||
|
||||
|
||||
(when (featurep! +bindings)
|
||||
;; Make M-x harder to miss
|
||||
(define-key! 'override
|
||||
"M-x" #'execute-extended-command
|
||||
"A-x" #'execute-extended-command)
|
||||
|
||||
;; A Doom convention where C-s on popups and interactive searches will invoke
|
||||
;; ivy/helm for their superior filtering.
|
||||
(define-key! :keymaps +default-minibuffer-maps
|
||||
"C-s" (if (featurep! :completion ivy)
|
||||
#'counsel-minibuffer-history
|
||||
#'helm-minibuffer-history))
|
||||
|
||||
;; Smarter C-a/C-e for both Emacs and Evil. C-a will jump to indentation.
|
||||
;; Pressing it again will send you to the true bol. Same goes for C-e, except
|
||||
;; it will ignore comments+trailing whitespace before jumping to eol.
|
||||
(map! :gi "C-a" #'doom/backward-to-bol-or-indent
|
||||
:gi "C-e" #'doom/forward-to-last-non-comment-or-eol
|
||||
;; Standardize the behavior of M-RET/M-S-RET as a "add new item
|
||||
;; below/above" key.
|
||||
:gni [M-return] #'+default/newline-below
|
||||
:gni [M-S-return] #'+default/newline-above
|
||||
:gni [C-return] #'+default/newline-below
|
||||
:gni [C-S-return] #'+default/newline-above))
|
||||
|
||||
|
||||
;;
|
||||
;;; Bootstrap configs
|
||||
|
||||
(if (featurep 'evil)
|
||||
(load! "+evil")
|
||||
(load! "+emacs"))
|
6
modules/config/default/packages.el
Normal file
6
modules/config/default/packages.el
Normal file
|
@ -0,0 +1,6 @@
|
|||
;; -*- no-byte-compile: t; -*-
|
||||
;;; config/default/packages.el
|
||||
|
||||
(unless (featurep! :feature evil)
|
||||
(package! winum)
|
||||
(package! expand-region))
|
14
modules/config/literate/autoload.el
Normal file
14
modules/config/literate/autoload.el
Normal file
|
@ -0,0 +1,14 @@
|
|||
;;; config/literate/autoload.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autoload
|
||||
(defalias '+literate/reload #'doom/reload)
|
||||
|
||||
;;;###autoload
|
||||
(defun +literate|recompile-maybe ()
|
||||
"Recompile config.org if we're editing an org file in our DOOMDIR.
|
||||
|
||||
We assume any org file in `doom-private-dir' is connected to your literate
|
||||
config, and should trigger a recompile if changed."
|
||||
(when (and (eq major-mode 'org-mode)
|
||||
(file-in-directory-p buffer-file-name doom-private-dir))
|
||||
(+literate-tangle 'force)))
|
49
modules/config/literate/init.el
Normal file
49
modules/config/literate/init.el
Normal file
|
@ -0,0 +1,49 @@
|
|||
;;; config/literate/init.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defvar +literate-config-file
|
||||
(expand-file-name "config.org" doom-private-dir)
|
||||
"The file path of your literate config file.")
|
||||
|
||||
(defvar +literate-config-cache-file
|
||||
(expand-file-name "literate-last-compile" doom-cache-dir)
|
||||
"The file path that `+literate-config-file' will be tangled to, then
|
||||
byte-compiled from.")
|
||||
|
||||
|
||||
;;
|
||||
(defun +literate-tangle (&optional force-p)
|
||||
"Tangles `+literate-config-file' if it has changed."
|
||||
(let ((default-directory doom-private-dir)
|
||||
(org +literate-config-file))
|
||||
(when (or force-p (file-newer-than-file-p org +literate-config-cache-file))
|
||||
(message "Compiling your literate config...")
|
||||
|
||||
(let* ((org (file-truename +literate-config-file))
|
||||
(dest (concat (file-name-sans-extension org) ".el")))
|
||||
(or (and (if (fboundp 'org-babel-tangle-file)
|
||||
(org-babel-tangle-file org dest "emacs-lisp")
|
||||
;; We tangle in a separate, blank process because loading it
|
||||
;; here would load all of :lang org (very expensive!).
|
||||
(zerop (call-process
|
||||
"emacs" nil nil nil
|
||||
"-q" "--batch" "-l" "ob-tangle" "--eval"
|
||||
(format "(org-babel-tangle-file %S %S \"emacs-lisp\")"
|
||||
org dest))))
|
||||
;; Write the cache file to serve as our mtime cache
|
||||
(with-temp-file +literate-config-cache-file
|
||||
(message "Done!")))
|
||||
(warn "There was a problem tangling your literate config!"))))))
|
||||
|
||||
|
||||
;; Let 'er rip!
|
||||
(when noninteractive
|
||||
(require 'ob-tangle nil t))
|
||||
|
||||
(+literate-tangle (or doom-reloading-p noninteractive))
|
||||
;; No need to load the resulting file. Doom will do this for us after all
|
||||
;; modules have finished loading.
|
||||
|
||||
|
||||
;; Recompile our literate config if we modify it
|
||||
(after! org
|
||||
(add-hook 'after-save-hook #'+literate|recompile-maybe))
|
32
modules/editor/fold/README.org
Normal file
32
modules/editor/fold/README.org
Normal file
|
@ -0,0 +1,32 @@
|
|||
#+TITLE: editor/fold
|
||||
#+DATE: February 17, 2019
|
||||
#+SINCE: v2.1
|
||||
#+STARTUP: inlineimages
|
||||
|
||||
* Table of Contents :TOC_3:noexport:
|
||||
- [[Description][Description]]
|
||||
- [[Module Flags][Module Flags]]
|
||||
- [[Plugins][Plugins]]
|
||||
- [[Prerequisites][Prerequisites]]
|
||||
- [[Features][Features]]
|
||||
- [[Configuration][Configuration]]
|
||||
- [[Troubleshooting][Troubleshooting]]
|
||||
|
||||
* Description
|
||||
This module marries hideshow, vimish-fold and outline-minor-mode to bring you
|
||||
marker, indent and syntax-based code folding for as many languages as possible.
|
||||
|
||||
** Module Flags
|
||||
This module provides no flags.
|
||||
|
||||
** Plugins
|
||||
+ evil-vimish-fold*
|
||||
|
||||
* Prerequisites
|
||||
This module has no prerequisites.
|
||||
|
||||
* TODO Features
|
||||
|
||||
* TODO Configuration
|
||||
|
||||
* TODO Troubleshooting
|
150
modules/editor/fold/autoload/evil.el
Normal file
150
modules/editor/fold/autoload/evil.el
Normal file
|
@ -0,0 +1,150 @@
|
|||
;;; editor/fold/autoload/evil.el -*- lexical-binding: t; -*-
|
||||
;;;###if (featurep! :feature evil)
|
||||
|
||||
(require 'hideshow)
|
||||
|
||||
;; `hideshow' is a decent code folding implementation, but it won't let you
|
||||
;; create custom folds. `vimish-fold' offers custom folds, but essentially
|
||||
;; ignores any other type of folding (indent or custom markers, which
|
||||
;; hs-minor-mode and `outline-mode' give you).
|
||||
;;
|
||||
;; So this is my effort to combine them.
|
||||
|
||||
(defun +fold--vimish-fold-p ()
|
||||
(and (featurep 'vimish-fold)
|
||||
(cl-some #'vimish-fold--vimish-overlay-p
|
||||
(overlays-at (point)))))
|
||||
|
||||
(defun +fold--outline-fold-p ()
|
||||
(and (or (bound-and-true-p outline-minor-mode)
|
||||
(derived-mode-p 'outline-mode))
|
||||
(outline-on-heading-p)))
|
||||
|
||||
(defun +fold--hideshow-fold-p ()
|
||||
(hs-minor-mode +1)
|
||||
(save-excursion
|
||||
(ignore-errors
|
||||
(or (hs-looking-at-block-start-p)
|
||||
(hs-find-block-beginning)))))
|
||||
|
||||
|
||||
;;
|
||||
;; Code folding
|
||||
|
||||
(defmacro +fold-from-eol (&rest body)
|
||||
"Perform action after moving to the end of the line."
|
||||
`(save-excursion
|
||||
(end-of-line)
|
||||
,@body))
|
||||
|
||||
;;;###autoload
|
||||
(defun +fold/toggle ()
|
||||
"Toggle the fold at point.
|
||||
|
||||
Targets `vimmish-fold', `hideshow' and `outline' folds."
|
||||
(interactive)
|
||||
(save-excursion
|
||||
(cond ((+fold--vimish-fold-p) (vimish-fold-toggle))
|
||||
((+fold--outline-fold-p)
|
||||
(cl-letf (((symbol-function #'outline-hide-subtree)
|
||||
(symbol-function #'outline-hide-entry)))
|
||||
(outline-toggle-children)))
|
||||
((+fold--hideshow-fold-p) (+fold-from-eol (hs-toggle-hiding))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +fold/open ()
|
||||
"Open the folded region at point.
|
||||
|
||||
Targets `vimmish-fold', `hideshow' and `outline' folds."
|
||||
(interactive)
|
||||
(save-excursion
|
||||
(cond ((+fold--vimish-fold-p) (vimish-fold-unfold))
|
||||
((+fold--outline-fold-p)
|
||||
(outline-show-children)
|
||||
(outline-show-entry))
|
||||
((+fold--hideshow-fold-p) (+fold-from-eol (hs-show-block))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +fold/close ()
|
||||
"Close the folded region at point.
|
||||
|
||||
Targets `vimmish-fold', `hideshow' and `outline' folds."
|
||||
(interactive)
|
||||
(save-excursion
|
||||
(cond ((+fold--vimish-fold-p) (vimish-fold-refold))
|
||||
((+fold--hideshow-fold-p) (+fold-from-eol (hs-hide-block)))
|
||||
((+fold--outline-fold-p) (outline-hide-subtree)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +fold/open-all (&optional level)
|
||||
"Open folds at LEVEL (or all folds if LEVEL is nil)."
|
||||
(interactive
|
||||
(list (if current-prefix-arg (prefix-numeric-value current-prefix-arg))))
|
||||
(when (featurep 'vimish-fold)
|
||||
(vimish-fold-unfold-all))
|
||||
(save-excursion
|
||||
(if (integerp level)
|
||||
(progn
|
||||
(outline-hide-sublevels (max 1 (1- level)))
|
||||
(hs-life-goes-on
|
||||
(hs-hide-level-recursive (1- level) (point-min) (point-max))))
|
||||
(hs-show-all)
|
||||
(when (fboundp 'outline-show-all)
|
||||
(outline-show-all)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +fold/close-all (&optional level)
|
||||
"Close folds at LEVEL (or all folds if LEVEL is nil)."
|
||||
(interactive
|
||||
(list (if current-prefix-arg (prefix-numeric-value current-prefix-arg))))
|
||||
(save-excursion
|
||||
(when (featurep 'vimish-fold)
|
||||
(vimish-fold-refold-all))
|
||||
(hs-life-goes-on
|
||||
(if (integerp level)
|
||||
(hs-hide-level-recursive (1- level) (point-min) (point-max))
|
||||
(hs-hide-all)))))
|
||||
|
||||
(defun +fold--invisible-points (count)
|
||||
(let (points)
|
||||
(save-excursion
|
||||
(catch 'abort
|
||||
(if (< count 0) (beginning-of-line))
|
||||
(while (re-search-forward hs-block-start-regexp nil t
|
||||
(if (> count 0) 1 -1))
|
||||
(unless (invisible-p (point))
|
||||
(end-of-line)
|
||||
(when (hs-already-hidden-p)
|
||||
(push (point) points)
|
||||
(when (>= (length points) count)
|
||||
(throw 'abort nil))))
|
||||
(forward-line (if (> count 0) 1 -1)))))
|
||||
points))
|
||||
|
||||
;;;###autoload
|
||||
(defun +fold/next (count)
|
||||
"Jump to the next vimish fold, outline heading or folded region."
|
||||
(interactive "p")
|
||||
(cl-loop with orig-pt = (point)
|
||||
for fn
|
||||
in (list (lambda ()
|
||||
(when hs-block-start-regexp
|
||||
(car (+fold--invisible-points count))))
|
||||
(lambda ()
|
||||
(if (> count 0)
|
||||
(evil-vimish-fold/next-fold count)
|
||||
(evil-vimish-fold/previous-fold (- count)))
|
||||
(if (/= (point) orig-pt) (point))))
|
||||
if (save-excursion (funcall fn))
|
||||
collect it into points
|
||||
finally do
|
||||
(if-let* ((pt (car (sort points (if (> count 0) #'< #'>)))))
|
||||
(goto-char pt)
|
||||
(message "No more folds %s point" (if (> count 0) "after" "before"))
|
||||
(goto-char orig-pt))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +fold/previous (count)
|
||||
"Jump to the previous vimish fold, outline heading or folded region."
|
||||
(interactive "p")
|
||||
(+fold/next (- count)))
|
87
modules/editor/fold/autoload/fold.el
Normal file
87
modules/editor/fold/autoload/fold.el
Normal file
|
@ -0,0 +1,87 @@
|
|||
;;; editor/fold/autoload/fold.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defface +fold-hideshow-folded-face
|
||||
`((t (:inherit font-lock-comment-face :weight light)))
|
||||
"Face to hightlight `hideshow' overlays."
|
||||
:group 'doom-themes)
|
||||
|
||||
;;;###autoload
|
||||
(defun +fold-hideshow*ensure-mode (&rest _)
|
||||
"Ensure hs-minor-mode is enabled."
|
||||
(unless (bound-and-true-p hs-minor-mode)
|
||||
(hs-minor-mode +1)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +fold-hideshow-haml-forward-sexp (arg)
|
||||
(haml-forward-sexp arg)
|
||||
(move-beginning-of-line 1))
|
||||
|
||||
;;;###autoload
|
||||
(defun +fold-hideshow-forward-block-by-indent (_arg)
|
||||
(let ((start (current-indentation)))
|
||||
(forward-line)
|
||||
(unless (= start (current-indentation))
|
||||
(let ((range (+fold-hideshow-indent-range)))
|
||||
(goto-char (cadr range))
|
||||
(end-of-line)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +fold-hideshow-set-up-overlay (ov)
|
||||
(when (eq 'code (overlay-get ov 'hs))
|
||||
(when (featurep 'vimish-fold)
|
||||
(overlay-put
|
||||
ov 'before-string
|
||||
(propertize "…" 'display
|
||||
(list vimish-fold-indication-mode
|
||||
'empty-line
|
||||
'vimish-fold-fringe))))
|
||||
(overlay-put
|
||||
ov 'display (propertize " [...] " 'face '+fold-hideshow-folded-face))))
|
||||
|
||||
|
||||
;;
|
||||
;; Indentation detection
|
||||
|
||||
(defun +fold--hideshow-empty-line-p ()
|
||||
(string= "" (string-trim (thing-at-point 'line))))
|
||||
|
||||
(defun +fold--hideshow-geq-or-empty-p ()
|
||||
(or (+fold--hideshow-empty-line-p) (>= (current-indentation) base)))
|
||||
|
||||
(defun +fold--hideshow-g-or-empty-p ()
|
||||
(or (+fold--hideshow-empty-line-p) (> (current-indentation) base)))
|
||||
|
||||
(defun +fold--hideshow-seek (start direction before skip predicate)
|
||||
"Seeks forward (if direction is 1) or backward (if direction is -1) from start, until predicate
|
||||
fails. If before is nil, it will return the first line where predicate fails, otherwise it returns
|
||||
the last line where predicate holds."
|
||||
(save-excursion
|
||||
(goto-char start)
|
||||
(goto-char (point-at-bol))
|
||||
(let ((bnd (if (> 0 direction)
|
||||
(point-min)
|
||||
(point-max)))
|
||||
(pt (point)))
|
||||
(when skip (forward-line direction))
|
||||
(cl-loop while (and (/= (point) bnd) (funcall predicate))
|
||||
do (progn
|
||||
(when before (setq pt (point-at-bol)))
|
||||
(forward-line direction)
|
||||
(unless before (setq pt (point-at-bol)))))
|
||||
pt)))
|
||||
|
||||
(defun +fold-hideshow-indent-range (&optional point)
|
||||
"Return the point at the begin and end of the text block with the same (or
|
||||
greater) indentation. If `point' is supplied and non-nil it will return the
|
||||
begin and end of the block surrounding point."
|
||||
(save-excursion
|
||||
(when point
|
||||
(goto-char point))
|
||||
(let ((base (current-indentation))
|
||||
(begin (point))
|
||||
(end (point)))
|
||||
(setq begin (+fold--hideshow-seek begin -1 t nil #'+fold--hideshow-geq-or-empty-p)
|
||||
begin (+fold--hideshow-seek begin 1 nil nil #'+fold--hideshow-g-or-empty-p)
|
||||
end (+fold--hideshow-seek end 1 t nil #'+fold--hideshow-geq-or-empty-p)
|
||||
end (+fold--hideshow-seek end -1 nil nil #'+fold--hideshow-empty-line-p))
|
||||
(list begin end base))))
|
73
modules/editor/fold/config.el
Normal file
73
modules/editor/fold/config.el
Normal file
|
@ -0,0 +1,73 @@
|
|||
;;; editor/fold/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
(when (featurep! :feature evil)
|
||||
;; Add vimish-fold, outline-mode & hideshow support to folding commands
|
||||
(define-key! 'global
|
||||
[remap evil-toggle-fold] #'+fold/toggle
|
||||
[remap evil-close-fold] #'+fold/close
|
||||
[remap evil-open-fold] #'+fold/open
|
||||
[remap evil-open-fold-rec] #'+fold/open
|
||||
[remap evil-close-folds] #'+fold/close-all
|
||||
[remap evil-open-folds] #'+fold/open-all)
|
||||
(evil-define-key* 'motion 'global
|
||||
"zj" #'+fold/next
|
||||
"zk" #'+fold/previous))
|
||||
|
||||
|
||||
;;
|
||||
;; Packages
|
||||
|
||||
(def-package! hideshow ; built-in
|
||||
:defer t
|
||||
:init
|
||||
;; Ensure `hs-minor-mode' is active when triggering these commands
|
||||
(advice-add #'hs-toggle-hiding :before #'+fold-hideshow*ensure-mode)
|
||||
(advice-add #'hs-hide-block :before #'+fold-hideshow*ensure-mode)
|
||||
(advice-add #'hs-hide-level :before #'+fold-hideshow*ensure-mode)
|
||||
(advice-add #'hs-show-all :before #'+fold-hideshow*ensure-mode)
|
||||
(advice-add #'hs-hide-all :before #'+fold-hideshow*ensure-mode)
|
||||
:config
|
||||
(setq hs-hide-comments-when-hiding-all nil
|
||||
;; Nicer code-folding overlays (with fringe indicators)
|
||||
hs-set-up-overlay #'+fold-hideshow-set-up-overlay)
|
||||
|
||||
;; extra folding support for more languages
|
||||
(unless (assq 't hs-special-modes-alist)
|
||||
(setq hs-special-modes-alist
|
||||
(append
|
||||
'((vimrc-mode "{{{" "}}}" "\"")
|
||||
(yaml-mode "\\s-*\\_<\\(?:[^:]+\\)\\_>"
|
||||
""
|
||||
"#"
|
||||
+fold-hideshow-forward-block-by-indent nil)
|
||||
(haml-mode "[#.%]" "\n" "/" +fold-hideshow-haml-forward-sexp nil)
|
||||
(ruby-mode "class\\|d\\(?:ef\\|o\\)\\|module\\|[[{]"
|
||||
"end\\|[]}]"
|
||||
"#\\|=begin"
|
||||
ruby-forward-sexp)
|
||||
(enh-ruby-mode "class\\|d\\(?:ef\\|o\\)\\|module\\|[[{]"
|
||||
"end\\|[]}]"
|
||||
"#\\|=begin"
|
||||
enh-ruby-forward-sexp nil)
|
||||
(matlab-mode "if\\|switch\\|case\\|otherwise\\|while\\|for\\|try\\|catch"
|
||||
"end"
|
||||
nil (lambda (_arg) (matlab-forward-sexp))))
|
||||
hs-special-modes-alist
|
||||
'((t))))))
|
||||
|
||||
|
||||
(def-package! evil-vimish-fold
|
||||
:when (featurep! :feature evil)
|
||||
:commands (evil-vimish-fold/next-fold evil-vimish-fold/previous-fold
|
||||
evil-vimish-fold/delete evil-vimish-fold/delete-all
|
||||
evil-vimish-fold/create evil-vimish-fold/create-line)
|
||||
:init
|
||||
(setq vimish-fold-dir (concat doom-cache-dir "vimish-fold/")
|
||||
vimish-fold-indication-mode 'right-fringe)
|
||||
(evil-define-key* 'motion 'global
|
||||
"zf" #'evil-vimish-fold/create
|
||||
"zF" #'evil-vimish-fold/create-line
|
||||
"zd" #'vimish-fold-delete
|
||||
"zE" #'vimish-fold-delete-all)
|
||||
:config
|
||||
(vimish-fold-global-mode +1))
|
|
@ -1,5 +1,5 @@
|
|||
;; -*- no-byte-compile: t; -*-
|
||||
;;; ui/evil-goggles/packages.el
|
||||
;;; editor/fold/packages.el
|
||||
|
||||
(when (featurep! :feature evil)
|
||||
(package! evil-goggles))
|
||||
(package! evil-vimish-fold))
|
8
modules/editor/format/autoload/evil.el
Normal file
8
modules/editor/format/autoload/evil.el
Normal file
|
@ -0,0 +1,8 @@
|
|||
;;; editor/format/autoload/evil.el -*- lexical-binding: t; -*-
|
||||
;;;###if (featurep! :feature evil)
|
||||
|
||||
;;;###autoload (autoload '+format:region "editor/format/autoload/evil" nil t)
|
||||
(evil-define-operator +format:region (beg end)
|
||||
"Evil ex interface to `+format/region'."
|
||||
(interactive "<r>")
|
||||
(+format/region beg end))
|
229
modules/editor/format/autoload/format.el
Normal file
229
modules/editor/format/autoload/format.el
Normal file
|
@ -0,0 +1,229 @@
|
|||
;;; editor/format/autoload.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defvar +format-region-p nil
|
||||
"Is non-nil if currently reformatting a selected region, rather than the whole
|
||||
buffer.")
|
||||
|
||||
;;;###autoload
|
||||
(autoload 'format-all-probe "format-all")
|
||||
|
||||
(defun +format--delete-whole-line (&optional arg)
|
||||
"Delete the current line without putting it in the `kill-ring'.
|
||||
Derived from function `kill-whole-line'. ARG is defined as for that
|
||||
function.
|
||||
|
||||
Stolen shamelessly from go-mode"
|
||||
(setq arg (or arg 1))
|
||||
(if (and (> arg 0)
|
||||
(eobp)
|
||||
(save-excursion (forward-visible-line 0) (eobp)))
|
||||
(signal 'end-of-buffer nil))
|
||||
(if (and (< arg 0)
|
||||
(bobp)
|
||||
(save-excursion (end-of-visible-line) (bobp)))
|
||||
(signal 'beginning-of-buffer nil))
|
||||
(cond ((zerop arg)
|
||||
(delete-region (progn (forward-visible-line 0) (point))
|
||||
(progn (end-of-visible-line) (point))))
|
||||
((< arg 0)
|
||||
(delete-region (progn (end-of-visible-line) (point))
|
||||
(progn (forward-visible-line (1+ arg))
|
||||
(unless (bobp)
|
||||
(backward-char))
|
||||
(point))))
|
||||
((delete-region (progn (forward-visible-line 0) (point))
|
||||
(progn (forward-visible-line arg) (point))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +format--apply-rcs-patch (patch-buffer)
|
||||
"Apply an RCS-formatted diff from PATCH-BUFFER to the current buffer.
|
||||
|
||||
Stolen shamelessly from go-mode"
|
||||
(let ((target-buffer (current-buffer))
|
||||
;; Relative offset between buffer line numbers and line numbers
|
||||
;; in patch.
|
||||
;;
|
||||
;; Line numbers in the patch are based on the source file, so
|
||||
;; we have to keep an offset when making changes to the
|
||||
;; buffer.
|
||||
;;
|
||||
;; Appending lines decrements the offset (possibly making it
|
||||
;; negative), deleting lines increments it. This order
|
||||
;; simplifies the forward-line invocations.
|
||||
(line-offset 0)
|
||||
(column (current-column)))
|
||||
(save-excursion
|
||||
(with-current-buffer patch-buffer
|
||||
(goto-char (point-min))
|
||||
(while (not (eobp))
|
||||
(unless (looking-at "^\\([ad]\\)\\([0-9]+\\) \\([0-9]+\\)")
|
||||
(error "Invalid rcs patch or internal error in +format--apply-rcs-patch"))
|
||||
(forward-line)
|
||||
(let ((action (match-string 1))
|
||||
(from (string-to-number (match-string 2)))
|
||||
(len (string-to-number (match-string 3))))
|
||||
(cond
|
||||
((equal action "a")
|
||||
(let ((start (point)))
|
||||
(forward-line len)
|
||||
(let ((text (buffer-substring start (point))))
|
||||
(with-current-buffer target-buffer
|
||||
(cl-decf line-offset len)
|
||||
(goto-char (point-min))
|
||||
(forward-line (- from len line-offset))
|
||||
(insert text)))))
|
||||
((equal action "d")
|
||||
(with-current-buffer target-buffer
|
||||
(goto-char (point-min))
|
||||
(forward-line (1- (- from line-offset)))
|
||||
(cl-incf line-offset len)
|
||||
(+format--delete-whole-line len)))
|
||||
((error "Invalid rcs patch or internal error in +format--apply-rcs-patch")))))))
|
||||
(move-to-column column)))
|
||||
|
||||
(defun +format--current-indentation ()
|
||||
(save-excursion
|
||||
(goto-char (point-min))
|
||||
(skip-chars-forward " \t\n")
|
||||
(current-indentation)))
|
||||
|
||||
|
||||
;;
|
||||
;; Public library
|
||||
|
||||
(defun +format-completing-read ()
|
||||
(require 'format-all)
|
||||
(let* ((fmtlist (mapcar #'symbol-name (hash-table-keys format-all-format-table)))
|
||||
(fmt (completing-read "Formatter: " fmtlist)))
|
||||
(if fmt (cons (intern fmt) t))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +format*probe (orig-fn)
|
||||
"Use `+format-with' instead, if it is set."
|
||||
(if +format-with
|
||||
(list +format-with t)
|
||||
(funcall orig-fn)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +format-buffer (formatter mode-result &optional preserve-indent-p)
|
||||
"Format the source code in the current buffer.
|
||||
|
||||
Returns any of the following values:
|
||||
|
||||
'unknown No formatter is defined for this major-mode
|
||||
'error Couldn't format buffer due to formatter errors
|
||||
'noop Buffer is already formatted
|
||||
|
||||
Otherwise, returns a list: (list OUTPUT ERRORS FIRST-DIFF), where OUTPUT is the
|
||||
formatted text, ERRORS are any errors in string format, and FIRST-DIFF is the
|
||||
position of the first change in the buffer.
|
||||
|
||||
See `+format/buffer' for the interactive version of this function, and
|
||||
`+format|buffer' to use as a `before-save-hook' hook."
|
||||
(if (not formatter)
|
||||
'no-formatter
|
||||
(let ((f-function (gethash formatter format-all-format-table))
|
||||
(executable (format-all-formatter-executable formatter))
|
||||
(indent 0))
|
||||
(pcase-let
|
||||
((`(,output ,errput ,first-diff)
|
||||
;; Since `format-all' functions (and various formatting functions,
|
||||
;; like `gofmt') widen the buffer, in order to only format a region of
|
||||
;; text, we must make a copy of the buffer to apply formatting to.
|
||||
(let ((output (buffer-substring-no-properties (point-min) (point-max))))
|
||||
(with-temp-buffer
|
||||
(insert output)
|
||||
;; Since we're piping a region of text to the formatter, remove
|
||||
;; any leading indentation to make it look like a file.
|
||||
(when preserve-indent-p
|
||||
(setq indent (+format--current-indentation))
|
||||
(when (> indent 0)
|
||||
(indent-rigidly (point-min) (point-max) (- indent))))
|
||||
(funcall f-function executable mode-result)))))
|
||||
(unwind-protect
|
||||
(cond ((null output) 'error)
|
||||
((eq output t) 'noop)
|
||||
((let ((tmpfile (make-temp-file "doom-format"))
|
||||
(patchbuf (get-buffer-create " *doom format patch*"))
|
||||
(coding-system-for-read 'utf-8)
|
||||
(coding-system-for-write 'utf-8))
|
||||
(unwind-protect
|
||||
(progn
|
||||
(with-current-buffer patchbuf
|
||||
(erase-buffer))
|
||||
(with-temp-file tmpfile
|
||||
(erase-buffer)
|
||||
(insert output)
|
||||
(when (> indent 0)
|
||||
;; restore indentation without affecting new
|
||||
;; indentation
|
||||
(indent-rigidly (point-min) (point-max)
|
||||
(max 0 (- indent (+format--current-indentation))))))
|
||||
(if (zerop (call-process-region (point-min) (point-max) "diff" nil patchbuf nil "-n" "-" tmpfile))
|
||||
'noop
|
||||
(+format--apply-rcs-patch patchbuf)
|
||||
(list output errput first-diff)))
|
||||
(kill-buffer patchbuf)
|
||||
(delete-file tmpfile)))))
|
||||
(unless (= 0 (length errput))
|
||||
(message "Formatter error output:\n%s" errput)))))))
|
||||
|
||||
|
||||
;;
|
||||
;; Commands
|
||||
|
||||
;;;###autoload
|
||||
(defun +format/buffer (&optional arg)
|
||||
"Format the source code in the current buffer."
|
||||
(interactive "P")
|
||||
(let ((+format-with (or (if arg (+format-completing-read)) +format-with)))
|
||||
(pcase-let ((`(,formatter ,mode-result) (format-all-probe)))
|
||||
(pcase
|
||||
(+format-buffer
|
||||
formatter mode-result
|
||||
(or +format-preserve-indentation +format-region-p))
|
||||
(`no-formatter
|
||||
(when (called-interactively-p 'any)
|
||||
(message "No formatter specified for %s" major-mode))
|
||||
nil)
|
||||
(`error (message "Failed to format buffer due to errors") nil)
|
||||
(`noop (message "Buffer was already formatted") nil)
|
||||
(_ (message "Formatted (%s)" formatter) t)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +format/region (beg end &optional arg)
|
||||
"Runs the active formatter on the lines within BEG and END.
|
||||
|
||||
WARNING: this may not work everywhere. It will throw errors if the region
|
||||
contains a syntax error in isolation. It is mostly useful for formatting
|
||||
snippets or single lines."
|
||||
(interactive "rP")
|
||||
(save-restriction
|
||||
(narrow-to-region beg end)
|
||||
(let ((+format-region-p t))
|
||||
(+format/buffer arg))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +format/region-or-buffer ()
|
||||
"Runs the active formatter on the selected region (or whole buffer, if nothing
|
||||
is selected)."
|
||||
(interactive)
|
||||
(call-interactively
|
||||
(if (use-region-p)
|
||||
#'+format/region
|
||||
#'+format/buffer)))
|
||||
|
||||
|
||||
;;
|
||||
;; Hooks
|
||||
|
||||
;;;###autoload
|
||||
(defun +format|enable-on-save ()
|
||||
"Enables formatting on save."
|
||||
(add-hook 'before-save-hook #'+format|buffer nil t))
|
||||
|
||||
;;;###autoload
|
||||
(defalias '+format|buffer #'+format/buffer
|
||||
"Format the source code in the current buffer with minimal feedback.
|
||||
|
||||
Meant for `before-save-hook'.")
|
203
modules/editor/format/autoload/settings.el
Normal file
203
modules/editor/format/autoload/settings.el
Normal file
|
@ -0,0 +1,203 @@
|
|||
;;; editor/format/autoload/settings.el -*- lexical-binding: t; -*-
|
||||
|
||||
;; This must be redefined here because `format-all' only makes it available at
|
||||
;; compile time.
|
||||
(defconst +format-system-type
|
||||
(cl-case system-type
|
||||
(windows-nt 'windows)
|
||||
(cygwin 'windows)
|
||||
(darwin 'macos)
|
||||
(gnu/linux 'linux)
|
||||
(berkeley-unix
|
||||
(save-match-data
|
||||
(let ((case-fold-search t))
|
||||
(cond ((string-match "freebsd" system-configuration) 'freebsd)
|
||||
((string-match "openbsd" system-configuration) 'openbsd)
|
||||
((string-match "netbsd" system-configuration) 'netbsd))))))
|
||||
"Current operating system according to the format-all package.")
|
||||
|
||||
(defun +format--resolve-system (choices)
|
||||
"Get first choice matching `format-all-system-type' from CHOICES."
|
||||
(cl-loop for choice in choices
|
||||
if (atom choice) return choice
|
||||
else if (eql +format-system-type (car choice))
|
||||
return (cadr choice)))
|
||||
|
||||
|
||||
(defun +format--make-command (formatter &rest _)
|
||||
`(format-all-buffer-thunk
|
||||
(lambda (input)
|
||||
(with-silent-modifications
|
||||
(setq buffer-file-name ,(buffer-file-name (buffer-base-buffer))
|
||||
default-directory ,default-directory)
|
||||
(delay-mode-hooks (funcall ',major-mode))
|
||||
(insert input)
|
||||
(condition-case e
|
||||
(progn
|
||||
(doom-log "formatter (commandp) %s" #',formatter)
|
||||
(call-interactively #',formatter)
|
||||
(list nil ""))
|
||||
(error (list t (error-message-string e))))))))
|
||||
|
||||
(defun +format--make-function (formatter &rest _)
|
||||
`(progn
|
||||
(doom-log "formatter (functionp) %s" #',formatter)
|
||||
(format-all-buffer-thunk #',formatter)))
|
||||
|
||||
(defun +format--make-shell-command (command ok-statuses error-regexp)
|
||||
(+format--make-shell-command-list (split-string command " " t)
|
||||
ok-statuses error-regexp))
|
||||
|
||||
(defun +format--make-shell-command-list (command-list ok-statuses error-regexp)
|
||||
`(let (args)
|
||||
(dolist (arg ',command-list)
|
||||
(cond ((stringp arg)
|
||||
(push arg args))
|
||||
((listp arg)
|
||||
(catch 'skip
|
||||
(let (subargs this)
|
||||
(while (setq this (pop arg))
|
||||
(cond ((not (stringp (car arg)))
|
||||
(let ((val (eval (pop arg) t)))
|
||||
(unless val (throw 'skip nil))
|
||||
(push (format this val) subargs)))
|
||||
((stringp this)
|
||||
(push this subargs))))
|
||||
(setq args (append subargs args)))))))
|
||||
(doom-log "formatter (arglist) %s" args)
|
||||
(if ,(and (or ok-statuses error-regexp) t)
|
||||
(apply #'format-all-buffer-hard
|
||||
',ok-statuses ,error-regexp
|
||||
(reverse args))
|
||||
(apply #'format-all-buffer-easy (reverse args)))))
|
||||
|
||||
(cl-defun +format--set (name &key function modes unset)
|
||||
(declare (indent defun))
|
||||
(when (and unset (not (gethash name format-all-format-table)))
|
||||
(error "'%s' formatter does not exist to be unset" name))
|
||||
(puthash name function format-all-format-table)
|
||||
(dolist (mode (doom-enlist modes))
|
||||
(cl-destructuring-bind (m &optional probe)
|
||||
(doom-enlist mode)
|
||||
(if unset
|
||||
(puthash m (assq-delete-all name (gethash key format-all-mode-table))
|
||||
format-all-mode-table)
|
||||
(format-all-pushhash
|
||||
m (cons name (if probe `(lambda () ,probe)))
|
||||
format-all-mode-table)))))
|
||||
|
||||
;;;###autodef
|
||||
(cl-defun set-formatter!
|
||||
(name formatter &key modes filter ok-statuses error-regexp)
|
||||
"Define (or modify) a formatter named NAME.
|
||||
|
||||
Supported keywords: :modes :install :filter :ok-statuses :error-regexp
|
||||
|
||||
NAME is a symbol that identifies this formatter.
|
||||
|
||||
FORMATTER can be a symbol referring to another formatter, a function, string or
|
||||
nested list.
|
||||
|
||||
If a function, it should be a formatter function that
|
||||
`format-all-buffer-thunk' will accept.
|
||||
If a string, it is assumed to be a shell command that the buffer's text will
|
||||
be piped to (through stdin).
|
||||
If a list, it should represent a shell command as a list of arguments. Each
|
||||
element is either a string or list (STRING ARG) where STRING is a format
|
||||
string and ARG is both a predicate and argument for STRING. If ARG is nil,
|
||||
STRING will be omitted from the vector.
|
||||
|
||||
MODES is a major mode, a list thereof, or a list of two-element sublists with
|
||||
the structure: (MAJOR-MODE FORM). FORM is evaluated when the buffer is formatted
|
||||
and its return value serves two purposes:
|
||||
|
||||
1. It is a predicate for this formatter. Assuming the MAJOR-MODE matches the
|
||||
current mode, if FORM evaluates to nil, the formatter is skipped.
|
||||
2. It's return value is made available to FORMATTER if it is a function or
|
||||
list of shell arguments via the `mode-result' variable.
|
||||
|
||||
FILTER is a function that takes three arguments: the formatted output, any error
|
||||
output and the position of the first change. This function must return these
|
||||
three after making whatever changes you like to them. This might be useful if
|
||||
the output contains ANSI color codes that need to be stripped out (as is the
|
||||
case with elm-format).
|
||||
|
||||
OK-STATUSES and ERROR-REGEXP are ignored if FORMATTER is not a shell command.
|
||||
|
||||
OK-STATUSES is a list of integer exit codes that should be treated as success
|
||||
codes. However, if ERROR-REGEXP is given, and the program's stderr contains that
|
||||
regexp, then the formatting is considered failed even if the exit status is in
|
||||
OK-STATUSES.
|
||||
|
||||
Basic examples:
|
||||
|
||||
(set-formatter! 'asmfmt \"asmfmt\" :modes '(asm-mode nasm-mode))
|
||||
(set-formatter! 'black \"black -q -\")
|
||||
(set-formatter! 'html-tidy \"tidy -q -indent\" :modes '(html-mode web-mode))
|
||||
|
||||
Advanced examples:
|
||||
|
||||
(set-formatter!
|
||||
'clang-format
|
||||
'(\"clang-format\"
|
||||
(\"-assume-filename=%S\" (or buffer-file-name mode-result \"\")))
|
||||
:modes
|
||||
'((c-mode \".c\")
|
||||
(c++-mode \".cpp\")
|
||||
(java-mode \".java\")
|
||||
(objc-mode \".m\")
|
||||
(protobuf-mode \".proto\")))
|
||||
|
||||
(set-formatter! 'html-tidy
|
||||
'(\"tidy\" \"-q\" \"-indent\"
|
||||
(\"-xml\" (memq major-mode '(nxml-mode xml-mode))))
|
||||
:modes
|
||||
'(html-mode
|
||||
(web-mode (and (equal \"none\" web-mode-engine)
|
||||
(car (member web-mode-content-type '(\"xml\" \"html\"))))))
|
||||
:ok-statuses '(0 1)
|
||||
:executable \"tidy\")
|
||||
|
||||
(set-formatter! 'html-tidy ; overwrite predefined html-tidy formatter
|
||||
'(\"tidy\" \"-q\" \"-indent\"
|
||||
\"--tidy-mark\" \"no\"
|
||||
\"--drop-empty-elements\" \"no\"
|
||||
\"--show-body-only\" \"auto\"
|
||||
(\"--indent-spaces\" \"%d\" tab-width)
|
||||
(\"--indent-with-tabs\" \"%s\" (if indent-tabs-mode \"yes\" \"no\"))
|
||||
(\"-xml\" (memq major-mode '(nxml-mode xml-mode))))
|
||||
:ok-statuses '(0 1)))
|
||||
|
||||
(set-formatter! 'elm-format
|
||||
\"elm-format --yes --stdin\"
|
||||
:filter
|
||||
(lambda (output errput first-diff)
|
||||
(list output
|
||||
(format-all-remove-ansi-color errput)
|
||||
first-diff)))"
|
||||
(declare (indent defun))
|
||||
(cl-check-type name symbol)
|
||||
(after! format-all
|
||||
(if (null formatter)
|
||||
(+format--set name
|
||||
:unset t
|
||||
:modes modes)
|
||||
(let ((fn (funcall (cond ((stringp formatter)
|
||||
#'+format--make-shell-command)
|
||||
((listp formatter)
|
||||
#'+format--make-shell-command-list)
|
||||
((and (commandp formatter)
|
||||
(not (stringp formatter)))
|
||||
#'+format--make-command)
|
||||
((functionp formatter)
|
||||
#'+format--make-function))
|
||||
formatter
|
||||
ok-statuses
|
||||
error-regexp)))
|
||||
(cl-check-type filter (or function null))
|
||||
(+format--set name
|
||||
:function
|
||||
`(lambda (executable mode-result)
|
||||
,(if filter `(apply #',filter ,fn) fn))
|
||||
:modes modes)
|
||||
name))))
|
58
modules/editor/format/config.el
Normal file
58
modules/editor/format/config.el
Normal file
|
@ -0,0 +1,58 @@
|
|||
;;; editor/format/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defvar +format-on-save-enabled-modes
|
||||
'(not emacs-lisp-mode ; elisp's mechanisms are good enough
|
||||
sql-mode) ; sqlformat is currently broken
|
||||
"A list of major modes in which to reformat the buffer upon saving.
|
||||
|
||||
If this list begins with `not', then it negates the list.
|
||||
If it is `t', it is enabled in all modes.
|
||||
If nil, it is disabled in all modes, the same as if the +onsave flag wasn't
|
||||
used at all.
|
||||
|
||||
Irrelevant if you do not have the +onsave flag enabled for this module.")
|
||||
|
||||
(defvar +format-preserve-indentation t
|
||||
"If non-nil, the leading indentation is preserved when formatting the whole
|
||||
buffer. This is particularly useful for partials.
|
||||
|
||||
Indentation is always preserved when formatting regions. ")
|
||||
|
||||
(defvar-local +format-with nil
|
||||
"Set this to explicitly use a certain formatter for the current buffer.")
|
||||
|
||||
|
||||
;;
|
||||
;; Bootstrap
|
||||
|
||||
(defun +format|enable-on-save-maybe ()
|
||||
"Enable formatting on save in certain major modes.
|
||||
|
||||
This is controlled by `+format-on-save-enabled-modes'."
|
||||
(unless (or (eq major-mode 'fundamental-mode)
|
||||
(cond ((booleanp +format-on-save-enabled-modes)
|
||||
(null +format-on-save-enabled-modes))
|
||||
((eq (car +format-on-save-enabled-modes) 'not)
|
||||
(memq major-mode (cdr +format-on-save-enabled-modes)))
|
||||
((not (memq major-mode +format-on-save-enabled-modes))))
|
||||
(not (require 'format-all nil t)))
|
||||
(add-hook 'before-save-hook #'+format|buffer nil t)))
|
||||
|
||||
(when (featurep! +onsave)
|
||||
(add-hook 'after-change-major-mode-hook #'+format|enable-on-save-maybe))
|
||||
|
||||
|
||||
;;
|
||||
;; Hacks
|
||||
|
||||
;; Allow a specific formatter to be used by setting `+format-with', either
|
||||
;; buffer-locally or let-bound.
|
||||
(advice-add #'format-all-probe :around #'+format*probe)
|
||||
|
||||
;; Doom uses a modded `format-all-buffer', which
|
||||
;; 1. Doesn't move the cursorafter reformatting,
|
||||
;; 2. Can reformat regions, rather than the entire buffer (while preserving
|
||||
;; leading indentation),
|
||||
;; 3. Applies changes via RCS patch, line by line, as not to protect buffer
|
||||
;; markers and avoid any jarring cursor+window scrolling.
|
||||
(advice-add #'format-all-buffer :override #'+format/buffer)
|
4
modules/editor/format/packages.el
Normal file
4
modules/editor/format/packages.el
Normal file
|
@ -0,0 +1,4 @@
|
|||
;; -*- no-byte-compile: t; -*-
|
||||
;;; editor/format/packages.el
|
||||
|
||||
(package! format-all)
|
103
modules/editor/format/test/test-format.el
Normal file
103
modules/editor/format/test/test-format.el
Normal file
|
@ -0,0 +1,103 @@
|
|||
;; -*- no-byte-compile: t; -*-
|
||||
;;; editor/format/test/test-format.el
|
||||
|
||||
(load! "../autoload/settings")
|
||||
(load! "../autoload/format")
|
||||
(require! :editor format)
|
||||
(require 'format-all)
|
||||
|
||||
;;
|
||||
(describe "editor/format"
|
||||
:var (format-all-format-table
|
||||
format-all-mode-table)
|
||||
|
||||
(before-each
|
||||
(setq format-all-format-table (make-hash-table)
|
||||
format-all-mode-table (make-hash-table)))
|
||||
|
||||
(describe "set-formatter!"
|
||||
(before-each
|
||||
(set-formatter! 'test (lambda () (interactive))))
|
||||
|
||||
(it "defines a formatter"
|
||||
(set-formatter! 'new (lambda () (interactive)))
|
||||
(expect (gethash 'new format-all-mode-table) :to-equal nil)
|
||||
(expect (functionp (gethash 'new format-all-format-table))))
|
||||
|
||||
(it "defines a formatter with modes"
|
||||
(set-formatter! 'new (lambda () (interactive))
|
||||
:modes '(a-mode (b-mode "x")))
|
||||
(expect (gethash 'a-mode format-all-mode-table)
|
||||
:to-equal '((new)))
|
||||
(expect (gethash 'b-mode format-all-mode-table)
|
||||
:to-equal '((new . (lambda () "x")))))
|
||||
|
||||
(it "replaces a pre-existing formatter"
|
||||
(let ((old-fn (gethash 'test format-all-format-table)))
|
||||
(set-formatter! 'test "echo")
|
||||
(expect (gethash 'test format-all-format-table) :not :to-equal old-fn)))
|
||||
|
||||
(it "unsets a pre-existing formatter"
|
||||
(set-formatter! 'test nil)
|
||||
(expect (gethash 'test format-all-format-table) :to-be nil))
|
||||
|
||||
(it "errors when unsetting non-existent formatter"
|
||||
(expect (set-formatter! 'doesnt-exist nil) :to-throw)))
|
||||
|
||||
|
||||
;; TODO
|
||||
(xdescribe "hooks"
|
||||
(describe "format|enable-on-save-maybe")
|
||||
(describe "format|enable-on-save"))
|
||||
|
||||
|
||||
;; TODO
|
||||
(xdescribe "formatting"
|
||||
(before-each
|
||||
(set-formatter! 'command
|
||||
(lambda ()
|
||||
(interactive)
|
||||
(let ((first-line (car (split-string (buffer-string) "\n"))))
|
||||
(erase-buffer)
|
||||
(insert first-line)))
|
||||
:modes '(text-mode))
|
||||
(set-formatter! 'faulty-command
|
||||
(lambda ()
|
||||
(interactive)
|
||||
(error "This is a test"))
|
||||
:modes '(text-mode))
|
||||
(set-formatter! 'function
|
||||
(lambda (input)
|
||||
(insert (car (split-string input "\n")))
|
||||
(list nil nil))
|
||||
:modes '(text-mode))
|
||||
(set-formatter! 'shellcmd "head -n 1"
|
||||
:modes '(text-mode))
|
||||
(set-formatter! 'cmdlist '("head" "-n" "1")
|
||||
:modes '(text-mode)))
|
||||
|
||||
(describe "with an interactive command"
|
||||
(it "formats a buffer" )
|
||||
(it "formats a region" )
|
||||
(it "no-ops if no change" )
|
||||
(it "doesn't modify the buffer in case of errors" )
|
||||
(it "preserves indentation" ))
|
||||
|
||||
(describe "with a function"
|
||||
(it "formats a buffer" )
|
||||
(it "formats a region" )
|
||||
(it "no-ops if no change" )
|
||||
(it "doesn't modify the buffer in case of errors" )
|
||||
(it "preserves indentation" ))
|
||||
|
||||
(describe "with a shell command")
|
||||
|
||||
(describe "with a shell command list"
|
||||
(it "formats a buffer" )
|
||||
(it "formats a region" )
|
||||
(it "no-ops if no change" )
|
||||
(it "doesn't modify the buffer in case of errors" )
|
||||
(it "preserves indentation" )
|
||||
|
||||
(it "interpolates non-strings into format strings" )
|
||||
(it "conditionally appends sublisted options" ))))
|
36
modules/editor/lispy/README.org
Normal file
36
modules/editor/lispy/README.org
Normal file
|
@ -0,0 +1,36 @@
|
|||
#+TITLE: :editor lispy
|
||||
|
||||
This modules adds [[https://github.com/noctuid/lispyville][lispy]] key functionality in Lisp languages.
|
||||
|
||||
This includes:
|
||||
|
||||
- Common Lisp
|
||||
- Emacs Lisp
|
||||
- Scheme
|
||||
- Racket
|
||||
- [[http://docs.hylang.org/en/stable/][Hy]]
|
||||
- [[http://lfe.io/][LFE]]
|
||||
- Clojure
|
||||
|
||||
If evil is enabled, lispyville would also be activated for every mode where
|
||||
lispy is active
|
||||
|
||||
The default key themes that are set are as follows:
|
||||
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
(lispyville-set-key-theme
|
||||
'((operators normal)
|
||||
c-w
|
||||
(prettify insert)
|
||||
(atom-movement normal visual)
|
||||
slurp/barf-lispy
|
||||
(wrap normal insert)
|
||||
additional
|
||||
additional-insert
|
||||
(additional-wrap normal insert)
|
||||
(escape insert)))
|
||||
#+END_SRC
|
||||
|
||||
See noctuid's [[https://github.com/noctuid/lispyville/blob/master/README.org][README]] for more info on specific keybindings (starting [[https://github.com/noctuid/lispyville#operators-key-theme][here]]) of
|
||||
each key theme. Think of ~lispyville-set-key-theme~ as adding
|
||||
~parinfer-extensions~ via ~(setq parinfer-extensions '(blah blah blah))~.
|
29
modules/editor/lispy/config.el
Normal file
29
modules/editor/lispy/config.el
Normal file
|
@ -0,0 +1,29 @@
|
|||
;;; editor/lispy/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
(def-package! lispy
|
||||
:hook ((common-lisp-mode . lispy-mode)
|
||||
(emacs-lisp-mode . lispy-mode)
|
||||
(scheme-mode . lispy-mode)
|
||||
(racket-mode . lispy-mode)
|
||||
(hy-mode . lispy-mode)
|
||||
(lfe-mode . lispy-mode)
|
||||
(clojure-mode . lispy-mode))
|
||||
:config
|
||||
(setq lispy-close-quotes-at-end-p t)
|
||||
(add-hook 'lispy-mode-hook #'turn-off-smartparens-mode))
|
||||
|
||||
(def-package! lispyville
|
||||
:when (featurep! :feature evil)
|
||||
:hook (lispy-mode . lispyville-mode)
|
||||
:config
|
||||
(lispyville-set-key-theme
|
||||
'((operators normal)
|
||||
c-w
|
||||
(prettify insert)
|
||||
(atom-movement normal visual)
|
||||
slurp/barf-lispy
|
||||
(wrap normal insert)
|
||||
additional
|
||||
additional-insert
|
||||
(additional-wrap normal insert)
|
||||
(escape insert))))
|
7
modules/editor/lispy/packages.el
Normal file
7
modules/editor/lispy/packages.el
Normal file
|
@ -0,0 +1,7 @@
|
|||
;; -*- no-byte-compile: t; -*-
|
||||
;;; editor/lispyville/packages.el
|
||||
|
||||
(package! lispy)
|
||||
|
||||
(when (featurep! :feature evil)
|
||||
(package! lispyville))
|
|
@ -1,7 +1,8 @@
|
|||
;;; feature/evil/autoload/evil-mc.el -*- lexical-binding: t; -*-
|
||||
;;; editor/multiple-cursors/autoload/evil-mc.el -*- lexical-binding: t; -*-
|
||||
;;;###if (featurep! :feature evil)
|
||||
|
||||
;;;###autoload
|
||||
(defun +evil/mc-toggle-cursors ()
|
||||
(defun +multiple-cursors/evil-mc-toggle-cursors ()
|
||||
"Toggle frozen state of evil-mc cursors."
|
||||
(interactive)
|
||||
(setq evil-mc-frozen (not (and (evil-mc-has-cursors-p)
|
||||
|
@ -10,8 +11,8 @@
|
|||
(message "evil-mc paused")
|
||||
(message "evil-mc resumed")))
|
||||
|
||||
;;;###autoload (autoload '+evil/mc-make-cursor-here "feature/evil/autoload/evil-mc" nil t)
|
||||
(evil-define-command +evil/mc-make-cursor-here ()
|
||||
;;;###autoload (autoload '+multiple-cursors/evil-mc-make-cursor-here "editor/multiple-cursors/autoload/evil-mc" nil t)
|
||||
(evil-define-command +multiple-cursors/evil-mc-make-cursor-here ()
|
||||
"Create a cursor at point. If in visual block or line mode, then create
|
||||
cursors on each line of the selection, on the column of the cursor. Otherwise
|
||||
pauses cursors."
|
||||
|
@ -43,23 +44,31 @@ pauses cursors."
|
|||
;; I assume I don't want the cursors to move yet
|
||||
(evil-mc-make-cursor-here))))
|
||||
|
||||
;;;###autoload (autoload '+evil:mc "feature/evil/autoload/evil-mc" nil t)
|
||||
(evil-define-command +evil:mc (beg end type pattern &optional bang)
|
||||
;;;###autoload (autoload '+multiple-cursors:evil-mc "editor/multiple-cursors/autoload/evil-mc" nil t)
|
||||
(evil-define-command +multiple-cursors:evil-mc (beg end type pattern &optional bang)
|
||||
"Create mc cursors at each match of PATTERN within BEG and END, and leave the
|
||||
cursor at the final match. If BANG, then treat PATTERN as literal."
|
||||
:move-point nil
|
||||
:evil-mc t
|
||||
(interactive "<R><//g><!>")
|
||||
(unless (and (stringp pattern)
|
||||
(not (string-empty-p pattern)))
|
||||
(user-error "A regexp pattern is required"))
|
||||
(require 'evil-mc)
|
||||
(setq evil-mc-pattern (cons (evil-mc-make-pattern (if bang (regexp-quote pattern) pattern) nil)
|
||||
(list beg end type)))
|
||||
(setq evil-mc-pattern
|
||||
(cons (evil-ex-make-search-pattern
|
||||
(if bang (regexp-quote pattern) pattern))
|
||||
(list beg end type)))
|
||||
(save-excursion
|
||||
(evil-with-restriction beg end
|
||||
(evil-mc-make-cursors-for-all)
|
||||
(evil-mc-print-cursors-info "Created")))
|
||||
(evil-mc-make-cursors-for-all)))
|
||||
(evil-exit-visual-state)
|
||||
(evil-mc-goto-cursor
|
||||
(if (= (evil-visual-direction) 1)
|
||||
(evil-mc-find-last-cursor)
|
||||
(evil-mc-find-first-cursor))
|
||||
nil))
|
||||
nil)
|
||||
(evil-mc-undo-cursor-at-pos (point))
|
||||
(if (evil-mc-has-cursors-p)
|
||||
(evil-mc-print-cursors-info "Created")
|
||||
(evil-mc-message "No cursors were created")))
|
115
modules/editor/multiple-cursors/config.el
Normal file
115
modules/editor/multiple-cursors/config.el
Normal file
|
@ -0,0 +1,115 @@
|
|||
;;; editor/multiple-cursors/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
(def-package! evil-mc
|
||||
:when (featurep! :feature evil)
|
||||
:commands (evil-mc-make-cursor-here evil-mc-make-all-cursors
|
||||
evil-mc-undo-all-cursors evil-mc-pause-cursors
|
||||
evil-mc-resume-cursors evil-mc-make-and-goto-first-cursor
|
||||
evil-mc-make-and-goto-last-cursor
|
||||
evil-mc-make-cursor-move-next-line
|
||||
evil-mc-make-cursor-move-prev-line evil-mc-make-cursor-at-pos
|
||||
evil-mc-has-cursors-p evil-mc-make-and-goto-next-cursor
|
||||
evil-mc-skip-and-goto-next-cursor evil-mc-make-and-goto-prev-cursor
|
||||
evil-mc-skip-and-goto-prev-cursor evil-mc-make-and-goto-next-match
|
||||
evil-mc-skip-and-goto-next-match evil-mc-skip-and-goto-next-match
|
||||
evil-mc-make-and-goto-prev-match evil-mc-skip-and-goto-prev-match)
|
||||
:init
|
||||
(defvar evil-mc-key-map (make-sparse-keymap))
|
||||
:config
|
||||
(global-evil-mc-mode +1)
|
||||
(setq evil-mc-enable-bar-cursor (not (or IS-MAC IS-WINDOWS)))
|
||||
|
||||
(after! smartparens
|
||||
;; Make evil-mc cooperate with smartparens better
|
||||
(let ((vars (cdr (assq :default evil-mc-cursor-variables))))
|
||||
(unless (memq (car sp--mc/cursor-specific-vars) vars)
|
||||
(setcdr (assq :default evil-mc-cursor-variables)
|
||||
(append vars sp--mc/cursor-specific-vars)))))
|
||||
|
||||
;; Add custom commands to whitelisted commands
|
||||
(dolist (fn '(doom/backward-to-bol-or-indent doom/forward-to-last-non-comment-or-eol
|
||||
doom/backward-kill-to-bol-and-indent delete-char))
|
||||
(add-to-list 'evil-mc-custom-known-commands `(,fn (:default . evil-mc-execute-default-call-with-count))))
|
||||
;; Have evil-mc work with explicit `evil-escape' (typically bound to C-g)
|
||||
(add-to-list 'evil-mc-custom-known-commands '(evil-escape (:default . evil-mc-execute-default-evil-normal-state)))
|
||||
|
||||
;; Activate evil-mc cursors upon switching to insert mode
|
||||
(add-hook 'evil-insert-state-entry-hook #'evil-mc-resume-cursors)
|
||||
|
||||
;; disable evil-escape in evil-mc; causes unwanted text on invocation
|
||||
(add-to-list 'evil-mc-incompatible-minor-modes 'evil-escape-mode nil #'eq)
|
||||
|
||||
(defun +multiple-cursors|escape-multiple-cursors ()
|
||||
"Clear evil-mc cursors and restore state."
|
||||
(when (evil-mc-has-cursors-p)
|
||||
(evil-mc-undo-all-cursors)
|
||||
(evil-mc-resume-cursors)
|
||||
t))
|
||||
(add-hook 'doom-escape-hook #'+multiple-cursors|escape-multiple-cursors)
|
||||
|
||||
;; Forward declare these so that ex completion and evil-mc support is
|
||||
;; recognized before the autoloaded functions are loaded.
|
||||
(evil-add-command-properties '+evil:align :evil-mc t)
|
||||
(evil-set-command-properties '+multiple-cursors:evil-mc
|
||||
:move-point nil
|
||||
:ex-arg 'global-match
|
||||
:ex-bang t
|
||||
:evil-mc t))
|
||||
|
||||
|
||||
(after! multiple-cursors-core
|
||||
(setq mc/list-file (concat doom-etc-dir "mc-lists.el"))
|
||||
|
||||
;; TODO multiple-cursors config for Emacs users?
|
||||
|
||||
;; mc doesn't play well with evil, this attempts to assuage some of its
|
||||
;; problems so that any plugins that depend on multiple-cursors (which I have
|
||||
;; no control over) can still use it in relative safety.
|
||||
(when (featurep! :feature evil)
|
||||
(evil-define-key* '(normal emacs) mc/keymap [escape] #'mc/keyboard-quit)
|
||||
|
||||
(defvar +mc--compat-evil-prev-state nil)
|
||||
(defvar +mc--compat-mark-was-active nil)
|
||||
|
||||
(defun +multiple-cursors|compat-switch-to-emacs-state ()
|
||||
(when (and (bound-and-true-p evil-mode)
|
||||
(not (memq evil-state '(insert emacs))))
|
||||
(setq +mc--compat-evil-prev-state evil-state)
|
||||
(when (region-active-p)
|
||||
(setq +mc--compat-mark-was-active t))
|
||||
(let ((mark-before (mark))
|
||||
(point-before (point)))
|
||||
(evil-emacs-state 1)
|
||||
(when (or +mc--compat-mark-was-active (region-active-p))
|
||||
(goto-char point-before)
|
||||
(set-mark mark-before)))))
|
||||
(add-hook 'multiple-cursors-mode-enabled-hook #'+multiple-cursors|compat-switch-to-emacs-state)
|
||||
|
||||
(defun +multiple-cursors|compat-back-to-previous-state ()
|
||||
(when +mc--compat-evil-prev-state
|
||||
(unwind-protect
|
||||
(case +mc--compat-evil-prev-state
|
||||
((normal visual) (evil-force-normal-state))
|
||||
(t (message "Don't know how to handle previous state: %S"
|
||||
+mc--compat-evil-prev-state)))
|
||||
(setq +mc--compat-evil-prev-state nil)
|
||||
(setq +mc--compat-mark-was-active nil))))
|
||||
(add-hook 'multiple-cursors-mode-disabled-hook #'+multiple-cursors|compat-back-to-previous-state)
|
||||
|
||||
;; When running edit-lines, point will return (position + 1) as a
|
||||
;; result of how evil deals with regions
|
||||
(defun +multiple-cursors*adjust-mark-for-evil (&rest _)
|
||||
(when (and (bound-and-true-p evil-mode)
|
||||
(not (memq evil-state '(insert emacs))))
|
||||
(if (> (point) (mark))
|
||||
(goto-char (1- (point)))
|
||||
(push-mark (1- (mark))))))
|
||||
(advice-add #'mc/edit-lines :before #'+multiple-cursors*adjust-mark-for-evil)
|
||||
|
||||
(defun +multiple-cursors|evil-compat-rect-switch-state ()
|
||||
(if rectangular-region-mode
|
||||
(+multiple-cursors|compat-switch-to-emacs-state)
|
||||
(setq +mc--compat-evil-prev-state nil)))
|
||||
(add-hook 'rectangular-region-mode-hook '+multiple-cursors|evil-compat-rect-switch-state)
|
||||
|
||||
(defvar mc--default-cmds-to-run-once nil)))
|
9
modules/editor/multiple-cursors/packages.el
Normal file
9
modules/editor/multiple-cursors/packages.el
Normal file
|
@ -0,0 +1,9 @@
|
|||
;; -*- no-byte-compile: t; -*-
|
||||
;;; editor/multiple-cursors/packages.el
|
||||
|
||||
(cond ((featurep! :feature evil)
|
||||
(package! evil-multiedit)
|
||||
(package! evil-mc))
|
||||
|
||||
((package! multiple-cursors)))
|
||||
|
3
modules/editor/parinfer/README.org
Normal file
3
modules/editor/parinfer/README.org
Normal file
|
@ -0,0 +1,3 @@
|
|||
#+TITLE: :editor parinfer
|
||||
|
||||
You can find out more about parinfer at https://shaunlebron.github.io/parinfer/
|
19
modules/editor/parinfer/config.el
Normal file
19
modules/editor/parinfer/config.el
Normal file
|
@ -0,0 +1,19 @@
|
|||
;;; editor/parinfer/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
(def-package! parinfer
|
||||
:hook ((emacs-lisp-mode clojure-mode scheme-mode lisp-mode) . parinfer-mode)
|
||||
:init
|
||||
(setq parinfer-extensions
|
||||
'(defaults
|
||||
pretty-parens
|
||||
smart-tab
|
||||
smart-yank))
|
||||
(when (featurep! :feature evil +everywhere)
|
||||
(push 'evil parinfer-extensions))
|
||||
:config
|
||||
(map! :map parinfer-mode-map
|
||||
"\"" nil ; smartparens handles this
|
||||
:i "<tab>" #'parinfer-smart-tab:dwim-right-or-complete
|
||||
:i "<backtab>" #'parinfer-smart-tab:dwim-left
|
||||
:localleader
|
||||
:desc "Toggle parinfer-mode" "m" #'parinfer-toggle-mode))
|
14
modules/editor/parinfer/packages.el
Normal file
14
modules/editor/parinfer/packages.el
Normal file
|
@ -0,0 +1,14 @@
|
|||
;; -*- no-byte-compile: t; -*-
|
||||
;;; editor/parinfer/packages.el
|
||||
|
||||
(when (featurep! :feature evil)
|
||||
;; Parinfer uses `evil-define-key' without loading evil, so if evil is
|
||||
;; installed *after* parinfer, parinfer will throw up void-function errors.
|
||||
;; because evil-define-key (a macro) wasn't expanded at compile-time. So we
|
||||
;; make sure evil is installed before parinfer...
|
||||
(package! evil)
|
||||
;; ...and that it can see `evil-define-key' if evil was installed in a
|
||||
;; separate session:
|
||||
(autoload 'evil-define-key "evil-core" nil nil 'macro))
|
||||
|
||||
(package! parinfer)
|
19
modules/editor/rotate-text/autoload.el
Normal file
19
modules/editor/rotate-text/autoload.el
Normal file
|
@ -0,0 +1,19 @@
|
|||
;;; editor/rotate-text/autoload.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autoload
|
||||
(after! rotate-text
|
||||
(add-to-list 'rotate-text-words '("true" "false")))
|
||||
|
||||
;;;###autodef
|
||||
(cl-defun set-rotate-patterns! (modes &key symbols words patterns)
|
||||
"Declare :symbols, :words or :patterns (all lists of strings) that
|
||||
`rotate-text' will cycle through."
|
||||
(declare (indent defun))
|
||||
(dolist (mode (doom-enlist modes))
|
||||
(let ((fn-name (intern (format "+rotate-text|init-%s" mode))))
|
||||
(fset fn-name
|
||||
(lambda ()
|
||||
(setq-local rotate-text-local-symbols symbols)
|
||||
(setq-local rotate-text-local-words words)
|
||||
(setq-local rotate-text-local-patterns patterns)))
|
||||
(add-hook (intern (format "%s-hook" mode)) fn-name))))
|
|
@ -1,4 +1,4 @@
|
|||
;; -*- no-byte-compile: t; -*-
|
||||
;;; tools/rotate-text/packages.el
|
||||
;;; editor/rotate-text/packages.el
|
||||
|
||||
(package! rotate-text :recipe (:fetcher github :repo "debug-ito/rotate-text.el"))
|
262
modules/emacs/dired/config.el
Normal file
262
modules/emacs/dired/config.el
Normal file
|
@ -0,0 +1,262 @@
|
|||
;;; tools/dired/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
(def-package! dired
|
||||
:commands dired-jump
|
||||
:init
|
||||
(setq ;; Always copy/delete recursively
|
||||
dired-recursive-copies 'always
|
||||
dired-recursive-deletes 'top
|
||||
;; Auto refresh dired, but be quiet about it
|
||||
global-auto-revert-non-file-buffers t
|
||||
auto-revert-verbose nil
|
||||
dired-hide-details-hide-symlink-targets nil
|
||||
;; files
|
||||
image-dired-dir (concat doom-cache-dir "image-dired/")
|
||||
image-dired-db-file (concat image-dired-dir "db.el")
|
||||
image-dired-gallery-dir (concat image-dired-dir "gallery/")
|
||||
image-dired-temp-image-file (concat image-dired-dir "temp-image")
|
||||
image-dired-temp-rotate-image-file (concat image-dired-dir "temp-rotate-image"))
|
||||
:config
|
||||
(let ((args (list "-aBhl" "--group-directories-first")))
|
||||
(when IS-BSD
|
||||
;; Use GNU ls as `gls' from `coreutils' if available. Add `(setq
|
||||
;; dired-use-ls-dired nil)' to your config to suppress the Dired warning
|
||||
;; when not using GNU ls.
|
||||
(if-let* ((gls (executable-find "gls")))
|
||||
(setq insert-directory-program gls)
|
||||
(setq args (delete "--group-directories-first" args))
|
||||
(message "Cannot find `gls` (GNU ls). Install coreutils via your system package manager")))
|
||||
(setq dired-listing-switches (string-join args " ")))
|
||||
|
||||
(defun +dired|sort-directories-first ()
|
||||
"List directories first in dired buffers."
|
||||
(save-excursion
|
||||
(let (buffer-read-only)
|
||||
(forward-line 2) ;; beyond dir. header
|
||||
(sort-regexp-fields t "^.*$" "[ ]*." (point) (point-max))))
|
||||
(and (featurep 'xemacs)
|
||||
(fboundp 'dired-insert-set-properties)
|
||||
(dired-insert-set-properties (point-min) (point-max)))
|
||||
(set-buffer-modified-p nil))
|
||||
(add-hook 'dired-after-readin-hook #'+dired|sort-directories-first)
|
||||
|
||||
;; Automatically create missing directories when creating new files
|
||||
(defun +dired|create-non-existent-directory ()
|
||||
(let ((parent-directory (file-name-directory buffer-file-name)))
|
||||
(when (and (not (file-exists-p parent-directory))
|
||||
(y-or-n-p (format "Directory `%s' does not exist! Create it?" parent-directory)))
|
||||
(make-directory parent-directory t))))
|
||||
(add-to-list 'find-file-not-found-functions '+dired|create-non-existent-directory nil #'eq)
|
||||
|
||||
;; Kill buffer when quitting dired buffers
|
||||
(define-key dired-mode-map [remap quit-window] (λ! (quit-window t))))
|
||||
|
||||
|
||||
(def-package! dired-k
|
||||
:unless (featurep! +ranger)
|
||||
:hook (dired-initial-position . dired-k)
|
||||
:hook (dired-after-readin . dired-k-no-revert)
|
||||
:config
|
||||
(defun +dired*interrupt-process (orig-fn &rest args)
|
||||
"Fixes dired-k killing git processes too abruptly, leaving behind disruptive
|
||||
.git/index.lock files."
|
||||
(cl-letf (((symbol-function #'kill-process)
|
||||
(symbol-function #'interrupt-process)))
|
||||
(apply orig-fn args)))
|
||||
(advice-add #'dired-k--start-git-status :around #'+dired*interrupt-process)
|
||||
|
||||
(defun +dired*dired-k-highlight (orig-fn &rest args)
|
||||
"Butt out if the requested directory is remote (i.e. through tramp)."
|
||||
(unless (file-remote-p default-directory)
|
||||
(apply orig-fn args)))
|
||||
(advice-add #'dired-k--highlight :around #'+dired*dired-k-highlight))
|
||||
|
||||
|
||||
(def-package! ranger
|
||||
:when (featurep! +ranger)
|
||||
:after dired
|
||||
:init
|
||||
;; set up image-dired to allow picture resize
|
||||
(setq image-dired-dir (concat doom-cache-dir "image-dir"))
|
||||
:config
|
||||
(unless (file-directory-p image-dired-dir)
|
||||
(make-directory image-dired-dir))
|
||||
|
||||
(set-popup-rule! "^\\*ranger" :ignore t)
|
||||
|
||||
(setq ranger-override-dired t
|
||||
ranger-cleanup-on-disable t
|
||||
ranger-omit-regexp "^\.DS_Store$"
|
||||
ranger-excluded-extensions '("mkv" "iso" "mp4")
|
||||
ranger-deer-show-details nil
|
||||
ranger-max-preview-size 10
|
||||
ranger-show-literal nil
|
||||
dired-omit-verbose nil))
|
||||
|
||||
|
||||
(def-package! all-the-icons-dired
|
||||
:when (featurep! +icons)
|
||||
:hook (dired-mode . all-the-icons-dired-mode))
|
||||
|
||||
|
||||
(def-package! dired-x
|
||||
:hook (dired-mode . dired-omit-mode)
|
||||
:config
|
||||
(setq dired-omit-verbose nil))
|
||||
|
||||
|
||||
;;
|
||||
;; Evil integration
|
||||
|
||||
(map! :when (featurep! :feature evil +everywhere)
|
||||
:after dired
|
||||
:map dired-mode-map
|
||||
:n "q" #'quit-window
|
||||
:m "j" #'dired-next-line
|
||||
:m "k" #'dired-previous-line
|
||||
:n [mouse-2] #'dired-mouse-find-file-other-window
|
||||
:n [follow-link] #'mouse-face
|
||||
;; Commands to mark or flag certain categories of files
|
||||
:n "#" #'dired-flag-auto-save-files
|
||||
:n "." #'dired-clean-directory
|
||||
:n "~" #'dired-flag-backup-files
|
||||
;; Upper case keys (except !) for operating on the marked files
|
||||
:n "A" #'dired-do-find-regexp
|
||||
:n "C" #'dired-do-copy
|
||||
:n "B" #'dired-do-byte-compile
|
||||
:n "D" #'dired-do-delete
|
||||
:n "gG" #'dired-do-chgrp ;; FIXME: This can probably live on a better binding.
|
||||
:n "H" #'dired-do-hardlink
|
||||
:n "L" #'dired-do-load
|
||||
:n "M" #'dired-do-chmod
|
||||
:n "O" #'dired-do-chown
|
||||
:n "P" #'dired-do-print
|
||||
:n "Q" #'dired-do-find-regexp-and-replace
|
||||
:n "R" #'dired-do-rename
|
||||
:n "S" #'dired-do-symlink
|
||||
:n "T" #'dired-do-touch
|
||||
:n "X" #'dired-do-shell-command
|
||||
:n "Z" #'dired-do-compress
|
||||
:n "c" #'dired-do-compress-to
|
||||
:n "!" #'dired-do-shell-command
|
||||
:n "&" #'dired-do-async-shell-command
|
||||
;; Comparison commands
|
||||
:n "=" #'dired-diff
|
||||
;; Tree Dired commands
|
||||
:n "M-C-?" #'dired-unmark-all-files
|
||||
:n "M-C-d" #'dired-tree-down
|
||||
:n "M-C-u" #'dired-tree-up
|
||||
:n "M-C-n" #'dired-next-subdir
|
||||
:n "M-C-p" #'dired-prev-subdir
|
||||
;; move to marked files
|
||||
:n "M-{" #'dired-prev-marked-file
|
||||
:n "M-}" #'dired-next-marked-file
|
||||
;; Make all regexp commands share a `%' prefix:
|
||||
;; We used to get to the submap via a symbol dired-regexp-prefix, but that
|
||||
;; seems to serve little purpose, and copy-keymap does a better job
|
||||
;; without it.
|
||||
:n "%" nil
|
||||
:n "%u" #'dired-upcase
|
||||
:n "%l" #'dired-downcase
|
||||
:n "%d" #'dired-flag-files-regexp
|
||||
:n "%g" #'dired-mark-files-containing-regexp
|
||||
:n "%m" #'dired-mark-files-regexp
|
||||
:n "%r" #'dired-do-rename-regexp
|
||||
:n "%C" #'dired-do-copy-regexp
|
||||
:n "%H" #'dired-do-hardlink-regexp
|
||||
:n "%R" #'dired-do-rename-regexp
|
||||
:n "%S" #'dired-do-symlink-regexp
|
||||
:n "%&" #'dired-flag-garbage-files
|
||||
;; mark
|
||||
:n "*" nil
|
||||
:n "**" #'dired-mark-executables
|
||||
:n "*/" #'dired-mark-directories
|
||||
:n "*@" #'dired-mark-symlinks
|
||||
:n "*%" #'dired-mark-files-regexp
|
||||
:n "*(" #'dired-mark-sexp
|
||||
:n "*." #'dired-mark-extension
|
||||
:n "*O" #'dired-mark-omitted
|
||||
:n "*c" #'dired-change-marks
|
||||
:n "*s" #'dired-mark-subdir-files
|
||||
:n "*m" #'dired-mark
|
||||
:n "*u" #'dired-unmark
|
||||
:n "*?" #'dired-unmark-all-files
|
||||
:n "*!" #'dired-unmark-all-marks
|
||||
:n "U" #'dired-unmark-all-marks
|
||||
:n "* <delete>" #'dired-unmark-backward
|
||||
:n "* C-n" #'dired-next-marked-file
|
||||
:n "* C-p" #'dired-prev-marked-file
|
||||
:n "*t" #'dired-toggle-marks
|
||||
;; Lower keys for commands not operating on all the marked files
|
||||
:n "a" #'dired-find-alternate-file
|
||||
:n "d" #'dired-flag-file-deletion
|
||||
:n "gf" #'dired-find-file
|
||||
:n "C-m" #'dired-find-file
|
||||
:n "gr" #'revert-buffer
|
||||
:n "i" #'dired-toggle-read-only
|
||||
:n "I" #'dired-maybe-insert-subdir
|
||||
:n "J" #'dired-goto-file
|
||||
:n "K" #'dired-do-kill-lines
|
||||
:n "r" #'dired-do-redisplay
|
||||
:n "m" #'dired-mark
|
||||
:n "t" #'dired-toggle-marks
|
||||
:n "u" #'dired-unmark ; also "*u"
|
||||
:n "W" #'browse-url-of-dired-file
|
||||
:n "x" #'dired-do-flagged-delete
|
||||
:n "gy" #'dired-show-file-type ;; FIXME: This could probably go on a better key.
|
||||
:n "Y" #'dired-copy-filename-as-kill
|
||||
:n "+" #'dired-create-directory
|
||||
;; open
|
||||
:n "<return>" #'dired-find-file
|
||||
:n "S-<return>" #'dired-find-file-other-window
|
||||
:n "M-<return>" #'dired-display-file
|
||||
:n "gO" #'dired-find-file-other-window
|
||||
:n "go" #'dired-view-file
|
||||
;; sort
|
||||
:n "o" #'dired-sort-toggle-or-edit
|
||||
;; moving
|
||||
:m "gj" #'dired-next-dirline
|
||||
:m "gk" #'dired-prev-dirline
|
||||
:n "[" #'dired-prev-dirline
|
||||
:n "]" #'dired-next-dirline
|
||||
:n "<" #'dired-prev-dirline
|
||||
:n ">" #'dired-next-dirline
|
||||
:n "^" #'dired-up-directory
|
||||
:n [?\S-\ ] #'dired-previous-line
|
||||
:n [remap next-line] #'dired-next-line
|
||||
:n [remap previous-line] #'dired-previous-line
|
||||
;; hiding
|
||||
:n "g$" #'dired-hide-subdir ;; FIXME: This can probably live on a better binding.
|
||||
:n "M-$" #'dired-hide-all
|
||||
:n "(" #'dired-hide-details-mode
|
||||
;; isearch
|
||||
:n "M-s a C-s" #'dired-do-isearch
|
||||
:n "M-s a M-C-s" #'dired-do-isearch-regexp
|
||||
:n "M-s f C-s" #'dired-isearch-filenames
|
||||
:n "M-s f M-C-s" #'dired-isearch-filenames-regexp
|
||||
;; misc
|
||||
:n [remap read-only-mode] #'dired-toggle-read-only
|
||||
;; `toggle-read-only' is an obsolete alias for `read-only-mode'
|
||||
:n [remap toggle-read-only] #'dired-toggle-read-only
|
||||
:n "g?" #'dired-summary
|
||||
:n "<delete>" #'dired-unmark-backward
|
||||
:n [remap undo] #'dired-undo
|
||||
:n [remap advertised-undo] #'dired-undo
|
||||
;; thumbnail manipulation (image-dired)
|
||||
:n "C-t d" #'image-dired-display-thumbs
|
||||
:n "C-t t" #'image-dired-tag-files
|
||||
:n "C-t r" #'image-dired-delete-tag
|
||||
:n "C-t j" #'image-dired-jump-thumbnail-buffer
|
||||
:n "C-t i" #'image-dired-dired-display-image
|
||||
:n "C-t x" #'image-dired-dired-display-external
|
||||
:n "C-t a" #'image-dired-display-thumbs-append
|
||||
:n "C-t ." #'image-dired-display-thumb
|
||||
:n "C-t c" #'image-dired-dired-comment-files
|
||||
:n "C-t f" #'image-dired-mark-tagged-files
|
||||
:n "C-t C-t" #'image-dired-dired-toggle-marked-thumbs
|
||||
:n "C-t e" #'image-dired-dired-edit-comment-and-tags
|
||||
;; encryption and decryption (epa-dired)
|
||||
:n ";d" #'epa-dired-do-decrypt
|
||||
:n ";v" #'epa-dired-do-verify
|
||||
:n ";s" #'epa-dired-do-sign
|
||||
:n ";e" #'epa-dired-do-encrypt)
|
8
modules/emacs/dired/packages.el
Normal file
8
modules/emacs/dired/packages.el
Normal file
|
@ -0,0 +1,8 @@
|
|||
;; -*- no-byte-compile: t; -*-
|
||||
;;; emacs/dired/packages.el
|
||||
|
||||
(package! dired-k)
|
||||
(when (featurep! +ranger)
|
||||
(package! ranger))
|
||||
(when (featurep! +icons)
|
||||
(package! all-the-icons-dired))
|
26
modules/emacs/electric/autoload.el
Normal file
26
modules/emacs/electric/autoload.el
Normal file
|
@ -0,0 +1,26 @@
|
|||
;;; emacs/electric/autoload.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autodef
|
||||
(defun set-electric! (modes &rest plist)
|
||||
"Declare that WORDS (list of strings) or CHARS (lists of chars) should trigger
|
||||
electric indentation.
|
||||
|
||||
Enables `electric-indent-local-mode' in MODES.
|
||||
|
||||
\(fn MODES &key WORDS CHARS)"
|
||||
(declare (indent defun))
|
||||
(dolist (mode (doom-enlist modes))
|
||||
(let ((hook (intern (format "%s-hook" mode)))
|
||||
(fn (intern (format "+electric|init-%s" mode))))
|
||||
(cond ((null (car-safe plist))
|
||||
(remove-hook hook fn)
|
||||
(unintern fn nil))
|
||||
((fset fn
|
||||
(lambda ()
|
||||
(when (eq major-mode mode)
|
||||
(setq-local electric-indent-inhibit nil)
|
||||
(cl-destructuring-bind (&key chars words) plist
|
||||
(electric-indent-local-mode +1)
|
||||
(if chars (setq electric-indent-chars chars))
|
||||
(if words (setq +electric-indent-words words))))))
|
||||
(add-hook hook fn))))))
|
19
modules/emacs/electric/config.el
Normal file
19
modules/emacs/electric/config.el
Normal file
|
@ -0,0 +1,19 @@
|
|||
;;; emacs/electric/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
;; Smarter, keyword-based electric-indent
|
||||
|
||||
(defvar-local +electric-indent-words '()
|
||||
"The list of electric words. Typing these will trigger reindentation of the
|
||||
current line.")
|
||||
|
||||
;;
|
||||
(after! electric
|
||||
(setq-default electric-indent-chars '(?\n ?\^?))
|
||||
|
||||
(defun +electric-indent|char (_c)
|
||||
(when (and (eolp) +electric-indent-words)
|
||||
(save-excursion
|
||||
(backward-word)
|
||||
(looking-at-p (concat "\\<" (regexp-opt +electric-indent-words))))))
|
||||
(add-to-list 'electric-indent-functions #'+electric-indent|char nil #'eq))
|
||||
|
18
modules/emacs/eshell/autoload/commands.el
Normal file
18
modules/emacs/eshell/autoload/commands.el
Normal file
|
@ -0,0 +1,18 @@
|
|||
;;; emacs/eshell/autoload/commands.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autoload
|
||||
(defun eshell/cd-to-project ()
|
||||
"Change to the project root of the current directory."
|
||||
(eshell/cd (doom-project-root (eshell/pwd))))
|
||||
|
||||
;;;###autoload
|
||||
(defun eshell/quit-and-close (&rest _)
|
||||
"Quit the current eshell buffer and close the window it's in."
|
||||
(setq-local +eshell-kill-window-on-exit t)
|
||||
(throw 'eshell-terminal t))
|
||||
|
||||
;;;###autoload
|
||||
(defun eshell/mkdir-and-cd (dir)
|
||||
"Create a directory then cd into it."
|
||||
(make-directory dir t)
|
||||
(eshell/cd dir))
|
297
modules/emacs/eshell/autoload/eshell.el
Normal file
297
modules/emacs/eshell/autoload/eshell.el
Normal file
|
@ -0,0 +1,297 @@
|
|||
;;; emacs/eshell/autoload/eshell.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defvar eshell-buffer-name "*doom eshell*")
|
||||
|
||||
(defvar +eshell-buffers (make-ring 25)
|
||||
"List of open eshell buffers.")
|
||||
|
||||
|
||||
(defvar +eshell--last-buffer nil)
|
||||
|
||||
|
||||
;;
|
||||
;; Helpers
|
||||
|
||||
(defun +eshell--add-buffer (buf)
|
||||
(ring-remove+insert+extend +eshell-buffers buf 'grow))
|
||||
|
||||
(defun +eshell--remove-buffer (buf)
|
||||
(when-let* ((idx (ring-member +eshell-buffers buf)))
|
||||
(ring-remove +eshell-buffers idx)
|
||||
t))
|
||||
|
||||
(defun +eshell--bury-buffer (&optional dedicated-p)
|
||||
(unless (switch-to-prev-buffer nil 'bury)
|
||||
(switch-to-buffer (doom-fallback-buffer)))
|
||||
(when (eq major-mode 'eshell-mode)
|
||||
(switch-to-buffer (doom-fallback-buffer)))
|
||||
(when +eshell-enable-new-shell-on-split
|
||||
(when-let* ((win (get-buffer-window (+eshell/open t))))
|
||||
(set-window-dedicated-p win dedicated-p))))
|
||||
|
||||
(defun +eshell--setup-window (window &optional flag)
|
||||
(when (window-live-p window)
|
||||
(set-window-parameter window 'no-other-window flag)
|
||||
(set-window-parameter window 'visible flag)))
|
||||
|
||||
(defun +eshell--unused-buffer (&optional new-p)
|
||||
(or (unless new-p
|
||||
(cl-loop for buf in (+eshell-buffers)
|
||||
if (and (buffer-live-p buf)
|
||||
(not (get-buffer-window buf t)))
|
||||
return buf))
|
||||
(generate-new-buffer eshell-buffer-name)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +eshell-last-buffer (&optional noerror)
|
||||
"Return the last opened eshell buffer."
|
||||
(let ((buffer (cl-find-if #'buffer-live-p (+eshell-buffers))))
|
||||
(cond (buffer)
|
||||
(noerror nil)
|
||||
((user-error "No live eshell buffers remaining")))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +eshell-buffers ()
|
||||
"TODO"
|
||||
(ring-elements +eshell-buffers))
|
||||
|
||||
;;;###autoload
|
||||
(defun +eshell-run-command (command &optional buffer)
|
||||
"TODO"
|
||||
(let ((buffer
|
||||
(or buffer
|
||||
(if (eq major-mode 'eshell-mode)
|
||||
(current-buffer)
|
||||
(cl-find-if #'buffer-live-p (+eshell-buffers))))))
|
||||
(unless buffer
|
||||
(user-error "No living eshell buffers available"))
|
||||
(unless (buffer-live-p buffer)
|
||||
(user-error "Cannot operate on a dead buffer"))
|
||||
(with-current-buffer buffer
|
||||
(goto-char eshell-last-output-end)
|
||||
(goto-char (line-end-position))
|
||||
(insert command)
|
||||
(eshell-send-input nil t))))
|
||||
|
||||
|
||||
;;
|
||||
;; Commands
|
||||
|
||||
;;;###autoload
|
||||
(defun +eshell/open (arg &optional command)
|
||||
"Open eshell in the current buffer."
|
||||
(interactive "P")
|
||||
(when (eq major-mode 'eshell-mode)
|
||||
(user-error "Already in an eshell buffer"))
|
||||
(let* ((default-directory (or (if arg default-directory (doom-project-root))
|
||||
default-directory))
|
||||
(buf (+eshell--unused-buffer)))
|
||||
(with-current-buffer (switch-to-buffer buf)
|
||||
(if (eq major-mode 'eshell-mode)
|
||||
(run-hooks 'eshell-mode-hook)
|
||||
(eshell-mode))
|
||||
(if command (+eshell-run-command command buf)))
|
||||
buf))
|
||||
|
||||
;;;###autoload
|
||||
(defun +eshell/open-popup (arg &optional command)
|
||||
"Open eshell in a popup window."
|
||||
(interactive "P")
|
||||
(let* ((default-directory (or (if arg default-directory (doom-project-root))
|
||||
default-directory))
|
||||
(buf (+eshell--unused-buffer)))
|
||||
(with-current-buffer (pop-to-buffer buf)
|
||||
(if (eq major-mode 'eshell-mode)
|
||||
(run-hooks 'eshell-mode-hook)
|
||||
(eshell-mode))
|
||||
(if command (+eshell-run-command command buf)))
|
||||
buf))
|
||||
|
||||
;;;###autoload
|
||||
(defun +eshell/open-fullscreen (arg &optional command)
|
||||
"Open eshell in a separate workspace. Requires the (:feature workspaces)
|
||||
module to be loaded."
|
||||
(interactive "P")
|
||||
(let ((default-directory (or (if arg default-directory (doom-project-root))
|
||||
default-directory))
|
||||
(buf (+eshell--unused-buffer 'new)))
|
||||
(set-frame-parameter nil 'saved-wconf (current-window-configuration))
|
||||
(delete-other-windows)
|
||||
(with-current-buffer (switch-to-buffer buf)
|
||||
(eshell-mode)
|
||||
(if command (+eshell-run-command command buf)))
|
||||
buf))
|
||||
|
||||
|
||||
;;
|
||||
;; Keybinds
|
||||
|
||||
;;;###autoload
|
||||
(defun +eshell/search-history ()
|
||||
"Search the eshell command history with helm, ivy or `eshell-list-history'."
|
||||
(interactive)
|
||||
(cond ((featurep! :completion ivy)
|
||||
(require 'em-hist)
|
||||
(let* ((ivy-completion-beg (eshell-bol))
|
||||
(ivy-completion-end (point-at-eol))
|
||||
(input (buffer-substring-no-properties
|
||||
ivy-completion-beg
|
||||
ivy-completion-end)))
|
||||
;; Better than `counsel-esh-history' because that doesn't
|
||||
;; pre-populate the initial input or selection.
|
||||
(ivy-read "Command: "
|
||||
(delete-dups
|
||||
(when (> (ring-size eshell-history-ring) 0)
|
||||
(ring-elements eshell-history-ring)))
|
||||
:initial-input input
|
||||
:action #'ivy-completion-in-region-action)))
|
||||
((featurep! :completion helm)
|
||||
(helm-eshell-history))
|
||||
((eshell-list-history))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +eshell/pcomplete ()
|
||||
"Use pcomplete with completion-in-region backend instead of popup window at
|
||||
bottom. This ties pcomplete into ivy or helm, if they are enabled."
|
||||
(interactive)
|
||||
(require 'pcomplete)
|
||||
(ignore-errors (pcomplete-std-complete)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +eshell/quit-or-delete-char (arg)
|
||||
"Delete a character (ahead of the cursor) or quit eshell if there's nothing to
|
||||
delete."
|
||||
(interactive "p")
|
||||
(if (and (eolp) (looking-back eshell-prompt-regexp nil))
|
||||
(eshell-life-is-too-much)
|
||||
(delete-char arg)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +eshell/split-below ()
|
||||
"Create a new eshell window below the current one."
|
||||
(interactive)
|
||||
(let ((ignore-window-parameters t)
|
||||
(dedicated-p (window-dedicated-p))
|
||||
(+eshell-enable-new-shell-on-split
|
||||
(or +eshell-enable-new-shell-on-split (frame-parameter nil 'saved-wconf))))
|
||||
(select-window (split-window-vertically))
|
||||
(+eshell--bury-buffer dedicated-p)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +eshell/split-right ()
|
||||
"Create a new eshell window to the right of the current one."
|
||||
(interactive)
|
||||
(let* ((ignore-window-parameters t)
|
||||
(dedicated-p (window-dedicated-p))
|
||||
(+eshell-enable-new-shell-on-split
|
||||
(or +eshell-enable-new-shell-on-split (frame-parameter nil 'saved-wconf))))
|
||||
(select-window (split-window-horizontally))
|
||||
(+eshell--bury-buffer dedicated-p)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +eshell/switch-to-next ()
|
||||
"Switch to the next eshell buffer."
|
||||
(interactive)
|
||||
(when (ring-empty-p +eshell-buffers)
|
||||
(user-error "No eshell buffers are available"))
|
||||
(switch-to-buffer (ring-next +eshell-buffers (current-buffer))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +eshell/switch-to-previous ()
|
||||
"Switch to the previous eshell buffer."
|
||||
(interactive)
|
||||
(when (ring-empty-p +eshell-buffers)
|
||||
(user-error "No eshell buffers are available"))
|
||||
(switch-to-buffer (ring-previous +eshell-buffers (current-buffer))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +eshell/switch-to-last ()
|
||||
"Switch to the last eshell buffer that was open (and is still alive)."
|
||||
(interactive)
|
||||
(unless (buffer-live-p +eshell--last-buffer)
|
||||
(setq +eshell--last-buffer nil)
|
||||
(user-error "No last eshell buffer to jump to"))
|
||||
(switch-to-buffer +eshell--last-buffer))
|
||||
|
||||
;;;###autoload
|
||||
(defun +eshell/switch-to (buffer)
|
||||
"Interactively switch to another eshell buffer."
|
||||
(interactive
|
||||
(let ((buffers (doom-buffers-in-mode
|
||||
'eshell-mode (delq (current-buffer) (+eshell-buffers)))))
|
||||
(if (not buffers)
|
||||
(user-error "No eshell buffers are available")
|
||||
(list
|
||||
(completing-read "Eshell buffers"
|
||||
(mapcar #'buffer-name buffers)
|
||||
#'get-buffer
|
||||
'require-match
|
||||
nil nil
|
||||
(when (eq major-mode 'eshell-mode)
|
||||
(buffer-name (current-buffer))))))))
|
||||
(if-let* ((window (get-buffer-window buffer)))
|
||||
(select-window window)
|
||||
(switch-to-buffer buffer)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +eshell/kill-and-close ()
|
||||
"Kill the current eshell buffer and close its window."
|
||||
(interactive)
|
||||
(unless (eq major-mode 'eshell-mode)
|
||||
(user-error "Not in an eshell buffer"))
|
||||
(let ((+eshell-kill-window-on-exit t))
|
||||
(kill-this-buffer)))
|
||||
|
||||
|
||||
;;
|
||||
;; Hooks
|
||||
|
||||
;;;###autoload
|
||||
(defun +eshell|init ()
|
||||
"Initialize and track this eshell buffer in `+eshell-buffers'."
|
||||
(let ((current-buffer (current-buffer)))
|
||||
(dolist (buf (+eshell-buffers))
|
||||
(unless (buffer-live-p buf)
|
||||
(+eshell--remove-buffer buf)))
|
||||
(+eshell--setup-window (get-buffer-window current-buffer))
|
||||
(+eshell--add-buffer current-buffer)
|
||||
(setq +eshell--last-buffer current-buffer)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +eshell|cleanup ()
|
||||
"Close window (or workspace) on quit."
|
||||
(let ((buf (current-buffer)))
|
||||
(when (+eshell--remove-buffer buf)
|
||||
(when-let* ((win (get-buffer-window buf)))
|
||||
(+eshell--setup-window win nil)
|
||||
(cond ((and (one-window-p t)
|
||||
(window-configuration-p (frame-parameter nil 'saved-wconf)))
|
||||
(set-window-configuration (frame-parameter nil 'saved-wconf))
|
||||
(set-frame-parameter win 'saved-wconf nil))
|
||||
((one-window-p)
|
||||
(let ((prev (save-window-excursion (previous-buffer))))
|
||||
(unless (and prev (doom-real-buffer-p prev))
|
||||
(switch-to-buffer (doom-fallback-buffer)))))
|
||||
((or (window-dedicated-p win)
|
||||
+eshell-kill-window-on-exit)
|
||||
(let ((ignore-window-parameters t)
|
||||
(popup-p (window-dedicated-p win)))
|
||||
(delete-window win)
|
||||
(when popup-p
|
||||
(cl-loop for win in (window-list)
|
||||
for buf = (window-buffer win)
|
||||
for mode = (buffer-local-value 'major-mode buf)
|
||||
if (eq mode 'eshell-mode)
|
||||
return (select-window win))))))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +eshell|switch-workspace (type)
|
||||
(when (eq type 'frame)
|
||||
(setq +eshell-buffers
|
||||
(or (persp-parameter 'eshell-buffers)
|
||||
(make-ring 25)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +eshell|save-workspace (_workspace target)
|
||||
(when (framep target)
|
||||
(set-persp-parameter 'eshell-buffers +eshell-buffers)))
|
76
modules/emacs/eshell/autoload/evil.el
Normal file
76
modules/emacs/eshell/autoload/evil.el
Normal file
|
@ -0,0 +1,76 @@
|
|||
;;; emacs/eshell/autoload/evil.el -*- lexical-binding: t; -*-
|
||||
;;;###if (featurep! :feature evil)
|
||||
|
||||
;;;###autoload
|
||||
(defun +eshell|init-evil ()
|
||||
"Replace `evil-collection-eshell-next-prompt-on-insert' with
|
||||
`+eshell|goto-prompt-on-insert', which ensures the point is on the prompt when
|
||||
changing to insert mode."
|
||||
(dolist (hook '(evil-replace-state-entry-hook evil-insert-state-entry-hook))
|
||||
(remove-hook hook 'evil-collection-eshell-next-prompt-on-insert t)
|
||||
(add-hook hook '+eshell|goto-prompt-on-insert nil t)))
|
||||
|
||||
;;;###autoload (autoload '+eshell:run "emacs/eshell/autoload/evil" nil t)
|
||||
(evil-define-command +eshell:run (command bang)
|
||||
"TODO"
|
||||
(interactive "<fsh><!>")
|
||||
(let ((buffer (+eshell-last-buffer))
|
||||
(command (+evil*resolve-vim-path command)))
|
||||
(cond (buffer
|
||||
(select-window (get-buffer-window buffer))
|
||||
(+eshell-run-command command buffer))
|
||||
(bang (+eshell/open nil command))
|
||||
((+eshell/open-popup nil command)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +eshell|goto-prompt-on-insert ()
|
||||
"Move cursor to the prompt when switching to insert mode (if point isn't
|
||||
already there)."
|
||||
(when (< (point) eshell-last-output-end)
|
||||
(goto-char
|
||||
(if (memq this-command '(evil-append evil-append-line))
|
||||
(point-max)
|
||||
eshell-last-output-end))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +eshell/goto-end-of-prompt ()
|
||||
"Move cursor to the prompt when switching to insert mode (if point isn't
|
||||
already there)."
|
||||
(interactive)
|
||||
(goto-char (point-max))
|
||||
(evil-append 1))
|
||||
|
||||
;;;###autoload (autoload '+eshell/evil-change "emacs/eshell/autoload/evil" nil t)
|
||||
(evil-define-operator +eshell/evil-change (beg end type register yank-handler delete-func)
|
||||
"Like `evil-change' but will not delete/copy the prompt."
|
||||
(interactive "<R><x><y>")
|
||||
(save-restriction
|
||||
(narrow-to-region eshell-last-output-end (point-max))
|
||||
(evil-change (max beg (point-min))
|
||||
(if (eq type 'line) (point-max) (min (or end (point-max)) (point-max)))
|
||||
type register yank-handler delete-func)))
|
||||
|
||||
;;;###autoload (autoload '+eshell/evil-change-line "emacs/eshell/autoload/evil" nil t)
|
||||
(evil-define-operator +eshell/evil-change-line (beg end type register yank-handler)
|
||||
"Change to end of line."
|
||||
:motion evil-end-of-line
|
||||
(interactive "<R><x><y>")
|
||||
(+eshell/evil-change beg end type register yank-handler #'evil-delete-line))
|
||||
|
||||
;;;###autoload (autoload '+eshell/evil-delete "emacs/eshell/autoload/evil" nil t)
|
||||
(evil-define-operator +eshell/evil-delete (beg end type register yank-handler)
|
||||
"Like `evil-delete' but will not delete/copy the prompt."
|
||||
(interactive "<R><x><y>")
|
||||
(save-restriction
|
||||
(narrow-to-region eshell-last-output-end (point-max))
|
||||
(evil-delete (if beg (max beg (point-min)) (point-min))
|
||||
(if (eq type 'line) (point-max) (min (or end (point-max)) (point-max)))
|
||||
type register yank-handler)))
|
||||
|
||||
;;;###autoload (autoload '+eshell/evil-delete-line "emacs/eshell/autoload/evil" nil t)
|
||||
(evil-define-operator +eshell/evil-delete-line (_beg end type register yank-handler)
|
||||
"Change to end of line."
|
||||
:motion nil
|
||||
:keep-visual t
|
||||
(interactive "<R><x>")
|
||||
(+eshell/evil-delete (point) end type register yank-handler))
|
34
modules/emacs/eshell/autoload/prompts.el
Normal file
34
modules/emacs/eshell/autoload/prompts.el
Normal file
|
@ -0,0 +1,34 @@
|
|||
;;; emacs/eshell/autoload/prompts.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autoload
|
||||
(defface +eshell-prompt-pwd '((t :inherit font-lock-constant-face))
|
||||
"TODO"
|
||||
:group 'eshell)
|
||||
|
||||
;;;###autoload
|
||||
(defface +eshell-prompt-git-branch '((t :inherit font-lock-builtin-face))
|
||||
"TODO"
|
||||
:group 'eshell)
|
||||
|
||||
|
||||
(defun +eshell--current-git-branch ()
|
||||
(let ((branch (car (cl-loop for match in (split-string (shell-command-to-string "git branch") "\n")
|
||||
if (string-match-p "^\*" match)
|
||||
collect match))))
|
||||
(if (not (eq branch nil))
|
||||
(format " [%s]" (substring branch 2))
|
||||
"")))
|
||||
|
||||
;;;###autoload
|
||||
(defun +eshell-default-prompt ()
|
||||
"Generate the prompt string for eshell. Use for `eshell-prompt-function'."
|
||||
(concat (if (bobp) "" "\n")
|
||||
(let ((pwd (eshell/pwd)))
|
||||
(propertize (if (equal pwd "~")
|
||||
pwd
|
||||
(abbreviate-file-name (shrink-path-file pwd)))
|
||||
'face '+eshell-prompt-pwd))
|
||||
(propertize (+eshell--current-git-branch)
|
||||
'face '+eshell-prompt-git-branch)
|
||||
(propertize " λ" 'face (if (zerop eshell-last-command-status) 'success 'error))
|
||||
" "))
|
20
modules/emacs/eshell/autoload/settings.el
Normal file
20
modules/emacs/eshell/autoload/settings.el
Normal file
|
@ -0,0 +1,20 @@
|
|||
;;; emacs/eshell/autoload/settings.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autodef
|
||||
(defun set-eshell-alias! (&rest aliases)
|
||||
"Define aliases for eshell."
|
||||
(or (cl-evenp (length aliases))
|
||||
(signal 'wrong-number-of-arguments (list 'even (length aliases))))
|
||||
(after! eshell
|
||||
(while aliases
|
||||
(let ((alias (pop aliases))
|
||||
(command (pop aliases)))
|
||||
(if-let* ((oldval (assoc alias +eshell-aliases)))
|
||||
(setcdr oldval (list command))
|
||||
(push (list alias command) +eshell-aliases))))
|
||||
(when (boundp 'eshell-command-aliases-list)
|
||||
(if +eshell--default-aliases
|
||||
(setq eshell-command-aliases-list
|
||||
(append +eshell--default-aliases
|
||||
+eshell-aliases))
|
||||
(setq eshell-command-aliases-list +eshell-aliases)))))
|
165
modules/emacs/eshell/config.el
Normal file
165
modules/emacs/eshell/config.el
Normal file
|
@ -0,0 +1,165 @@
|
|||
;;; emacs/eshell/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
;; see:
|
||||
;; + `+eshell/open': open in current buffer
|
||||
;; + `+eshell/open-popup': open in a popup
|
||||
;; + `+eshell/open-fullscreen': open eshell fullscreen (will restore window
|
||||
;; config when quitting the last eshell buffer)
|
||||
|
||||
(defvar +eshell-config-dir
|
||||
(expand-file-name "eshell/" doom-private-dir)
|
||||
"Where to store eshell configuration files, as opposed to
|
||||
`eshell-directory-name', which is where Doom will store temporary/data files.")
|
||||
|
||||
(defvar +eshell-enable-new-shell-on-split t
|
||||
"If non-nil, spawn a new eshell session after splitting from an eshell
|
||||
buffer.")
|
||||
|
||||
(defvar +eshell-kill-window-on-exit nil
|
||||
"If non-nil, eshell will close windows along with its eshell buffers.")
|
||||
|
||||
(defvar +eshell-aliases
|
||||
'(("q" "exit") ; built-in
|
||||
("f" "find-file $1")
|
||||
("bd" "eshell-up $1") ; `eshell-up'
|
||||
("rg" "rg --color=always $*")
|
||||
("ag" "ag --color=always $*")
|
||||
("l" "ls -lh")
|
||||
("ll" "ls -lah")
|
||||
("clear" "clear-scrollback")) ; more sensible than default
|
||||
"An alist of default eshell aliases, meant to emulate useful shell utilities,
|
||||
like fasd and bd. Note that you may overwrite these in your
|
||||
`eshell-aliases-file'. This is here to provide an alternative, elisp-centric way
|
||||
to define your aliases.
|
||||
|
||||
You should use `det-eshell-alias!' to change this.")
|
||||
|
||||
;;
|
||||
(defvar eshell-directory-name (concat doom-etc-dir "eshell"))
|
||||
|
||||
;; These files are exceptions, because they may contain configuration
|
||||
(defvar eshell-aliases-file (concat +eshell-config-dir "alias"))
|
||||
(defvar eshell-rc-script (concat +eshell-config-dir "profile"))
|
||||
(defvar eshell-login-script (concat +eshell-config-dir "login"))
|
||||
|
||||
|
||||
(defvar +eshell--default-aliases nil)
|
||||
|
||||
|
||||
;;
|
||||
;; Packages
|
||||
|
||||
(after! eshell ; built-in
|
||||
(setq eshell-banner-message
|
||||
'(format "%s %s\n"
|
||||
(propertize (format " %s " (string-trim (buffer-name)))
|
||||
'face 'mode-line-highlight)
|
||||
(propertize (current-time-string)
|
||||
'face 'font-lock-keyword-face))
|
||||
eshell-scroll-to-bottom-on-input 'all
|
||||
eshell-scroll-to-bottom-on-output 'all
|
||||
eshell-buffer-shorthand t
|
||||
eshell-kill-processes-on-exit t
|
||||
eshell-hist-ignoredups t
|
||||
;; don't record command in history if prefixed with whitespace
|
||||
eshell-input-filter #'eshell-input-filter-initial-space
|
||||
;; em-prompt
|
||||
eshell-prompt-regexp "^.* λ "
|
||||
eshell-prompt-function #'+eshell-default-prompt
|
||||
;; em-glob
|
||||
eshell-glob-case-insensitive t
|
||||
eshell-error-if-no-glob t)
|
||||
|
||||
;; Consider eshell buffers real
|
||||
(add-hook 'eshell-mode-hook #'doom|mark-buffer-as-real)
|
||||
|
||||
;; Keep track of open eshell buffers
|
||||
(add-hook 'eshell-mode-hook #'+eshell|init)
|
||||
(add-hook 'eshell-exit-hook #'+eshell|cleanup)
|
||||
|
||||
;; Enable autopairing in eshell
|
||||
(add-hook 'eshell-mode-hook #'smartparens-mode)
|
||||
|
||||
;; Persp-mode/workspaces integration
|
||||
(when (featurep! :feature workspaces)
|
||||
(add-hook 'persp-activated-functions #'+eshell|switch-workspace)
|
||||
(add-hook 'persp-before-switch-functions #'+eshell|save-workspace))
|
||||
|
||||
;; UI enhancements
|
||||
(defun +eshell|remove-fringes ()
|
||||
(set-window-fringes nil 0 0)
|
||||
(set-window-margins nil 1 nil))
|
||||
(add-hook 'eshell-mode-hook #'+eshell|remove-fringes)
|
||||
|
||||
(defun +eshell|enable-text-wrapping ()
|
||||
(visual-line-mode +1)
|
||||
(set-display-table-slot standard-display-table 0 ?\ ))
|
||||
(add-hook 'eshell-mode-hook #'+eshell|enable-text-wrapping)
|
||||
|
||||
(add-hook 'eshell-mode-hook #'hide-mode-line-mode)
|
||||
|
||||
;; Don't auto-write our aliases! Let us manage our own `eshell-aliases-file'
|
||||
;; or configure `+eshell-aliases' via elisp.
|
||||
(advice-add #'eshell-write-aliases-list :override #'ignore)
|
||||
|
||||
;; Visual commands require a proper terminal. Eshell can't handle that, so
|
||||
;; it delegates these commands to a term buffer.
|
||||
(after! em-term
|
||||
(dolist (cmd '("tmux" "htop" "vim" "nvim" "ncmpcpp"))
|
||||
(add-to-list 'eshell-visual-commands cmd)))
|
||||
|
||||
(defun +eshell|init-aliases ()
|
||||
(setq +eshell--default-aliases eshell-command-aliases-list
|
||||
eshell-command-aliases-list
|
||||
(append eshell-command-aliases-list
|
||||
+eshell-aliases)))
|
||||
(add-hook 'eshell-alias-load-hook #'+eshell|init-aliases)
|
||||
|
||||
(when (featurep! :feature evil +everywhere)
|
||||
(add-hook 'eshell-mode-hook #'+eshell|init-evil))
|
||||
|
||||
(defun +eshell|init-keymap ()
|
||||
"Setup eshell keybindings. This must be done in a hook because eshell-mode
|
||||
redefines its keys every time `eshell-mode' is enabled."
|
||||
(map! :map eshell-mode-map
|
||||
:n [return] #'+eshell/goto-end-of-prompt
|
||||
:n "c" #'+eshell/evil-change
|
||||
:n "C" #'+eshell/evil-change-line
|
||||
:n "d" #'+eshell/evil-delete
|
||||
:n "D" #'+eshell/evil-delete-line
|
||||
:i [tab] #'+eshell/pcomplete
|
||||
:i "C-j" #'evil-window-down
|
||||
:i "C-k" #'evil-window-up
|
||||
:i "C-h" #'evil-window-left
|
||||
:i "C-l" #'evil-window-right
|
||||
:i "C-d" #'+eshell/quit-or-delete-char
|
||||
:i "C-p" #'eshell-previous-input
|
||||
:i "C-n" #'eshell-next-input
|
||||
"C-s" #'+eshell/search-history
|
||||
"C-c s" #'+eshell/split-below
|
||||
"C-c v" #'+eshell/split-right
|
||||
"C-c x" #'+eshell/kill-and-close
|
||||
[remap split-window-below] #'+eshell/split-below
|
||||
[remap split-window-right] #'+eshell/split-right
|
||||
[remap doom/backward-to-bol-or-indent] #'eshell-bol
|
||||
[remap doom/backward-kill-to-bol-and-indent] #'eshell-kill-input
|
||||
[remap evil-window-split] #'+eshell/split-below
|
||||
[remap evil-window-vsplit] #'+eshell/split-right))
|
||||
(add-hook 'eshell-first-time-mode-hook #'+eshell|init-keymap))
|
||||
|
||||
|
||||
(def-package! eshell-up
|
||||
:commands (eshell-up eshell-up-peek))
|
||||
|
||||
|
||||
(def-package! shrink-path
|
||||
:commands shrink-path-file)
|
||||
|
||||
|
||||
(def-package! eshell-z
|
||||
:after eshell
|
||||
:config
|
||||
;; Use zsh's db if it exists, otherwise, store it in `doom-cache-dir'
|
||||
(unless (file-exists-p eshell-z-freq-dir-hash-table-file-name)
|
||||
(setq eshell-z-freq-dir-hash-table-file-name
|
||||
(expand-file-name "z" eshell-directory-name))))
|
6
modules/emacs/eshell/packages.el
Normal file
6
modules/emacs/eshell/packages.el
Normal file
|
@ -0,0 +1,6 @@
|
|||
;; -*- no-byte-compile: t; -*-
|
||||
;;; emacs/eshell/packages.el
|
||||
|
||||
(package! eshell-up)
|
||||
(package! eshell-z)
|
||||
(package! shrink-path)
|
11
modules/emacs/imenu/config.el
Normal file
11
modules/emacs/imenu/config.el
Normal file
|
@ -0,0 +1,11 @@
|
|||
;;; emacs/imenu/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
;; `imenu-anywhere'
|
||||
(setq imenu-anywhere-delimiter ": ")
|
||||
|
||||
|
||||
(after! imenu-list
|
||||
(setq imenu-list-idle-update-delay 0.5)
|
||||
|
||||
(set-popup-rule! "^\\*Ilist"
|
||||
:side 'right :size 35 :quit nil :select nil :ttl 0))
|
|
@ -1,5 +1,5 @@
|
|||
;; -*- no-byte-compile: t; -*-
|
||||
;;; tools/imenu/packages.el
|
||||
;;; emacs/imenu/packages.el
|
||||
|
||||
(package! imenu-anywhere)
|
||||
(package! imenu-list)
|
33
modules/emacs/term/autoload.el
Normal file
33
modules/emacs/term/autoload.el
Normal file
|
@ -0,0 +1,33 @@
|
|||
;;; emacs/term/autoload.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autoload
|
||||
(defun +term/open (arg)
|
||||
"Open a terminal buffer in the current window. If ARG (universal argument) is
|
||||
non-nil, cd into the current project's root."
|
||||
(interactive "P")
|
||||
(let ((default-directory
|
||||
(if arg
|
||||
(or (doom-project-root) default-directory)
|
||||
default-directory)))
|
||||
;; Doom's switch-buffer hooks prevent themselves from triggering when
|
||||
;; switching from buffer A back to A. Because `multi-term' uses `set-buffer'
|
||||
;; before `switch-to-buffer', the hooks don't trigger, so we use this
|
||||
;; roundabout way to trigger them properly.
|
||||
(switch-to-buffer (save-window-excursion (multi-term)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +term/open-popup (arg)
|
||||
"Open a terminal popup window. If ARG (universal argument) is
|
||||
non-nil, cd into the current project's root."
|
||||
(interactive "P")
|
||||
(let ((default-directory
|
||||
(if arg
|
||||
(or (doom-project-root) default-directory)
|
||||
default-directory)))
|
||||
(pop-to-buffer (save-window-excursion (multi-term)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +term/open-popup-in-project ()
|
||||
"Open a terminal popup window in the root of the current project."
|
||||
(interactive)
|
||||
(+term/open-popup t))
|
8
modules/emacs/term/config.el
Normal file
8
modules/emacs/term/config.el
Normal file
|
@ -0,0 +1,8 @@
|
|||
;;; emacs/term/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
;; `multi-term'
|
||||
(setq multi-term-dedicated-window-height 20
|
||||
multi-term-switch-after-close 'PREVIOUS)
|
||||
|
||||
;; `term' (built-in)
|
||||
(add-hook 'term-mode-hook #'doom|mark-buffer-as-real)
|
|
@ -1,4 +1,4 @@
|
|||
;; -*- no-byte-compile: t; -*-
|
||||
;;; tools/term/packages.el
|
||||
;;; emacs/term/packages.el
|
||||
|
||||
(package! multi-term)
|
8
modules/emacs/vc/autoload/evil.el
Normal file
8
modules/emacs/vc/autoload/evil.el
Normal file
|
@ -0,0 +1,8 @@
|
|||
;;; emacs/vc/autoload/evil.el -*- lexical-binding: t; -*-
|
||||
;;;###if (featurep! :feature evil)
|
||||
|
||||
;;;###autoload (autoload '+vc:git-browse "emacs/vc/autoload/evil" nil t)
|
||||
(evil-define-command +vc:git-browse (bang)
|
||||
"Ex interface to `+vc/git-browse-region-or-line'."
|
||||
(interactive "<!>")
|
||||
(+vc/git-browse-region-or-line bang))
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue