Merge branch 'develop'

* develop: (173 commits)
  v2.0.6 bump
  Add zunit file template & file extension
  Remove +workspace/cleanup (doom/cleanup-buffers is better)
  Remove org/org dependency on doom-themes
  General refactor & docstring updates
  Revert "Preload modules before compiling #219"
  Fix 'variable reference to nil’ compiler warning
  Fix markdown specific keybindings being added to global map
  Fix bad doom/other-popup alias breaking which-key #223
  Preload modules before compiling #219
  Popup library: minor refactor & appease byte-compiler
  Fix compile error caused by defunct keybinding #219
  Remove org/org-notebook
  Add org unit tests to init.test.el
  Add unit test file template
  Fix modeline duplication in buffer-file-name :help-echo
  org/org: new command +org/remove-link, bound to C-c C-S-l
  org/org: add tests for +org/insert-item
  Add test helper macros to test.el lib
  +org/insert-item: fix one-too-many prepended newlines in 1st-level headers
  ...
This commit is contained in:
Henrik Lissner
2017-10-06 02:39:17 +02:00
126 changed files with 2900 additions and 2247 deletions

View File

@ -10,6 +10,7 @@ before_install:
env: env:
- EVM_EMACS=emacs-25.1-travis - EVM_EMACS=emacs-25.1-travis
- EVM_EMACS=emacs-25.2-travis - EVM_EMACS=emacs-25.2-travis
- EVM_EMACS=emacs-25.3-travis
script: script:
- emacs --version - emacs --version
- make test - make test

View File

@ -1,7 +1,7 @@
#+TITLE: Changelog #+TITLE: Changelog
- [[#todo][Todo]] - [[#unreleased-develop][Unreleased (develop)]]
- [[#unreleased-master][Unreleased (master)]] - [[#206-oct-05-2017][2.0.6 (Oct 05, 2017)]]
- [[#205-sep-03-2017][2.0.5 (Sep 03, 2017)]] - [[#205-sep-03-2017][2.0.5 (Sep 03, 2017)]]
- [[#204-jul-14-2017][2.0.4 (Jul 14, 2017)]] - [[#204-jul-14-2017][2.0.4 (Jul 14, 2017)]]
- [[#203-jun-11-2017][2.0.3 (Jun 11, 2017)]] - [[#203-jun-11-2017][2.0.3 (Jun 11, 2017)]]
@ -9,72 +9,116 @@
- [[#201-apr-8-2017][2.0.1 (Apr 8, 2017)]] - [[#201-apr-8-2017][2.0.1 (Apr 8, 2017)]]
- [[#200-jan-17-2017][2.0.0 (Jan 17, 2017)]] - [[#200-jan-17-2017][2.0.0 (Jan 17, 2017)]]
* Todo * Unreleased (develop)
+ *Potential plugins:*
+ =app/present= [[https://github.com/larstvei/Focus][focus]], for presenting code
+ [[https://github.com/emacs-lsp/lsp-mode][lsp-mode]], client for MS Language Server Protocol, keep an eye on this
+ =lang/javascript= [[https://github.com/NicolasPetton/Indium][indium]] (IDE), keep an eye on this
+ =lang/javascript= [[https://github.com/codesuki/add-node-modules-path][add-node-modules-path]] (adds node_modules to ~exec-path~)
+ =lang/javascript= [[https://github.com/lbolla/emacs-flycheck-flow][flycheck-flow]] (Flow support for JS)
+ =lang/org= [[https://github.com/Malabarba/latex-extra][orgit]] (org links to magit buffers)
+ =lang/org= [[https://github.com/jkitchin/org-ref][org-ref]] (bibtex/citation helper)
+ =lang/org= [[https://github.com/tashrifsanil/org-easy-img-insert][org-easy-img-insert]]
+ =lang/latex= [[https://github.com/Malabarba/latex-extra][latex-extra]] (utility commands)
+ =lang/latex= [[**https://github.com/jsinglet/latex-preview-pane][latex-preview-pane]]
+ =lang/julia= [[ https://github.com/dennisog/julia-shell-mode][julia-shell]] (unsure if better than inferior-julia in julia-mode)
+ =lang/python= [[https://github.com/Wilfred/pyimport][pyimport]] or [[https://github.com/anachronic/importmagic.el][importmagic]]
+ [[https://github.com/mhayashi1120/Emacs-imagex][emacs-imagex]], for manipulating images at point (zooming?)
+ =tools/term= [[https://github.com/riscy/shx-for-emacs][shx]], an extension for the shell in Emacs
+ =app/crm= [[https://github.com/skeeto/emacsql][emacsql]], a sqlite backend; possibly useful for CRM storage.
+ =core= [[https://github.com/Wilfred/helpful][helpful]], a better help buffer; replacement for ~describe-function~?
+ *Planned modules:*
+ =app/crm= -- Customer Relations Management, in Emacs, using org-mode.
+ =app/write= -- Make Emacs into a focused plaintext word processor (using markdown, org and rst) for writing papers and stories.
+ =app/regex= -- PCRE IDE, with live buffer matching, search/replace support, and an export-to-code feature for various languages.
+ +Perl backend+
+ Search and replace support
+ Highlight replaced segments
+ Export-to-code feature for:
+ python (use ~re~ or ~regex~)
+ php (~preg_(match(_all)?|replace)~)
+ ruby (~%r[.+]~)
+ javascript (node) (~/.+/.test(...)~)
+ C (~regex.h~ + ~regcomp~)
+ C++ (~regex reg(regexp, ...)~)
+ Syntax highlighter for ~+regex-mode~ (plus make it a major mode)
+ Optimize: communicate with perl process (with ~make-process~ instead of ~call-process~)
+ =org/org-publish= -- publishing org files to HTML (thanks to [[https://github.com/matthewgraybosch][matthewgraybosch]])
+ =org/org-attach= -- my own, simpler attachment system with drag-drop image attachment support and centralized storage.
+ =app/torrents= -- Emacs as a torrent client (powered by transmission.el)
+ =core-ui= Replace or fix ~winner-mode~ unreliability (will close windows trying to revive killed buffers). Perhaps make ~doom/kill-this-buffer~ only disassociate buffer from persp-mode or bury buffer if persp-mode is inactive.
+ =org=
+ Better shackle + org-agenda integration
+ Fix janky visual line motions (~evil-next-visual-line~, etc)
+ Fix janky cursor positioning when jumping between org-table cells from insert mode.
+ Certain characters/keys--when typed in a table--cause the cell to shrink (likely cause: custom self-insert-char behavior -- like smartparens pairs & custom SPC/BKSPC binds)
+ =feature/jump= Automatic etags generation (for dwim go-to-definition and, perhaps, code-completion for some languages; lua maybe?).
+ =lang/php= Automatic and async tags generation using [[https://github.com/xcwen/phpctags][phpctags]].
+ =lang/lua= True, dynamic code-completion? Looks like [[https://github.com/immerrr/lua-mode/pull/119][this PR in lua-mode]] may have the answer. Does it make ~company-lua~ redundant?
+ =tools/upload= Add ~+upload/open-remote-file~ command to open current file on the remote (with TRAMP).
+ Add =bin/org-alert= script -- a cron script that scans TODOs in org files and dispatches system alerts.
+ =feature/workspaces= Add a bookmarks feature, but for wconfs, that can revive file buffers. Also needs an interface.
+ =ui/doom-modeline=
+ Fix hardcoded spacing in between segments.
+ Fix ~0/0~ leftover panel in modeline (caused by lingering anzu state).
+ Update =bin/org-capture= to read from stdin in the absence of arguments.
+ =core-popups= Add support for moving popup windows to the ~+evil/window-move-*~ commands #171
* Unreleased (master) * 2.0.6 (Oct 05, 2017)
+ =doom= + *Module changes:*
+ Added new module: ~lang/ledger~, for editing ledger files. + Add =lang/ledger=
+ Fixed ~make update~ to work even if Doom is installed somewhere other than ~\~/.emacs.d~ (see [[https://github.com/hlissner/doom-emacs/issues/190][#190]]). + Add =ui/vi-tilde-fringe= -- used to be in =core-ui=; indicates beyond-EOB,
using tildes in the fringe (inspired by vim).
+ Add =feature/services= -- used to be =tools/prodigy=. Adds a way of managing
external processes and services.
+ Add =tools/make= -- for running project Makefile commands from Emacs.
+ Add =tools/imenu= -- adds a sidebar for imenu (~imenu-list~), and a way of
jumping to imenu entries across all open buffers (~imenu-anywhere~).
+ Move =feature/hydra= into =core-keybinds=.
+ Rename =feature/debug= to =feature/debugger= (and disabled it by default; it
is currently unstable and needs some work).
+ Remove =org/org-notebook=. It was unused and too small to warrant its own
module. Useful tidbits were merged into =org/org=.
+ =general=
+ =Makefile=
+ Fix ~make update~ to work even if Doom is installed somewhere other than
~\~/.emacs.d~ (see [[https://github.com/hlissner/doom-emacs/issues/190][#190]]).
+ Removed colons from makefile task target names (like =compile:core=);
replaced them with dashses, e.g. =compile-core=. Colons broke compatibility
with certain versions of make.
+ =autoload=
+ New library: =menu.el= -- allows context-sensitive and customizable
fuzzy-searchable menus; this was written to replace long lists of
major-mode-local key bindings, like refactoring and code building
commands. This replaces =feature/eval='s build task system.
+ =editor.el= Fix old scratch buffer commands and renamed them:
~doom/open-scratch-buffer~ and ~doom/open-project-scratch-buffer~. The
former opens a temporary, transient scratch buffer, the latter opens a
permanent one tied to the current project, kept in
~doom-scratch-files-dir~.
+ =window.el= Changed ~doom-resize-window~ to accept two more arguments,
=WINDOW= and =FORCE-P=: ~doom-resize-window WINDOW NEW-SIZE &optional
HORIZONTAL FORCE-P~. If =FORCE-P= is non-nil, this function will resize a
window regardless of ~window-size-fixed~.
+ =core-keybinds= Add new =def-hydra!= alias macro for ~defhydra~ (for
consistency, and in case we want to wrap it later).
+ =core-projects= Redesign ~def-project-mode!~ for efficiency, and:
+ The =:init FORM= property is now =:on-load FORM=.
+ Three new properties: =:on-enter FORM=, =:on-exit FORM= and =:add-hooks
LIST=.
+ =core-popups=
+ Added two new popup properties:
+ ~:static~ If non-nil, treat this popup like a permanent window, making
it impervious to automatic closing and being tracked in popup history.
This is excellent for semi-permanent popups, like sidebars (think
Neotree or imenu-list).
+ ~:autofit~ If non-nil, this popup will resize to fit its buffer
contents. This only works with popups where the buffer content is
immediately available, and not for, say, buffers tied to async
processes.
+ ~doom-popup-buffer~ and ~doom-popup-file~ no longer take a variadic
argument. Their signature is now ~doom-popup-buffer buffer plist &optional
extend-p~ and ~doom-popup-file file plist &optional extend-p~, where
=EXTEND-P= will cause =PLIST= to extend from the base rule for that
buffer.
+ Rename ~doom-popup-prop~ to ~doom-popup-property~.
+ Add support for moving popup windows. See the ~doom/popup-move-*~
commands. There are used by ~+evil/window-move-*~, which provides
universal support for moving windows.
+ Add command: ~doom/popup-raise~, for promoting a popup into a regular
window.
+ Add helper macro: ~save-popup! BODY~ -- hides the popups before running
BODY.
+ Fix ~doom/popup-toggle~ and ~save-popups!~ killing popups with an
=:autokill= property.
+ =feature= + =feature=
+ =hydra= Display a separator along the bottom of hydra windows for extra contrast. + =hydra= Display a separator on the bottom of hydra windows for contrast.
+ =eval= Build-task management has been removed from =feature/eval= in favor
of ~def-menu!~.
+ =ui= + =ui=
+ =doom-dashboard= Elements are now centered using window-local margins, which fixes discrepancies when multiple dashboards are visible in different sized windows and/or frames (see [[https://github.com/hlissner/doom-emacs/issues/192][#192]]). + =doom-dashboard=
+ Fix /horizontal/ centering discrepancies caused by multiple visible
dashboards in windows/frames with different sizes (see [[https://github.com/hlissner/doom-emacs/issues/192][#192]]). Still
doesn't address vertical centering.
+ Fix dashboard's default-directory not changing to the last open project
when switched to.
+ =doom-modeline= Add a new style to ~+doom-modeline-buffer-file-name-style~:
~relative-from-project~, which displays on the buffer's path relative to
(and including) the project.
+ =hl-todo= Add face-based detection for commented regions, so hl-todo can
work in modes with no/poor syntax-table support.
+ =tools=
+ =neotree=
+ Fix neotree shrinking by 1 when vertical splits were closed.
+ Fix Neotree popup rule not taking ~neo-window-width~ and
~neo-window-position~ into account.
+ =term= Renamed commands for consistency (to ~+term/open~, ~+term/open-popup~
and ~+term/open-popup-in-project~).
+ =eshell= Renamed commands for consistency (to ~+eshell/open~,
~+eshell/open-popup~ and ~+eshell/open-workspace~).
+ =lang=
+ =ruby= Add rake support. See the ~rake~ command.
+ =web= Only install company-web if =:completion company= is enabled.
+ =javascript=
+ Add eslint_d and eslint_d-fix detection and support.
+ =./node_modules/.bin= is now added to ~exec-path~ in NPM project buffers.
+ =haskell= There is no longer a 'default' implementation for Haskell. The
=+intero= and/or =+dante= module flags must be specified in init.el.
+ =java= Meghanada is no longer the 'default' implementation for Java. The
=+meghanada= and/or =+eclim= module flags must be specified in init.el.
+ =org= + =org=
+ If a table is under point when ~+org/toggle-fold~ is invoked, the table is realigned. + If a table is under point when ~+org/toggle-fold~ is invoked, the table is
+ =org-capture= Fix a vestigial reference to a long-since-renamed function: ~doom/project-root~. realigned.
+ Fix the incorrect version of org being loaded (site, instead of ELPA) by
pushing it up further in the ~load-path~.
+ Fix ~+org/insert-item~ not jumping over sublists to append a new list item.
* 2.0.5 (Sep 03, 2017) * 2.0.5 (Sep 03, 2017)
+ =doom= + =doom=
@ -150,7 +194,7 @@
+ Unit-tests have been moved to their respective modules (and =core/test/=). + Unit-tests have been moved to their respective modules (and =core/test/=).
+ Fix ~def-setting!~ to act more like ~defmacro~; don't aggressively evaluate its arguments on expansion. + Fix ~def-setting!~ to act more like ~defmacro~; don't aggressively evaluate its arguments on expansion.
+ New function: ~doom-set-buffer-real BUFFER FLAG~ -- makes Doom consider BUFFER real, no matter what. + New function: ~doom-set-buffer-real BUFFER FLAG~ -- makes Doom consider BUFFER real, no matter what.
+ Add ~INSTALLED-ONLY-P~ argument to ~doom-get-packages~ to filter packages that aren't installed. + Add INSTALLED-ONLY-P argument to ~doom-get-packages~ to filter packages that aren't installed.
+ =core-ui= + =core-ui=
+ Add quit confirmation when trying to close a frame that contains real buffers. + Add quit confirmation when trying to close a frame that contains real buffers.
+ Fix quit confirmations for clients connected to ~emacs --daemon~ with ~emacsclient~. + Fix quit confirmations for clients connected to ~emacs --daemon~ with ~emacsclient~.
@ -170,7 +214,7 @@
+ =core-packages= + =core-packages=
+ Generalize ~doom-package-*-p~ functions into ~(doom-package-prop NAME PROPERTY)~. + Generalize ~doom-package-*-p~ functions into ~(doom-package-prop NAME PROPERTY)~.
+ Fix quelpa temporary files (in ~quelpa-build-dir~) not being removed when a quelpa package was uninstalled. + Fix quelpa temporary files (in ~quelpa-build-dir~) not being removed when a quelpa package was uninstalled.
+ New hook: ~doom-reload-hook~ (sort of). This has been around for a while, but now it is defined and documented. It runs when ~doom/reload~ is called (which gets called remotely if you run package management while an Emacs session is active). + New hook: ~doom-reload-hook~ (sort of). This has been around for a while, but now it is defined and documented. It runs when ~doom/reload-load-path~ is called (which gets called remotely if you run package management while an Emacs session is active).
+ ~load!~ can now accept a string as its first argument (the path). + ~load!~ can now accept a string as its first argument (the path).
+ =feature= + =feature=
+ =feature/evil= + =feature/evil=
@ -354,7 +398,7 @@
+ =feature= + =feature=
+ =feature/eval= + =feature/eval=
+ Fix ~:repl~ & ~+eval/repl-send-region~. + Fix ~:repl~ & ~+eval/repl-send-region~.
+ Fix ~+eval/region~ failing only on first invocation because ~+eval-runners-alist~ wasn't populated until quickrun is loaded. + Fix ~+eval/region~ failing only on first invocation because ~+eval-runners~ wasn't populated until quickrun is loaded.
+ Add TAB auto-completion in comint-mode and REPL buffers + Add TAB auto-completion in comint-mode and REPL buffers
+ =feature/evil= + =feature/evil=
+ Fix ~:mv~ & ~:rm~. + Fix ~:mv~ & ~:rm~.

View File

@ -7,6 +7,15 @@ MODULES=$(patsubst modules/%, %, $(shell find modules/ -maxdepth 2 -type d))
all: autoloads autoremove install all: autoloads autoremove install
## Aliases
a: autoloads
i: install
u: update
r: autoremove
c: compile
cc: compile-core
ce: compile-elpa
## Package management ## Package management
install: init.el .local/autoloads.el install: init.el .local/autoloads.el
@$(EMACS) -f doom/packages-install @$(EMACS) -f doom/packages-install
@ -23,21 +32,21 @@ autoloads: init.el
## Byte compilation ## Byte compilation
# compile # compile
# compile:core # compile-core
# compile:module # compile-module
# compile:module/submodule # compile-module/submodule
compile: init.el clean compile: init.el clean
@$(EMACS) -f doom/compile @$(EMACS) -f doom/compile
compile\:core: init.el clean compile-core: init.el clean
@$(EMACS) -f doom/compile -- init.el core @$(EMACS) -f doom/compile -- init.el core
compile\:elpa: init.el compile-elpa: init.el
@$(EMACS) -f doom/recompile-packages @$(EMACS) -f doom/recompile-packages
$(patsubst %, compile\:%, $(MODULES)): init.el .local/autoloads.el $(patsubst %, compile-%, $(MODULES)): init.el .local/autoloads.el
@rm -fv $(shell find $(patsubst compile:%, modules/%, $@) -type f -name '*.elc') @rm -fv $(shell find $(patsubst compile-%, modules/%, $@) -type f -name '*.elc')
@$(EMACS) -f doom/compile -- $(patsubst compile:%, modules/%, $@) @$(EMACS) -f doom/compile -- $(patsubst compile-%, modules/%, $@)
recompile: init.el recompile: init.el
@$(EMACS) -f doom/recompile @$(EMACS) -f doom/recompile
@ -54,14 +63,14 @@ reset:
## Unit tests ## Unit tests
# test # test
# test:core # test-core
# test:module # test-module
# test:module/submodule # test-module/submodule
test: init.el .local/autoloads.el test: init.el .local/autoloads.el
@$(EMACS) -f doom-run-tests @$(EMACS) -f doom-run-tests
test\:core $(patsubst %, test\:%, $(MODULES)): init.el .local/autoloads.el test-core $(patsubst %, test-%, $(MODULES)): init.el .local/autoloads.el
@$(EMACS) -f doom-run-tests -- $(subst test:, , $@) @$(EMACS) -f doom-run-tests -- $(subst test-, , $@)
# run tests interactively # run tests interactively
testi: init.el .local/autoloads.el testi: init.el .local/autoloads.el

View File

@ -9,20 +9,35 @@
set -e set -e
key="${1:-n}"
cleanup() { cleanup() {
emacsclient --eval '(kill-emacs)' emacsclient --eval '(kill-emacs)'
} }
# If emacs isn't running, we start a temporary daemon, solely for this window.
daemon=
if ! pgrep emacs >/dev/null; then if ! pgrep emacs >/dev/null; then
emacs --daemon emacs --daemon
trap cleanup EXIT INT TERM trap cleanup EXIT INT TERM
daemon=1
fi fi
# TODO Allow piping from stdin # org-capture key mapped to argument flags
keys=$(emacsclient -e "(+org-capture-available-keys)" | cut -d '"' -f2)
while getopts $keys opt; do
key="\"$opt\""
break
done
shift $((OPTIND-1))
emacsclient -c \ [ -t 0 ] && str="$*" || str=$(cat)
-F '((name . "org-capture") (width . 70) (height . 25))' \
--eval "(+org-capture/dwim \"$2\" \"$key\")"
if [[ $daemon ]]; then
emacsclient -a "" \
-c -F '((name . "org-capture") (width . 70) (height . 25))' \
-e "(+org-capture/open-frame \"$str\" ${key:-nil})"
else
# Non-daemon servers flicker a lot if frames are created from terminal, so
# we do it internally instead.
emacsclient -a "" \
-e "(+org-capture/open-frame \"$str\" ${key:-nil})"
fi

View File

@ -53,7 +53,7 @@ Inspired from http://demonastery.org/2013/04/emacs-evil-narrow-region/"
If no project is active, return all buffers." If no project is active, return all buffers."
(let ((buffers (doom-buffer-list))) (let ((buffers (doom-buffer-list)))
(if-let (project-root (doom-project-root t)) (if-let (project-root (if (doom-project-p) (doom-project-root)))
(cl-loop for buf in buffers (cl-loop for buf in buffers
if (projectile-project-buffer-p buf project-root) if (projectile-project-buffer-p buf project-root)
collect buf) collect buf)
@ -116,9 +116,9 @@ buffers. If there's nothing left, switch to `doom-fallback-buffer'. See
(project-dir (doom-project-root))) (project-dir (doom-project-root)))
(cond ((or (not buffers) (cond ((or (not buffers)
(zerop (% n (1+ (length buffers))))) (zerop (% n (1+ (length buffers)))))
(set-window-buffer nil (doom-fallback-buffer))) (switch-to-buffer (doom-fallback-buffer) nil t))
((= (length buffers) 1) ((= (length buffers) 1)
(set-window-buffer nil (car buffers))) (switch-to-buffer (car buffers) nil t))
(t (t
;; Why this instead of switching straight to the Nth buffer in ;; Why this instead of switching straight to the Nth buffer in
;; BUFFERS? Because `switch-to-next-buffer' and ;; BUFFERS? Because `switch-to-next-buffer' and
@ -136,9 +136,19 @@ buffers. If there's nothing left, switch to `doom-fallback-buffer'. See
;;;###autoload ;;;###autoload
(defun doom-real-buffer-p (&optional buffer-or-name) (defun doom-real-buffer-p (&optional buffer-or-name)
"Returns t if BUFFER-OR-NAME is a 'real' buffer. Real means it a) isn't a "Returns t if BUFFER-OR-NAME is a 'real' buffer. The complete criteria for a
popup window/buffer and b) isn't a special buffer." real buffer is:
(let ((buf (window-normalize-buffer buffer-or-name)))
1. The buffer-local value of `doom-real-buffer-p' (variable) is non-nil OR
2. Any function in `doom-real-buffer-functions' must return non-nil when
passed this buffer OR
3. The current buffer:
a) has a `buffer-file-name' defined AND
b) is not in a popup window (see `doom-popup-p') AND
c) is not a special buffer (its name isn't something like *Help*)
If BUFFER-OR-NAME is omitted or nil, the current buffer is tested."
(when-let (buf (ignore-errors (window-normalize-buffer buffer-or-name)))
(or (buffer-local-value 'doom-real-buffer-p buf) (or (buffer-local-value 'doom-real-buffer-p buf)
(run-hook-with-args-until-success 'doom-real-buffer-functions buf) (run-hook-with-args-until-success 'doom-real-buffer-functions buf)
(not (or (doom-popup-p buf) (not (or (doom-popup-p buf)
@ -148,14 +158,14 @@ popup window/buffer and b) isn't a special buffer."
;;;###autoload ;;;###autoload
(defun doom/next-buffer () (defun doom/next-buffer ()
"Switch to the next real buffer, skipping special buffers. See "Switch to the next real buffer, skipping non-real buffers. See
`doom-real-buffer-p' for what 'real' means." `doom-real-buffer-p' for what 'real' means."
(interactive) (interactive)
(doom--cycle-real-buffers +1)) (doom--cycle-real-buffers +1))
;;;###autoload ;;;###autoload
(defun doom/previous-buffer () (defun doom/previous-buffer ()
"Switch to the previous real buffer, skipping special buffers. See "Switch to the previous real buffer, skipping non-real buffers. See
`doom-real-buffer-p' for what 'real' means." `doom-real-buffer-p' for what 'real' means."
(interactive) (interactive)
(doom--cycle-real-buffers -1)) (doom--cycle-real-buffers -1))
@ -163,7 +173,10 @@ popup window/buffer and b) isn't a special buffer."
;;;###autoload ;;;###autoload
(defun doom-kill-buffer (&optional buffer dont-save) (defun doom-kill-buffer (&optional buffer dont-save)
"Kill BUFFER (falls back to current buffer if omitted) then switch to a real "Kill BUFFER (falls back to current buffer if omitted) then switch to a real
buffer, but only bury the buffer if it is present in another window. buffer. If the buffer is present in another window, only bury it.
Will prompt to save unsaved buffers when attempting to kill them, unless
DONT-SAVE is non-nil.
See `doom-real-buffer-p' for what 'real' means." See `doom-real-buffer-p' for what 'real' means."
(setq buffer (or buffer (current-buffer))) (setq buffer (or buffer (current-buffer)))
@ -250,10 +263,12 @@ regex PATTERN. Returns the number of killed buffers."
;;;###autoload ;;;###autoload
(defun doom/kill-all-buffers (&optional project-p) (defun doom/kill-all-buffers (&optional project-p)
"Kill all buffers. "Kill all buffers and closes their windows.
If PROJECT-P, kill all buffers that belong to the current project." If PROJECT-P (universal argument), kill only buffers that belong to the current
project."
(interactive "P") (interactive "P")
(doom/popup-kill-all)
(let ((buffers (if project-p (doom-project-buffer-list) (doom-buffer-list)))) (let ((buffers (if project-p (doom-project-buffer-list) (doom-buffer-list))))
(mapc #'doom-kill-buffer-and-windows buffers) (mapc #'doom-kill-buffer-and-windows buffers)
(unless (doom-real-buffer-p) (unless (doom-real-buffer-p)
@ -262,10 +277,10 @@ If PROJECT-P, kill all buffers that belong to the current project."
;;;###autoload ;;;###autoload
(defun doom/kill-other-buffers (&optional project-p) (defun doom/kill-other-buffers (&optional project-p)
"Kill all other buffers. "Kill all other buffers (besides the current one).
If PROJECT-P (universal argument), kill only the other buffers that belong to If PROJECT-P (universal argument), kill only buffers that belong to the current
the current project." project."
(interactive "P") (interactive "P")
(let ((buffers (if project-p (doom-project-buffer-list) (doom-buffer-list))) (let ((buffers (if project-p (doom-project-buffer-list) (doom-buffer-list)))
(current-buffer (current-buffer))) (current-buffer (current-buffer)))
@ -291,7 +306,7 @@ project."
;;;###autoload ;;;###autoload
(defun doom/cleanup-buffers (&optional all-p) (defun doom/cleanup-buffers (&optional all-p)
"Clean up buried and process buffers in the current workspace." "Clean up buried and inactive process buffers in the current workspace."
(interactive "P") (interactive "P")
(let ((buffers (doom-buried-buffers (if all-p (buffer-list)))) (let ((buffers (doom-buried-buffers (if all-p (buffer-list))))
(n 0)) (n 0))

View File

@ -7,18 +7,13 @@
Interactively prints the list to the echo area. Noninteractively, returns a list Interactively prints the list to the echo area. Noninteractively, returns a list
whose car is the list of faces and cadr is the list of overlay faces." whose car is the list of faces and cadr is the list of overlay faces."
(interactive) (interactive)
(unless pos (let* ((pos (or pos (point)))
(setq pos (point))) (faces (let ((face (get-text-property pos 'face)))
(let ((faces (let ((face (get-text-property pos 'face)))
(if (keywordp (car-safe face)) (if (keywordp (car-safe face))
(list face) (list face)
(cl-loop for f in (if (listp face) face (list face)) (cl-loop for f in (doom-enlist face) collect f))))
collect f))))
(overlays (cl-loop for ov in (overlays-at pos (1+ pos)) (overlays (cl-loop for ov in (overlays-at pos (1+ pos))
nconc (cl-loop with face = (overlay-get ov 'face) nconc (doom-enlist (overlay-get ov 'face)))))
for f in (if (listp face) face (list face))
collect f))))
(cond ((called-interactively-p 'any) (cond ((called-interactively-p 'any)
(message "%s %s\n%s %s" (message "%s %s\n%s %s"
(propertize "Faces:" 'face 'font-lock-comment-face) (propertize "Faces:" 'face 'font-lock-comment-face)
@ -63,6 +58,8 @@ selection of all minor-modes, active or not."
"Test to see if your root certificates are securely configured in emacs." "Test to see if your root certificates are securely configured in emacs."
(declare (interactive-only t)) (declare (interactive-only t))
(interactive) (interactive)
(unless (string-match-p "\\_<GNUTLS\\_>" system-configuration-features)
(warn "gnutls support isn't built into Emacs, there may be problems"))
(if-let (bad-hosts (if-let (bad-hosts
(cl-loop for bad (cl-loop for bad
in '("https://wrong.host.badssl.com/" in '("https://wrong.host.badssl.com/"

View File

@ -15,21 +15,19 @@
(interactive) (interactive)
(doom/sudo-find-file (file-truename buffer-file-name))) (doom/sudo-find-file (file-truename buffer-file-name)))
(defun doom--goto-first-non-blank ()
(beginning-of-visual-line)
(skip-chars-forward " \t\r"))
;;;###autoload ;;;###autoload
(defun doom/backward-to-bol-or-indent () (defun doom/backward-to-bol-or-indent ()
"Move back to the current line's indentation. If already there, move to the "Move back to the current line's indentation. If already there, move to the
beginning of the line instead. If at bol, do nothing." beginning of the line instead. If at bol, do nothing."
(interactive) (interactive)
(let ((boi (save-excursion (back-to-indentation) (point))) (if (bound-and-true-p visual-line-mode)
(point (point)))
(if (= boi point)
(beginning-of-visual-line) (beginning-of-visual-line)
(unless (= (line-beginning-position) point) (let ((ci (current-indentation))
(doom--goto-first-non-blank))))) (cc (current-column)))
(cond ((or (> cc ci) (= cc 0))
(back-to-indentation))
((<= cc ci)
(beginning-of-visual-line))))))
;;;###autoload ;;;###autoload
(defun doom/forward-to-last-non-comment-or-eol () (defun doom/forward-to-last-non-comment-or-eol ()
@ -80,22 +78,28 @@ If already there, do nothing."
(interactive) (interactive)
(if indent-tabs-mode (if indent-tabs-mode
(call-interactively #'backward-delete-char) (call-interactively #'backward-delete-char)
(unless (bolp)
(save-excursion (save-excursion
(unless (looking-back "^[\s\t]*" (line-beginning-position)) (when (> (current-column) (current-indentation))
(doom--goto-first-non-blank)) (back-to-indentation))
(let* ((movement (% (current-column) tab-width)) (let ((movement (% (current-column) tab-width)))
(spaces (if (= 0 movement) tab-width (- tab-width movement)))) (delete-char
(delete-char (- spaces)))))) (- (if (= 0 movement)
tab-width
(- tab-width movement)))))))))
;;;###autoload ;;;###autoload
(defun doom/backward-kill-to-bol-and-indent () (defun doom/backward-kill-to-bol-and-indent ()
"Kill line to the first non-blank character. If invoked again "Kill line to the first non-blank character. If invoked again
afterwards, kill line to column 1." afterwards, kill line to column 1."
(interactive) (interactive)
(let ((empty-line (save-excursion (beginning-of-line) (looking-at-p "[ \t]*$")))) (let ((empty-line-p (save-excursion (beginning-of-line)
(funcall (if (featurep 'evil) #'evil-delete #'delete-region) (looking-at-p "[ \t]*$"))))
(funcall (if (featurep 'evil)
#'evil-delete
#'delete-region)
(point-at-bol) (point)) (point-at-bol) (point))
(unless empty-line (unless empty-line-p
(indent-according-to-mode)))) (indent-according-to-mode))))
;;;###autoload ;;;###autoload
@ -131,7 +135,9 @@ possible, or just one char if that's not possible."
(save-match-data (save-match-data
(if (string-match "\\w*\\(\\s-+\\)$" (if (string-match "\\w*\\(\\s-+\\)$"
(buffer-substring-no-properties (max (point-min) (- p movement)) p)) (buffer-substring-no-properties (max (point-min) (- p movement)) p))
(sp-delete-char (- 0 (- (match-end 1) (match-beginning 1)))) (sp-delete-char
(- 0 (- (match-end 1)
(match-beginning 1))))
(call-interactively delete-backward-char))))) (call-interactively delete-backward-char)))))
;; Otherwise do a regular delete ;; Otherwise do a regular delete
@ -157,18 +163,19 @@ spaces on either side of the point if so. Resorts to
`doom/backward-delete-whitespace-to-column' otherwise." `doom/backward-delete-whitespace-to-column' otherwise."
(interactive) (interactive)
(save-match-data (save-match-data
(cond ((doom--surrounded-p) (if (doom--surrounded-p)
(let ((whitespace-match (match-string 1))) (let ((whitespace-match (match-string 1)))
(cond ((not whitespace-match) (cond ((not whitespace-match)
(call-interactively #'delete-backward-char)) (call-interactively #'delete-backward-char))
((string-match "\n" whitespace-match) ((string-match "\n" whitespace-match)
(funcall (if (featurep 'evil) #'evil-delete #'delete-region) (funcall (if (featurep 'evil)
#'evil-delete
#'delete-region)
(point-at-bol) (point)) (point-at-bol) (point))
(call-interactively #'delete-backward-char) (call-interactively #'delete-backward-char)
(save-excursion (call-interactively #'delete-char))) (save-excursion (call-interactively #'delete-char)))
(t (just-one-space 0))))) (t (just-one-space 0))))
(t (doom/backward-delete-whitespace-to-column))))
(doom/backward-delete-whitespace-to-column)))))
;;;###autoload ;;;###autoload
(defun doom/newline-and-indent () (defun doom/newline-and-indent ()
@ -179,15 +186,16 @@ with weak native support."
(cond ((sp-point-in-string) (cond ((sp-point-in-string)
(newline)) (newline))
((sp-point-in-comment) ((sp-point-in-comment)
(cond ((memq major-mode '(js2-mode rjsx-mode)) (pcase major-mode
((or 'js2-mode 'rjsx-mode)
(call-interactively #'js2-line-break)) (call-interactively #'js2-line-break))
((memq major-mode '(java-mode php-mode)) ((or 'java-mode 'php-mode)
(c-indent-new-comment-line)) (c-indent-new-comment-line))
((memq major-mode '(c-mode c++-mode objc-mode css-mode scss-mode js2-mode)) ((or 'c-mode 'c++-mode 'objc-mode 'css-mode 'scss-mode 'js2-mode)
(newline-and-indent) (newline-and-indent)
(insert "* ") (insert "* ")
(indent-according-to-mode)) (indent-according-to-mode))
(t (_
;; Fix an off-by-one cursor-positioning issue ;; Fix an off-by-one cursor-positioning issue
;; with `indent-new-comment-line' ;; with `indent-new-comment-line'
(let ((col (save-excursion (comment-beginning) (current-column)))) (let ((col (save-excursion (comment-beginning) (current-column))))
@ -210,47 +218,8 @@ consistent throughout a selected region, depending on `indent-tab-mode'."
(tabify beg end) (tabify beg end)
(untabify beg end))) (untabify beg end)))
;;;###autoload
(defun doom/toggle-sticky (&optional beg end)
"Make a selection sticky by placing it in the header line. Possibly helpful
for function signatures or notes. Run again to clear the header line."
(interactive "r")
(setq header-line-format
(when mark-active
(concat (propertize (format linum-format (line-number-at-pos beg))
'face 'font-lock-comment-face)
(let ((content (buffer-substring beg end)))
(setq content (replace-regexp-in-string "\n" " " content t t))
(setq content (replace-regexp-in-string "\\s-+" " " content))
content)))))
;;;###autoload
(defun doom/scratch-buffer (&optional arg)
"Opens the scratch buffer in a popup window.
If ARG (universal argument) is non-nil, open it in the current window instead of
a popup.
If a region is active, copy it into the scratch buffer."
(interactive "P")
(let ((text (and (region-active-p)
(buffer-substring-no-properties
(region-beginning) (region-end))))
(mode major-mode)
(derived-p (derived-mode-p 'prog-mode 'text-mode))
(old-project (doom-project-root))
(new-buf (get-buffer-create "*doom:scratch*")))
(if arg
(switch-to-buffer new-buf)
(doom-popup-buffer new-buf))
(with-current-buffer new-buf
(setq default-directory old-project)
(when (and (not (eq major-mode mode))
derived-p
(functionp mode))
(funcall mode))
(if text (insert text)))))
;;;###autoload ;;;###autoload
(defun doom|enable-delete-trailing-whitespace () (defun doom|enable-delete-trailing-whitespace ()
"Attaches `delete-trailing-whitespace' to a buffer-local `before-save-hook'."
(add-hook 'before-save-hook #'delete-trailing-whitespace nil t)) (add-hook 'before-save-hook #'delete-trailing-whitespace nil t))

View File

@ -6,7 +6,7 @@
(interactive (interactive
;; TODO try to read setting from whole line ;; TODO try to read setting from whole line
(list (completing-read "Describe setting%s: " (list (completing-read "Describe setting%s: "
(mapcar #'car doom-settings) (sort (mapcar #'car doom-settings) #'string-lessp)
nil t nil nil))) nil t nil nil)))
(let ((fn (cdr (assq (intern setting) doom-settings)))) (let ((fn (cdr (assq (intern setting) doom-settings))))
(unless fn (unless fn

104
core/autoload/menu.el Normal file
View File

@ -0,0 +1,104 @@
;;; ../core/autoload/menu.el -*- lexical-binding: t; -*-
;; Command dispatchers: basically M-x, but context sensitive, customizable and
;; persistent across Emacs sessions.
(defvar doom-menu-display-fn #'doom-menu-read-default
"The method to use to prompt the user with the menu. This takes two arguments:
PROMPT (a string) and COMMAND (a list of command plists; see `def-menu!').")
(defun doom-menu-read-default (prompt commands)
"Default method for displaying a completion-select prompt."
(completing-read prompt (mapcar #'car commands)))
(defun doom--menu-read (prompt commands)
(if-let (choice (funcall doom-menu-display-fn prompt commands))
(cdr (assoc choice commands))
(user-error "Aborted")))
(defun doom--menu-exec (plist)
(let ((command (plist-get plist :exec))
(cwd (plist-get plist :cwd)))
(let ((default-directory
(cond ((eq cwd t) (doom-project-root))
((stringp cwd) cwd)
(t default-directory))))
(cond ((stringp command)
(with-current-buffer (get-buffer-create "*compilation*")
(setq command (doom-resolve-vim-path command))
(save-window-excursion
(compile command))
(setq header-line-format
(concat (propertize "$ " 'face 'font-lock-doc-face)
(propertize command 'face 'font-lock-preprocessor-face)))
(doom-resize-window
(doom-popup-buffer (current-buffer)
'(:autokill t :autoclose t)) 12)))
((or (symbolp command)
(functionp command))
(call-interactively command))
((and command (listp command))
(eval command t))
(t
(error "Not a valid command: %s" command))))))
;;;###autoload
(defmacro def-menu! (name desc commands &rest plist)
"Defines a menu and returns a function symbol for invoking it.
A dispatcher is an interactive command named NAME (a symbol). When called, this
dispatcher prompts you to select a command to run. This list is filtered
depending on its properties. Each command is takes the form of:
(DESCRIPTION :exec COMMAND &rest PROPERTIES)
PROPERTIES accepts the following properties:
:when FORM
:unless FORM
:region BOOL
:cwd t|PATH
:project BOOL|DIRECTORY
COMMAND can be a string (a shell command), a symbol (an elisp function) or a
lisp form.
`def-menu!'s PLIST supports the following properties:
:prompt STRING"
(declare (indent defun) (doc-string 2))
(let ((commands-var (intern (format "%s-commands" name)))
(prop-prompt (or (plist-get plist :prompt) "> "))
(prop-sort (plist-get plist :sort)))
`(progn
(defvar ,commands-var
,(if prop-sort
`(cl-sort ,commands #'string-lessp :key #'car)
commands)
,(format "Menu for %s" name))
(defun ,name ()
,desc
(interactive)
(unless ,commands-var
(user-error "The '%s' menu is empty" ',name))
(doom--menu-exec
(or (doom--menu-read
,prop-prompt
(or (cl-remove-if-not
(let ((project-root (doom-project-root)))
(lambda (cmd)
(let ((plist (cdr cmd)))
(and (cond ((not (plist-member plist :region)) t)
((plist-get plist :region) (use-region-p))
(t (not (use-region-p))))
(let ((when (plist-get plist :when))
(unless (plist-get plist :unless))
(project (plist-get plist :project)))
(or (or (not when) (eval when))
(or (not unless) (not (eval unless)))
(and (stringp project)
(file-in-directory-p buffer-file-name project-root))))))))
,commands-var)
(user-error "No commands available here")))
(user-error "No command selected")))))))

View File

@ -354,7 +354,7 @@ package.el as appropriate."
""))))) "")))))
(message! (bold (green "Finished!"))) (message! (bold (green "Finished!")))
(doom/reload)))) (doom/reload-load-path))))
;;;###autoload ;;;###autoload
(defun doom/packages-update () (defun doom/packages-update ()
@ -395,7 +395,7 @@ package.el as appropriate."
(if result "DONE" "FAILED")))))) (if result "DONE" "FAILED"))))))
(message! (bold (green "Finished!"))) (message! (bold (green "Finished!")))
(doom/reload))))) (doom/reload-load-path)))))
;;;###autoload ;;;###autoload
(defun doom/packages-autoremove () (defun doom/packages-autoremove ()
@ -428,7 +428,7 @@ package.el as appropriate."
pkg))))) pkg)))))
(message! (bold (green "Finished!"))) (message! (bold (green "Finished!")))
(doom/reload))))) (doom/reload-load-path)))))
;;;###autoload ;;;###autoload
(defalias 'doom/install-package #'package-install) (defalias 'doom/install-package #'package-install)

View File

@ -1,158 +1,95 @@
;;; core/autoload/popups.el -*- lexical-binding: t; -*- ;;; core/autoload/popups.el -*- lexical-binding: t; -*-
(defvar doom-popup-remember-history)
;;;###autoload ;;;###autoload
(defun doom-popup-p (&optional target) (defun doom-popup-p (&optional target)
"Return TARGET (a window) if TARGET (a window or buffer) is a popup. Uses "Return t if TARGET (a window or buffer) is a popup. Uses current window if
current window if omitted." omitted."
(when-let (target (or target (selected-window))) (when-let (target (or target (selected-window)))
(cond ((bufferp target) (cond ((bufferp target)
(buffer-local-value 'doom-popup-mode target)) (and (buffer-live-p target)
(buffer-local-value 'doom-popup-mode target)))
((windowp target) ((windowp target)
(and (window-parameter target 'popup) (and (window-live-p target)
target))))) (window-parameter target 'popup))))))
;;;###autoload ;;;###autoload
(defun doom-popup-buffer (buffer &rest plist) (defun doom-popup-buffer (buffer &optional plist extend-p)
"Display BUFFER in a shackle popup. See `shackle-rules' for possible rules. "Display BUFFER in a shackle popup with PLIST rules. See `shackle-rules' for
Returns the new popup window." possible rules. If EXTEND-P is non-nil, don't overwrite the original rules for
this popup, just the specified properties. Returns the new popup window."
(declare (indent defun)) (declare (indent defun))
(unless (bufferp buffer) (unless (bufferp buffer)
(error "%s is not a valid buffer" buffer)) (error "%s is not a valid buffer" buffer))
(setq plist (append plist (shackle-match buffer)))
(shackle-display-buffer (shackle-display-buffer
buffer buffer
nil (or plist (shackle-match buffer)))) nil (or (if extend-p
(append plist (shackle-match buffer))
plist)
(shackle-match buffer))))
;;;###autoload ;;;###autoload
(defun doom-popup-switch-to-buffer (buffer) (defun doom-popup-switch-to-buffer (buffer)
"Switch the current (or closest) pop-up window to BUFFER." "Switch the current (or closest) pop-up window to BUFFER."
(unless (doom-popup-p) (unless (doom-popup-p)
(let ((popups (doom-popup-windows))) (if-let (popups (doom-popup-windows))
(unless popups (select-window (car popups))
(error "No popups to switch")) (error "No popups to switch to")))
(select-window (car popups))))
(set-window-dedicated-p nil nil) (set-window-dedicated-p nil nil)
(switch-to-buffer buffer nil t) (switch-to-buffer buffer nil t)
(prog1 (selected-window) (prog1 (selected-window)
(set-window-dedicated-p nil t))) (set-window-dedicated-p nil t)))
;;;###autoload ;;;###autoload
(defun doom-popup-file (file &rest plist) (defun doom-popup-fit-to-buffer (&optional window max-size)
"Fit WINDOW to the size of its content."
(unless (string-empty-p (buffer-string))
(let* ((window-size (doom-popup-size window))
(max-size (or max-size (doom-popup-property :size window)))
(size (+ 2 (if (floatp max-size) (truncate (* max-size window-size)) window-size))))
(fit-window-to-buffer window size nil size))))
;;;###autoload
(defun doom-popup-move (direction)
"Move a popup window to another side of the frame, in DIRECTION, which can be
one of the following: 'left 'right 'above 'below"
(when (doom-popup-p)
(let ((buffer (current-buffer))
(doom-popup-inhibit-autokill t))
(doom/popup-close)
(doom-popup-buffer buffer `(:align ,direction) 'extend))))
;;;###autoload
(defun doom-popup-file (file &optional plist extend-p)
"Display FILE in a shackle popup, with PLIST rules. See `shackle-rules' for "Display FILE in a shackle popup, with PLIST rules. See `shackle-rules' for
possible rules." possible rules."
(unless (file-exists-p file) (unless (file-exists-p file)
(user-error "Can't display file in popup, it doesn't exist: %s" file)) (user-error "Can't display file in popup, it doesn't exist: %s" file))
(doom-popup-buffer (find-file-noselect file t) plist)) (doom-popup-buffer (find-file-noselect file t) plist extend-p))
;;;###autoload ;;;###autoload
(defun doom-popup-windows () (defun doom-popup-windows (&optional filter-static-p)
"Get a list of open pop up windows." "Get a list of open pop up windows."
(cl-remove-if-not #'doom-popup-p doom-popup-windows)) (cl-loop for window in doom-popup-windows
if (and (doom-popup-p window)
(not (and filter-static-p
(doom-popup-property :static window))))
collect window))
;;;###autoload ;;;###autoload
(defun doom/popup-restore () (defun doom-popup-properties (window-or-buffer)
"Restore the last open popups. If the buffers have been killed, and "Returns a window's popup property list, if possible. The buffer-local
represented real files, they will be restored. Dead special buffers or buffers `doom-popup-rules' always takes priority, but this will fall back to the popup
with non-nil :autokill properties will not be. window parameter."
(cond ((windowp window-or-buffer)
Returns t if popups were restored, nil otherwise." (or (window-parameter window-or-buffer 'popup)
(interactive) (doom-popup-properties (window-buffer window-or-buffer))))
(unless doom-popup-history ((bufferp window-or-buffer)
(error "No popups to restore")) (buffer-local-value 'doom-popup-rules window-or-buffer))))
(let (any-p)
(dolist (spec doom-popup-history)
(let ((buffer (get-buffer (car spec)))
(file (plist-get (cdr spec) :file))
(rules (plist-get (cdr spec) :rules))
(size (plist-get (cdr spec) :size)))
(when (and (not buffer) file)
(setq buffer
(if-let (buf (get-file-buffer file))
(clone-indirect-buffer (buffer-name buf) nil t)
(find-file-noselect file t))))
(when size
(setq rules (plist-put rules :size size)))
(when (and buffer (apply #'doom-popup-buffer buffer rules) (not any-p))
(setq any-p t))))
(when any-p
(setq doom-popup-history '()))
any-p))
;;;###autoload ;;;###autoload
(defun doom/popup-toggle () (defun doom-popup-property (prop &optional window)
"Toggle popups."
(interactive)
(when (doom-popup-p)
(if doom-popup-other-window
(select-window doom-popup-other-window)
(other-window 1)))
(if (doom-popup-windows)
(doom/popup-close-all t)
(doom/popup-restore)))
;;;###autoload
(defun doom/popup-close (&optional window)
"Find and close WINDOW if it's a popup. If WINDOW is omitted, defaults to
`selected-window'. The contained buffer is buried, unless it has an :autokill
property."
(interactive)
(when-let (window (doom-popup-p window))
(delete-window window)))
;;;###autoload
(defun doom/popup-close-all (&optional force-p)
"Closes all open popups. If FORCE-P is non-nil, or this function is called
interactively, it will close all popups without question. Otherwise, it will
only close popups that have an :autoclose property in their rule (see
`shackle-rules')."
(interactive)
(when-let (popups (doom-popup-windows))
(let (success doom-popup-remember-history)
(setq doom-popup-history (mapcar #'doom--popup-data popups))
(dolist (window popups)
(when (or force-p
(called-interactively-p 'interactive)
(doom-popup-prop :autoclose window))
(delete-window window)
(setq success t)))
success)))
;;;###autoload
(defun doom/popup-close-maybe ()
"Close the current popup *if* its window doesn't have a noesc parameter."
(interactive)
(if (doom-popup-prop :noesc)
(call-interactively
(if (featurep 'evil)
#'evil-force-normal-state
#'keyboard-escape-quit))
(delete-window)))
;;;###autoload
(defun doom/popup-this-buffer ()
"Display currently selected buffer in a popup window."
(interactive)
(doom-popup-buffer (current-buffer) :align t :autokill t))
;;;###autoload
(defun doom/popup-toggle-messages ()
"Toggle *Messages* buffer."
(interactive)
(if-let (win (get-buffer-window "*Messages*"))
(doom/popup-close win)
(doom-popup-buffer (get-buffer "*Messages*"))))
;;;###autoload
(defun doom-popup-prop (prop &optional window)
"Returns a `doom-popup-rules' PROPerty from WINDOW." "Returns a `doom-popup-rules' PROPerty from WINDOW."
(or (plist-get (or (if window (or (plist-get (doom-popup-properties (or window (selected-window)))
(ignore-errors
(buffer-local-value 'doom-popup-rules
(window-buffer window)))
doom-popup-rules)
(window-parameter window 'popup))
prop) prop)
(pcase prop (pcase prop
(:size shackle-default-size) (:size shackle-default-size)
@ -161,7 +98,7 @@ only close popups that have an :autoclose property in their rule (see
;;;###autoload ;;;###autoload
(defun doom-popup-side (&optional window) (defun doom-popup-side (&optional window)
"Return what side a popup WINDOW came from ('left 'right 'above or 'below)." "Return what side a popup WINDOW came from ('left 'right 'above or 'below)."
(let ((align (doom-popup-prop :align window))) (let ((align (doom-popup-property :align window)))
(when (eq align t) (when (eq align t)
(setq align shackle-default-alignment)) (setq align shackle-default-alignment))
(when (functionp align) (when (functionp align)
@ -186,11 +123,137 @@ only close popups that have an :autoclose property in their rule (see
(defmacro with-popup-rules! (rules &rest body) (defmacro with-popup-rules! (rules &rest body)
"TODO" "TODO"
(declare (indent defun)) (declare (indent defun))
`(let ((old-shackle-rules shackle-rules)) `(let (shackle-rules)
,@(cl-loop for rule in rules ,@(cl-loop for rule in rules
collect `(set! :popup ,@rule)) collect `(set! :popup ,@rule))
,@body ,@body))
(setq shackle-rules old-shackle-rules)))
;;;###autoload
(defmacro save-popups! (&rest body)
"Sets aside all popups before executing the original function, usually to
prevent the popup(s) from messing up the UI (or vice versa)."
`(let ((in-popup-p (doom-popup-p))
(popups (doom-popup-windows))
(doom-popup-remember-history t)
(doom-popup-inhibit-autokill t))
(when popups
(mapc #'doom/popup-close popups))
(unwind-protect
(progn ,@body)
(when popups
(let ((origin (selected-window)))
(doom/popup-restore)
(unless in-popup-p
(select-window origin)))))))
;; --- Commands ---------------------------
;;;###autoload
(defun doom/popup-restore ()
"Restore the last open popups. If the buffers have been killed, and
represented real files, they will be restored. Dead special buffers or buffers
with non-nil :autokill properties will not be.
Returns t if popups were restored, nil otherwise."
(interactive)
(unless doom-popup-history
(error "No popups to restore"))
(let (any-p)
(dolist (spec doom-popup-history)
(let ((buffer (get-buffer (car spec)))
(file (plist-get (cdr spec) :file))
(rules (plist-get (cdr spec) :rules))
(size (plist-get (cdr spec) :size)))
(when (and (not buffer) file)
(setq buffer
(if-let (buf (get-file-buffer file))
(clone-indirect-buffer (buffer-name buf) nil t)
(find-file-noselect file t))))
(when size
(setq rules (plist-put rules :size size)))
(when (and buffer (doom-popup-buffer buffer rules) (not any-p))
(setq any-p t))))
(when any-p
(setq doom-popup-history '()))
any-p))
;;;###autoload
(defun doom/popup-toggle ()
"Toggle popups on and off. If used outside of popups (and popups are
available), it will select the nearest popup window."
(interactive)
(when (doom-popup-p)
(if doom-popup-other-window
(select-window doom-popup-other-window)
(other-window 1)))
(if (doom-popup-windows t)
(let ((doom-popup-inhibit-autokill t))
(doom/popup-close-all t))
(doom/popup-restore)))
;;;###autoload
(defun doom/popup-close (&optional window)
"Find and close WINDOW if it's a popup. If WINDOW is omitted, defaults to
`selected-window'. The contained buffer is buried, unless it has an :autokill
property."
(interactive)
(when (doom-popup-p window)
(delete-window (or window (selected-window)))))
;;;###autoload
(defun doom/popup-close-all (&optional force-p)
"Closes most open popups.
Does not close popups that are :static or don't have an :autoclose property (see
`shackle-rules').
If FORCE-P is non-nil (or this function is called interactively), ignore popups'
:autoclose property. This command will never close :static popups."
(interactive
(list (called-interactively-p 'interactive)))
(when-let (popups (doom-popup-windows t))
(let (success doom-popup-remember-history)
(setq doom-popup-history (delq nil (mapcar #'doom--popup-data popups)))
(dolist (window popups success)
(when (or force-p (doom-popup-property :autoclose window))
(delete-window window)
(setq success t))))))
;;;###autoload
(defun doom/popup-kill-all ()
"Like `doom/popup-close-all', but kill *all* popups, including :static ones,
without leaving any trace behind (muahaha)."
(interactive)
(when-let (popups (doom-popup-windows))
(let (doom-popup-remember-history)
(setq doom-popup-history nil)
(mapc #'delete-window popups))))
;;;###autoload
(defun doom/popup-close-maybe ()
"Close the current popup *if* its window doesn't have a noesc parameter."
(interactive)
(if (doom-popup-property :noesc)
(call-interactively
(if (featurep 'evil)
#'evil-force-normal-state
#'keyboard-escape-quit))
(delete-window)))
;;;###autoload
(defun doom/popup-this-buffer ()
"Display currently selected buffer in a popup window."
(interactive)
(doom-popup-buffer (current-buffer) '(:align t :autokill t)))
;;;###autoload
(defun doom/popup-toggle-messages ()
"Toggle *Messages* buffer."
(interactive)
(if-let (win (get-buffer-window "*Messages*"))
(doom/popup-close win)
(doom-popup-buffer (get-buffer "*Messages*"))))
;;;###autoload ;;;###autoload
(defun doom/other-popup (count) (defun doom/other-popup (count)
@ -207,3 +270,155 @@ only close popups that have an :autoclose property in their rule (see
(cl-decf count)) (cl-decf count))
(when (/= count 0) (when (/= count 0)
(other-window count))))) (other-window count)))))
;;;###autoload
(defalias 'other-popup #'doom/other-popup)
;;;###autoload
(defun doom/popup-raise (&optional window)
"Turn a popup window into a normal window."
(interactive)
(let ((window (or window (selected-window))))
(unless (doom-popup-p window)
(user-error "Not a valid popup to raise"))
(with-selected-window window
(doom-popup-mode -1))))
;;;###autoload
(defun doom/popup-move-top () "See `doom-popup-move'." (interactive) (doom-popup-move 'above))
;;;###autoload
(defun doom/popup-move-bottom () "See `doom-popup-move'." (interactive) (doom-popup-move 'below))
;;;###autoload
(defun doom/popup-move-left () "See `doom-popup-move'." (interactive) (doom-popup-move 'left))
;;;###autoload
(defun doom/popup-move-right () "See `doom-popup-move'." (interactive) (doom-popup-move 'right))
;; --- doom-popup-mode --------------------
;;;###autoload
(define-minor-mode doom-popup-mode
"Minor mode for popup windows."
:init-value nil
:keymap doom-popup-mode-map
(let ((window (selected-window)))
;; If `doom-popup-rules' isn't set for some reason, try to set it
(setq-local doom-popup-rules (doom-popup-properties window))
;; Ensure that buffer-opening functions/commands (like
;; `switch-to-buffer-other-window' won't use this window).
(set-window-parameter window 'no-other-window doom-popup-mode)
;; Makes popup window resist interactively changing its buffer.
(set-window-dedicated-p window doom-popup-mode)
(cond (doom-popup-mode
(when doom-popup-no-fringes
(set-window-fringes window 0 0 fringes-outside-margins))
;; Save metadata into window parameters so it can be saved by window
;; config persisting plugins like workgroups or persp-mode.
(set-window-parameter window 'popup (or doom-popup-rules t))
(when doom-popup-rules
(cl-loop for param in doom-popup-window-parameters
when (plist-get doom-popup-rules param)
do (set-window-parameter window param it))))
(t
(when doom-popup-no-fringes
(set-window-fringes window
doom-fringe-size doom-fringe-size
fringes-outside-margins))
;; Ensure window parameters are cleaned up
(set-window-parameter window 'popup nil)
(dolist (param doom-popup-window-parameters)
(set-window-parameter window param nil))))))
(put 'doom-popup-mode 'permanent-local t)
;;;###autoload
(defun doom|hide-modeline-in-popup ()
"Don't show modeline in popup windows without a :modeline rule. If one exists
and it's a symbol, use `doom-modeline' to grab the format. If non-nil, show the
mode-line as normal. If nil (or omitted, by default), then hide the modeline
entirely."
(if doom-popup-mode
(let ((modeline (plist-get doom-popup-rules :modeline)))
(cond ((or (eq modeline 'nil)
(not modeline))
(doom-hide-modeline-mode +1))
((and (symbolp modeline)
(not (eq modeline 't)))
(setq-local doom--modeline-format (doom-modeline modeline))
(when doom--modeline-format
(doom-hide-modeline-mode +1)))))
(when doom-hide-modeline-mode
(doom-hide-modeline-mode -1))))
;; --- Advice functions -------------------
;;;###autoload
(defun doom*shackle-always-align (plist)
"Ensure popups are always aligned and selected by default. Eliminates the need
for :align t on every rule."
(when plist
(unless (or (plist-member plist :align)
(plist-member plist :same)
(plist-member plist :frame))
(plist-put plist :align t))
(unless (or (plist-member plist :select)
(plist-member plist :noselect))
(plist-put plist :select t)))
plist)
;;;###autoload
(defun doom*popup-init (orig-fn &rest args)
"Initializes a window as a popup window by enabling `doom-popup-mode' in it
and setting `doom-popup-rules' within it. Returns the window."
(unless (doom-popup-p)
(setq doom-popup-other-window (selected-window)))
(let* ((target (car args))
(plist (or (nth 2 args)
(cond ((windowp target)
(and (window-live-p target)
(shackle-match (window-buffer target))))
((bufferp target)
(and (buffer-live-p target)
(shackle-match target))))))
(buffer (get-buffer target))
(window-min-height (if (plist-get plist :modeline) 4 2))
window)
(when (and (doom-real-buffer-p buffer)
(get-buffer-window-list buffer nil t))
(setq plist (append (list :autokill t) plist))
(setcar args (clone-indirect-buffer (buffer-name target) nil t)))
(unless (setq window (apply orig-fn args))
(error "No popup window was found for %s: %s" target plist))
(cl-pushnew window doom-popup-windows :test #'eq)
(with-selected-window window
(unless (eq plist t)
(setq-local doom-popup-rules plist))
(doom-popup-mode +1)
(when (plist-get plist :autofit)
(doom-popup-fit-to-buffer window)))
window))
;;;###autoload
(defun doom*popups-save (orig-fn &rest args)
"Sets aside all popups before executing the original function, usually to
prevent the popup(s) from messing up the UI (or vice versa)."
(save-popups! (apply orig-fn args)))
;;;###autoload
(defun doom*delete-popup-window (&optional window)
"Ensure that popups are deleted properly, and killed if they have :autokill
properties."
(or window (setq window (selected-window)))
(when (doom-popup-p window)
(setq doom-popup-windows (delq window doom-popup-windows))
(when doom-popup-remember-history
(setq doom-popup-history (list (doom--popup-data window))))
(let ((autokill-p (and (not doom-popup-inhibit-autokill)
(doom-popup-property :autokill window))))
(with-selected-window window
(doom-popup-mode -1)
(when autokill-p
(when-let (process (get-buffer-process (current-buffer)))
(set-process-query-on-exit-flag process nil))
(kill-buffer (current-buffer)))))))

53
core/autoload/scratch.el Normal file
View File

@ -0,0 +1,53 @@
;;; core/autoload/scratch.el -*- lexical-binding: t; -*-
(defvar doom-scratch-files-dir (concat doom-etc-dir "scratch/")
"Where to store project scratch files, created by
`doom/open-project-scratch-buffer'.")
(defvar doom-scratch-buffer-hook ()
"The hooks to run after a scratch buffer is made.")
(defun doom--create-scratch-buffer (&optional project-p)
(let ((text (and (region-active-p)
(buffer-substring-no-properties
(region-beginning) (region-end))))
(mode major-mode)
(derived-p (derived-mode-p 'prog-mode 'text-mode))
(old-project (doom-project-root)))
(unless (file-directory-p doom-scratch-files-dir)
(mkdir doom-scratch-files-dir t))
(with-current-buffer
(if project-p
(find-file-noselect
(expand-file-name (replace-regexp-in-string
"\\." "_" (projectile-project-name)
t t)
doom-scratch-files-dir)
nil t)
(get-buffer-create "*doom:scratch*"))
(when project-p
(rename-buffer (format "*doom:scratch (%s)*" (projectile-project-name))))
(setq default-directory old-project)
(when (and (not (eq major-mode mode))
derived-p
(functionp mode))
(funcall mode))
(if text (insert text))
(run-hooks 'doom-scratch-buffer-hook)
(current-buffer))))
;;;###autoload
(defun doom/open-scratch-buffer ()
"Opens a temporary scratch buffer in a popup window. It is discarded once it
is closed. If a region is active, copy it to the scratch buffer."
(interactive)
(doom-popup-buffer (doom--create-scratch-buffer)))
;;;###autoload
(defun doom/open-project-scratch-buffer ()
"Opens a (persistent) scratch buffer associated with the current project in a
popup window. Scratch buffers are stored in `doom-scratch-files-dir'. If a
region is active, copy it to the scratch buffer."
(interactive)
(doom-popup-buffer (doom--create-scratch-buffer t)))

View File

@ -1,19 +1,5 @@
;;; core/autoload/test.el -*- lexical-binding: t; -*- ;;; core/autoload/test.el -*- lexical-binding: t; -*-
;;;###autoload
(defmacro def-test! (name &rest body)
"Define a namespaced ERT test."
(declare (indent defun) (doc-string 2))
(unless (plist-get body :disabled)
`(ert-deftest
,(cl-loop with path = (file-relative-name (file-name-sans-extension load-file-name)
doom-emacs-dir)
for (rep . with) in '(("/test/" . "/") ("/" . ":"))
do (setq path (replace-regexp-in-string rep with path t t))
finally return (intern (format "%s::%s" path name))) ()
()
,@body)))
;;;###autoload ;;;###autoload
(defun doom-run-tests (&optional modules) (defun doom-run-tests (&optional modules)
"Run all loaded tests, specified by MODULES (a list of module cons cells) or "Run all loaded tests, specified by MODULES (a list of module cons cells) or
@ -83,3 +69,78 @@ If neither is available, run all tests in all enabled modules."
(lwarn 'doom-test :error (lwarn 'doom-test :error
"%s -> %s" "%s -> %s"
(car ex) (error-message-string ex))))) (car ex) (error-message-string ex)))))
;; --- Test helpers -----------------------
(defmacro def-test! (name &rest body)
"Define a namespaced ERT test."
(declare (indent defun) (doc-string 2))
(unless (plist-get body :disabled)
`(ert-deftest
,(cl-loop with path = (file-relative-name (file-name-sans-extension load-file-name)
doom-emacs-dir)
for (rep . with) in '(("/test/" . "/") ("/" . ":"))
do (setq path (replace-regexp-in-string rep with path t t))
finally return (intern (format "%s::%s" path name))) ()
()
,@body)))
(defmacro should-buffer! (initial expected &rest body)
"Test that a buffer with INITIAL text, run BODY, then test it against EXPECTED.
INITIAL will recognize cursor markers in the form {[0-9]}. A {0} marker marks
where the cursor should be after setup. Otherwise, the cursor will be placed at
`point-min'.
EXPECTED will recognize one (optional) cursor marker: {|}, this is the
'expected' location of the cursor after BODY is finished, and will be tested
against."
(declare (indent 2))
`(with-temp-buffer
(cl-loop for line in ',initial
do (insert line "\n"))
(goto-char (point-min))
(let (marker-list)
(save-excursion
(while (re-search-forward "{\\([0-9]\\)}" nil t)
(push (cons (match-string 1)
(set-marker (make-marker) (match-beginning 0)))
marker-list)
(replace-match "" t t))
(if (not marker-list)
(goto-char (point-min))
(sort marker-list
(lambda (m1 m2) (< (marker-position m1)
(marker-position m2))))
(when (equal (caar marker-list) "0")
(goto-char! 0)))
,@body
(let ((result-text (buffer-substring-no-properties (point-min) (point-max)))
(point (point))
same-point
expected-text)
(with-temp-buffer
(cl-loop for line in ',expected
do (insert line "\n"))
(save-excursion
(goto-char 1)
(when (re-search-forward "{|}" nil t)
(setq same-point (= point (match-beginning 0)))
(replace-match "" t t)))
(setq expected-text (buffer-substring-no-properties (point-min) (point-max)))
(should (equal expected-text result-text))
(should same-point)))))))
(defmacro goto-char! (index)
"Meant to be used with `should-buffer!'. Will move the cursor to one of the
cursor markers. e.g. Go to marker {2} with (goto-char! 2)."
`(goto-char (point! ,index)))
(defmacro point! (index)
"Meant to be used with `should-buffer!'. Returns the position of a cursor
marker. e.g. {2} can be retrieved with (point! 2)."
`(cdr (assoc ,(cond ((numberp index) (number-to-string index))
((symbolp index) (symbol-name index))
((stringp index) index))
marker-list)))

View File

@ -26,10 +26,13 @@
(error "No line number plugin detected")))) (error "No line number plugin detected"))))
;;;###autoload ;;;###autoload
(defun doom-resize-window (new-size &optional horizontal) (defun doom-resize-window (window new-size &optional horizontal force-p)
"Resize a window to NEW-SIZE. If HORIZONTAL, do it width-wise." "Resize a window to NEW-SIZE. If HORIZONTAL, do it width-wise.
If FORCE-P is omitted when `window-size-fixed' is non-nil, resizing will fail."
(with-selected-window (or window (selected-window))
(let ((window-size-fixed (unless force-p window-size-fixed)))
(enlarge-window (- new-size (if horizontal (window-width) (window-height))) (enlarge-window (- new-size (if horizontal (window-width) (window-height)))
horizontal)) horizontal))))
;;;###autoload ;;;###autoload
(defun doom/window-zoom () (defun doom/window-zoom ()
@ -52,8 +55,8 @@ window changes before then, the undo expires."
(assoc ?_ register-alist)) (assoc ?_ register-alist))
(ignore (jump-to-register ?_)) (ignore (jump-to-register ?_))
(window-configuration-to-register ?_) (window-configuration-to-register ?_)
(doom-resize-window (truncate (/ (frame-width) 1.2)) t) (doom-resize-window nil (truncate (/ (frame-width) 1.2)) t)
(doom-resize-window (truncate (/ (frame-height) 1.2))) (doom-resize-window nil (truncate (/ (frame-height) 1.2)))
t))) t)))
;;;###autoload ;;;###autoload

View File

@ -155,7 +155,6 @@ with functions that require it (like modeline segments)."
(setq recentf-save-file (concat doom-cache-dir "recentf") (setq recentf-save-file (concat doom-cache-dir "recentf")
recentf-max-menu-items 0 recentf-max-menu-items 0
recentf-max-saved-items 300 recentf-max-saved-items 300
recentf-filename-handlers '(abbreviate-file-name)
recentf-exclude recentf-exclude
(list "^/tmp/" "^/ssh:" "\\.?ido\\.last$" "\\.revive$" "/TAGS$" (list "^/tmp/" "^/ssh:" "\\.?ido\\.last$" "\\.revive$" "/TAGS$"
"^/var/folders/.+$" "^/var/folders/.+$"
@ -173,7 +172,6 @@ with functions that require it (like modeline segments)."
;; specify their own formatting rules. ;; specify their own formatting rules.
(def-package! editorconfig (def-package! editorconfig
:demand t :demand t
:mode ("\\.?editorconfig$" . editorconfig-conf-mode)
:init :init
(def-setting! :editorconfig (action value) (def-setting! :editorconfig (action value)
":add or :remove an entry in `editorconfig-indentation-alist'." ":add or :remove an entry in `editorconfig-indentation-alist'."
@ -201,6 +199,9 @@ with functions that require it (like modeline segments)."
(whitespace-mode +1)))) (whitespace-mode +1))))
(add-hook 'editorconfig-custom-hooks #'doom|editorconfig-whitespace-mode-maybe)) (add-hook 'editorconfig-custom-hooks #'doom|editorconfig-whitespace-mode-maybe))
(def-package! editorconfig-conf-mode
:mode "\\.?editorconfig$")
;; Auto-close delimiters and blocks as you type ;; Auto-close delimiters and blocks as you type
(def-package! smartparens (def-package! smartparens
:demand t :demand t
@ -269,11 +270,6 @@ with functions that require it (like modeline segments)."
:commands (describe-buffer describe-command describe-file :commands (describe-buffer describe-command describe-file
describe-keymap describe-option describe-option-of-type)) describe-keymap describe-option describe-option-of-type))
(def-package! imenu-anywhere
:commands (ido-imenu-anywhere ivy-imenu-anywhere helm-imenu-anywhere))
(def-package! imenu-list :commands imenu-list-minor-mode)
(def-package! pcre2el :commands rxt-quote-pcre) (def-package! pcre2el :commands rxt-quote-pcre)
(def-package! smart-forward (def-package! smart-forward

View File

@ -36,6 +36,60 @@
(add-hook 'doom-init-hook #'which-key-mode)) (add-hook 'doom-init-hook #'which-key-mode))
(def-package! hydra
:demand t
:init
;; In case I later need to wrap defhydra in any special functionality.
(defalias 'def-hydra! 'defhydra)
(defalias 'def-hydra-radio! 'defhydradio)
:config
(setq lv-use-seperator t)
(def-hydra! doom@text-zoom (:hint t :color red)
"
Text zoom: _j_:zoom in, _k_:zoom out, _0_:reset
"
("j" text-scale-increase "in")
("k" text-scale-decrease "out")
("0" (text-scale-set 0) "reset"))
(def-hydra! doom@window-nav (:hint nil)
"
Split: _v_ert _s_:horz
Delete: _c_lose _o_nly
Switch Window: _h_:left _j_:down _k_:up _l_:right
Buffers: _p_revious _n_ext _b_:select _f_ind-file
Resize: _H_:splitter left _J_:splitter down _K_:splitter up _L_:splitter right
Move: _a_:up _z_:down _i_menu
"
("z" scroll-up-line)
("a" scroll-down-line)
("i" idomenu)
("h" windmove-left)
("j" windmove-down)
("k" windmove-up)
("l" windmove-right)
("p" doom/previous-buffer)
("n" doom/next-buffer)
("b" switch-to-buffer)
("f" find-file)
("s" split-window-below)
("v" split-window-right)
("c" delete-window)
("o" delete-other-windows)
("H" hydra-move-splitter-left)
("J" hydra-move-splitter-down)
("K" hydra-move-splitter-up)
("L" hydra-move-splitter-right)
("q" nil)))
;; ;;
(defun doom--keybind-register (key desc &optional modes) (defun doom--keybind-register (key desc &optional modes)
"Register a description for KEY with `which-key' in MODES. "Register a description for KEY with `which-key' in MODES.

View File

@ -29,7 +29,7 @@
;; Helpers ;; Helpers
;; ;;
(defun doom--resolve-paths (paths &optional root) (defun doom--resolve-path-forms (paths &optional root)
(cond ((stringp paths) (cond ((stringp paths)
`(file-exists-p `(file-exists-p
(expand-file-name (expand-file-name
@ -39,10 +39,10 @@
(or root `(doom-project-root)))))) (or root `(doom-project-root))))))
((listp paths) ((listp paths)
(cl-loop for i in paths (cl-loop for i in paths
collect (doom--resolve-paths i root))) collect (doom--resolve-path-forms i root)))
(t paths))) (t paths)))
(defun doom--resolve-hooks (hooks) (defun doom--resolve-hook-forms (hooks)
(cl-loop with quoted-p = (eq (car-safe hooks) 'quote) (cl-loop with quoted-p = (eq (car-safe hooks) 'quote)
for hook in (doom-enlist (doom-unquote hooks)) for hook in (doom-enlist (doom-unquote hooks))
if (eq (car-safe hook) 'quote) if (eq (car-safe hook) 'quote)
@ -61,6 +61,81 @@
"Return EXP wrapped in a list, or as-is if already a list." "Return EXP wrapped in a list, or as-is if already a list."
(if (listp exp) exp (list exp))) (if (listp exp) exp (list exp)))
(defun doom-resolve-vim-path (file-name)
"Take a path and resolve any vim-like filename modifiers in it. This adds
support for these special modifiers:
%:P Resolves to `doom-project-root'.
See http://vimdoc.sourceforge.net/htmldoc/cmdline.html#filename-modifiers."
(let* (case-fold-search
(regexp (concat "\\(?:^\\|[^\\\\]\\)"
"\\([#%]\\)"
"\\(\\(?::\\(?:[PphtreS~.]\\|g?s[^:\t\n ]+\\)\\)*\\)"))
(matches
(cl-loop with i = 0
while (and (< i (length file-name))
(string-match regexp file-name i))
do (setq i (1+ (match-beginning 0)))
and collect
(cl-loop for j to (/ (length (match-data)) 2)
collect (match-string j file-name)))))
(dolist (match matches)
(let ((flags (split-string (car (cdr (cdr match))) ":" t))
(path (and buffer-file-name
(pcase (car (cdr match))
("%" (file-relative-name buffer-file-name))
("#" (save-excursion (other-window 1) (file-relative-name buffer-file-name))))))
flag global)
(if (not path)
(setq path "")
(while flags
(setq flag (pop flags))
(when (string-suffix-p "\\" flag)
(setq flag (concat flag (pop flags))))
(when (string-prefix-p "gs" flag)
(setq global t
flag (substring flag 1)))
(setq path
(or (pcase (substring flag 0 1)
("p" (expand-file-name path))
("~" (concat "~/" (file-relative-name path "~")))
("." (file-relative-name path default-directory))
("t" (file-name-nondirectory (directory-file-name path)))
("r" (file-name-sans-extension path))
("e" (file-name-extension path))
("S" (shell-quote-argument path))
("h"
(let ((parent (file-name-directory (expand-file-name path))))
(unless (equal (file-truename path)
(file-truename parent))
(if (file-name-absolute-p path)
(directory-file-name parent)
(file-relative-name parent)))))
("s"
(if (featurep 'evil)
(when-let (args (evil-delimited-arguments (substring flag 1) 2))
(let ((pattern (evil-transform-vim-style-regexp (car args)))
(replace (cadr args)))
(replace-regexp-in-string
(if global pattern (concat "\\(" pattern "\\).*\\'"))
(evil-transform-vim-style-regexp replace) path t t
(unless global 1))))
path))
("P"
(let ((default-directory (file-name-directory (expand-file-name path))))
(abbreviate-file-name (doom-project-root))))
(_ path))
"")))
;; strip trailing slash, if applicable
(when (and (not (string= path "")) (equal (substring path -1) "/"))
(setq path (substring path 0 -1))))
(setq file-name
(replace-regexp-in-string (format "\\(?:^\\|[^\\\\]\\)\\(%s\\)"
(regexp-quote (string-trim-left (car match))))
path file-name t t 1))))
(replace-regexp-in-string regexp "\\1" file-name t)))
;; ;;
;; Library ;; Library
@ -153,7 +228,7 @@ Body forms can access the hook's arguments through the let-bound variable
(:append (setq append-p t)) (:append (setq append-p t))
(:local (setq local-p t)) (:local (setq local-p t))
(:remove (setq hook-fn 'remove-hook)))) (:remove (setq hook-fn 'remove-hook))))
(let ((hooks (doom--resolve-hooks (pop args))) (let ((hooks (doom--resolve-hook-forms (pop args)))
(funcs (funcs
(let ((val (car args))) (let ((val (car args)))
(if (memq (car-safe val) '(quote function)) (if (memq (car-safe val) '(quote function))
@ -199,11 +274,11 @@ Body forms can access the hook's arguments through the let-bound variable
(not ,mode) (not ,mode)
(and buffer-file-name (not (file-remote-p buffer-file-name))) (and buffer-file-name (not (file-remote-p buffer-file-name)))
,(if match `(if buffer-file-name (string-match-p ,match buffer-file-name)) t) ,(if match `(if buffer-file-name (string-match-p ,match buffer-file-name)) t)
,(if files (doom--resolve-paths files) t) ,(if files (doom--resolve-path-forms files) t)
,(or pred-form t)) ,(or pred-form t))
(,mode 1))) (,mode 1)))
,@(if (and modes (listp modes)) ,@(if (and modes (listp modes))
(cl-loop for hook in (doom--resolve-hooks modes) (cl-loop for hook in (doom--resolve-hook-forms modes)
collect `(add-hook ',hook ',hook-name)) collect `(add-hook ',hook ',hook-name))
`((add-hook 'after-change-major-mode-hook ',hook-name)))))) `((add-hook 'after-change-major-mode-hook ',hook-name))))))
(match (match
@ -213,11 +288,10 @@ Body forms can access the hook's arguments through the let-bound variable
;; I'm a fan of concise, hassle-free front-facing configuration. Rather than ;; I'm a fan of concise, hassle-free front-facing configuration. Rather than
;; littering my config with `after!' blocks, these two macros offer a faster and ;; littering my config with `after!' blocks, and checking if features and
;; more robust alternative. The motivation: to facilitate concise cross-module ;; modules are loaded before every line of config, I wrote `set!' as a more
;; configuration. ;; robust alternative. If a setting doesn't exist at run-time, the `set!' call
;; ;; is ignored. It also benefits from byte-compilation.
;; It also benefits from byte-compilation.
(defvar doom-settings nil) (defvar doom-settings nil)
(defmacro def-setting! (keyword arglist &optional docstring &rest forms) (defmacro def-setting! (keyword arglist &optional docstring &rest forms)

View File

@ -76,7 +76,7 @@ missing) and shouldn't be deleted.")
"A list of packages that should be ignored by `def-package!'.") "A list of packages that should be ignored by `def-package!'.")
(defvar doom-reload-hook nil (defvar doom-reload-hook nil
"A list of hooks to run when `doom/reload' is called.") "A list of hooks to run when `doom/reload-load-path' is called.")
(defvar doom--site-load-path load-path (defvar doom--site-load-path load-path
"The load path of built in Emacs libraries.") "The load path of built in Emacs libraries.")
@ -90,7 +90,6 @@ missing) and shouldn't be deleted.")
"A backup of `load-path' before it was altered by `doom-initialize'. Used as a "A backup of `load-path' before it was altered by `doom-initialize'. Used as a
base by `doom!' and for calculating how many packages exist.") base by `doom!' and for calculating how many packages exist.")
(defvar doom--module nil)
(defvar doom--refresh-p nil) (defvar doom--refresh-p nil)
(setq load-prefer-newer (or noninteractive doom-debug-mode) (setq load-prefer-newer (or noninteractive doom-debug-mode)
@ -136,33 +135,27 @@ base by `doom!' and for calculating how many packages exist.")
(defun doom-initialize (&optional force-p) (defun doom-initialize (&optional force-p)
"Initialize installed packages (using package.el) and ensure the core packages "Initialize installed packages (using package.el) and ensure the core packages
are installed. If you byte-compile core/core.el, this function will be avoided are installed.
to speed up startup."
If you byte-compile core/core.el, this function will be avoided to speed up
startup."
;; Called early during initialization; only use native functions! ;; Called early during initialization; only use native functions!
(when (or (not doom-package-init-p) force-p) (when (or (not doom-package-init-p) force-p)
(unless noninteractive
(message "Doom initialized"))
(setq load-path doom--base-load-path (setq load-path doom--base-load-path
package-activated-list nil) package-activated-list nil)
;; Ensure core folders exist ;; Ensure core folders exist
(dolist (dir (list doom-local-dir doom-etc-dir doom-cache-dir package-user-dir)) (dolist (dir (list doom-local-dir doom-etc-dir doom-cache-dir package-user-dir))
(unless (file-directory-p dir) (unless (file-directory-p dir)
(make-directory dir t))) (make-directory dir t)))
(package-initialize t) (package-initialize t)
;; Sure, we could let `package-initialize' fill `load-path', but package ;; We could let `package-initialize' fill `load-path', but it costs precious
;; activation costs precious milliseconds and does other stuff I don't ;; milliseconds and does other stuff I don't need (like load autoload
;; really care about (like load autoload files). My premature optimization ;; files). My premature optimization quota isn't filled yet.
;; quota isn't filled yet.
;; ;;
;; Also, in some edge cases involving package initialization during a ;; Also, in some edge cases involving package initialization during a
;; non-interactive session, `package-initialize' fails to fill `load-path'. ;; non-interactive session, `package-initialize' fails to fill `load-path'.
;; If we want something done right, do it ourselves!
(setq doom--package-load-path (directory-files package-user-dir t "^[^.]" t) (setq doom--package-load-path (directory-files package-user-dir t "^[^.]" t)
load-path (append load-path doom--package-load-path)) load-path (append load-path doom--package-load-path))
;; Ensure core packages are installed ;; Ensure core packages are installed
(dolist (pkg doom-core-packages) (dolist (pkg doom-core-packages)
(unless (package-installed-p pkg) (unless (package-installed-p pkg)
@ -174,11 +167,11 @@ to speed up startup."
(if (package-installed-p pkg) (if (package-installed-p pkg)
(message "Installed %s" pkg) (message "Installed %s" pkg)
(error "Couldn't install %s" pkg)))) (error "Couldn't install %s" pkg))))
(load "quelpa" nil t) (load "quelpa" nil t)
(load "use-package" nil t) (load "use-package" nil t)
(setq doom-package-init-p t)
(setq doom-package-init-p t))) (unless noninteractive
(message "Doom initialized"))))
(defun doom-initialize-autoloads () (defun doom-initialize-autoloads ()
"Ensures that `doom-autoload-file' exists and is loaded. Otherwise run "Ensures that `doom-autoload-file' exists and is loaded. Otherwise run
@ -195,6 +188,7 @@ If FORCE-P is non-nil, do it even if they are.
This aggressively reloads core autoload files." This aggressively reloads core autoload files."
(doom-initialize force-p) (doom-initialize force-p)
(let ((noninteractive t) (let ((noninteractive t)
(load-prefer-newer t)
(load-fn (load-fn
(lambda (file &optional noerror) (lambda (file &optional noerror)
(condition-case-unless-debug ex (condition-case-unless-debug ex
@ -219,9 +213,7 @@ This aggressively reloads core autoload files."
(funcall load-fn (expand-file-name "packages.el" doom-core-dir)) (funcall load-fn (expand-file-name "packages.el" doom-core-dir))
(cl-loop for (module . submodule) in (doom--module-pairs) (cl-loop for (module . submodule) in (doom--module-pairs)
for path = (doom-module-path module submodule "packages.el") for path = (doom-module-path module submodule "packages.el")
do do (funcall load-fn path t)))))
(let ((doom--module (cons module submodule)))
(funcall load-fn path t))))))
(defun doom-initialize-modules (modules) (defun doom-initialize-modules (modules)
"Adds MODULES to `doom-modules'. MODULES must be in mplist format. "Adds MODULES to `doom-modules'. MODULES must be in mplist format.
@ -233,14 +225,10 @@ This aggressively reloads core autoload files."
:rehash-threshold 1.0))) :rehash-threshold 1.0)))
(let (mode) (let (mode)
(dolist (m modules) (dolist (m modules)
(cond ((keywordp m) (cond ((keywordp m) (setq mode m))
(setq mode m)) ((not mode) (error "No namespace specified on `doom!' for %s" m))
((not mode) ((listp m) (doom-module-enable mode (car m) (cdr m)))
(error "No namespace specified on `doom!' for %s" m)) (t (doom-module-enable mode m))))))
((listp m)
(doom-module-enable mode (car m) (cdr m)))
(t
(doom-module-enable mode m))))))
(defun doom-module-path (module submodule &optional file) (defun doom-module-path (module submodule &optional file)
"Get the full path to a module: e.g. :lang emacs-lisp maps to "Get the full path to a module: e.g. :lang emacs-lisp maps to
@ -252,6 +240,12 @@ This aggressively reloads core autoload files."
(expand-file-name (concat module "/" submodule "/" file) (expand-file-name (concat module "/" submodule "/" file)
doom-modules-dir)) doom-modules-dir))
(defun doom-module-from-path (path)
"Get module cons cell (MODULE . SUBMODULE) for PATH, if possible."
(when (string-match (concat doom-modules-dir "\\([^/]+\\)/\\([^/]+\\)/") path)
(cons (intern (concat ":" (match-string 1 path)))
(intern (match-string 2 path)))))
(defun doom-module-flags (module submodule) (defun doom-module-flags (module submodule)
"Returns a list of flags provided for MODULE SUBMODULE." "Returns a list of flags provided for MODULE SUBMODULE."
(and (hash-table-p doom-modules) (and (hash-table-p doom-modules)
@ -288,14 +282,13 @@ added, if the file exists."
if (file-exists-p path) if (file-exists-p path)
collect path)) collect path))
(defun doom--display-benchmark () (defun doom--display-benchmark ()
(message "Loaded %s packages in %.03fs" (message "Doom loaded %s packages across %d modules in %.03fs"
;; Certainly imprecise, especially where custom additions to ;; Certainly imprecise, especially where custom additions to
;; load-path are concerned, but I don't mind a [small] margin of ;; load-path are concerned, but I don't mind a [small] margin of
;; error in the plugin count in exchange for faster startup. ;; error in the plugin count in exchange for faster startup.
(- (length load-path) (length doom--base-load-path)) (- (length load-path) (length doom--base-load-path))
(hash-table-size doom-modules)
(setq doom-init-time (float-time (time-subtract after-init-time before-init-time))))) (setq doom-init-time (float-time (time-subtract after-init-time before-init-time)))))
@ -326,7 +319,8 @@ MODULES is an malformed plist of modules to load."
(unless (server-running-p) (unless (server-running-p)
(server-start))) (server-start)))
(add-hook 'doom-init-hook #'doom--display-benchmark t)))) (add-hook 'doom-init-hook #'doom--display-benchmark t)
(message "Doom modules initialized"))))
(defmacro def-package! (name &rest plist) (defmacro def-package! (name &rest plist)
"A thin wrapper around `use-package'. "A thin wrapper around `use-package'.
@ -406,8 +400,7 @@ The module is only loaded once. If RELOAD-P is non-nil, load it again."
(unless loaded-p (unless loaded-p
(doom-module-enable module submodule flags)) (doom-module-enable module submodule flags))
`(condition-case-unless-debug ex `(condition-case-unless-debug ex
(let ((doom--module ',(cons module submodule))) (load! config ,(doom-module-path module submodule) t)
(load! config ,(doom-module-path module submodule) t))
('error ('error
(lwarn 'doom-modules :error (lwarn 'doom-modules :error
"%s in '%s %s' -> %s" "%s in '%s %s' -> %s"
@ -418,11 +411,13 @@ The module is only loaded once. If RELOAD-P is non-nil, load it again."
"A convenience macro wrapper for `doom-module-loaded-p'. It is evaluated at "A convenience macro wrapper for `doom-module-loaded-p'. It is evaluated at
compile-time/macro-expansion time." compile-time/macro-expansion time."
(unless submodule (unless submodule
(unless doom--module (let* ((path (or load-file-name byte-compile-current-file))
(error "featurep! was used incorrectly (doom--module wasn't unset)")) (module-pair (doom-module-from-path path)))
(unless module-pair
(error "featurep! couldn't detect what module I'm in! (in %s)" path))
(setq flag module (setq flag module
module (car doom--module) module (car module-pair)
submodule (cdr doom--module))) submodule (cdr module-pair))))
(if flag (if flag
(and (memq flag (doom-module-flags module submodule)) t) (and (memq flag (doom-module-flags module submodule)) t)
(doom-module-loaded-p module submodule))) (doom-module-loaded-p module submodule)))
@ -487,7 +482,7 @@ loads MODULE SUBMODULE's packages.el file."
;; Commands ;; Commands
;; ;;
(defun doom/reload () (defun doom/reload-load-path ()
"Reload `load-path' and recompile files (if necessary). "Reload `load-path' and recompile files (if necessary).
Use this when `load-path' is out of sync with your plugins. This should only Use this when `load-path' is out of sync with your plugins. This should only
@ -495,12 +490,12 @@ happen if you manually modify/update/install packages from outside Emacs, while
an Emacs session is running. an Emacs session is running.
This isn't necessary if you use Doom's package management commands because they This isn't necessary if you use Doom's package management commands because they
call `doom/reload' remotely (through emacsclient)." call `doom/reload-load-path' remotely (through emacsclient)."
(interactive) (interactive)
(cond (noninteractive (cond (noninteractive
(message "Reloading...") (message "Reloading...")
(require 'server) (require 'server)
(unless (ignore-errors (server-eval-at "server" '(doom/reload))) (unless (ignore-errors (server-eval-at "server" '(doom/reload-load-path)))
(message "Recompiling") (message "Recompiling")
(doom/recompile))) (doom/recompile)))
(t (t
@ -626,6 +621,7 @@ If ONLY-RECOMPILE-P is non-nil, only recompile out-of-date files."
total-fail) total-fail)
(t (t
(message! (green "Compiled %s" short-name)) (message! (green "Compiled %s" short-name))
(quiet! (load file t t))
total-success)))))) total-success))))))
(message! (message!
(bold (bold

View File

@ -18,10 +18,6 @@
"A list of popups that were last closed. Used by `doom/popup-restore' and "A list of popups that were last closed. Used by `doom/popup-restore' and
`doom*popups-save'.") `doom*popups-save'.")
(defvar doom-popup-remember-history t
"If non-nil, DOOM will remember the last popup(s) that were open in
`doom-popup-history'.")
(defvar doom-popup-other-window nil (defvar doom-popup-other-window nil
"The last window selected before a popup was opened.") "The last window selected before a popup was opened.")
@ -32,33 +28,53 @@
"A list of open popup windows.") "A list of open popup windows.")
(defvar-local doom-popup-rules nil (defvar-local doom-popup-rules nil
"The shackle rule that caused this buffer to be recognized as a popup.") "The shackle rule that caused this buffer to be recognized as a popup. Don't
edit this directly.")
(put 'doom-popup-rules 'permanent-local t)
(defvar doom-popup-window-parameters (defvar doom-popup-window-parameters
'(:noesc :modeline :autokill :autoclose) '(:noesc :modeline :autokill :autoclose :autofit :static)
"A list of window parameters that are set (and cleared) when `doom-popup-mode "A list of window parameters that are set (and cleared) when `doom-popup-mode
is enabled/disabled.'") is enabled/disabled.'")
(defvar doom-popup-remember-history t
"Don't modify this directly. If non-nil, DOOM will remember the last popup(s)
that was/were open in `doom-popup-history'.")
(defvar doom-popup-inhibit-autokill nil
"Don't modify this directly. When it is non-nil, no buffers will be killed
when their associated popup windows are closed, despite their :autokill
property.")
(def-setting! :popup (&rest rules) (def-setting! :popup (&rest rules)
"Prepend a new popup rule to `shackle-rules' (see for format details). "Prepend a new popup rule to `shackle-rules' (see for format details).
Several custom properties have been added that are not part of shackle, but are Several custom properties have been added that are not part of shackle, but are
recognized by DOOM's popup system. They are: recognized by DOOM's popup system. They are:
:noesc If non-nil, pressing ESC *inside* the popup will close it. :noesc If non-nil, the popup won't be closed if you press ESC from *inside*
Used by `doom/popup-close-maybe'. its window. Used by `doom/popup-close-maybe'.
:modeline By default, mode-lines are hidden in popups unless this :modeline By default, mode-lines are hidden in popups unless this is non-nil.
is non-nil. If it is a symbol, it'll use `doom-modeline' If it is a symbol, it'll use `doom-modeline' to fetch a modeline
to fetch a modeline config (in `doom-popup-mode'). config (in `doom-popup-mode').
:autokill If non-nil, the popup's buffer will be killed when the :autokill If non-nil, the popup's buffer will be killed when the popup is
popup is closed. Used by `doom*delete-popup-window'. closed. Used by `doom*delete-popup-window'. NOTE
NOTE `doom/popup-restore' can't restore non-file popups `doom/popup-restore' can't restore non-file popups that have an
that have an :autokill property. :autokill property.
:autoclose If non-nil, close popup if ESC is pressed from outside :autoclose If non-nil, close popup if ESC is pressed from outside the popup
the popup window." window.
:autofit If non-nil, resize the popup to fit its content. Uses the value of
the :size property as the maximum height/width. This will not work
if the popup has no content when displayed.
:static If non-nil, don't treat this window like a popup. This makes it
impervious to being automatically closed or tracked in popup
history. Excellent for permanent sidebars."
(if (cl-every #'listp (mapcar #'doom-unquote rules)) (if (cl-every #'listp (mapcar #'doom-unquote rules))
`(setq shackle-rules (nconc (list ,@rules) shackle-rules)) `(setq shackle-rules (nconc (list ,@rules) shackle-rules))
`(push (list ,@rules) shackle-rules))) `(push (list ,@rules) shackle-rules)))
@ -74,17 +90,19 @@ recognized by DOOM's popup system. They are:
(setq shackle-default-alignment 'below (setq shackle-default-alignment 'below
shackle-default-size 8 shackle-default-size 8
shackle-rules shackle-rules
'(("^\\*ftp " :noselect t :autokill t :noesc t) '(("^\\*eww" :regexp t :size 0.5 :select t :autokill t :noesc t)
("^\\*ftp " :noselect t :autokill t :noesc t)
;; doom ;; doom
("^\\*doom:" :regexp t :size 0.35 :noesc t :select t :modeline t) ("^\\*doom:scratch" :regexp t :size 15 :noesc t :select t :modeline t :autokill t :static t)
("^\\*doom " :regexp t :noselect t :autokill t :autoclose t) ("^\\*doom:" :regexp t :size 0.35 :noesc t :select t)
("^ ?\\*doom " :regexp t :noselect t :autokill t :autoclose t :autofit t)
;; built-in (emacs) ;; built-in (emacs)
("*ert*" :same t :modeline t) ("*ert*" :same t :modeline t)
("*info*" :size 0.5 :select t :autokill t) ("*info*" :size 0.5 :select t :autokill t)
("*Backtrace*" :size 20 :noselect t) ("*Backtrace*" :size 20 :noselect t)
("*Warnings*" :size 8 :noselect t) ("*Warnings*" :size 12 :noselect t :autofit t)
("*Messages*" :size 12 :noselect t) ("*Messages*" :size 12 :noselect t)
("*Help*" :size 0.3) ("*Help*" :size 0.4 :autofit t)
("^\\*.*Shell Command.*\\*$" :regexp t :size 20 :noselect t :autokill t) ("^\\*.*Shell Command.*\\*$" :regexp t :size 20 :noselect t :autokill t)
(apropos-mode :size 0.3 :autokill t :autoclose t) (apropos-mode :size 0.3 :autokill t :autoclose t)
(Buffer-menu-mode :size 20 :autokill t) (Buffer-menu-mode :size 20 :autokill t)
@ -92,171 +110,43 @@ recognized by DOOM's popup system. They are:
(grep-mode :size 25 :noselect t :autokill t) (grep-mode :size 25 :noselect t :autokill t)
(profiler-report-mode :size 0.3 :regexp t :autokill t :modeline minimal) (profiler-report-mode :size 0.3 :regexp t :autokill t :modeline minimal)
(tabulated-list-mode :noesc t) (tabulated-list-mode :noesc t)
(special-mode :noselect t :autokill t :autoclose t) ("^ ?\\*" :regexp t :size 0.3 :noselect t :autokill t :autoclose t :autofit t)))
("^\\*" :regexp t :noselect t :autokill t)
("^ \\*" :regexp t :size 12 :noselect t :autokill t :autoclose t)))
:config :config
(add-hook 'doom-post-init-hook #'shackle-mode) (add-hook 'doom-post-init-hook #'shackle-mode)
(defun doom*shackle-always-align (plist) ;; no modeline in popups
"Ensure popups are always aligned and selected by default. Eliminates the need (add-hook 'doom-popup-mode-hook #'doom|hide-modeline-in-popup)
for :align t on every rule." ;; ensure every rule without an :align, :same or :frame property has an
(when plist ;; implicit :align (see `shackle-default-alignment')
(unless (or (plist-member plist :align) (advice-add #'shackle--match :filter-return #'doom*shackle-always-align)
(plist-member plist :same)
(plist-member plist :frame))
(plist-put plist :align t))
(unless (or (plist-member plist :select)
(plist-member plist :noselect))
(plist-put plist :select t)))
plist)
(advice-add #'shackle--match :filter-return #'doom*shackle-always-align))
;; bootstrap popup system
(advice-add #'shackle-display-buffer :around #'doom*popup-init)
(advice-add #'balance-windows :around #'doom*popups-save)
(advice-add #'delete-window :before #'doom*delete-popup-window)
;; ;; Tell `window-state-get' and `current-window-configuration' to recognize
;; Integration ;; these custom parameters. Helpful for `persp-mode' and persisting window
;; ;; configs that have popups in them.
(dolist (param `(popup ,@doom-popup-window-parameters))
;; Tell `window-state-get' and `current-window-configuration' to recognize these
;; custom parameters. Helpful for `persp-mode' and persisting window configs
;; that have popups in them.
(dolist (param (cons 'popup doom-popup-window-parameters))
(push (cons param 'writable) window-persistent-parameters)) (push (cons param 'writable) window-persistent-parameters))
(defvar doom-popup-mode-map (defvar doom-popup-mode-map
(let ((map (make-sparse-keymap))) (let ((map (make-sparse-keymap)))
(define-key map [escape] 'doom/popup-close-maybe) (define-key map [escape] #'doom/popup-close-maybe)
(define-key map (kbd "ESC") 'doom/popup-close-maybe) (define-key map (kbd "ESC") #'doom/popup-close-maybe)
(define-key map [remap doom-kill-buffer] 'kill-this-buffer) (define-key map [remap quit-window] #'doom/popup-close-maybe)
(define-key map [remap doom/kill-this-buffer] 'kill-this-buffer) (define-key map [remap delete-window] #'doom/popup-close-maybe)
(define-key map [remap split-window-right] 'ignore) (define-key map [remap doom/kill-this-buffer] #'delete-window)
(define-key map [remap split-window-below] 'ignore) (define-key map [remap split-window-right] #'ignore)
(define-key map [remap split-window-horizontally] 'ignore) (define-key map [remap split-window-below] #'ignore)
(define-key map [remap split-window-vertically] 'ignore) (define-key map [remap split-window-horizontally] #'ignore)
(define-key map [remap mouse-split-window-horizontally] 'ignore) (define-key map [remap split-window-vertically] #'ignore)
(define-key map [remap mouse-split-window-vertically] 'ignore) (define-key map [remap mouse-split-window-horizontally] #'ignore)
(define-key map [remap mouse-split-window-vertically] #'ignore)
map) map)
"Active keymap in popup windows.") "Active keymap in popup windows."))
(define-minor-mode doom-popup-mode
"Minor mode for popup windows."
:init-value nil
:keymap doom-popup-mode-map
(let ((window (selected-window)))
;; If `doom-popup-rules' isn't set for some reason, try to set it
(when-let (plist (and (not doom-popup-rules)
(window-parameter window 'popup)))
(setq-local doom-popup-rules (window-parameter window 'popup)))
;; Ensure that buffer-opening functions/commands (like
;; `switch-to-buffer-other-window' won't use this window).
(set-window-parameter window 'no-other-window doom-popup-mode)
;; Makes popup window resist interactively changing its buffer.
(set-window-dedicated-p window doom-popup-mode)
(cond (doom-popup-mode
(when doom-popup-no-fringes
(set-window-fringes window 0 0 fringes-outside-margins))
;; Save metadata into window parameters so it can be saved by window
;; config persisting plugins like workgroups or persp-mode.
(set-window-parameter window 'popup (or doom-popup-rules t))
(when doom-popup-rules
(cl-loop for param in doom-popup-window-parameters
when (plist-get doom-popup-rules param)
do (set-window-parameter window param it))))
(t
(when doom-popup-no-fringes
(set-window-fringes window
doom-fringe-size doom-fringe-size
fringes-outside-margins))
;; Ensure window parameters are cleaned up
(set-window-parameter window 'popup nil)
(dolist (param doom-popup-window-parameters)
(set-window-parameter window param nil))))))
;; Major mode changes (and other things) may call `kill-all-local-variables',
;; turning off things like `doom-popup-mode'. This prevents that.
(put 'doom-popup-mode 'permanent-local t)
(put 'doom-popup-rules 'permanent-local t)
(defun doom|hide-modeline-in-popup ()
"Don't show modeline in popup windows without a :modeline rule. If one exists
and it's a symbol, use `doom-modeline' to grab the format. If non-nil, show the
mode-line as normal. If nil (or omitted, by default), then hide the modeline
entirely."
(if doom-popup-mode
(let ((modeline (plist-get doom-popup-rules :modeline)))
(cond ((or (eq modeline 'nil)
(not modeline))
(doom-hide-modeline-mode +1))
((and (symbolp modeline)
(not (eq modeline 't)))
(setq-local doom--modeline-format (doom-modeline modeline))
(when doom--modeline-format
(doom-hide-modeline-mode +1)))))
(when doom-hide-modeline-mode
(doom-hide-modeline-mode -1))))
(add-hook 'doom-popup-mode-hook #'doom|hide-modeline-in-popup)
;;
(defun doom*popup-init (orig-fn &rest args)
"Initializes a window as a popup window by enabling `doom-popup-mode' in it
and setting `doom-popup-rules' within it. Returns the window."
(unless (doom-popup-p)
(setq doom-popup-other-window (selected-window)))
(let* ((plist (or (nth 2 args)
(cond ((windowp (car args))
(shackle-match (window-buffer (car args))))
((bufferp (car args))
(shackle-match (car args))))))
(buffer (get-buffer (car args)))
(window-min-height (if (plist-get plist :modeline) 4 2))
window)
(when (and (doom-real-buffer-p buffer)
(get-buffer-window-list buffer nil t))
(setq plist (append (list :autokill t) plist))
(setcar args (clone-indirect-buffer (buffer-name (car args)) nil t)))
(unless (setq window (apply orig-fn args))
(error "No popup window was found for %s: %s" (car args) plist))
(cl-pushnew window doom-popup-windows :test #'eq)
(with-selected-window window
(unless (eq plist t)
(setq-local doom-popup-rules plist))
(doom-popup-mode +1))
window))
(defun doom*popups-save (orig-fn &rest args)
"Sets aside all popups before executing the original function, usually to
prevent the popup(s) from messing up the UI (or vice versa)."
(let ((in-popup-p (doom-popup-p))
(popups (doom-popup-windows))
(doom-popup-remember-history t))
(when popups
(mapc #'doom/popup-close popups))
(unwind-protect (apply orig-fn args)
(when popups
(let ((origin (selected-window)))
(doom/popup-restore)
(unless in-popup-p
(select-window origin)))))))
(defun doom*delete-popup-window (&optional window)
"Ensure that popups are deleted properly, and killed if they have :autokill
properties."
(let ((window (or window (selected-window))))
(when (doom-popup-p window)
(setq doom-popup-windows (delq window doom-popup-windows))
(when doom-popup-remember-history
(setq doom-popup-history (list (doom--popup-data window))))
(let ((autokill-p (plist-get doom-popup-rules :autokill)))
(with-selected-window window
(doom-popup-mode -1)
(when autokill-p
(kill-buffer (current-buffer))))))))
(advice-add #'shackle-display-buffer :around #'doom*popup-init)
(advice-add #'balance-windows :around #'doom*popups-save)
(advice-add #'delete-window :before #'doom*delete-popup-window)
;; ;;
@ -264,20 +154,20 @@ properties."
;; ;;
(progn ; hacks for built-in functions (progn ; hacks for built-in functions
(defun doom*buffer-menu (&optional arg)
"Open `buffer-menu' in a popup window."
(interactive "P")
(let ((buf (list-buffers-noselect arg)))
(doom-popup-buffer buf)
(with-current-buffer buf
(setq mode-line-format "Commands: d, s, x, u; f, o, 1, 2, m, v; ~, %; q to quit; ? for help."))))
(advice-add #'buffer-menu :override #'doom*buffer-menu)
(defun doom*suppress-pop-to-buffer-same-window (orig-fn &rest args) (defun doom*suppress-pop-to-buffer-same-window (orig-fn &rest args)
(cl-letf (((symbol-function 'pop-to-buffer-same-window) (cl-letf (((symbol-function 'pop-to-buffer-same-window)
(symbol-function 'pop-to-buffer))) (symbol-function 'pop-to-buffer)))
(apply orig-fn args))) (apply orig-fn args)))
(advice-add #'info :around #'doom*suppress-pop-to-buffer-same-window)) (advice-add #'info :around #'doom*suppress-pop-to-buffer-same-window)
(advice-add #'eww :around #'doom*suppress-pop-to-buffer-same-window)
(advice-add #'eww-browse-url :around #'doom*suppress-pop-to-buffer-same-window)
(defun doom*popup-buffer-menu (&optional arg)
"Open `buffer-menu' in a popup window."
(interactive "P")
(with-selected-window (doom-popup-buffer (list-buffers-noselect arg))
(setq mode-line-format "Commands: d, s, x, u; f, o, 1, 2, m, v; ~, %; q to quit; ? for help.")))
(advice-add #'buffer-menu :override #'doom*popup-buffer-menu))
(after! comint (after! comint
@ -310,12 +200,12 @@ properties."
(after! evil (after! evil
(let ((map doom-popup-mode-map)) (let ((map doom-popup-mode-map))
(define-key map [remap evil-window-delete] #'doom/popup-close) (define-key map [remap evil-window-delete] #'doom/popup-close-maybe)
(define-key map [remap evil-save-modified-and-close] #'doom/popup-close) (define-key map [remap evil-save-modified-and-close] #'doom/popup-close-maybe)
(define-key map [remap evil-window-move-very-bottom] #'ignore) (define-key map [remap evil-window-move-very-bottom] #'doom/popup-move-bottom)
(define-key map [remap evil-window-move-very-top] #'ignore) (define-key map [remap evil-window-move-very-top] #'doom/popup-move-top)
(define-key map [remap evil-window-move-far-left] #'ignore) (define-key map [remap evil-window-move-far-left] #'doom/popup-move-left)
(define-key map [remap evil-window-move-far-right] #'ignore) (define-key map [remap evil-window-move-far-right] #'doom/popup-move-right)
(define-key map [remap evil-window-split] #'ignore) (define-key map [remap evil-window-split] #'ignore)
(define-key map [remap evil-window-vsplit] #'ignore)) (define-key map [remap evil-window-vsplit] #'ignore))
@ -323,10 +213,9 @@ properties."
"If current window is a popup, close it. If minibuffer is open, close it. If "If current window is a popup, close it. If minibuffer is open, close it. If
not in a popup, close all popups with an :autoclose property." not in a popup, close all popups with an :autoclose property."
(cond ((doom-popup-p) (cond ((doom-popup-p)
(unless (doom-popup-prop :noesc) (unless (doom-popup-property :noesc)
(delete-window))) (delete-window)))
(t (t (doom/popup-close-all))))
(doom/popup-close-all))))
(add-hook '+evil-esc-hook #'doom|popup-close-maybe t) (add-hook '+evil-esc-hook #'doom|popup-close-maybe t)
;; Make evil-mode cooperate with popups ;; Make evil-mode cooperate with popups
@ -384,10 +273,10 @@ the command buffer."
(after! helm (after! helm
;; Helm tries to clean up after itself, but shackle has already done this. ;; Helm tries to clean up after itself, but shackle has already done this,
;; This fixes that. To reproduce, add a helm rule in `shackle-rules', open two ;; causing problems. This fixes that. To reproduce, add a helm rule in
;; splits side-by-side, move to the buffer on the right and invoke helm. It ;; `shackle-rules', open two splits side-by-side, move to the buffer on the
;; will close all but the left-most buffer. ;; right and invoke helm. It will close all but the left-most buffer.
(setq-default helm-reuse-last-window-split-state t (setq-default helm-reuse-last-window-split-state t
helm-split-window-in-side-p t) helm-split-window-in-side-p t)
@ -486,7 +375,7 @@ the command buffer."
(after! mu4e (after! mu4e
(defun doom*mu4e-popup-window (buf _height) (defun doom*mu4e-popup-window (buf _height)
(doom-popup-buffer buf :size 10 :noselect t) (doom-popup-buffer buf '(:size 10 :noselect t))
buf) buf)
(advice-add #'mu4e~temp-window :override #'doom*mu4e-popup-window)) (advice-add #'mu4e~temp-window :override #'doom*mu4e-popup-window))
@ -502,7 +391,7 @@ the command buffer."
;; ;;
;; By handing neotree over to shackle, which is better integrated into the ;; By handing neotree over to shackle, which is better integrated into the
;; rest of my config (and persp-mode), this is no longer a problem. ;; rest of my config (and persp-mode), this is no longer a problem.
(set! :popup " *NeoTree*" :align 'left :size 25) (set! :popup " *NeoTree*" :align neo-window-position :size neo-window-width :static t)
(defun +evil-neotree-display-fn (buf _alist) (defun +evil-neotree-display-fn (buf _alist)
"Hand neotree off to shackle." "Hand neotree off to shackle."
@ -515,7 +404,10 @@ the command buffer."
"Repair neotree state whenever its popup state is restored. This ensures "Repair neotree state whenever its popup state is restored. This ensures
that `doom*popup-save' won't break it." that `doom*popup-save' won't break it."
(when (equal (buffer-name) neo-buffer-name) (when (equal (buffer-name) neo-buffer-name)
(setq neo-global--window (selected-window)))) (setq neo-global--window (selected-window))
;; Fix neotree shrinking when closing nearby vertical splits
(when neo-window-fixed-size
(doom-resize-window neo-global--window neo-window-width t t))))
(add-hook 'doom-popup-mode-hook #'+evil|neotree-fix-popup)) (add-hook 'doom-popup-mode-hook #'+evil|neotree-fix-popup))
@ -523,7 +415,7 @@ that `doom*popup-save' won't break it."
(defun doom*persp-mode-restore-popups (&rest _) (defun doom*persp-mode-restore-popups (&rest _)
"Restore popup windows when loading a perspective from file." "Restore popup windows when loading a perspective from file."
(dolist (window (window-list)) (dolist (window (window-list))
(when-let (plist (window-parameter window 'popup)) (when-let (plist (doom-popup-properties window))
(with-selected-window window (with-selected-window window
(unless doom-popup-mode (unless doom-popup-mode
(setq-local doom-popup-rules plist) (setq-local doom-popup-rules plist)
@ -582,16 +474,16 @@ you came from."
'("*Org Links*" :size 5 :noselect t) '("*Org Links*" :size 5 :noselect t)
'("*Org Export Dispatcher*" :noselect t) '("*Org Export Dispatcher*" :noselect t)
'(" *Agenda Commands*" :noselect t) '(" *Agenda Commands*" :noselect t)
'("^\\*Org Agenda" :regexp t :size 30) '("^\\*Org Agenda" :regexp t :size 20)
'("*Org Clock*" :noselect t) '("*Org Clock*" :noselect t)
'("^\\*Org Src" :regexp t :size 0.35 :noesc t) '("^\\*Org Src" :regexp t :size 0.35 :noesc t)
'("*Edit Formulas*" :size 10) '("*Edit Formulas*" :size 10)
'("^\\*Org-Babel" :regexp t :size 25 :noselect t) '("^\\*Org-Babel" :regexp t :size 25 :noselect t)
'("^CAPTURE.*\\.org$" :regexp t :size 20)) '("^CAPTURE.*\\.org$" :regexp t :size 20))
;; Org has its own window management system with a scorched earth philosophy ;; Org has a scorched-earth window management system I'm not fond of. i.e.
;; I'm not fond of. i.e. it kills all windows and monopolizes the frame. No ;; it kills all windows and monopolizes the frame. No thanks. We can do
;; thanks. We can do better with shackle's help. ;; better with shackle's help.
(defun doom*suppress-delete-other-windows (orig-fn &rest args) (defun doom*suppress-delete-other-windows (orig-fn &rest args)
(cl-letf (((symbol-function 'delete-other-windows) (cl-letf (((symbol-function 'delete-other-windows)
(symbol-function 'ignore))) (symbol-function 'ignore)))
@ -600,17 +492,17 @@ you came from."
(advice-add #'org-capture-place-template :around #'doom*suppress-delete-other-windows) (advice-add #'org-capture-place-template :around #'doom*suppress-delete-other-windows)
(advice-add #'org-export--dispatch-ui :around #'doom*suppress-delete-other-windows) (advice-add #'org-export--dispatch-ui :around #'doom*suppress-delete-other-windows)
;; `org-edit-src-code' simply clones and narrows the buffer to a src block, ;; Hand off the src-block window to a shackle popup window.
;; so we are secretly manipulating the same buffer. Since truely killing it (defun doom*org-src-pop-to-buffer (buffer _context)
;; would kill the original org buffer we've got to do things differently. "Open the src-edit in a way that shackle can detect."
(defun doom*org-src-switch-to-buffer (buffer _context)
(if (eq org-src-window-setup 'switch-invisibly) (if (eq org-src-window-setup 'switch-invisibly)
(set-buffer buffer) (set-buffer buffer)
(pop-to-buffer buffer))) (pop-to-buffer buffer)))
(advice-add #'org-src-switch-to-buffer :override #'doom*org-src-switch-to-buffer) (advice-add #'org-src-switch-to-buffer :override #'doom*org-src-pop-to-buffer)
;; Ensure todo, agenda, and other minor popups handed off to shackle. ;; Ensure todo, agenda, and other minor popups are delegated to shackle.
(defun doom*org-pop-to-buffer (&rest args) (defun doom*org-pop-to-buffer (&rest args)
"Use `pop-to-buffer' instead of `switch-to-buffer' to open buffer.'"
(let ((buf (car args))) (let ((buf (car args)))
(pop-to-buffer (pop-to-buffer
(cond ((stringp buf) (get-buffer-create buf)) (cond ((stringp buf) (get-buffer-create buf))
@ -624,15 +516,13 @@ you came from."
;; Hide modeline in org-agenda ;; Hide modeline in org-agenda
(add-hook 'org-agenda-finalize-hook #'doom-hide-modeline-mode) (add-hook 'org-agenda-finalize-hook #'doom-hide-modeline-mode)
(add-hook 'org-agenda-finalize-hook #'org-fit-window-to-buffer)
;; Don't monopolize frame! ;; Don't monopolize frame!
(advice-add #'org-agenda :around #'doom*suppress-delete-other-windows) (advice-add #'org-agenda :around #'doom*suppress-delete-other-windows)
;; ensure quit keybindings work propertly
(map! :map org-agenda-mode-map (map! :map org-agenda-mode-map
:m [escape] 'org-agenda-Quit :m [escape] 'org-agenda-Quit
:m "ESC" 'org-agenda-Quit) :m "ESC" 'org-agenda-Quit))))
(let ((map org-agenda-mode-map))
(define-key map "q" 'org-agenda-Quit)
(define-key map "Q" 'org-agenda-Quit)))))
(add-hook 'doom-init-hook #'doom|init-org-popups) (add-hook 'doom-init-hook #'doom|init-org-popups)
(provide 'core-popups) (provide 'core-popups)

View File

@ -10,43 +10,35 @@ state are passed in.")
:config :config
(setq projectile-cache-file (concat doom-cache-dir "projectile.cache") (setq projectile-cache-file (concat doom-cache-dir "projectile.cache")
projectile-enable-caching (not noninteractive) projectile-enable-caching (not noninteractive)
projectile-file-exists-remote-cache-expire nil
projectile-indexing-method 'alien projectile-indexing-method 'alien
projectile-known-projects-file (concat doom-cache-dir "projectile.projects") projectile-known-projects-file (concat doom-cache-dir "projectile.projects")
projectile-require-project-root nil projectile-require-project-root nil
projectile-project-root-files projectile-globally-ignored-files '(".DS_Store" "Icon
'(".git" ".hg" ".svn" ".project" "package.json" "setup.py" "Gemfile" " "TAGS")
"build.gradle") projectile-globally-ignored-file-suffixes '(".elc" ".pyc" ".o"))
projectile-other-file-alist
(append '(("less" "css") ;; a more generic project root file
("styl" "css") (push ".project" projectile-project-root-files-bottom-up)
("sass" "css")
("scss" "css") (nconc projectile-globally-ignored-directories (list doom-local-dir ".sync"))
("css" "scss" "sass" "less" "styl") (nconc projectile-other-file-alist '(("css" . ("scss" "sass" "less" "style"))
("jade" "html") ("scss" . ("css"))
("pug" "html") ("sass" . ("css"))
("html" "jade" "pug" "jsx" "tsx")) ("less" . ("css"))
projectile-other-file-alist)
projectile-globally-ignored-file-suffixes '(".elc" ".pyc" ".o")
projectile-globally-ignored-files '(".DS_Store" "Icon
")
projectile-globally-ignored-directories
(append (list doom-local-dir ".sync")
("styl" . ("css")))) ("styl" . ("css"))))
;; Projectile root-searching functions can cause an infinite loop on TRAMP ;; Projectile root-searching functions can cause an infinite loop on TRAMP
;; connections, so disable them. ;; connections, so disable them.
(defun doom*projectile-locate-dominating-file (orig-fn &rest args) (defun doom*projectile-locate-dominating-file (orig-fn &rest args)
"Don't traverse the file system if on a remote connection." "Don't traverse the file system if on a remote connection."
(unless (file-remote-p default-directory) (unless (file-remote-p default-directory)
(apply orig-fn args))) (apply orig-fn args)))
(advice-add #'projectile-locate-dominating-file :around #'doom*projectile-locate-dominating-file) (advice-add #'projectile-locate-dominating-file :around #'doom*projectile-locate-dominating-file)
(defun doom*projectile-cache-current-file (orig-fun &rest args) (defun doom*projectile-cache-current-file (orig-fun &rest args)
"Don't cache ignored files." "Don't cache ignored files."
(unless (cl-some (lambda (path) (unless (cl-loop for path in (projectile-ignored-directories)
(string-prefix-p buffer-file-name if (string-prefix-p buffer-file-name (expand-file-name path))
(expand-file-name path)))
return t) return t)
(apply orig-fun args))) (apply orig-fun args)))
(advice-add #'projectile-cache-current-file :around #'doom*projectile-cache-current-file)) (advice-add #'projectile-cache-current-file :around #'doom*projectile-cache-current-file))
@ -55,26 +47,34 @@ state are passed in.")
;; ;;
;; Library ;; Library
;; ;;
(defun doom/reload-project ()
"Reload the project root cache."
(interactive)
(projectile-invalidate-cache nil)
(projectile-reset-cached-project-root)
(dolist (fn projectile-project-root-files-functions)
(remhash (format "%s-%s" fn default-directory) projectile-project-root-cache)))
(defun doom-project-p () (defun doom-project-p ()
"Whether or not this buffer is currently in a project or not." "Whether or not this buffer is currently in a project or not."
(let ((projectile-require-project-root t)) (let ((projectile-require-project-root t))
(projectile-project-p))) (projectile-project-p)))
(defun doom-project-root (&optional strict-p) (defun doom-project-root ()
"Get the path to the root of your project." "Get the path to the root of your project.
(let ((projectile-require-project-root strict-p)) If STRICT-P, return nil if no project was found, otherwise return
`default-directory'."
(let (projectile-require-project-root)
(projectile-project-root))) (projectile-project-root)))
(defun doom*project-root (&rest _)
"An advice function used to replace project-root-detection functions in other
libraries."
(defalias 'doom-project-expand #'projectile-expand-root) (defalias 'doom-project-expand #'projectile-expand-root)
(defmacro doom-project-has! (files) (defmacro doom-project-has! (files)
"Checks if the project has the specified FILES, relative to the project root, "Checks if the project has the specified FILES.
unless the path begins with ./ or ../, in which case it's relative to Paths are relative to the project root, unless they start with ./ or ../ (in
`default-directory'. Recognizes (and ...) and/or (or ...) forms." which case they're relative to `default-directory'). If they start with a slash,
they are absolute."
(doom--resolve-path-forms files (doom-project-root))) (doom--resolve-path-forms files (doom-project-root)))
@ -82,21 +82,22 @@ unless the path begins with ./ or ../, in which case it's relative to
;; Projects ;; Projects
;; ;;
(defvar-local doom-project nil (defvar-local doom-project nil
"A list of project mode to enable. Used for .dir-locals.el.") "A list of project mode to enable. Used for .dir-locals.el.")
(defun doom|autoload-project-mode () (defun doom|autoload-project-mode ()
"Auto-enable projects listed in `doom-project', which is meant to be set from "Auto-enable projects listed in `doom-project', which is meant to be set from
.dir-locals.el files." .dir-locals.el files."
(dolist (mode doom-project) (cl-loop for mode in doom-project
unless (symbol-value mode)
do (funcall mode))) do (funcall mode)))
(add-hook 'after-change-major-mode-hook #'doom|autoload-project-mode) (add-hook 'after-change-major-mode-hook #'doom|autoload-project-mode)
(defmacro def-project-mode! (name &rest plist) (defmacro def-project-mode! (name &rest plist)
"Define a project minor-mode named NAME and declare where and how it is "Define a project minor-mode named NAME (a symbol) and declare where and how
activated. Project modes allow you to configure 'sub-modes' for major-modes that it is activated. Project modes allow you to configure 'sub-modes' for
are specific to a specific folder, certain project structure, framework or major-modes that are specific to a specific folder, certain project structure,
arbitrary context you define. These project modes can have their own settings, framework or arbitrary context you define. These project modes can have their
own settings, keymaps, hooks, snippets, etc. own settings, keymaps, hooks, snippets, etc.
This creates NAME-hook and NAME-map as well. This creates NAME-hook and NAME-map as well.
@ -118,34 +119,52 @@ should be activated. If they are *all* true, NAME is activated.
:when PREDICATE -- if PREDICATE returns true (can be a form or the symbol of a :when PREDICATE -- if PREDICATE returns true (can be a form or the symbol of a
function) function)
:add-hooks HOOKS -- HOOKS is a list of hooks to add this mode's hook.
:on-load FORM -- FORM to run the first time this project mode is enabled.
:on-enter FORM -- FORM is run each time the mode is activated.
:on-exit FORM -- FORM is run each time the mode is disabled. :on-exit FORM -- FORM is run each time the mode is disabled.
Relevant: `doom-project-hook'." Relevant: `doom-project-hook'."
(declare (indent 1)) (declare (indent 1) (doc-string 2))
(let ((doc-string (if (stringp (car plist))
(prog1 (car plist)
(setq plist (cdr plist)))
"A project minor mode."))
(modes (plist-get plist :modes)) (modes (plist-get plist :modes))
(files (plist-get plist :files)) (files (plist-get plist :files))
(when (plist-get plist :when)) (when (plist-get plist :when))
(match (plist-get plist :match)) (match (plist-get plist :match))
(init-form (plist-get plist :init)) (hooks (plist-get plist :add-hooks))
(load-form (plist-get plist :on-load))
(enter-form (plist-get plist :on-enter))
(exit-form (plist-get plist :on-exit))
(init-var (intern (format "%s-init" name)))) (init-var (intern (format "%s-init" name))))
`(progn `(progn
(defvar ,keymap-sym (make-sparse-keymap)
,(if load-form `(defvar ,init-var nil)) ,(if load-form `(defvar ,init-var nil))
(define-minor-mode ,name (define-minor-mode ,name
,doc-string ,doc-string
:init-value nil :init-value nil
:lighter ""
:keymap (make-sparse-keymap)
(if (not ,name)
,exit-form
(run-hook-with-args 'doom-project-hook ',name)
,(when load-form
`(unless ,init-var
,load-form
(setq ,init-var t)))
,enter-form))
,(when hooks
`(setq ,(intern (format "%s-hook" name)) ',hooks)) `(setq ,(intern (format "%s-hook" name)) ',hooks))
,(when (or modes match files when) ,(when (or modes match files when)
`(associate! ,name `(associate! ,name
:modes ,modes :modes ,modes
:match ,match :match ,match
:files ,files :files ,files
:when ,when))
(add-hook! ,name
(run-hook-with-args 'doom-project-hook ',name))
,(when init-form
`(add-transient-hook! ',(intern (format "%s-hook" name))
:when ,when))))) :when ,when)))))
(provide 'core-projects) (provide 'core-projects)

View File

@ -34,22 +34,32 @@ shorter major mode name in the mode-line. See `doom|set-mode-name'.")
;; Settings ;; Settings
(def-setting! :theme (theme) (def-setting! :theme (theme)
"Sets the current THEME (a symbol)."
`(unless doom-theme `(unless doom-theme
(setq doom-theme ,theme))) (setq doom-theme ,theme)))
(def-setting! :font (family &rest spec) (def-setting! :font (family &rest spec)
"Sets the default font (if one wasn't already set). FAMILY is the name of the
font, and SPEC is a `font-spec'."
`(unless doom-font `(unless doom-font
(setq doom-font (font-spec :family ,family ,@spec)))) (setq doom-font (font-spec :family ,family ,@spec))))
(def-setting! :variable-pitch-font (family &rest spec) (def-setting! :variable-pitch-font (family &rest spec)
"Sets the default font for the variable-pitch face and minor mode (if one
wasn't already set). FAMILY is the name of the font, and SPEC is a `font-spec'."
`(unless doom-variable-pitch-font `(unless doom-variable-pitch-font
(setq doom-variable-pitch-font (font-spec :family ,family ,@spec)))) (setq doom-variable-pitch-font (font-spec :family ,family ,@spec))))
(def-setting! :big-font (family &rest spec) (def-setting! :big-font (family &rest spec)
"Sets the font to use for `doom-big-font-mode' (if one wasn't already set).
FAMILY is the name of the font, and SPEC is a `font-spec'."
`(unless doom-big-font `(unless doom-big-font
(setq doom-big-font (font-spec :family ,family ,@spec)))) (setq doom-big-font (font-spec :family ,family ,@spec))))
(def-setting! :unicode-font (family &rest spec) (def-setting! :unicode-font (family &rest spec)
"Sets the font to use for unicode characters (if one wasn't already set).
FAMILY is the name of the font, and SPEC is a `font-spec'. This is ignored if
the ':ui unicode' module is enabled."
`(unless doom-unicode-font `(unless doom-unicode-font
(setq doom-unicode-font (font-spec :family ,family ,@spec)))) (setq doom-unicode-font (font-spec :family ,family ,@spec))))
@ -169,12 +179,9 @@ local value, whether or not it's permanent-local. Therefore, we cycle
"Set the major mode's `mode-name', as dictated by `doom-major-mode-names'." "Set the major mode's `mode-name', as dictated by `doom-major-mode-names'."
(when-let (name (cdr (assq major-mode doom-major-mode-names))) (when-let (name (cdr (assq major-mode doom-major-mode-names)))
(setq mode-name (setq mode-name
(cond ((functionp name) (cond ((functionp name) (funcall name))
(funcall name)) ((stringp name) name)
((stringp name) (t (error "'%s' isn't a valid name for %s" name major-mode))))))
name)
(t
(error "'%s' isn't a valid name for %s" name major-mode))))))
(add-hook 'after-change-major-mode-hook #'doom|set-mode-name) (add-hook 'after-change-major-mode-hook #'doom|set-mode-name)
@ -227,7 +234,7 @@ local value, whether or not it's permanent-local. Therefore, we cycle
;; prompts the user for confirmation when deleting a non-empty frame ;; prompts the user for confirmation when deleting a non-empty frame
(define-key global-map [remap delete-frame] #'doom/delete-frame) (define-key global-map [remap delete-frame] #'doom/delete-frame)
;; buffer name in frame title ;; simple name in frame title
(setq-default frame-title-format '("DOOM Emacs")) (setq-default frame-title-format '("DOOM Emacs"))
;; auto-enabled in Emacs 25+; I'll do it myself ;; auto-enabled in Emacs 25+; I'll do it myself
(global-eldoc-mode -1) (global-eldoc-mode -1)
@ -273,6 +280,7 @@ local value, whether or not it's permanent-local. Therefore, we cycle
:commands (fringe-helper-define fringe-helper-convert) :commands (fringe-helper-define fringe-helper-convert)
:init :init
(unless (fboundp 'define-fringe-bitmap) (unless (fboundp 'define-fringe-bitmap)
;; doesn't exist in terminal Emacs; define it to prevent errors
(defun define-fringe-bitmap (&rest _)))) (defun define-fringe-bitmap (&rest _))))
(def-package! hideshow ; built-in (def-package! hideshow ; built-in
@ -310,13 +318,6 @@ local value, whether or not it's permanent-local. Therefore, we cycle
:config (setq rainbow-delimiters-max-face-count 3) :config (setq rainbow-delimiters-max-face-count 3)
:init (add-hook 'lisp-mode-hook #'rainbow-delimiters-mode)) :init (add-hook 'lisp-mode-hook #'rainbow-delimiters-mode))
;; indicators for empty lines past EOF
(def-package! vi-tilde-fringe
:commands (global-vi-tilde-fringe-mode vi-tilde-fringe-mode)
:init
(add-hook 'doom-init-ui-hook #'global-vi-tilde-fringe-mode)
(defun doom|disable-vi-tilde-fringe () (vi-tilde-fringe-mode -1)))
;; For a distractions-free-like UI, that dynamically resizes margets and can ;; For a distractions-free-like UI, that dynamically resizes margets and can
;; center a buffer. ;; center a buffer.
(def-package! visual-fill-column (def-package! visual-fill-column

View File

@ -8,14 +8,16 @@
;; doom:... an evil operator, motion or command ;; doom:... an evil operator, motion or command
;; doom|... hook function ;; doom|... hook function
;; doom*... advising functions ;; doom*... advising functions
;; doom@... a hydra command
;; ...! a macro or function that configures DOOM ;; ...! a macro or function that configures DOOM
;; =... an interactive command that starts an app module
;; %... functions used for in-snippet logic ;; %... functions used for in-snippet logic
;; +... Any of the above but part of a module, e.g. `+emacs-lisp|init-hook' ;; +... Any of the above but part of a module, e.g. `+emacs-lisp|init-hook'
;; ;;
;; Autoloaded functions are in core/autoload/*.el and modules/*/*/autoload.el or ;; Autoloaded functions are in core/autoload/*.el and modules/*/*/autoload.el or
;; modules/*/*/autoload/*.el. ;; modules/*/*/autoload/*.el.
(defvar doom-version "2.0.5" (defvar doom-version "2.0.6"
"Current version of DOOM emacs.") "Current version of DOOM emacs.")
(defvar doom-debug-mode (or (getenv "DEBUG") init-file-debug) (defvar doom-debug-mode (or (getenv "DEBUG") init-file-debug)
@ -61,8 +63,7 @@ problems.")
(defvar doom-packages-dir (concat doom-local-dir "packages/") (defvar doom-packages-dir (concat doom-local-dir "packages/")
"Where package.el and quelpa plugins (and their caches) are stored.") "Where package.el and quelpa plugins (and their caches) are stored.")
(defvar doom-autoload-file (defvar doom-autoload-file (concat doom-local-dir "autoloads.el")
(concat doom-local-dir "autoloads.el")
"Location of the autoloads file generated by `doom/reload-autoloads'.") "Location of the autoloads file generated by `doom/reload-autoloads'.")
(defgroup doom nil (defgroup doom nil

View File

@ -22,7 +22,6 @@
(package! nlinum-hl) (package! nlinum-hl)
(package! nlinum-relative)) (package! nlinum-relative))
(package! rainbow-delimiters) (package! rainbow-delimiters)
(package! vi-tilde-fringe)
(package! visual-fill-column) (package! visual-fill-column)
;; core-popups.el ;; core-popups.el
@ -36,8 +35,6 @@
(package! editorconfig) (package! editorconfig)
(package! expand-region) (package! expand-region)
(package! help-fns+) (package! help-fns+)
(package! imenu-anywhere)
(package! imenu-list)
(package! pcre2el) (package! pcre2el)
(package! smart-forward) (package! smart-forward)
(package! smartparens) (package! smartparens)
@ -49,3 +46,4 @@
;; core-keybinds.el ;; core-keybinds.el
(package! which-key) (package! which-key)
(package! hydra)

View File

@ -3,18 +3,18 @@
;; --- Helpers ---------------------------- ;; --- Helpers ----------------------------
;; `doom--resolve-paths' ;; `doom--resolve-path-forms'
(def-test! resolve-paths (def-test! resolve-path-forms
(should (should
(equal (doom--resolve-paths '(and "fileA" "fileB")) (equal (doom--resolve-path-forms '(and "fileA" "fileB"))
'(and (file-exists-p (expand-file-name "fileA" (doom-project-root))) '(and (file-exists-p (expand-file-name "fileA" (doom-project-root)))
(file-exists-p (expand-file-name "fileB" (doom-project-root))))))) (file-exists-p (expand-file-name "fileB" (doom-project-root)))))))
;; `doom--resolve-hooks' ;; `doom--resolve-hook-forms'
(def-test! resolve-hooks (def-test! resolve-hook-forms
(should (equal (doom--resolve-hooks '(js2-mode haskell-mode)) (should (equal (doom--resolve-hook-forms '(js2-mode haskell-mode))
'(js2-mode-hook haskell-mode-hook))) '(js2-mode-hook haskell-mode-hook)))
(should (equal (doom--resolve-hooks '(quote (js2-mode-hook haskell-mode-hook))) (should (equal (doom--resolve-hook-forms '(quote (js2-mode-hook haskell-mode-hook)))
'(js2-mode-hook haskell-mode-hook)))) '(js2-mode-hook haskell-mode-hook))))
;; `doom-unquote' ;; `doom-unquote'
@ -32,6 +32,48 @@
(should (equal (doom-enlist 'a) '(a))) (should (equal (doom-enlist 'a) '(a)))
(should (equal (doom-enlist '(a)) '(a)))) (should (equal (doom-enlist '(a)) '(a))))
;; `doom-resolve-vim-path'
(def-test! resolve-vim-path
(cl-flet ((do-it #'doom-resolve-vim-path))
;; file modifiers
(let ((buffer-file-name "~/.emacs.d/test/modules/feature/test-evil.el")
(default-directory "~/.emacs.d/test/modules/"))
(should (equal (do-it "%") "feature/test-evil.el"))
(should (equal (do-it "%:r") "feature/test-evil"))
(should (equal (do-it "%:r.elc") "feature/test-evil.elc"))
(should (equal (do-it "%:e") "el"))
(should (equal (do-it "%:p") (expand-file-name buffer-file-name)))
(should (equal (do-it "%:h") "feature"))
(should (equal (do-it "%:t") "test-evil.el"))
(should (equal (do-it "%:.") "feature/test-evil.el"))
(should (equal (do-it "%:~") "~/.emacs.d/test/modules/feature/test-evil.el"))
(should (equal (file-truename (do-it "%:p"))
(file-truename buffer-file-name))))
;; nested file modifiers
(let ((buffer-file-name "~/vim/src/version.c")
(default-directory "~/vim/"))
(should (equal (do-it "%:p") (expand-file-name "~/vim/src/version.c")))
(should (equal (do-it "%:p:.") "src/version.c"))
(should (equal (do-it "%:p:~") "~/vim/src/version.c"))
(should (equal (do-it "%:h") "src"))
(should (equal (do-it "%:p:h") (expand-file-name "~/vim/src")))
(should (equal (do-it "%:p:h:h") (expand-file-name "~/vim")))
(should (equal (do-it "%:t") "version.c"))
(should (equal (do-it "%:p:t") "version.c"))
(should (equal (do-it "%:r") "src/version"))
(should (equal (do-it "%:p:r") (expand-file-name "~/vim/src/version")))
(should (equal (do-it "%:t:r") "version")))
;; empty file modifiers
(let (buffer-file-name default-directory)
(should (equal (do-it "%") ""))
(should (equal (do-it "%:r") ""))
(should (equal (do-it "%:e") ""))
(should (equal (do-it "%:h") ""))
(should (equal (do-it "%:t") ""))
(should (equal (do-it "%:.") ""))
(should (equal (do-it "%:~") ""))
(should (equal (do-it "%:P") "")))))
;; --- Macros ----------------------------- ;; --- Macros -----------------------------

View File

@ -30,17 +30,17 @@
(require 'core (concat user-emacs-directory "core/core")) (require 'core (concat user-emacs-directory "core/core"))
(doom! :feature (doom! :feature
;debugger ; FIXME stepping through code, to help you add bugs
eval ; run code, run (also, repls)
evil ; come to the dark side, we have cookies evil ; come to the dark side, we have cookies
jump ; helping you get around
snippets ; my elves. They type so I don't have to
file-templates ; auto-snippets for empty files file-templates ; auto-snippets for empty files
hydra ; keybindings that stick around jump ; helping you get around
services ; TODO managing external services & code builders
snippets ; my elves. They type so I don't have to
spellcheck ; tasing you for misspelling mispelling spellcheck ; tasing you for misspelling mispelling
syntax-checker ; tasing you for every semicolon you forget syntax-checker ; tasing you for every semicolon you forget
version-control ; remember, remember that commit in November version-control ; remember, remember that commit in November
workspaces ; tab emulation, persistence & separate workspaces workspaces ; tab emulation, persistence & separate workspaces
eval ; repls, runners 'n builders; run code, run
;debug ; FIXME stepping through code, to help you add bugs
:completion :completion
company ; the ultimate code completion backend company ; the ultimate code completion backend
@ -58,55 +58,57 @@
evil-goggles ; display visual hints when editing in evil evil-goggles ; display visual hints when editing in evil
;unicode ; extended unicode support for various languages ;unicode ; extended unicode support for various languages
;tabbar ; FIXME an (incomplete) tab bar for Emacs ;tabbar ; FIXME an (incomplete) tab bar for Emacs
vi-tilde-fringe ; fringe tildes to mark beyond EOB
:tools :tools
dired ; making dired pretty [functional] dired ; making dired pretty [functional]
electric-indent ; smarter, keyword-based electric-indent electric-indent ; smarter, keyword-based electric-indent
eshell ; a consistent, cross-platform shell (WIP) eshell ; a consistent, cross-platform shell (WIP)
gist ; interacting with github gists gist ; interacting with github gists
imenu ; an imenu sidebar and searchable code index
impatient-mode ; show off code over HTTP impatient-mode ; show off code over HTTP
;macos ; MacOS-specific commands ;macos ; MacOS-specific commands
make ; run make tasks from Emacs
neotree ; a project drawer, like NERDTree for vim neotree ; a project drawer, like NERDTree for vim
password-store ; password manager for nerds password-store ; password manager for nerds
prodigy ; manage external services from within emacs
rotate-text ; cycle region at point between text candidates rotate-text ; cycle region at point between text candidates
term ; terminals in Emacs term ; terminals in Emacs
tmux ; an API for interacting with tmux tmux ; an API for interacting with tmux
upload ; map local to remote projects via ssh/ftp upload ; map local to remote projects via ssh/ftp
:lang :lang
;assembly ; assembly for fun or debugging assembly ; assembly for fun or debugging
;cc ; C/C++/Obj-C madness cc ; C/C++/Obj-C madness
;crystal ; ruby at the speed of c crystal ; ruby at the speed of c
;csharp ; unity, .NET, and mono shenanigans csharp ; unity, .NET, and mono shenanigans
;data ; config/data formats data ; config/data formats
;elixir ; erlang done right elixir ; erlang done right
;elm ; care for a cup of TEA? elm ; care for a cup of TEA?
emacs-lisp ; drown in parentheses emacs-lisp ; drown in parentheses
;go ; the hipster dialect go ; the hipster dialect
;haskell ; a language that's lazier than I am (haskell +intero) ; a language that's lazier than I am
;hy ; readability of scheme w/ speed of python hy ; readability of scheme w/ speed of python
;java ; the poster child for carpal tunnel syndrome (java +meghanada) ; the poster child for carpal tunnel syndrome
;javascript ; all(hope(abandon(ye(who(enter(here)))))) javascript ; all(hope(abandon(ye(who(enter(here))))))
;julia ; a better, faster MATLAB julia ; a better, faster MATLAB
;latex ; writing papers in Emacs has never been so fun latex ; writing papers in Emacs has never been so fun
;ledger ; an accounting system in Emacs ledger ; an accounting system in Emacs
;lua ; one-based indices? one-based indices lua ; one-based indices? one-based indices
;markdown ; writing docs for people to ignore markdown ; writing docs for people to ignore
;ocaml ; an objective camel ocaml ; an objective camel
;perl ; write code no one else can comprehend perl ; write code no one else can comprehend
;php ; make php less awful to work with php ; make php less awful to work with
;plantuml ; diagrams for confusing people more plantuml ; diagrams for confusing people more
;purescript ; javascript, but functional purescript ; javascript, but functional
;python ; beautiful is better than ugly python ; beautiful is better than ugly
;rest ; Emacs as a REST client rest ; Emacs as a REST client
;ruby ; 1.step do {|i| p "Ruby is #{i.even? ? 'love' : 'life'}"} ruby ; 1.step do {|i| p "Ruby is #{i.even? ? 'love' : 'life'}"}
;rust ; Fe2O3.unwrap().unwrap().unwrap().unwrap() rust ; Fe2O3.unwrap().unwrap().unwrap().unwrap()
;scala ; java, but good scala ; java, but good
;sh ; she sells (ba|z)sh shells on the C xor sh ; she sells (ba|z)sh shells on the C xor
;swift ; who asked for emoji variables? swift ; who asked for emoji variables?
;typescript ; javascript, but better typescript ; javascript, but better
;web ; the tubes web ; the tubes
:org :org
org ; organize your plain life in plain text org ; organize your plain life in plain text
@ -114,7 +116,6 @@
org-attach ; a simpler attachment system org-attach ; a simpler attachment system
org-capture ; a better org-capture, in or outside of Emacs org-capture ; a better org-capture, in or outside of Emacs
org-export ; a custom, centralized export system org-export ; a custom, centralized export system
org-notebook ; org-mode as a notebook
org-present ; using org-mode for presentations org-present ; using org-mode for presentations
;org-sync ; TODO sync with mobile ;org-sync ; TODO sync with mobile
;org-publish ; TODO org + blogs ;org-publish ; TODO org + blogs
@ -130,7 +131,6 @@
write ; emacs as a word processor (latex + org + markdown) write ; emacs as a word processor (latex + org + markdown)
;; Private modules named after your username are loaded automatically. ;; Private modules named after your username are loaded automatically.
;; Leaving this here is harmless though. Also, they are omitted from ;; Leaving this here is harmless though.
;; source control (except for mine; use it as a reference).
:private hlissner) :private hlissner)

View File

@ -15,5 +15,8 @@
:lang :lang
web web
:org
org
:private :private
hlissner) hlissner)

View File

@ -26,9 +26,11 @@
(all-files-p +ivy--file-search-all-files-p) (all-files-p +ivy--file-search-all-files-p)
(query (query
(or query (or query
(if (evil-visual-state-p)
(and beg end (and beg end
(> (abs (- end beg)) 1) (> (abs (- end beg)) 1)
(rxt-quote-pcre (buffer-substring-no-properties beg end))) (rxt-quote-pcre (buffer-substring-no-properties beg end)))
+ivy--file-last-search)
+ivy--file-last-search)) +ivy--file-last-search))
(prompt (prompt
(format "%s%%s %s" (format "%s%%s %s"

View File

@ -10,9 +10,8 @@
face to render it with.") face to render it with.")
(defmacro +ivy-do-action! (action) (defmacro +ivy-do-action! (action)
"A factory function that returns an interactive lamba that sets the current "Returns an interactive lambda that sets the current ivy action and
ivy action and immediately runs it on the current candidate (ending the ivy immediately runs it on the current candidate (ending the ivy session)."
session)."
`(lambda () `(lambda ()
(interactive) (interactive)
(ivy-set-action ,action) (ivy-set-action ,action)
@ -63,10 +62,10 @@ session)."
[remap describe-face] #'counsel-describe-face) [remap describe-face] #'counsel-describe-face)
;; Show more buffer information in switch-buffer commands ;; 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 #'+ivy-buffer-transformer)
(ivy-set-display-transformer 'ivy-switch-buffer-other-window #'+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 #'+ivy/switch-workspace-buffer #'+ivy-buffer-transformer)
(ivy-set-display-transformer 'counsel-recentf #'abbreviate-file-name) (ivy-set-display-transformer #'counsel-recentf #'abbreviate-file-name)
(when (featurep! :feature workspaces) (when (featurep! :feature workspaces)
(nconc ivy-sort-functions-alist (nconc ivy-sort-functions-alist
@ -111,3 +110,51 @@ session)."
(setq smex-save-file (concat doom-cache-dir "/smex-items")) (setq smex-save-file (concat doom-cache-dir "/smex-items"))
(smex-initialize)) (smex-initialize))
(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)
;; ("d" ivy-done :exit t)
("RET" ivy-done :exit t)
("C-m" ivy-done :exit t)
("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)))

View File

@ -6,3 +6,4 @@
(package! counsel-projectile) (package! counsel-projectile)
(package! smex) (package! smex)
(package! swiper) (package! swiper)
(package! ivy-hydra)

View File

@ -1,9 +0,0 @@
;;; feature/debug/autoload/debug.el -*- lexical-binding: t; -*-
;;;###autoload
(defun +debug/quit ()
(interactive)
(ignore-errors (call-interactively 'realgud:cmd-quit))
(doom/popup-close)
(evil-normal-state))

View File

@ -1,31 +0,0 @@
;;; feature/debug/autoload/evil.el -*- lexical-binding: t; -*-
;;;###autoload (autoload '+debug:run "feature/debug/autoload/evil" nil t)
(evil-define-command +debug:run (&optional path)
"Initiate debugger for current major mode"
(interactive "<f>")
(let ((default-directory (doom-project-root)))
(cond ((memq major-mode '(c-mode c++-mode))
(realgud:gdb (if path (concat "gdb " path))))
((memq major-mode '(ruby-mode enh-ruby-mode))
(doom:repl nil (format "run '%s'" (file-name-nondirectory (or path buffer-file-name)))))
((eq major-mode 'sh-mode)
(let ((shell sh-shell))
(when (string= shell "sh")
(setq shell "bash"))
(cond ((string= shell "bash")
(realgud:bashdb (if path (concat "bashdb " path))))
((string= shell "zsh")
(realgud:zshdb (if path (concat "zshdb " path))))
(t (user-error "No shell debugger for %s" shell)))))
;; TODO Add python debugging
((memq major-mode '(js-mode js2-mode js3-mode))
(realgud:trepanjs))
((eq major-mode 'haskell-mode)
(haskell-debug))
(t (user-error "No debugger for %s" major-mode)))))
;;;###autoload (autoload '+debug:toggle-breakpoint "feature/debug/autoload/evil" nil t)
(evil-define-command +debug:toggle-breakpoint (&optional bang)
(interactive "<!>")
(call-interactively (if bang 'realgud:cmd-clear 'realgud:cmd-break)))

View File

@ -0,0 +1,11 @@
;;; feature/debugger/autoload/debug.el -*- lexical-binding: t; -*-
;;;###autoload
(defun +debugger/quit ()
"Quit the active debugger, if any."
(interactive)
(ignore-errors (call-interactively #'realgud:cmd-quit))
(doom/popup-close)
(when (featurep 'evil)
(evil-normal-state)))

View File

@ -0,0 +1,33 @@
;;; feature/debugger/autoload/evil.el -*- lexical-binding: t; -*-
;;;###autoload (autoload '+debugger:start "feature/debugger/autoload/evil" nil t)
(evil-define-command +debugger:start (&optional path)
"Initiate debugger for current major mode"
(interactive "<f>")
;; TODO Add python debugging
(let ((default-directory (doom-project-root)))
(pcase major-mode
((or 'c-mode 'c++-mode)
(realgud:gdb (if path (concat "gdb " path))))
((or 'ruby-mode 'enh-ruby-mode)
;; FIXME
(doom:repl nil (format "run '%s'" (file-name-nondirectory (or path buffer-file-name)))))
('sh-mode
(let ((shell sh-shell))
(when (string= shell "sh")
(setq shell "bash"))
(pcase shell
("bash"
(realgud:bashdb (if path (concat "bashdb " path))))
("zsh"
(realgud:zshdb (if path (concat "zshdb " path))))
(_ (user-error "No shell debugger for %s" shell)))))
((or 'js-mode 'js2-mode 'js3-mode)
(realgud:trepanjs))
('haskell-mode (haskell-debug))
(_ (user-error "No debugger for %s" major-mode)))))
;;;###autoload (autoload '+debugger:toggle-breakpoint "feature/debugger/autoload/evil" nil t)
(evil-define-command +debugger:toggle-breakpoint (&optional bang)
(interactive "<!>")
(call-interactively (if bang #'realgud:cmd-clear #'realgud:cmd-break)))

View File

@ -1,4 +1,4 @@
;;; feature/debug/config.el -*- lexical-binding: t; -*- ;;; feature/debugger/config.el -*- lexical-binding: t; -*-
(def-package! realgud (def-package! realgud
:commands (realgud:gdb realgud:trepanjs realgud:bashdb realgud:zshdb) :commands (realgud:gdb realgud:trepanjs realgud:bashdb realgud:zshdb)
@ -19,7 +19,7 @@
;; Monkey-patch `realgud:run-process' to run in a popup. ;; Monkey-patch `realgud:run-process' to run in a popup.
;; TODO Find a more elegant solution ;; TODO Find a more elegant solution
;; FIXME Causes realgud:cmd-* to focus popup on every invocation ;; FIXME Causes realgud:cmd-* to focus popup on every invocation
(defun +debug*realgud-run-process (defun +debugger*realgud-run-process
(debugger-name script-filename cmd-args minibuffer-history-var &optional no-reset) (debugger-name script-filename cmd-args minibuffer-history-var &optional no-reset)
(let* ((cmd-buf (apply #'realgud-exec-shell debugger-name script-filename (let* ((cmd-buf (apply #'realgud-exec-shell debugger-name script-filename
(car cmd-args) no-reset (cdr cmd-args))) (car cmd-args) no-reset (cdr cmd-args)))
@ -42,5 +42,5 @@
(if cmd-buf (switch-to-buffer cmd-buf)) (if cmd-buf (switch-to-buffer cmd-buf))
(message "Error running command: %s" (mapconcat #'identity cmd-args " ")))) (message "Error running command: %s" (mapconcat #'identity cmd-args " "))))
cmd-buf)) cmd-buf))
(advice-add #'realgud:run-process :override #'+debug*realgud-run-process)) (advice-add #'realgud:run-process :override #'+debugger*realgud-run-process))

View File

@ -1,4 +1,4 @@
;; -*- no-byte-compile: t; -*- ;; -*- no-byte-compile: t; -*-
;;; feature/debug/packages.el ;;; feature/debugger/packages.el
(package! realgud) (package! realgud)

View File

@ -1,13 +1,12 @@
#+TITLE: :feature eval #+TITLE: :feature eval
This modules adds support for REPLs, build tasks and code evaluation. This modules adds support for evaluating code from inside Emacs. This includes REPLs and direct access to the interpreters and compilers of many languages.
* Table of Contents :TOC: * Table of Contents :TOC:
- [[#install][Install]] - [[#install][Install]]
- [[#usage][Usage]] - [[#usage][Usage]]
- [[#configuration][Configuration]] - [[#configuration][Configuration]]
- [[#repls][REPLs]] - [[#repls][REPLs]]
- [[#build-tasks][Build Tasks]]
- [[#code-evaluation][Code Evaluation]] - [[#code-evaluation][Code Evaluation]]
* Install * Install
@ -20,15 +19,8 @@ Check the README.org in that language's module for details.
Invoked via: Invoked via:
+ ~:repl~ (evil ex-command) + ~:repl~ (evil ex-command)
+ =<leader> o r= in normal mode (or visual mode, which sends the selection to the open REPL) + =<leader> o r= in normal mode (or visual mode, which sends the selection to the open REPL)
+ ~M-x +eval/repl~ + ~M-x +eval/open-repl~
+ ~M-x +eval/repl-send-region~ while a selection (and REPL) is active + ~M-x +eval/send-region-to-repl~ while a selection (and REPL) is active
+ *Build Tasks*
You will be prompted to select a task. Only the ones that meet the predicate will be listed.
+ ~:build~ (evil ex-command)
+ =M-b= (by default)
+ =<leader> o b= in normal mode
+ ~M-x +eval/build~
+ *Code Evaluation* + *Code Evaluation*
Quickrun can be invoked via: Quickrun can be invoked via:
@ -60,35 +52,6 @@ FUNCTION must return the repl buffer. Any window changes are ignored, then hande
(set! :repl 'emacs-lisp-mode #'+emacs-lisp/repl) (set! :repl 'emacs-lisp-mode #'+emacs-lisp/repl)
#+END_SRC #+END_SRC
** Build Tasks
A build task is little more than a major-mode-local interactive command that performs a task, such as compiling the current project or running unit tests. A predicate function can be supplied to ensure a command is only available when it is appropriate.
#+BEGIN_SRC emacs-lisp
(defun +lua/run-love ()
"Run the current project in love 10.0."
(async-shell-command
(format "/usr/bin/love %s"
(shell-quote-argument (doom-project-root)))))
(defun +lua/build ()
"Run a build script in the project root."
(let ((default-directory (doom-project-root)))
(compile "luajit build.lua")))
(defun +lua/generate-docs ()
"Generate project documentation."
(let ((default-directory (doom-project-root)))
(compile "luadoc *.lua")))
(defun +lua-love-p ()
"Returns non-nil if the current project is a love project."
(doom-project-has! (and "main.lua" "config.lua")))
(set! :build 'run 'lua-mode #'+lua/run-love :when (+lua-love-p))
(set! :build 'build-project 'lua-mode #'+lua/build :when (+lua-love-p))
(set! :build 'generate-docs 'lua-mode #'+lua/generate-docs)
#+END_SRC
** Code Evaluation ** Code Evaluation
Run regions or entire buffers with [[https://github.com/syohex/emacs-quickrun][Quickrun]]. Output will be sent to a popup window. Run regions or entire buffers with [[https://github.com/syohex/emacs-quickrun][Quickrun]]. Output will be sent to a popup window.

View File

@ -1,45 +0,0 @@
;;; feature/eval/autoload/build.el -*- lexical-binding: t; -*-
(defvar-local +eval-last-builder nil
"The last builder run in the current buffer.")
(defvar +eval-current-builder nil
"The spec for the currently running builder. Available from inside builder
functions.")
(defun +eval--read-builder ()
(when-let (builders
(cl-remove-if-not
(lambda (plist)
(if-let (pred (plist-get plist :when))
(and (or (symbolp pred)
(functionp pred))
(funcall pred))
t))
(cl-delete-duplicates
(reverse (cdr (assq major-mode +eval-builders)))
:key 'car)
:key 'cdr))
(if (= (length builders) 1)
(car builders)
(when-let (builder (completing-read "Build: " (mapcar #'car builders) nil t))
(assq (intern builder) builders)))))
;;;###autoload
(defun +eval/build (builder)
"TODO"
(interactive
(list (or +eval-last-builder
(+eval--read-builder)
(error "No builder for this buffer"))))
(unless builder
(error "Builder not found in registered builders"))
(let ((name (car builder))
(fn (plist-get (cdr builder) :fn)))
(message "Running %s" name)
(if (or (functionp fn)
(and (symbolp fn) (fboundp fn)))
(let ((+eval-current-builder builder))
(funcall fn))
(error "'%s' builder is invalid" name))))

View File

@ -4,22 +4,22 @@
(defun +eval/buffer () (defun +eval/buffer ()
"Evaluate the whole buffer." "Evaluate the whole buffer."
(interactive) (interactive)
(cond ((assq major-mode +eval-runners-alist) (cond ((assq major-mode +eval-runners)
(+eval/region (point-min) (point-max))) (+eval/region (point-min) (point-max)))
(t (quickrun)))) (t (quickrun))))
;;;###autoload ;;;###autoload
(defun +eval/region (beg end) (defun +eval/region (beg end)
"Evaluate a region and, if large enough, prints its output to a popup buffer (if an "Evaluate a region between BEG and END and display the output."
elisp buffer). Otherwise forward the region to Quickrun."
(interactive "r") (interactive "r")
(let ((load-file-name buffer-file-name)) (let ((load-file-name buffer-file-name))
(if-let (runner (cdr (assq major-mode +eval-runners-alist))) (if-let (runner (cdr (assq major-mode +eval-runners)))
(funcall runner beg end) (funcall runner beg end)
(quickrun-region beg end)))) (quickrun-region beg end))))
;;;###autoload ;;;###autoload
(defun +eval/region-and-replace (beg end) (defun +eval/region-and-replace (beg end)
"Evaluation a region between BEG and END, and replace it with the result."
(interactive "r") (interactive "r")
(cond ((eq major-mode 'emacs-lisp-mode) (cond ((eq major-mode 'emacs-lisp-mode)
(kill-region beg end) (kill-region beg end)

View File

@ -18,5 +18,5 @@
:move-point nil :move-point nil
(interactive "<r><!>") (interactive "<r><!>")
(if (evil-normal-state-p) (if (evil-normal-state-p)
(+eval/repl) (+eval/open-repl)
(+eval/repl-send-region beg end bang))) (+eval/send-region-to-repl beg end bang)))

View File

@ -25,7 +25,7 @@
t)))) t))))
;;;###autoload ;;;###autoload
(defun +eval/repl () (defun +eval/open-repl ()
"Opens (or reopens) the REPL associated with the current major-mode and place "Opens (or reopens) the REPL associated with the current major-mode and place
the cursor at the prompt." the cursor at the prompt."
(interactive) (interactive)
@ -36,7 +36,7 @@ the cursor at the prompt."
t))) t)))
;;;###autoload ;;;###autoload
(defun +eval/repl-send-region (beg end &optional auto-execute-p) (defun +eval/send-region-to-repl (beg end &optional auto-execute-p)
"REPL must be open! Sends a selected region to it. If AUTO-EXECUTE-P, then "REPL must be open! Sends a selected region to it. If AUTO-EXECUTE-P, then
execute it immediately after." execute it immediately after."
(interactive "r") (interactive "r")

View File

@ -1,39 +1,15 @@
;;; feature/eval/config.el -*- lexical-binding: t; -*- ;;; feature/eval/config.el -*- lexical-binding: t; -*-
;;
;; Code building
;;
(defvar +eval-builders nil
"A nested alist, mapping major modes to build function plists. Used by
`+eval/build' and filled with the `:build' setting.")
(def-setting! :build (name modes fn &rest plist)
"Define a build function (FN) for MODES (can be major or minor) called NAME.
PLIST accepts the following properties:
:when FORM A predicate to determine if the builder is appropriate for this
buffer."
`(dolist (mode ',(doom-enlist (doom-unquote modes)) +eval-builders)
(unless (assq mode +eval-builders)
(push (list mode) +eval-builders))
(cl-pushnew (cons ,name (append (list :fn ,fn) (list ,@plist)))
(cdr (assq mode +eval-builders))
:test #'eq :key #'car)))
;; ;;
;; REPLs ;; REPLs
;; ;;
(defvar +eval-repls nil (defvar +eval-repls nil
"An alist mapping major modes to plists that describe REPLs. Used by "An alist mapping major modes to plists that describe REPLs. Used by
`+eval/repl' and filled with the `:repl' setting.") `+eval/open-repl' and filled with the `:repl' setting.")
(define-minor-mode +eval-repl-mode (define-minor-mode +eval-repl-mode
"A minor mode for REPL buffers." "A minor mode for REPL buffers.")
:init-value nil)
(def-setting! :repl (mode command) (def-setting! :repl (mode command)
"Define a REPL for a mode. MODE is a major mode symbol and COMMAND is a "Define a REPL for a mode. MODE is a major mode symbol and COMMAND is a
@ -53,7 +29,7 @@ function that creates and returns the REPL buffer."
(setq eval-expression-print-length nil (setq eval-expression-print-length nil
eval-expression-print-level nil) eval-expression-print-level nil)
(defvar +eval-runners-alist nil (defvar +eval-runners nil
"Alist mapping major modes to interactive runner functions.") "Alist mapping major modes to interactive runner functions.")
(def-setting! :eval (mode command) (def-setting! :eval (mode command)
@ -67,10 +43,10 @@ function that creates and returns the REPL buffer."
3. If MODE is not a string and COMMAND is an alist, see `quickrun-add-command': 3. If MODE is not a string and COMMAND is an alist, see `quickrun-add-command':
(quickrun-add-command MODE COMMAND :mode MODE). (quickrun-add-command MODE COMMAND :mode MODE).
4. If MODE is not a string and COMMANd is a symbol, add it to 4. If MODE is not a string and COMMANd is a symbol, add it to
`+eval-runners-alist', which is used by `+eval/region'." `+eval-runners', which is used by `+eval/region'."
(let ((command (doom-unquote command))) (let ((command (doom-unquote command)))
(cond ((symbolp command) (cond ((symbolp command)
`(push (cons ,mode ',command) +eval-runners-alist)) `(push (cons ,mode ',command) +eval-runners))
((stringp command) ((stringp command)
`(after! quickrun `(after! quickrun
(push (cons ,mode ',command) (push (cons ,mode ',command)
@ -91,12 +67,10 @@ function that creates and returns the REPL buffer."
quickrun-compile-only quickrun-compile-only
quickrun-replace-region) quickrun-replace-region)
:init :init
(add-hook 'quickrun--mode-hook #'nlinum-mode) (unless (boundp 'display-line-numbers)
(add-hook 'quickrun--mode-hook #'nlinum-mode))
:config :config
(set! :popup (set! :popup "*quickrun*" :size 6 :autokill t :autoclose t)
'("*quickrun*" :size 10 :noesc t :autokill t :autoclose t)
'("*eval*" :size 12 :noselect t :autokill t :autoclose t)
'("*Pp Eval Output*" :size 12 :noselect t :autokill t :autoclose t))
(defun +eval*quickrun-auto-close (&rest _) (defun +eval*quickrun-auto-close (&rest _)
"Allows us to silently re-run quickrun from within the quickrun buffer." "Allows us to silently re-run quickrun from within the quickrun buffer."
@ -111,6 +85,7 @@ function that creates and returns the REPL buffer."
(defun +eval|quickrun-scroll-to-bof () (defun +eval|quickrun-scroll-to-bof ()
"Ensures window is scrolled to BOF on invocation." "Ensures window is scrolled to BOF on invocation."
(with-selected-window (get-buffer-window quickrun--buffer-name) (with-selected-window (get-buffer-window quickrun--buffer-name)
(goto-char (point-min)))) (goto-char (point-min))
(doom-popup-fit-to-buffer)))
(add-hook 'quickrun-after-run-hook #'+eval|quickrun-scroll-to-bof)) (add-hook 'quickrun-after-run-hook #'+eval|quickrun-scroll-to-bof))

View File

@ -26,81 +26,13 @@
(save-excursion (goto-char beg) (point-marker)) (save-excursion (goto-char beg) (point-marker))
end))) end)))
;;;###autoload
(defun +evil*ex-replace-special-filenames (file-name)
"Replace special symbols in FILE-NAME. Modified to include other substitution
flags. See http://vimdoc.sourceforge.net/htmldoc/cmdline.html#filename-modifiers."
(let* (case-fold-search
(regexp (concat "\\(?:^\\|[^\\\\]\\)"
"\\([#%]\\)"
"\\(\\(?::\\(?:[PphtreS~.]\\|g?s[^:\t\n ]+\\)\\)*\\)"))
(matches
(cl-loop with i = 0
while (and (< i (length file-name))
(string-match regexp file-name i))
do (setq i (1+ (match-beginning 0)))
and collect
(cl-loop for j to (/ (length (match-data)) 2)
collect (match-string j file-name)))))
(dolist (match matches)
(let ((flags (split-string (car (cdr (cdr match))) ":" t))
(path (and buffer-file-name
(pcase (car (cdr match))
("%" (file-relative-name buffer-file-name))
("#" (save-excursion (other-window 1) (file-relative-name buffer-file-name))))))
flag global)
(if (not path)
(setq path "")
(while flags
(setq flag (pop flags))
(when (string-suffix-p "\\" flag)
(setq flag (concat flag (pop flags))))
(when (string-prefix-p "gs" flag)
(setq global t
flag (substring flag 1)))
(setq path
(or (pcase (substring flag 0 1)
("p" (expand-file-name path))
("~" (concat "~/" (file-relative-name path "~")))
("." (file-relative-name path default-directory))
("t" (file-name-nondirectory (directory-file-name path)))
("r" (file-name-sans-extension path))
("e" (file-name-extension path))
("S" (shell-quote-argument path))
("h"
(let ((parent (file-name-directory (expand-file-name path))))
(unless (equal (file-truename path)
(file-truename parent))
(if (file-name-absolute-p path)
(directory-file-name parent)
(file-relative-name parent)))))
("s"
(when-let (args (evil-delimited-arguments (substring flag 1) 2))
(let ((pattern (evil-transform-vim-style-regexp (car args)))
(replace (cadr args)))
(replace-regexp-in-string
(if global pattern (concat "\\(" pattern "\\).*\\'"))
(evil-transform-vim-style-regexp replace) path t t
(unless global 1)))))
("P"
(let ((default-directory (file-name-directory (expand-file-name path))))
(abbreviate-file-name (doom-project-root))))
(_ path))
"")))
;; strip trailing slash, if applicable
(when (and (not (string= path "")) (equal (substring path -1) "/"))
(setq path (substring path 0 -1))))
(setq file-name
(replace-regexp-in-string (format "\\(?:^\\|[^\\\\]\\)\\(%s\\)"
(regexp-quote (string-trim-left (car match))))
path file-name t t 1))))
(setq file-name (replace-regexp-in-string regexp "\\1" file-name t))))
(defun +evil--window-swap (direction) (defun +evil--window-swap (direction)
"Move current window to the next window in DIRECTION. If there are no windows "Move current window to the next window in DIRECTION. If there are no windows
there and there is only one window, split in that direction and place this there and there is only one window, split in that direction and place this
window there. If there are no windows and this isn't the only window, use window there. If there are no windows and this isn't the only window, use
evil-window-move-* (e.g. `evil-window-move-far-left')" evil-window-move-* (e.g. `evil-window-move-far-left')"
(when (doom-popup-p)
(doom/popup-raise))
(let* ((this-window (get-buffer-window)) (let* ((this-window (get-buffer-window))
(this-buffer (current-buffer)) (this-buffer (current-buffer))
(that-window (windmove-find-other-window direction nil this-window)) (that-window (windmove-find-other-window direction nil this-window))

View File

@ -92,7 +92,7 @@
;; --- evil hacks ------------------------- ;; --- evil hacks -------------------------
(defvar +evil-esc-hook '(t) (defvar +evil-esc-hook '(t)
"A hook run after ESC is pressed in normal mode (invoked by "A hook run after ESC is pressed in normal mode (invoked by
`evil-force-normal-state'). If a hook returns non-nil, all hooks after it are `evil-force-normal-state'). If any hook returns non-nil, all hooks after it are
ignored.") ignored.")
(defun +evil*attach-escape-hook () (defun +evil*attach-escape-hook ()
@ -124,8 +124,7 @@ across windows."
;; monkey patch `evil-ex-replace-special-filenames' to add more ex ;; monkey patch `evil-ex-replace-special-filenames' to add more ex
;; substitution flags to evil-mode ;; substitution flags to evil-mode
(advice-add #'evil-ex-replace-special-filenames (advice-add #'evil-ex-replace-special-filenames :override #'doom-resolve-vim-path)
:override #'+evil*ex-replace-special-filenames)
;; These arg types will highlight matches in the current buffer ;; These arg types will highlight matches in the current buffer
(evil-ex-define-argument-type buffer-match :runner +evil-ex-buffer-match) (evil-ex-define-argument-type buffer-match :runner +evil-ex-buffer-match)
@ -144,6 +143,8 @@ across windows."
(evil-define-interactive-code "<//g>" (evil-define-interactive-code "<//g>"
:ex-arg global-match (list (when (evil-ex-p) evil-ex-argument))) :ex-arg global-match (list (when (evil-ex-p) evil-ex-argument)))
;; Forward declare these so that ex completion works, even if the autoloaded
;; functions aren't loaded yet.
(evil-set-command-properties (evil-set-command-properties
'+evil:align :move-point t :ex-arg 'buffer-match :ex-bang t :evil-mc t :keep-visual t :suppress-operator t) '+evil:align :move-point t :ex-arg 'buffer-match :ex-bang t :evil-mc t :keep-visual t :suppress-operator t)
(evil-set-command-properties (evil-set-command-properties
@ -282,7 +283,7 @@ the new algorithm is confusing, like in python or ruby."
:commands (evil-mc-make-cursor-here evil-mc-make-all-cursors :commands (evil-mc-make-cursor-here evil-mc-make-all-cursors
evil-mc-undo-all-cursors evil-mc-pause-cursors evil-mc-undo-all-cursors evil-mc-pause-cursors
evil-mc-resume-cursors evil-mc-make-and-goto-first-cursor evil-mc-resume-cursors evil-mc-make-and-goto-first-cursor
evil-mc-make-and-goto-last-cursor evil-mc-make-cursor-here evil-mc-make-and-goto-last-cursor
evil-mc-make-cursor-move-next-line evil-mc-make-cursor-move-next-line
evil-mc-make-cursor-move-prev-line evil-mc-make-cursor-at-pos 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-has-cursors-p evil-mc-make-and-goto-next-cursor
@ -382,3 +383,64 @@ the new algorithm is confusing, like in python or ruby."
(def-package! evil-textobj-anyblock (def-package! evil-textobj-anyblock
:commands (evil-textobj-anyblock-inner-block evil-textobj-anyblock-a-block)) :commands (evil-textobj-anyblock-inner-block evil-textobj-anyblock-a-block))
;;
;; Multiple cursors compatibility (for the plugins that use it)
;;
;; mc doesn't play well with evil, this attempts to assuage some of its problems
;; so that certain plugins (which I have no control over) can still use it in
;; relative safety.
(after! multiple-cursors-core
(map! :map mc/keymap :ne "<escape>" #'mc/keyboard-quit)
(defvar +evil--mc-compat-evil-prev-state nil)
(defvar +evil--mc-compat-mark-was-active nil)
(defsubst +evil--visual-or-normal-p ()
"True if evil mode is enabled, and we are in normal or visual mode."
(and (bound-and-true-p evil-mode)
(not (memq evil-state '(insert emacs)))))
(defun +evil|mc-compat-switch-to-emacs-state ()
(when (+evil--visual-or-normal-p)
(setq +evil--mc-compat-evil-prev-state evil-state)
(when (region-active-p)
(setq +evil--mc-compat-mark-was-active t))
(let ((mark-before (mark))
(point-before (point)))
(evil-emacs-state 1)
(when (or +evil--mc-compat-mark-was-active (region-active-p))
(goto-char point-before)
(set-mark mark-before)))))
(defun +evil|mc-compat-back-to-previous-state ()
(when +evil--mc-compat-evil-prev-state
(unwind-protect
(case +evil--mc-compat-evil-prev-state
((normal visual) (evil-force-normal-state))
(t (message "Don't know how to handle previous state: %S"
+evil--mc-compat-evil-prev-state)))
(setq +evil--mc-compat-evil-prev-state nil)
(setq +evil--mc-compat-mark-was-active nil))))
(add-hook 'multiple-cursors-mode-enabled-hook '+evil|mc-compat-switch-to-emacs-state)
(add-hook 'multiple-cursors-mode-disabled-hook '+evil|mc-compat-back-to-previous-state)
(defun +evil|mc-evil-compat-rect-switch-state ()
(if rectangular-region-mode
(+evil|mc-compat-switch-to-emacs-state)
(setq +evil--mc-compat-evil-prev-state nil)))
;; When running edit-lines, point will return (position + 1) as a
;; result of how evil deals with regions
(defadvice mc/edit-lines (before change-point-by-1 activate)
(when (+evil--visual-or-normal-p)
(if (> (point) (mark))
(goto-char (1- (point)))
(push-mark (1- (mark))))))
(add-hook 'rectangular-region-mode-hook '+evil|mc-evil-compat-rect-switch-state)
(defvar mc--default-cmds-to-run-once nil))

View File

@ -3,52 +3,19 @@
(require! :feature evil) (require! :feature evil)
;; `+evil*ex-replace-special-filenames' ;; `evil-ex-replace-special-filenames'
;; NOTE The majority of this function is tested in core/test/core-lib.el, this
;; only tests the evil-mode-specific functionality.
(def-test! file-modifiers (def-test! file-modifiers
(cl-flet ((do-it #'+evil*ex-replace-special-filenames)) (cl-flet ((do-it #'evil-ex-replace-special-filenames))
(let ((buffer-file-name "~/.emacs.d/test/modules/feature/test-evil.el") (let ((buffer-file-name "~/.emacs.d/test/modules/feature/test-evil.el")
(default-directory "~/.emacs.d/test/modules/")) (default-directory "~/.emacs.d/test/modules/"))
(should (equal (do-it "%") "feature/test-evil.el"))
(should (equal (do-it "%:r") "feature/test-evil"))
(should (equal (do-it "%:r.elc") "feature/test-evil.elc"))
(should (equal (do-it "%:e") "el"))
(should (equal (do-it "%:p") (expand-file-name buffer-file-name)))
(should (equal (do-it "%:h") "feature"))
(should (equal (do-it "%:t") "test-evil.el"))
(should (equal (do-it "%:.") "feature/test-evil.el"))
(should (equal (do-it "%:~") "~/.emacs.d/test/modules/feature/test-evil.el"))
(should (equal (do-it "%:s?e?x?") "fxature/test-evil.el")) (should (equal (do-it "%:s?e?x?") "fxature/test-evil.el"))
(should (equal (do-it "%:gs?e?x?") "fxaturx/txst-xvil.xl")) (should (equal (do-it "%:gs?e?x?") "fxaturx/txst-xvil.xl")))))
(should (equal (file-truename (do-it "%:p"))
(file-truename buffer-file-name))))))
(def-test! nested-file-modifiers
(cl-flet ((do-it #'+evil*ex-replace-special-filenames))
(let ((buffer-file-name "~/vim/src/version.c")
(default-directory "~/vim/"))
(should (equal (do-it "%:p") (expand-file-name "~/vim/src/version.c")))
(should (equal (do-it "%:p:.") "src/version.c"))
(should (equal (do-it "%:p:~") "~/vim/src/version.c"))
(should (equal (do-it "%:h") "src"))
(should (equal (do-it "%:p:h") (expand-file-name "~/vim/src")))
(should (equal (do-it "%:p:h:h") (expand-file-name "~/vim")))
(should (equal (do-it "%:t") "version.c"))
(should (equal (do-it "%:p:t") "version.c"))
(should (equal (do-it "%:r") "src/version"))
(should (equal (do-it "%:p:r") (expand-file-name "~/vim/src/version")))
(should (equal (do-it "%:t:r") "version")))))
(def-test! empty-file-modifiers (def-test! empty-file-modifiers
(cl-flet ((do-it #'+evil*ex-replace-special-filenames)) (cl-flet ((do-it #'evil-ex-replace-special-filenames))
(let (buffer-file-name default-directory) (let (buffer-file-name default-directory)
(should (equal (do-it "%") ""))
(should (equal (do-it "%:r") ""))
(should (equal (do-it "%:e") ""))
(should (equal (do-it "%:h") ""))
(should (equal (do-it "%:t") ""))
(should (equal (do-it "%:.") ""))
(should (equal (do-it "%:~") ""))
(should (equal (do-it "%:s?e?x?") "")) (should (equal (do-it "%:s?e?x?") ""))
(should (equal (do-it "%:gs?e?x?") "")) (should (equal (do-it "%:gs?e?x?") "")))))
(should (equal (do-it "%:P") "")))))

View File

@ -4,7 +4,7 @@
(defvar +file-templates-dir (defvar +file-templates-dir
(expand-file-name "templates/" (file-name-directory load-file-name)) (expand-file-name "templates/" (file-name-directory load-file-name))
"") "The path to a directory of yasnippet folders to use for file templates.")
(def-package! autoinsert ; built-in (def-package! autoinsert ; built-in
:defer 1 :defer 1
@ -12,12 +12,12 @@
(setq auto-insert-query nil ; Don't prompt before insertion (setq auto-insert-query nil ; Don't prompt before insertion
auto-insert-alist nil) ; Tabula rasa auto-insert-alist nil) ; Tabula rasa
(after! yasnippet
(push '+file-templates-dir yas-snippet-dirs))
:config :config
(auto-insert-mode 1) (auto-insert-mode 1)
(after! yasnippet
(push '+file-templates-dir yas-snippet-dirs))
(defun +file-templates--expand (key &optional mode project-only) (defun +file-templates--expand (key &optional mode project-only)
"Auto insert a snippet of yasnippet into new file." "Auto insert a snippet of yasnippet into new file."
(when (if project-only (doom-project-p) t) (when (if project-only (doom-project-p) t)
@ -55,6 +55,7 @@
("-test\\.el$" "__" emacs-ert-mode) ("-test\\.el$" "__" emacs-ert-mode)
("/.emacs.d/.+\\.el$" "__doom-module" emacs-lisp-mode) ("/.emacs.d/.+\\.el$" "__doom-module" emacs-lisp-mode)
("/.emacs.d/.+/packages\\.el$" "__doom-packages" emacs-lisp-mode) ("/.emacs.d/.+/packages\\.el$" "__doom-packages" emacs-lisp-mode)
("/.emacs.d/.+/test\\.el$" "__doom-test" emacs-lisp-mode)
("/.emacs.d/.+/README\\.org$" "__doom-readme" org-mode) ("/.emacs.d/.+/README\\.org$" "__doom-readme" org-mode)
(snippet-mode "__" snippet-mode) (snippet-mode "__" snippet-mode)
;; Go ;; Go
@ -72,8 +73,6 @@
("/bower\\.json$" "__bower.json" json-mode) ("/bower\\.json$" "__bower.json" json-mode)
("/gulpfile\\.js$" "__gulpfile.js" js-mode) ("/gulpfile\\.js$" "__gulpfile.js" js-mode)
("/webpack\\.config\\.js$" "__webpack.config.js" js-mode) ("/webpack\\.config\\.js$" "__webpack.config.js" js-mode)
("\\.lbaction/.+/Info.plist$" "__Info.plst" lb6-mode)
("\\.lbaction/.+/\\(default\\|suggestions\\)\\.js$" "__default.js" lb6-mode)
;; Lua ;; Lua
("/main\\.lua$" "__main.lua" love-mode) ("/main\\.lua$" "__main.lua" love-mode)
("/conf\\.lua$" "__conf.lua" love-mode) ("/conf\\.lua$" "__conf.lua" love-mode)
@ -107,5 +106,6 @@
;; Slim ;; Slim
("/\\(index\\|main\\)\\.slim$" "__" slim-mode) ("/\\(index\\|main\\)\\.slim$" "__" slim-mode)
;; Shell scripts ;; Shell scripts
("\\.z?sh$" "__" sh-mode)))) ("\\.z?sh$" "__" sh-mode)
("\\.zunit$" "__zunit" sh-mode))))

View File

@ -0,0 +1,4 @@
;; -*- no-byte-compile: t; -*-
;;; `(file-relative-name buffer-file-name doom-modules-dir)`
$0

View File

@ -1,99 +0,0 @@
# -*- mode: snippet -*-
# name: Info.plist File Template
# condition: (eq major-mode 'nxml-mode)
# --
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleIdentifier</key>
<string>io.henrik.launchbar.${1:$(replace-regexp-in-string "[^a-zA-Z0-9]" "" yas-text)}</string>
<key>CFBundleName</key>
<string>${1:Action Name}</string>
<!-- <key>CFBundleShortVersionString</key> -->
<!-- <value>1.0</value> -->
<key>CFBundleVersion</key>
<string>${2:1.0.0}</string>
<key>CFBundleIconFile</key>
<string>icon</string>
<key>LBDebugLogEnabled</key>
<false />
<!-- LB stuff -->
<!-- <key>LSMinimumSystemVersion</key> -->
<!-- <string>10.9.1</string> -->
<!-- Per-action keys -->
<!-- <key>LBTextInputTitle</key> -->
<!-- <string>Rate</string> -->
<key>LBScripts</key>
<dict>
<key>LBDefaultScript</key>
<dict>
<key>LBScriptName</key>
<string>default.js</string>
<key>LBRunInBackground</key>
<false />
<key>LBRequiresArgument</key>
<true />
<key>LBAcceptedArgumentTypes</key>
<array>
<string>string</string>
</array>
<key>LBReturnsResult</key>
<true />
<key>LBReturnType</key>
<string>string</string>
</dict>
</dict>
<key>LBDescription</key>
<dict>
<key>LBAuthor</key>
<string>Henrik Lissner</string>
<key>LBWebsite</key>
<string>http://v0.io/launchbar</string>
<key>LBEmail</key>
<string>contact@henrik.io</string>
<key>LBTwitter</key>
<string>@vnought</string>
<key>LBSummary</key>
<string>${3:A short summary of this action}</string>
<key>LBArgument</key>
<string>${4:Describe the input(s) for this action}</string>${5:
<key>LBResult</key>
<string>${6:Describe the result type}</string>
}${7:<key>LBRequirements</key>
<string>${8:The requirements for this action to work}</string>}
<!-- <key>LBChangelog</key> -->
<!-- <string></string> -->
<key>LBUpdateURL</key>
<string>https://github.com/hlissner/lb6-${2:actions}/blob/master/$1.lbaction/Contents/Info.plist</string>
<key>LBDownloadURL</key>
<string>https://dl.v0.io/launchbar/$1.lbaction</string>
</dict>
</dict>
</plist>

View File

@ -1,40 +0,0 @@
# -*- mode: snippet -*-
# name: default.js file template
# condition: (memq major-mode '(js-mode js2-mode))
# --
/*global Lib*/
${1:include("shared/${2:lib.js}");
}// This function is called when the user runs the action without any argument,
// e.g. by selecting it and hitting enter, space or the right arrow key
// (assuming the action does not specify that it requires an argument in its
// Info.plist). run is also called as a fallback if the more specific runWith...
// function for a given argument type (see below) is not implemented.
function run() {
$0
}
// This function is called after text input or when the user sends text to the
// action using Send To.
function runWithString(string) {
}
// This function is called when a URL item is sent to the action. This is also
// the case when a Action URL is called.
function runWithURL(url, details) {
}
// This function is called when the user sends a result item of a previous
// action run to the action using Send To.
function runWithItem(item) {
}
// This function is called after showing a file open dialog or when the user
// sends one or more files or folders to the action using Send To.
function runWithPaths(paths) {
}

View File

@ -0,0 +1,5 @@
#!/usr/bin/env zunit
@test '...' {
$0
}

View File

@ -1,2 +0,0 @@
;;; feature/hydra/autoload/evil.el -*- lexical-binding: t; -*-

View File

@ -1,2 +0,0 @@
;;; feature/hydra/autoload/hydras.el -*- lexical-binding: t; -*-

View File

@ -1,97 +0,0 @@
;;; feature/hydra/config.el -*- lexical-binding: t; -*-
(def-package! hydra
:commands (+hydra-zoom/body +hydra-window/body defhydra)
:config
(setq lv-use-separator t)
(defhydra +hydra-zoom (:hint t :color red)
"zoom"
("j" text-scale-increase "in")
("k" text-scale-decrease "out")
("0" (text-scale-set 0) "reset"))
(defhydra +hydra-window (:hint nil)
"
Split: _v_ert _s_:horz
Delete: _c_lose _o_nly
Switch Window: _h_:left _j_:down _k_:up _l_:right
Buffers: _p_revious _n_ext _b_:select _f_ind-file
Resize: _H_:splitter left _J_:splitter down _K_:splitter up _L_:splitter right
Move: _a_:up _z_:down _i_menu"
("z" scroll-up-line)
("a" scroll-down-line)
("i" idomenu)
("h" windmove-left)
("j" windmove-down)
("k" windmove-up)
("l" windmove-right)
("p" previous-buffer)
("n" next-buffer)
("b" ido-switch-buffer)
("f" ido-find-file)
("s" split-window-below)
("v" split-window-right)
("c" delete-window)
("o" delete-other-windows)
("H" hydra-move-splitter-left)
("J" hydra-move-splitter-down)
("K" hydra-move-splitter-up)
("L" hydra-move-splitter-right)
("q" nil)))
(def-package! ivy-hydra
:when (featurep! :completion ivy)
:after hydra
:config
(define-key ivy-mode-map (kbd "C-o")
(defhydra coo-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
("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)
;; ("d" ivy-done :exit t)
("RET" ivy-done :exit t)
("C-m" ivy-done :exit t)
("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))))

View File

@ -1,7 +0,0 @@
;; -*- no-byte-compile: t; -*-
;;; feature/hydra/packages.el
(package! hydra)
(when (featurep! :completion ivy)
(package! ivy-hydra))

View File

@ -130,5 +130,5 @@ for the provider."
(when (string-empty-p search) (when (string-empty-p search)
(user-error "The search query is empty")) (user-error "The search query is empty"))
(setq +jump--online-last provider) (setq +jump--online-last provider)
(browse-url (format url (url-encode-url search)))) (funcall +jump-search-browser-fn (format url (url-encode-url search))))
('error (setq +jump--online-last nil)))) ('error (setq +jump--online-last nil))))

View File

@ -16,17 +16,29 @@
(defvar +jump-search-provider-alist (defvar +jump-search-provider-alist
'(("Google" . "https://google.com/search?q=%s") '(("Google" . "https://google.com/search?q=%s")
("Google images" . "https://google.com/images?q=%s")
("Google maps" . "https://maps.google.com/maps?q=%s")
("Project Gutenberg" . "http://www.gutenberg.org/ebooks/search/?query=%s")
("DuckDuckGo" . "https://duckduckgo.com/?q=%s") ("DuckDuckGo" . "https://duckduckgo.com/?q=%s")
("DevDocs.io" . "http://devdocs.io/#q=%s") ("DevDocs.io" . "http://devdocs.io/#q=%s")
("StackOverflow" . "https://stackoverflow.com/search?q=%s")) ("StackOverflow" . "https://stackoverflow.com/search?q=%s")
("Github" . "https://github.com/search?ref=simplesearch&q=%s")
("Youtube" . "https://youtube.com/results?aq=f&oq=&search_query=%s")
("Wolfram alpha" . "https://wolframalpha.com/input/?i=%s")
("Wikipedia" . "https://wikipedia.org/search-redirect.php?language=en&go=Go&search=%s"))
"An alist that maps online resources to their search url or a function that "An alist that maps online resources to their search url or a function that
produces an url. Used by `+jump/online'.") produces an url. Used by `+jump/online'.")
(defconst +jump-search-browser-fn #'browse-url
"Function to use to open search urls.")
(defvar +jump-function-alist nil (defvar +jump-function-alist nil
"TODO") "An alist mapping major modes to jump function plists, describing what to do
with `+jump/definition', `+jump/references' and `+jump/documentation' are
called.")
(defvar-local +jump-current-functions nil (defvar-local +jump-current-functions nil
"TODO") "The enabled jump functions for the current buffer.")
(def-setting! :jump (modes &rest plist) (def-setting! :jump (modes &rest plist)
"Definies a jump target for major MODES. PLIST accepts the following "Definies a jump target for major MODES. PLIST accepts the following

View File

@ -0,0 +1,34 @@
;;; feature/services/autoload.el -*- lexical-binding: t; -*-
;;;###autoload
(defun +services/create ()
"Interactively create a new prodigy service."
(interactive)
;; TODO
)
;;;###autoload
(defun +services/prodigy-delete (arg)
"Delete service at point. Asks for confirmation."
(interactive "P")
(prodigy-with-refresh
(-when-let (service (prodigy-service-at-pos))
(let ((name (plist-get service :name)))
(cond ((or arg
(y-or-n-p (format "Delete '%s' service?" name)))
(setq prodigy-services (delete service prodigy-services))
(ignore-errors
(prodigy-goto-next-line))
(message "Successfully deleted service: %s" name))
(t
(message "Aborted")))))))
;;;###autoload
(defun +services/cleanup ()
"Delete all services associated with projects that don't exist."
(interactive)
(cl-loop for service in prodigy-services
if (and (plist-member service :project)
(file-directory-p (plist-get service :project)))
collect service into services
finally do (setq prodigy-service services)))

View File

@ -0,0 +1,50 @@
;;; feature/services/config.el -*- lexical-binding: t; -*-
(def-setting! :service (&rest plist)
"TODO"
`(after! prodigy
(prodigy-define-service ,@plist)))
;;
;; Plugins
;;
(def-package! prodigy
:commands (prodigy prodigy-view-mode prodigy-add-filter)
:config
(set! :evil-state 'prodigy-mode 'emacs)
;; Make services, etc persistent between Emacs sessions
(setq prodigy-services (persistent-soft-fetch 'prodigy-services "prodigy")
prodigy-tags (persistent-soft-fetch 'prodigy-tags "prodigy")
prodigy-filters (persistent-soft-fetch 'prodigy-filters "prodigy"))
(defun +services|save ()
"Save all services, tags and filters to files."
(+services/cleanup)
(cl-loop for sym in '(prodigy-services prodigy-tags prodigy-filters)
do (persistent-soft-store sym (symbol-value sym) "prodigy")))
(add-hook 'kill-emacs-hook #'+services|save)
(defun +services*prodigy-services (orig-fn &rest args)
"Adds a new :project property to prodigy services, which hides the service
unless invoked from the relevant project."
(let ((project-root (downcase (doom-project-root)))
(services (apply orig-fn args)))
(if current-prefix-arg
services
(cl-remove-if-not (lambda (service)
(let ((project (plist-get service :project)))
(or (not project)
(file-in-directory-p project-root project))))
services))))
(advice-add #'prodigy-services :around #'+services*prodigy-services)
;; Keybindings
(map! :map prodigy-mode-map "d" #'+services/prodigy-delete)
(when (featurep! :feature evil)
(map! :map prodigy-mode-map
"j" #'prodigy-next
"k" #'prodigy-prev)))

View File

@ -1,4 +1,4 @@
;; -*- no-byte-compile: t; -*- ;; -*- no-byte-compile: t; -*-
;;; tools/prodigy/packages.el ;;; feature/services/packages.el
(package! prodigy) (package! prodigy)

View File

@ -29,14 +29,11 @@
"Refresh git-gutter on ESC. Return nil to prevent shadowing other "Refresh git-gutter on ESC. Return nil to prevent shadowing other
`+evil-esc-hook' hooks." `+evil-esc-hook' hooks."
(when git-gutter-mode (when git-gutter-mode
(git-gutter) (ignore (git-gutter))))
nil))
(add-hook '+evil-esc-hook #'+version-control|update-git-gutter t)) (add-hook '+evil-esc-hook #'+version-control|update-git-gutter t))
(def-hydra! +version-control@git-gutter
(when (featurep! :feature hydra) (:body-pre (git-gutter-mode 1) :hint nil)
(defhydra +hydra-git-gutter (:body-pre (git-gutter-mode 1)
:hint nil)
" "
╭─────────────────┐ ╭─────────────────┐
Movement Hunk Actions Misc. │ gg: +%-4s(car (git-gutter:statistic))/ -%-3s(cdr (git-gutter:statistic)) │ Movement Hunk Actions Misc. │ gg: +%-4s(car (git-gutter:statistic))/ -%-3s(cdr (git-gutter:statistic)) │
@ -47,21 +44,17 @@
^↓ ^ [_p_] popup ╭────────────────────── ^↓ ^ [_p_] popup ╭──────────────────────
^_j_^ │[_q_] quit ^_j_^ │[_q_] quit
^_G_^ │[_Q_] Quit and disable" ^_G_^ │[_Q_] Quit and disable"
("j" (progn (git-gutter:next-hunk 1) ("j" (progn (git-gutter:next-hunk 1) (recenter)))
(recenter))) ("k" (progn (git-gutter:previous-hunk 1) (recenter)))
("k" (progn (git-gutter:previous-hunk 1) ("g" (progn (goto-char (point-min)) (git-gutter:next-hunk 1)))
(recenter))) ("G" (progn (goto-char (point-min)) (git-gutter:previous-hunk 1)))
("g" (progn (goto-char (point-min))
(git-gutter:next-hunk 1)))
("G" (progn (goto-char (point-min))
(git-gutter:previous-hunk 1)))
("s" git-gutter:stage-hunk) ("s" git-gutter:stage-hunk)
("r" git-gutter:revert-hunk) ("r" git-gutter:revert-hunk)
("m" git-gutter:mark-hunk) ("m" git-gutter:mark-hunk)
("p" git-gutter:popup-hunk) ("p" git-gutter:popup-hunk)
("R" git-gutter:set-start-revision) ("R" git-gutter:set-start-revision)
("q" nil :color blue) ("q" nil :color blue)
("Q" (git-gutter-mode -1) :color blue)))) ("Q" (git-gutter-mode -1) :color blue)))
(def-package! git-timemachine (def-package! git-timemachine

View File

@ -1,10 +1,7 @@
;;; feature/version-control/config.el -*- lexical-binding: t; -*- ;;; feature/version-control/config.el -*- lexical-binding: t; -*-
(unless (featurep! -git) (or (featurep! -git) (load! +git))
(load! +git)) ;; TODO (or (featurep! -hg) (load! +hg))
;; TODO hg support
;; (unless (featurep! -hg)
;; (load! +hg))
;; ;;
(setq vc-make-backup-files nil) (setq vc-make-backup-files nil)
@ -26,17 +23,14 @@
:init :init
(add-hook 'find-file-hook #'+vcs|enable-smerge-mode-maybe) (add-hook 'find-file-hook #'+vcs|enable-smerge-mode-maybe)
:config :config
(when (featurep! :feature hydra)
(require 'hydra)
(when (version< emacs-version "26") (when (version< emacs-version "26")
(defalias 'smerge-keep-upper 'smerge-keep-mine) (defalias #'smerge-keep-upper #'smerge-keep-mine)
(defalias 'smerge-keep-lower 'smerge-keep-other) (defalias #'smerge-keep-lower #'smerge-keep-other)
(defalias 'smerge-diff-base-upper 'smerge-diff-base-mine) (defalias #'smerge-diff-base-upper #'smerge-diff-base-mine)
(defalias 'smerge-diff-upper-lower 'smerge-diff-mine-other) (defalias #'smerge-diff-upper-lower #'smerge-diff-mine-other)
(defalias 'smerge-diff-base-lower 'smerge-diff-base-other)) (defalias #'smerge-diff-base-lower #'smerge-diff-base-other))
(defhydra +hydra-smerge (:hint nil (def-hydra! +hydra-smerge (:hint nil
:pre (smerge-mode 1) :pre (smerge-mode 1)
;; Disable `smerge-mode' when quitting hydra if ;; Disable `smerge-mode' when quitting hydra if
;; no merge conflicts remain. ;; no merge conflicts remain.
@ -71,4 +65,4 @@
("C" smerge-combine-with-next) ("C" smerge-combine-with-next)
("r" smerge-resolve) ("r" smerge-resolve)
("R" smerge-kill-current) ("R" smerge-kill-current)
("q" nil :color blue)))) ("q" nil :color blue)))

View File

@ -37,7 +37,7 @@
;;;###autoload (autoload '+workspace:delete "feature/workspaces/autoload/evil" nil t) ;;;###autoload (autoload '+workspace:delete "feature/workspaces/autoload/evil" nil t)
(evil-define-command +workspace:delete () (evil-define-command +workspace:delete ()
"Ex wrapper around `+workspace/delete'." "Ex wrapper around `+workspace/delete'."
(interactive "<a>") (+workspace/delete (+workspace-current-name))) (interactive) (+workspace/delete (+workspace-current-name)))
;;;###autoload (autoload '+workspace:switch-next "feature/workspaces/autoload/evil" nil t) ;;;###autoload (autoload '+workspace:switch-next "feature/workspaces/autoload/evil" nil t)
(evil-define-command +workspace:switch-next (&optional count) (evil-define-command +workspace:switch-next (&optional count)

View File

@ -1,20 +1,70 @@
;;; feature/workspaces/autoload/workspaces.el -*- lexical-binding: t; -*- ;;; feature/workspaces/autoload/workspaces.el -*- lexical-binding: t; -*-
(defvar +workspace-workspace-file "_workspaces" (defvar +workspace-data-file "_workspaces"
"The file basename in which to store single workspace perspectives.") "The file basename in which to store single workspace perspectives.")
(defvar +workspace--last nil) (defvar +workspace--last nil)
(defvar +workspace--index 0)
(defface +workspace-tab-selected-face ;;
'((t (:inherit 'highlight))) (defface +workspace-tab-selected-face '((t (:inherit 'highlight)))
"The face for selected tabs displayed by `+workspace/display'" "The face for selected tabs displayed by `+workspace/display'"
:group 'doom) :group 'doom)
(defface +workspace-tab-face (defface +workspace-tab-face '((t (:inherit 'default)))
'((t (:inherit 'default)))
"The face for selected tabs displayed by `+workspace/display'" "The face for selected tabs displayed by `+workspace/display'"
:group 'doom) :group 'doom)
;;
;; Library
;;
(defun +workspace--protected-p (name)
(equal name persp-nil-name))
(defun +workspace--generate-id ()
(or (cl-loop for name in (+workspace-list-names)
when (string-match-p "^#[0-9]+$" name)
maximize (string-to-number (substring name 1)) into max
finally return (if max (1+ max)))
1))
;; --- Predicates -------------------------
;;;###autoload
(defalias #'+workspace-p #'persp-p "Return t if OBJ is a perspective hash table.")
;;;###autoload
(defun +workspace-exists-p (name)
"Returns t if NAME is the name of an existing workspace."
(cl-assert (stringp name) t)
(member name (+workspace-list-names)))
;;;###autoload
(defun +workspace-contains-buffer-p (buffer &optional workspace)
"Return non-nil if buffer is in workspace (defaults to current workspace)."
(persp-contain-buffer-p buffer (or workspace (+workspace-current)) nil))
;; --- Getters ----------------------------
;;;###autoload
(defun +workspace-get (name &optional noerror)
"Returns a workspace (perspective hash table) named NAME."
(when-let (persp (persp-get-by-name name))
(cond ((+workspace-p persp) persp)
((not noerror) (error "'%s' is an invalid workspace" name)))))
;;;###autoload
(defalias '+workspace-current #'get-current-persp)
;;;###autoload
(defun +workspace-current-name ()
"Get the name of the current workspace."
(safe-persp-name (get-current-persp)))
;;;###autoload ;;;###autoload
(defun +workspace-list () (defun +workspace-list ()
"Return a list of workspace structs." "Return a list of workspace structs."
@ -36,123 +86,92 @@ PERSP can be a string (name of a workspace) or a perspective hash (satisfies
If PERSP is t, then return a list of orphaned buffers associated with no If PERSP is t, then return a list of orphaned buffers associated with no
perspectives." perspectives."
(unless persp (let ((persp (or persp (+workspace-current))))
(setq persp (get-current-persp)))
(if (eq persp t) (if (eq persp t)
(cl-remove-if #'persp--buffer-in-persps (buffer-list)) (cl-remove-if #'persp--buffer-in-persps (buffer-list))
(when (stringp persp) (cl-assert (+workspace-p persp) t)
(setq persp (+workspace-get persp t)))
(cl-loop for buf in (buffer-list) (cl-loop for buf in (buffer-list)
if (persp-contain-buffer-p buf persp) if (+workspace-contains-buffer-p buf persp)
collect buf))) collect buf))))
;;;###autoload
(defun +workspace-p (obj)
"Return t if OBJ is a perspective hash table."
(and obj
(cl-struct-p obj)
(perspective-p obj)))
;;;###autoload ;; --- Actions ----------------------------
(defun +workspace-exists-p (name)
"Returns t if NAME is the name of an existing workspace."
(when (symbolp name)
(setq name (symbol-name name)))
(unless (stringp name)
(error "Expected a string, got a %s" (type-of name)))
(member name (+workspace-list-names)))
;;;###autoload
(defun +workspace-get (name &optional noerror)
"Returns a workspace (perspective hash table) named NAME."
(unless (equal name persp-nil-name)
(let ((persp (persp-get-by-name name)))
(when (and (not noerror)
(or (null persp)
(equal persp persp-not-persp)))
(error "%s is not an available workspace" name))
persp)))
;;;###autoload
(defalias '+workspace-current #'get-current-persp)
;;;###autoload
(defun +workspace-current-name ()
"Get the name of the currently active workspace."
(safe-persp-name (get-current-persp)))
;;;###autoload
(defun +workspace-contains-buffer-p (&optional buffer workspace)
"Return non-nil if buffer is in workspace (defaults to current workspace)."
(unless workspace
(setq workspace (+workspace-current)))
(persp-contain-buffer-p buffer workspace nil))
;;;###autoload ;;;###autoload
(defun +workspace-load (name) (defun +workspace-load (name)
"Loads and inserts a single workspace (named NAME) into the current session. "Loads a single workspace (named NAME) into the current session. Can only
Can only retrieve perspectives that were explicitly saved with retrieve perspectives that were explicitly saved with `+workspace-save'.
`+workspace-save'.
Returns t if successful, nil otherwise." Returns t if successful, nil otherwise."
(unless (+workspace-exists-p name) (when (+workspace-exists-p name)
(persp-load-from-file-by-names +workspace-workspace-file *persp-hash* (list name))) (error "A workspace named '%s' already exists." name))
(persp-load-from-file-by-names
(expand-file-name +workspace-data-file persp-save-dir)
*persp-hash* (list name))
(+workspace-exists-p name)) (+workspace-exists-p name))
;;;###autoload ;;;###autoload
(defun +workspace-load-session (&optional name) (defun +workspace-load-session (&optional name)
"Replace current session with the entire session named NAME. If NAME is nil, "Replace current session with the entire session named NAME. If NAME is nil,
use `persp-auto-save-fname'." use `persp-auto-save-fname'."
(persp-load-state-from-file (or name persp-auto-save-fname))) (persp-load-state-from-file
(expand-file-name (or name persp-auto-save-fname) persp-save-dir)))
;;;###autoload ;;;###autoload
(defun +workspace-save (name) (defun +workspace-save (name)
"Saves a single workspace (TARGET) from the current session. Can be loaded "Saves a single workspace (NAME) from the current session. Can be loaded again
again with `+workspace-load'. TARGET can be the string name of a workspace or with `+workspace-load'. NAME can be the string name of a workspace or its
its perspective hash table. perspective hash table.
Returns t on success, nil otherwise." Returns t on success, nil otherwise."
(unless (+workspace-exists-p name) (unless (+workspace-exists-p name)
(error "%s is not an available workspace" name)) (error "'%s' is an invalid workspace" name))
(persp-save-to-file-by-names (let ((fname (expand-file-name +workspace-data-file persp-save-dir)))
+workspace-workspace-file *persp-hash* (list name) t) (persp-save-to-file-by-names fname *persp-hash* (list name))
(memq name (persp-list-persp-names-in-file (concat persp-save-dir +workspace-workspace-file)))) (and (member name (persp-list-persp-names-in-file fname))
t)))
;;;###autoload ;;;###autoload
(defun +workspace-save-session (&optional name) (defun +workspace-save-session (&optional name)
"Save a whole session as NAME. If NAME is nil, use `persp-auto-save-fname'. "Save a whole session as NAME. If NAME is nil, use `persp-auto-save-fname'.
Return t on success, nil otherwise." Return t on success, nil otherwise."
(when (or (not name) (let ((fname (expand-file-name (or name persp-auto-save-fname)
persp-save-dir))
(persp-auto-save-opt
(if (or (not name)
(equal name persp-auto-save-fname)) (equal name persp-auto-save-fname))
(setq name persp-auto-save-fname 0
persp-auto-save-opt 0)) persp-auto-save-opt)))
(and (persp-save-state-to-file name) t)) (and (persp-save-state-to-file fname) t)))
;;;###autoload ;;;###autoload
(defun +workspace-new (name) (defun +workspace-new (name)
"Create a new workspace named NAME. If one already exists, return nil. "Create a new workspace named NAME. If one already exists, return nil.
Otherwise return t on success, nil otherwise." Otherwise return t on success, nil otherwise."
(when (+workspace-protected-p name) (when (+workspace--protected-p name)
(error "Can't create a new '%s' workspace" name)) (error "Can't create a new '%s' workspace" name))
(unless (+workspace-exists-p name) (when (+workspace-exists-p name)
(and (persp-add-new name) t))) (error "A workspace named '%s' already exists" name))
(and (persp-add-new name) t))
;;;###autoload ;;;###autoload
(defun +workspace-rename (name new-name) (defun +workspace-rename (name new-name)
"Rename the current workspace named NAME to NEW-NAME. Returns old name on "Rename the current workspace named NAME to NEW-NAME. Returns old name on
success, nil otherwise." success, nil otherwise."
(when (+workspace-protected-p name) (when (+workspace--protected-p name)
(error "Can't rename '%s' workspace" name)) (error "Can't rename '%s' workspace" name))
(persp-rename new-name (+workspace-get name))) (persp-rename new-name (+workspace-get name)))
;;;###autoload ;;;###autoload
(defun +workspace-delete (name &optional inhibit-kill-p) (defun +workspace-delete (name &optional inhibit-kill-p)
"Delete the workspace denoted by TARGET, which can be the name of a "Delete the workspace denoted by NAME, which can be the name of a perspective
perspective or its hash table." or its hash table. If INHIBIT-KILL-P is non-nil, don't kill this workspace's
(when (+workspace-protected-p name) buffers."
(when (+workspace--protected-p name)
(error "Can't delete '%s' workspace" name)) (error "Can't delete '%s' workspace" name))
(+workspace-get name) ;; error checking (+workspace-get name) ; error checking
(persp-kill name inhibit-kill-p)) (persp-kill name inhibit-kill-p)
(not (+workspace-exists-p name)))
;;;###autoload ;;;###autoload
(defun +workspace-switch (name &optional auto-create-p) (defun +workspace-switch (name &optional auto-create-p)
@ -168,17 +187,6 @@ perspective or its hash table."
+workspaces-main))) +workspaces-main)))
(persp-frame-switch name)) (persp-frame-switch name))
(defun +workspace--generate-id ()
(or (cl-loop for name in (+workspace-list-names)
when (string-match-p "^#[0-9]+$" name)
maximize (string-to-number (substring name 1)) into max
finally return (if max (1+ max)))
1))
(defun +workspace-protected-p (name)
(or (equal name persp-nil-name)
(equal name +workspaces-main)))
;; ;;
;; Interactive commands ;; Interactive commands
@ -192,9 +200,10 @@ current workspace (by name) from session files."
(list (list
(if current-prefix-arg (if current-prefix-arg
(+workspace-current-name) (+workspace-current-name)
(completing-read "Workspace to load: " (completing-read
"Workspace to load: "
(persp-list-persp-names-in-file (persp-list-persp-names-in-file
(concat persp-save-dir +workspace-workspace-file)))))) (expand-file-name +workspace-data-file persp-save-dir))))))
(if (not (+workspace-load name)) (if (not (+workspace-load name))
(+workspace-error (format "Couldn't load workspace %s" name)) (+workspace-error (format "Couldn't load workspace %s" name))
(+workspace/switch-to name) (+workspace/switch-to name)
@ -272,20 +281,30 @@ workspace to delete."
nil nil current-name) nil nil current-name)
current-name)))) current-name))))
(condition-case ex (condition-case ex
(if (not (+workspace-delete name)) (+workspace-message
(error "Couldn't delete %s workspace" name) (let ((workspaces (length (+workspace-list-names))))
(cond ((> workspaces 1)
(+workspace-delete name)
(+workspace-switch
(if (+workspace-exists-p +workspace--last) (if (+workspace-exists-p +workspace--last)
(+workspace-switch +workspace--last) +workspace--last
(+workspace-switch +workspaces-main t)) (car (+workspace-list-names))))
(+workspace-message (format "Deleted '%s' workspace" name) 'success)) (format "Deleted '%s' workspace" name))
((= workspaces 1)
(format "Can't delete the last workspace!"))
(t
(+workspace-delete name)
(+workspace-switch +workspaces-main t)
(switch-to-buffer (doom-fallback-buffer))
(format "No workspaces detected! Auto-creating '%s' workspace" +workspaces-main))))
'success)
('error (+workspace-error (cadr ex) t)))) ('error (+workspace-error (cadr ex) t))))
;;;###autoload ;;;###autoload
(defun +workspace/kill-session () (defun +workspace/kill-session ()
"Delete the current session, clears all workspaces, windows and buffers." "Delete the current session, clears all workspaces, windows and buffers."
(interactive) (interactive)
(unless (cl-every #'+workspace-delete (unless (cl-every #'+workspace-delete (+workspace-list-names))
(delete +workspaces-main (+workspace-list-names)))
(+workspace-error "Could not clear session")) (+workspace-error "Could not clear session"))
(+workspace-switch +workspaces-main t) (+workspace-switch +workspaces-main t)
(doom/kill-all-buffers) (doom/kill-all-buffers)
@ -388,7 +407,7 @@ the workspace and move to the next."
(if (doom-popup-p) (if (doom-popup-p)
(doom/popup-close) (doom/popup-close)
(let ((current-persp-name (+workspace-current-name))) (let ((current-persp-name (+workspace-current-name)))
(cond ((or (+workspace-protected-p current-persp-name) (cond ((or (+workspace--protected-p current-persp-name)
(> (length (doom-visible-windows)) 1)) (> (length (doom-visible-windows)) 1))
(if (bound-and-true-p evil-mode) (if (bound-and-true-p evil-mode)
(evil-window-delete) (evil-window-delete)
@ -397,15 +416,17 @@ the workspace and move to the next."
(+workspace/delete current-persp-name)))))) (+workspace/delete current-persp-name))))))
;;;###autoload ;;;###autoload
(defun +workspace/cleanup () (defun +workspace/close-workspace-or-frame ()
"Clean up orphaned buffers and processes." "Close the current workspace. If it's the last, delete the frame instead."
(interactive) (interactive)
(let ((buffers (cl-remove-if #'persp--buffer-in-persps (buffer-list))) (let ((frames (length (frame-list)))
(n (doom-kill-process-buffers))) (workspaces (length (+workspace-list-names))))
(mapc #'kill-buffer buffers) (cond ((> workspaces 1)
(when (called-interactively-p 'any) (call-interactively #'+workspace/delete))
(message "Cleaned up %d buffers and %d processes" ((> frames 1)
(length buffers) n)))) (call-interactively #'delete-frame))
(t
(error "Can't delete last frame.")))))
;; ;;

View File

@ -1,7 +1,7 @@
;;; lang/cc/autoload.el -*- lexical-binding: t; -*- ;;; lang/cc/autoload.el -*- lexical-binding: t; -*-
;;;###autoload ;;;###autoload
(defun +cc*lineup-arglist (orig-fun &rest args) (defun +cc*align-lambda-arglist (orig-fun &rest args)
"Improve indentation of continued C++11 lambda function opened as argument." "Improve indentation of continued C++11 lambda function opened as argument."
(if (and (eq major-mode 'c++-mode) (if (and (eq major-mode 'c++-mode)
(ignore-errors (ignore-errors
@ -21,59 +21,62 @@
(backward-char) (backward-char)
(looking-at-p "[^ \t]>")) (looking-at-p "[^ \t]>"))
(forward-char) (forward-char)
(call-interactively 'self-insert-command))) (call-interactively #'self-insert-command)))
(defun +cc--copy-face (new-face face)
"Define NEW-FACE from existing FACE."
(copy-face face new-face)
(eval `(defvar ,new-face nil))
(set new-face new-face))
;;;###autoload
(defun +cc|extra-fontify-c++ ()
;; We could place some regexes into `c-mode-common-hook', but
;; note that their evaluation order matters.
;; NOTE modern-cpp-font-lock will eventually supercede some of these rules
(font-lock-add-keywords
nil '(;; c++11 string literals
;; L"wide string"
;; L"wide string with UNICODE codepoint: \u2018"
;; u8"UTF-8 string", u"UTF-16 string", U"UTF-32 string"
("\\<\\([LuU8]+\\)\".*?\"" 1 font-lock-keyword-face)
;; R"(user-defined literal)"
;; R"( a "quot'd" string )"
;; R"delimiter(The String Data" )delimiter"
;; R"delimiter((a-z))delimiter" is equivalent to "(a-z)"
("\\(\\<[uU8]*R\"[^\\s-\\\\()]\\{0,16\\}(\\)" 1 font-lock-keyword-face t) ; start delimiter
( "\\<[uU8]*R\"[^\\s-\\\\()]\\{0,16\\}(\\(.*?\\))[^\\s-\\\\()]\\{0,16\\}\"" 1 font-lock-string-face t) ; actual string
( "\\<[uU8]*R\"[^\\s-\\\\()]\\{0,16\\}(.*?\\()[^\\s-\\\\()]\\{0,16\\}\"\\)" 1 font-lock-keyword-face t) ; end delimiter
) t))
;;;###autoload
(defun +cc|extra-fontify-c/c++ ()
(font-lock-add-keywords
nil '(;; PREPROCESSOR_CONSTANT, PREPROCESSORCONSTANT
("\\<[A-Z]*_[A-Z_]+\\>" . font-lock-constant-face)
("\\<[A-Z]\\{3,\\}\\>" . font-lock-constant-face)
;; integer/float/scientific numbers
("\\<\\([\\-+]*[0-9\\.]+\\)\\>" 1 font-lock-constant-face t)
("\\<\\([\\-+]*[0-9\\.]+\\)\\(f\\)\\>"
(1 font-lock-constant-face t)
(2 font-lock-keyword-face t))
("\\<\\([\\-+]*[0-9\\.]+\\)\\([eE]\\)\\([\\-+]?[0-9]+\\)\\>"
(1 font-lock-constant-face t)
(2 font-lock-keyword-face t)
(3 font-lock-constant-face t))
) t))
;;;###autoload ;;;###autoload
(defun +cc-sp-point-is-template-p (id action context) (defun +cc-sp-point-is-template-p (id action context)
"Return t if point is in the right place for C++ angle-brackets."
(and (sp-in-code-p id action context) (and (sp-in-code-p id action context)
(sp-point-after-word-p id action context))) (sp-point-after-word-p id action context)))
;;;###autoload ;;;###autoload
(defun +cc-sp-point-after-include-p (id action context) (defun +cc-sp-point-after-include-p (id action context)
"Return t if point is in an #include."
(and (sp-in-code-p id action context) (and (sp-in-code-p id action context)
(save-excursion (save-excursion
(goto-char (line-beginning-position)) (goto-char (line-beginning-position))
(looking-at-p "[ ]*#include[^<]+")))) (looking-at-p "[ ]*#include[^<]+"))))
;;;###autoload
(defun +cc-c-lineup-inclass (_langelem)
"Indent privacy keywords at same level as class properties."
(if (memq major-mode '(c-mode c++-mode))
(let ((inclass (assq 'inclass c-syntactic-context)))
(save-excursion
(goto-char (c-langelem-pos inclass))
(if (or (looking-at "struct")
(looking-at "typedef struct"))
'+
'++)))
'+))
;;
;; Hooks
;;
;;;###autoload
(defun +cc|fontify-constants ()
"Better fontification for preprocessor constants"
(font-lock-add-keywords
nil '(("\\<[A-Z]*_[A-Z_]+\\>" . font-lock-constant-face)
("\\<[A-Z]\\{3,\\}\\>" . font-lock-constant-face))
t))
;;;###autoload
(defun +cc|irony-init-compile-options ()
"Initialize compiler options for irony-mode. It searches for the nearest
compilation database and initailizes it. If none was found, it uses
`+cc-c++-compiler-options'.
See https://github.com/Sarcasm/irony-mode#compilation-database for details on
compilation dbs."
(when (memq major-mode '(c-mode c++-mode objc-mode))
(require 'irony-cdb)
(unless (irony-cdb-autosetup-compile-options)
(irony-cdb--update-compile-options
(append (delq nil (cdr-safe (assq major-mode +cc-compiler-options)))
(cl-loop for path in +cc-include-paths
nconc (list "-I" path)))
(doom-project-root)))))

View File

@ -1,12 +1,35 @@
;;; lang/cc/config.el --- c, c++, and obj-c -*- lexical-binding: t; -*- ;;; lang/cc/config.el --- c, c++, and obj-c -*- lexical-binding: t; -*-
(defvar +cc-include-paths (list "include/")
"A list of paths, relative to a project root, to search for headers in C/C++.
Paths can be absolute.
The purpose of this variable is to ensure syntax checkers and code-completion
knows where to look for headers.")
(defvar +cc-compiler-options
`((c-mode . nil)
(c++-mode
. ,(list "-std=c++11" ; use C++11 by default
(when IS-MAC
;; NOTE beware: you'll get abi-inconsistencies when passing
;; std-objects to libraries linked with libstdc++ (e.g. if you
;; use boost which wasn't compiled with libc++)
(list "-stdlib=libc++"))))
(objc-mode . nil))
"A list of default compiler options for the C family. These are ignored if a
compilation database is present in the project.")
;;
;; Plugins
;;
(def-package! cc-mode (def-package! cc-mode
:commands (c-mode c++-mode objc-mode java-mode) :commands (c-mode c++-mode objc-mode java-mode)
:mode ("\\.mm" . objc-mode) :mode ("\\.mm" . objc-mode)
:init :preface
(setq-default c-basic-offset tab-width) (defun +cc-c++-header-file-p ()
(defun +cc--c++-header-file-p ()
(and buffer-file-name (and buffer-file-name
(equal (file-name-extension buffer-file-name) "h") (equal (file-name-extension buffer-file-name) "h")
(or (file-exists-p (expand-file-name (or (file-exists-p (expand-file-name
@ -17,89 +40,68 @@
(projectile-current-project-files)))) (projectile-current-project-files))))
(equal (file-name-extension file) "cpp"))))) (equal (file-name-extension file) "cpp")))))
(defun +cc--objc-header-file-p () (defun +cc-objc-header-file-p ()
(and buffer-file-name (and buffer-file-name
(equal (file-name-extension buffer-file-name) "h") (equal (file-name-extension buffer-file-name) "h")
(re-search-forward "@\\<interface\\>" magic-mode-regexp-match-limit t))) (re-search-forward "@\\<interface\\>" magic-mode-regexp-match-limit t)))
;; Auto-detect C++/Obj-C header files (push (cons #'+cc-c++-header-file-p 'c++-mode) magic-mode-alist)
(push (cons #'+cc--c++-header-file-p 'c++-mode) magic-mode-alist) (push (cons #'+cc-objc-header-file-p 'objc-mode) magic-mode-alist)
(push (cons #'+cc--objc-header-file-p 'objc-mode) magic-mode-alist)
:init
(setq-default c-basic-offset tab-width)
:config :config
(setq c-tab-always-indent nil
c-electric-flag nil)
(set! :electric '(c-mode c++-mode objc-mode java-mode) (set! :electric '(c-mode c++-mode objc-mode java-mode)
:chars '(?\n ?\})) :chars '(?\n ?\}))
(set! :company-backend (set! :company-backend
'(c-mode c++-mode objc-mode) '(c-mode c++-mode objc-mode)
'(company-irony-c-headers company-irony)) '(company-irony-c-headers company-irony))
(add-hook 'c-mode-common-hook #'rainbow-delimiters-mode) ;;; Style/formatting
(add-hook 'c-mode-hook #'highlight-numbers-mode) ; fontify numbers in C ;; C/C++ style settings
(add-hook 'c++-mode-hook #'+cc|extra-fontify-c++) ; fontify C++11 string literals (c-toggle-electric-state -1)
(c-toggle-auto-newline -1)
(c-set-offset 'substatement-open '0) ; don't indent brackets
(c-set-offset 'inline-open '+)
(c-set-offset 'block-open '+)
(c-set-offset 'brace-list-open '+)
(c-set-offset 'case-label '+)
(c-set-offset 'access-label '-)
(c-set-offset 'arglist-intro '+)
(c-set-offset 'arglist-close '0)
;; Indent privacy keywords at same level as class properties
;; (c-set-offset 'inclass #'+cc-c-lineup-inclass)
;;; Better fontification (also see `modern-cpp-font-lock')
(add-hook 'c-mode-common-hook #'rainbow-delimiters-mode)
(add-hook! (c-mode c++-mode) #'highlight-numbers-mode)
(add-hook! (c-mode c++-mode) #'+cc|fontify-constants)
;; Improve indentation of inline lambdas in C++11
(advice-add #'c-lineup-arglist :around #'+cc*align-lambda-arglist)
;;; Keybindings
;; Completely disable electric keys because it interferes with smartparens and
;; custom bindings. We'll do this ourselves.
(setq c-tab-always-indent nil
c-electric-flag nil)
(dolist (key '("#" "{" "}" "/" "*" ";" "," ":" "(" ")"))
(define-key c-mode-base-map key nil))
;; Smartparens and cc-mode both try to autoclose angle-brackets intelligently.
;; The result isn't very intelligent (causes redundant characters), so just do
;; it ourselves.
(map! :map c++-mode-map
"<" nil
:i ">" #'+cc/autoclose->-maybe)
;; ...and leave it to smartparens
(sp-with-modes '(c-mode c++-mode objc-mode java-mode) (sp-with-modes '(c-mode c++-mode objc-mode java-mode)
(sp-local-pair "<" ">" :when '(+cc-sp-point-is-template-p +cc-sp-point-after-include-p)) (sp-local-pair "<" ">" :when '(+cc-sp-point-is-template-p +cc-sp-point-after-include-p))
(sp-local-pair "/*" "*/" :post-handlers '(("||\n[i]" "RET") ("| " "SPC"))) (sp-local-pair "/*" "*/" :post-handlers '(("||\n[i]" "RET") ("| " "SPC")))
;; Doxygen blocks ;; Doxygen blocks
(sp-local-pair "/**" "*/" :post-handlers '(("||\n[i]" "RET") ("||\n[i]" "SPC"))) (sp-local-pair "/**" "*/" :post-handlers '(("||\n[i]" "RET") ("||\n[i]" "SPC")))
(sp-local-pair "/*!" "*/" :post-handlers '(("||\n[i]" "RET") ("[d-1]< | " "SPC")))) (sp-local-pair "/*!" "*/" :post-handlers '(("||\n[i]" "RET") ("[d-1]< | " "SPC")))))
;; Improve indentation of inline lambdas in C++11
(advice-add #'c-lineup-arglist :around #'+cc*lineup-arglist)
;; C/C++ style settings
(c-toggle-electric-state -1)
(c-toggle-auto-newline -1)
(c-set-offset 'substatement-open '0) ; brackets should be at same indentation level as the statements they open
(c-set-offset 'inline-open '+)
(c-set-offset 'block-open '+)
(c-set-offset 'brace-list-open '+) ; all "opens" should be indented by the c-indent-level
(c-set-offset 'case-label '+) ; indent case labels by c-indent-level, too
(c-set-offset 'access-label '-)
(c-set-offset 'arglist-intro '+)
(c-set-offset 'arglist-close '0)
(defun +cc--c-lineup-inclass (_langelem)
(if (memq major-mode '(c-mode c++-mode))
(let ((inclass (assq 'inclass c-syntactic-context)))
(save-excursion
(goto-char (c-langelem-pos inclass))
(if (or (looking-at "struct")
(looking-at "typedef struct"))
'+
'++)))
'+))
(c-set-offset 'inclass #'+cc--c-lineup-inclass)
;; Certain electric mappings interfere with smartparens and custom bindings,
;; so unbind them
(map! :map c-mode-map
"DEL" nil
"#" #'self-insert-command
"{" #'self-insert-command
"}" #'self-insert-command
"/" #'self-insert-command
"*" #'self-insert-command
";" #'self-insert-command
"," #'self-insert-command
":" #'self-insert-command
"(" #'self-insert-command
")" #'self-insert-command
:map c++-mode-map
"}" nil
;; Smartparens and cc-mode both try to autoclose angle-brackets
;; intelligently. The result isn't very intelligent (causes redundant
;; characters), so just do it ourselves.
"<" nil
:map (c-mode-base-map c++-mode-map)
:i ">" #'+cc/autoclose->-maybe))
(def-package! modern-cpp-font-lock (def-package! modern-cpp-font-lock
@ -113,29 +115,25 @@
:preface :preface
(setq irony-server-install-prefix (concat doom-etc-dir "irony-server/")) (setq irony-server-install-prefix (concat doom-etc-dir "irony-server/"))
:init :init
(defun +cc|init-irony-mode () (add-hook! (c-mode c++-mode objc-mode) #'irony-mode)
(when (and (memq major-mode '(c-mode c++-mode objc-mode))
(file-directory-p irony-server-install-prefix))
(irony-mode +1)))
(add-hook 'c-mode-common-hook #'+cc|init-irony-mode)
:config :config
(add-hook! 'irony-mode-hook #'(irony-eldoc flycheck-mode)) (unless (file-directory-p irony-server-install-prefix)
(warn "irony-mode: server isn't installed; run M-x irony-install-server"))
(defun +cc|init-c++11-clang-options () ;; Initialize compilation database, if present. Otherwise, fall back on
(make-local-variable 'irony-additional-clang-options) ;; `+cc-compiler-options'.
(cl-pushnew "-std=c++11" irony-additional-clang-options :test 'equal)) (add-hook 'irony-mode-hook #'+cc|irony-init-compile-options))
(add-hook 'c++-mode-hook #'+cc|init-c++11-clang-options)
(map! :map irony-mode-map (def-package! irony-eldoc
[remap completion-at-point] #'counsel-irony :after irony
[remap complete-symbol] #'counsel-irony)) :config (add-hook 'irony-mode-hook #'irony-eldoc))
(def-package! irony-eldoc :after irony)
(def-package! flycheck-irony (def-package! flycheck-irony
:when (featurep! :feature syntax-checker) :when (featurep! :feature syntax-checker)
:after irony :after irony
:config (flycheck-irony-setup)) :config
(add-hook 'irony-mode-hook #'flycheck-mode)
(flycheck-irony-setup))
;; ;;
@ -181,7 +179,6 @@
(def-package! company-irony-c-headers :after company-irony) (def-package! company-irony-c-headers :after company-irony)
(def-package! company-glsl (def-package! company-glsl
:when (featurep! :completion company)
:after glsl-mode :after glsl-mode
:config :config
(if (executable-find "glslangValidator") (if (executable-find "glslangValidator")

View File

@ -29,12 +29,7 @@
(def-package! dockerfile-mode (def-package! dockerfile-mode
:mode "/Dockerfile$" :mode "/Dockerfile$")
:config
;; TODO
;; (set! :build 'build-image 'dockerfile-mode '+data/dockerfile-build
;; :when '+data-dockerfile-p)
)
;; For ROM hacking or debugging ;; For ROM hacking or debugging

View File

@ -1,9 +1,5 @@
;;; lang/go/autoload.el -*- lexical-binding: t; -*- ;;; lang/go/autoload.el -*- lexical-binding: t; -*-
;;;###autoload
;; TODO (defun +go/build ())
;; ;;
;; Tests ;; Tests
;; ;;

View File

@ -4,45 +4,64 @@
:mode "\\.go$" :mode "\\.go$"
:interpreter "go" :interpreter "go"
:config :config
(setq gofmt-command "goimports")
(add-hook 'go-mode-hook #'flycheck-mode) (add-hook 'go-mode-hook #'flycheck-mode)
(setq gofmt-command "goimports")
(if (not (executable-find "goimports")) (if (not (executable-find "goimports"))
(warn "go-mode: couldn't find goimports; no code formatting/fixed imports on save") (warn "go-mode: couldn't find goimports; no code formatting/fixed imports on save")
(add-hook! go-mode (add-hook 'before-save-hook #'gofmt-before-save nil t))) (add-hook! go-mode (add-hook 'before-save-hook #'gofmt-before-save nil t)))
(set! :build 'go-build 'go-mode #'+go/build)
(set! :repl 'go-mode #'gorepl-run) (set! :repl 'go-mode #'gorepl-run)
(set! :jump 'go-mode
:definition #'go-guru-definition
:references #'go-guru-referrers
:documentation #'godoc-at-point)
(def-menu! +go/refactor-menu
"Refactoring commands for `go-mode' buffers."
'(("Add import" :exec go-import-add :region nil)
("Remove unused imports" :exec go-remove-unused-imports :region nil)
("Format buffer (gofmt)" :exec go-gofmt))
:prompt "Refactor: ")
(def-menu! +go/build-menu
"Build/compilation commands for `go-mode' buffers."
'(("Build project" :exec "go build")
("Build & run project" :exec "go run")
("Clean files" :exec "go clean"))
:prompt "Run test: ")
(def-menu! +go/test-menu
"Test commands for `go-mode' buffers."
'(("Last test" :exec +go/test-rerun)
("All tests" :exec +go/test-all)
("Single test" :exec +go/test-single)
("Nested test" :exec +go/test-nested))
:prompt "Run test: ")
(def-menu! +go/help-menu
"Help and information commands for `go-mode' buffers."
'(("Go to imports" :exec go-goto-imports)
("Lookup in godoc" :exec godoc-at-point)
("Describe this" :exec go-guru-describe)
("List free variables" :exec go-guru-freevars)
("What does this point to" :exec go-guru-pointsto)
("Implements relations for package types" :exec go-guru-implements :region nil)
("List peers for channel" :exec go-guru-peers)
("List references to object" :exec go-guru-referrers)
("Which errors" :exec go-guru-whicerrs)
("What query" :exec go-guru-what)
("Show callers of this function" :exec go-guru-callers :region nil)
("Show callees of this function" :exec go-guru-callees :region nil)))
(map! :map go-mode-map (map! :map go-mode-map
:n "gd" #'go-guru-definition :localleader
:n "gD" #'go-guru-referrers "r" #'+go/refactor-menu
(:localleader "b" #'+go/build-menu
"h" #'+go/help-menu
"t" #'+go/test-menu
:n "gr" #'go-play-buffer :n "gr" #'go-play-buffer
:v "gr" #'go-play-region :v "gr" #'go-play-region))
(:prefix "f"
:n "i" #'go-goto-imports
:n "h" #'godoc-at-point
:n "d" #'go-guru-describe
:n "v" #'go-guru-freevars
:n "I" #'go-guru-implements
:n "p" #'go-guru-peers
:n "r" #'go-guru-referrers
:n "t" #'go-guru-pointsto
:n "s" #'go-guru-callstack
:n "e" #'go-guru-whicherrs
:n "c" #'go-guru-callers
:n "C" #'go-guru-callees)
(:prefix "r"
:n "a" #'go-import-add
:n "i" #'go-remove-unused-imports
:nv "f" #'gofmt)
(:prefix "t"
:n "r" #'+go/test-rerun
:n "a" #'+go/test-all
:n "s" #'+go/test-single
:n "n" #'+go/test-nested))))
(def-package! go-eldoc (def-package! go-eldoc

View File

@ -12,7 +12,7 @@
(add-hook! 'intero-mode-hook #'(flycheck-mode eldoc-mode)) (add-hook! 'intero-mode-hook #'(flycheck-mode eldoc-mode))
(set! :popup "^intero:backend:" :regex t :size 12) (set! :popup "^intero:backend:" :regex t :size 12)
(set! :jump :definition #'intero-goto-definition)) (set! :jump 'haskell-mode :definition #'intero-goto-definition))
(def-package! hindent (def-package! hindent

View File

@ -1,5 +1,13 @@
;;; lang/haskell/config.el -*- lexical-binding: t; -*- ;;; lang/haskell/config.el -*- lexical-binding: t; -*-
(cond ((featurep! +intero) (load! +intero))
((featurep! +dante) (load! +dante)))
;;
;; Common plugins
;;
(def-package! haskell-mode (def-package! haskell-mode
:mode "\\.hs$" :mode "\\.hs$"
:mode ("\\.ghci$" . ghci-script-mode) :mode ("\\.ghci$" . ghci-script-mode)
@ -30,8 +38,3 @@
(setq company-ghc-show-info 'oneline)) (setq company-ghc-show-info 'oneline))
;;
(if (featurep! +dante)
(load! +dante)
(load! +intero))

View File

@ -0,0 +1,58 @@
;;; lang/java/+eclim.el -*- lexical-binding: t; -*-
;; NOTE This submodule is incomplete
(def-package! eclim
:init
(add-hook 'java-mode-hook #'eclim-mode)
:config
(set! :jump 'java-mode
:definition #'eclim-java-find-declaration
:references #'eclim-java-find-references
:documentation #'eclim-java-show-documentation-for-current-element)
(require 'eclimd)
(setq help-at-pt-display-when-idle t
help-at-pt-timer-delay 0.1)
(help-at-pt-set-timer)
;;
(def-menu! +java/refactor-menu
"Refactoring commands for `java-mode' buffers."
'(("Generate constructor" :exec eclim-java-constructor)
("Generate getter & setter" :exec eclim-java-generate-getter-and-setter)
("Organize imports" :exec eclim-java-import-organize)
("Reformat" :exec eclim-java-format)
("Rename symbol at point" :exec eclim-java-refactor-rename-symbol-at-point :region nil)))
(def-menu! +java/help-menu
"Help and information commands for `java-mode' buffers."
'(("Find documentation for current element" :exec eclim-java-show-documentation-for-current-element)
("Find references" :exec eclim-java-find-references)
("View call hierarchy" :exec eclim-java-call-hierarchy)
("View hierarchy" :exec eclim-java-hierarchy)
("View problems" :exec eclim-problems)))
(def-menu! +java/project-menu
"Building/compilation commands for `java-mode' buffers."
'(("Build project" :exec eclim-project-build)
("Create project" :exec eclim-project-create)
("Delete project" :exec eclim-project-delete)
("Go to project" :exec eclim-project-goto)
("Import project" :exec eclim-project-import)
("Close project" :exec eclim-project-close)
("Open project" :exec eclim-project-open)
("Update project" :exec eclim-project-update)))
(map! :map java-mode-map
:localleader
:nv "r" #'+java/refactor-menu
:nv "c" #'+java/compile-menu
:nv "p" #'+java/project-menu))
(def-package! company-emacs-eclim
:when (featurep! :completion company)
:after java
:config
(set! :company-backend 'java-mode '(company-emacs-eclim)))

View File

@ -0,0 +1,46 @@
;;; lang/java/+meghanada.el -*- lexical-binding: t; -*-
(def-package! meghanada
:commands meghanada-mode
:config
(setq meghanada-server-install-dir (concat doom-etc-dir "meghanada-server/")
meghanada-use-company (featurep! :completion company)
meghanada-use-flycheck (featurep! :feature syntax-checker)
meghanada-use-eldoc t
meghanada-use-auto-start t)
(add-hook 'java-mode-hook #'(rainbow-delimiters-mode eldoc-mode))
;; Setup on first use
(meghanada-install-server)
(if (file-exists-p (meghanada--locate-server-jar))
(add-hook! 'java-mode-hook #'(meghanada-mode flycheck-mode))
(warn "java-mode: meghanada-server not installed, java-mode will run with reduced functionality"))
(set! :jump 'java-mode
:definition #'meghanada-jump-declaration
:references #'meghanada-reference)
;;
(def-menu! +java/refactor-menu
"Refactoring commands for `java-mode' buffers."
'(("Add imports for unqualified classes" :exec meghanada-import-all)
("Optimize and clean up imports" :exec meghanada-optimize-import)
("Introduce local variable" :exec meghanada-local-variable)
("Format buffer code" :exec meghanada-code-beautify)))
(def-menu! +java/help-menu
"Help and information commands for `java-mode' buffers."
'(("Find usages" :exec meghanada-reference)
("Show type hierarchives and implemented interfaces" :exec meghanada-typeinfo)))
(def-menu! +java/project-menu
"Project commands for `java-mode' buffers."
'(("Compile current file" :exec meghanada-compile-file)
("Compile project" :exec meghanada-compile-project)))
(map! :map java-mode-map
:localleader
:nv "r" #'+java/refactor-menu
:nv "c" #'+java/compile-menu
:nv "p" #'+java/project-menu))

View File

@ -1,29 +1,14 @@
;;; lang/java/config.el -*- lexical-binding: t; -*- ;;; lang/java/config.el -*- lexical-binding: t; -*-
(def-package! meghanada (cond ((featurep! +meghanada) (load! +meghanada))
:commands meghanada-mode ((featurep! +eclim) ; FIXME lang/java +eclim
:config ;;(load! +eclim)
(setq meghanada-server-install-dir (concat doom-etc-dir "meghanada-server/") (warn "java-mode: eclim support isn't implemented yet")))
meghanada-use-company (featurep! :completion company)
meghanada-use-flycheck (featurep! :feature syntax-checker)
meghanada-use-eldoc t
meghanada-use-auto-start t)
;; Setup on first use
(meghanada-install-server)
(if (file-exists-p (meghanada--locate-server-jar))
(add-hook! 'java-mode-hook #'(meghanada-mode flycheck-mode))
(warn "java-mode: meghanada-server not installed, java-mode will run with reduced functionality"))
(add-hook 'java-mode-hook #'(rainbow-delimiters-mode eldoc-mode))
(set! :build 'compile-file 'java-mode #'meghanada-compile-file)
(set! :build 'compile-project 'java-mode #'meghanada-compile-project)
(set! :jump 'java-mode :definition #'meghanada-jump-declaration)
(map! :map meghanada-mode-map :m "gd" #'meghanada-jump-declaration))
;;
;; Common plugins
;;
(def-package! android-mode (def-package! android-mode
:commands android-mode :commands android-mode

View File

@ -1,7 +1,14 @@
;; -*- no-byte-compile: t; -*- ;; -*- no-byte-compile: t; -*-
;;; lang/java/packages.el ;;; lang/java/packages.el
(package! meghanada)
(package! android-mode) (package! android-mode)
(package! groovy-mode) (package! groovy-mode)
(when (featurep! +meghanada)
(package! meghanada))
(when (featurep! +eclim)
(package! eclim)
(when (featurep! :completion company)
(package! company-emacs-eclim)))

View File

@ -169,7 +169,6 @@
(defun +javascript|init-screeps-mode () (defun +javascript|init-screeps-mode ()
(when (eq major-mode 'js2-mode) (when (eq major-mode 'js2-mode)
(cl-pushnew 'javascript-jshint flycheck-disabled-checkers) (push 'javascript-jshint flycheck-disabled-checkers)
(setq js2-additional-externs (append '("_") screeps-objects screeps-constants)))) (setq js2-additional-externs (append '("_") screeps-objects screeps-constants))))
(add-hook '+javascript-screeps-mode-hook #'+javascript|init-screeps-mode)

View File

@ -18,50 +18,29 @@
;; Conform switch-case indentation to editorconfig's config ;; Conform switch-case indentation to editorconfig's config
(set! :editorconfig :add '(js2-mode js2-basic-offset js-switch-indent-offset)) (set! :editorconfig :add '(js2-mode js2-basic-offset js-switch-indent-offset))
;; Favor local eslint over global, if available
(defun +javascript|init-flycheck-elint ()
(when (derived-mode-p 'js-mode)
(when-let ((eslint (expand-file-name "node_modules/eslint/bin/eslint.js"
(doom-project-root)))
(exists-p (file-exists-p eslint))
(executable-p (file-executable-p eslint)))
(setq-local flycheck-javascript-eslint-executable eslint))))
(add-hook 'flycheck-mode-hook #'+javascript|init-flycheck-elint)
(sp-with-modes '(js2-mode rjsx-mode) (sp-with-modes '(js2-mode rjsx-mode)
(sp-local-pair "/* " " */" :post-handlers '(("| " "SPC")))) (sp-local-pair "/* " " */" :post-handlers '(("| " "SPC"))))
;; If it's available globally, use eslint_d
(setq flycheck-javascript-eslint-executable (executable-find "eslint_d"))
(defun +javascript|init-flycheck-eslint ()
"Favor local eslint over global installs and configure flycheck for eslint."
(when (derived-mode-p 'js-mode)
(when-let ((exec-path (list (doom-project-expand "node_modules/.bin")))
(eslint (executable-find "eslint")))
(setq-local flycheck-javascript-eslint-executable eslint))
(when (flycheck-find-checker-executable 'javascript-eslint)
;; Flycheck has it's own trailing command and semicolon warning that was
;; conflicting with the eslint settings.
(setq-local js2-strict-trailing-comma-warning nil)
(setq-local js2-strict-missing-semi-warning nil))))
(add-hook 'flycheck-mode-hook #'+javascript|init-flycheck-eslint)
(map! :map js2-mode-map (map! :map js2-mode-map
:localleader :localleader
:n "S" #'+javascript/skewer-this-buffer "r" #'+javascript/refactor-menu
"S" #'+javascript/skewer-this-buffer))
:prefix "r"
:n "g" #'js2r-add-to-globals-annotation
:n "ca" #'js2r-arguments-to-object
:n "Xa" #'js2r-contract-array
:n "Xf" #'js2r-contract-function
:n "Xo" #'js2r-contract-object
:nv "d" #'js2r-debug-this
:n "xa" #'js2r-expand-array
:n "xf" #'js2r-expand-function
:n "xo" #'js2r-expand-object
:v "ef" #'js2r-extract-function
:v "em" #'js2r-extract-method
:v "ev" #'js2r-extract-var
:n "F" #'js2r-forward-barf
:n "f" #'js2r-forward-slurp
:v "ii" #'js2r-inject-global-in-iife
:v "iv" #'js2r-inline-var
:v "p" #'js2r-introduce-parameter
:n "p" #'js2r-localize-parameter
:nv "l" #'js2r-log-this
:n "r" #'js2r-rename-var
:n "ss" #'js2r-split-string
:n "sv" #'js2r-split-var-declaration
:n "ct" #'js2r-ternary-to-if
:v "u" #'js2r-unwrap
:n "t" #'js2r-var-to-this
:n "ii" #'js2r-wrap-buffer-in-iife))
;; A find-{definition,references} backend for js2-mode. NOTE The xref API is ;; A find-{definition,references} backend for js2-mode. NOTE The xref API is
@ -81,14 +60,43 @@
js2r-add-to-globals-annotation js2r-extract-var js2r-inline-var js2r-add-to-globals-annotation js2r-extract-var js2r-inline-var
js2r-rename-var js2r-var-to-this js2r-arguments-to-object js2r-ternary-to-if js2r-rename-var js2r-var-to-this js2r-arguments-to-object js2r-ternary-to-if
js2r-split-var-declaration js2r-split-string js2r-unwrap js2r-log-this js2r-split-var-declaration js2r-split-string js2r-unwrap js2r-log-this
js2r-debug-this js2r-forward-slurp js2r-forward-barf)) js2r-debug-this js2r-forward-slurp js2r-forward-barf)
:init
(def-menu! +javascript/refactor-menu
"Refactoring commands for `js2-mode' buffers."
'(("Extract into function" :exec js2r-extract-function :region t)
("Extract into method" :exec js2r-extract-method :region t)
("Introduce parameter to function" :exec js2r-introduce-parameter :region t)
("Localize parameter" :exec js2r-localize-parameter :region nil)
("Expand object" :exec js2r-expand-object :region nil)
("Expand function" :exec js2r-expand-function :region nil)
("Expand array" :exec js2r-expand-array :region nil)
("Contract object" :exec js2r-contract-object :region nil)
("Contract function" :exec js2r-contract-function :region nil)
("Contract array" :exec js2r-contract-array :region nil)
("Wrap buffer in IIFE" :exec js2r-wrap-buffer-in-iife :region nil)
("Inject global into IIFE" :exec js2r-inject-global-in-iife :region t)
("Add to globals annotation" :exec js2r-add-to-globals-annotation :region nil)
("Extract variable" :exec js2r-extract-var :region t)
("Inline variable" :exec js2r-inline-var :region t)
("Rename variable" :exec js2r-rename-var :region nil)
("Replace var with this" :exec js2r-var-to-this :region nil)
("Arguments to object" :exec js2r-arguments-to-object :region nil)
("Ternary to if" :exec js2r-ternary-to-if :region nil)
("Split var declaration" :exec js2r-split-var-declaration :region nil)
("Split string" :exec js2r-split-string :region nil)
("Unwrap" :exec js2r-unwrap :region t)
("Log this" :exec js2r-log-this)
("Debug this" :exec js2r-debug-this)
("Reformat buffer (eslint_d)" :exec eslintd-fix :region nil :when (fboundp 'eslintd-fix)))
:prompt "Refactor: "))
(def-package! tern (def-package! tern
:commands tern-mode :commands tern-mode
:init (add-hook 'js2-mode-hook #'tern-mode) :init (add-hook 'js2-mode-hook #'tern-mode)
:config :config
(advice-add #'tern-project-dir :override #'doom*project-root)) (advice-add #'tern-project-dir :override #'doom-project-root))
(def-package! company-tern (def-package! company-tern
@ -103,17 +111,15 @@
:mode "\\.jsx$" :mode "\\.jsx$"
:mode "components/.+\\.js$" :mode "components/.+\\.js$"
:init :init
;; auto-detect JSX file (defun +javascript-jsx-file-p ()
(push (cons (lambda ()
(and buffer-file-name (and buffer-file-name
(equal (file-name-extension buffer-file-name) "js") (equal (file-name-extension buffer-file-name) "js")
(re-search-forward "\\(^\\s-*import React\\|\\( from \\|require(\\)[\"']react\\)" (re-search-forward "\\(^\\s-*import React\\|\\( from \\|require(\\)[\"']react\\)"
magic-mode-regexp-match-limit t) magic-mode-regexp-match-limit t)
(progn (progn (goto-char (match-beginning 1))
(goto-char (match-beginning 1))
(not (sp-point-in-string-or-comment))))) (not (sp-point-in-string-or-comment)))))
'rjsx-mode)
magic-mode-alist) (push (cons #'+javascript-jsx-file-p 'rjsx-mode) magic-mode-alist)
:config :config
(set! :electric 'rjsx-mode :chars '(?\} ?\) ?. ?>)) (set! :electric 'rjsx-mode :chars '(?\} ?\) ?. ?>))
@ -138,6 +144,10 @@
(map! :map* (json-mode js2-mode-map) :n "gQ" #'web-beautify-js)) (map! :map* (json-mode js2-mode-map) :n "gQ" #'web-beautify-js))
(def-package! eslintd-fix
:commands (eslintd-fix-mode eslintd-fix))
;; ;;
;; Skewer-mode ;; Skewer-mode
;; ;;
@ -174,28 +184,27 @@
;; ;;
(def-project-mode! +javascript-screeps-mode (def-project-mode! +javascript-screeps-mode
:match "/screeps/.+$" :match "/screeps\\(-ai\\)?/.+$"
:modes (+javascript-npm-mode) :modes (+javascript-npm-mode)
:init (load! +screeps)) :add-hooks (+javascript|init-screeps-mode)
:on-load (load! +screeps))
(def-project-mode! +javascript-gulp-mode (def-project-mode! +javascript-gulp-mode
:files "gulpfile.js") :files "gulpfile.js")
(def-project-mode! +javascript-npm-mode (def-project-mode! +javascript-npm-mode
:modes (html-mode css-mode web-mode js2-mode markdown-mode) :modes (html-mode css-mode web-mode js2-mode markdown-mode)
:files "package.json") :files "package.json"
:on-enter
(when (make-local-variable 'exec-path)
(push (doom-project-expand "node_modules/.bin")
exec-path)))
(def-project-mode! +javascript-lb6-mode
:modes (web-mode js2-mode nxml-mode markdown-mode) ;;
:match "\\.lb\\(action\\|ext\\)/" ;; Tools
:init ;;
;; TODO
;; (when IS-MAC (def-project-mode! +javascript-eslintd-fix-mode
;; (set! :build 'launchbar-action '+javascript-lb6-mode :add-hooks (eslintd-fix-mode))
;; (lambda ()
;; (when-let (dir (f-traverse-upwards (lambda (f) (f-ext? f "lbaction"))))
;; (shell-command (format "open '%s'" dir))))
;; :when
;; (lambda () (f-traverse-upwards (lambda (f) (f-ext? f "lbaction"))))))
)

View File

@ -11,6 +11,7 @@
(package! tern) (package! tern)
(package! web-beautify) (package! web-beautify)
(package! skewer-mode) (package! skewer-mode)
(package! eslintd-fix)
(when (featurep! :completion company) (when (featurep! :completion company)
(package! company-tern)) (package! company-tern))

View File

@ -3,15 +3,13 @@
(def-package! ledger-mode (def-package! ledger-mode
:mode "\\.ledger$" :mode "\\.ledger$"
:commands ledger-mode :commands ledger-mode
:config :config (setq ledger-clear-whole-transactions 1))
(setq ledger-clear-whole-transactions 1))
(def-package! evil-ledger (def-package! evil-ledger
:when (featurep! :feature evil) :when (featurep! :feature evil)
:after ledger-mode :after ledger-mode
:config :config (add-hook 'ledger-mode-hook #'evil-ledger-mode))
(add-hook 'ledger-mode-hook #'evil-ledger-mode))
(def-package! flycheck-ledger (def-package! flycheck-ledger

View File

@ -7,3 +7,13 @@
(lua-start-process "lua" "lua") (lua-start-process "lua" "lua")
(pop-to-buffer lua-process-buffer)) (pop-to-buffer lua-process-buffer))
;;;###autoload
(defun +lua/run-love-game ()
"Run the current project with Love2D."
(interactive)
(async-shell-command
(format "%s %s"
(or (executable-find "love")
(if IS-MAC "open -a love.app"))
(shell-quote-argument (doom-project-root)))))

View File

@ -10,7 +10,16 @@
(set! :repl 'lua-mode #'+lua/repl) (set! :repl 'lua-mode #'+lua/repl)
;; sp's lua-specific rules are obnoxious, so we disable them ;; sp's lua-specific rules are obnoxious, so we disable them
(setq sp-pairs (delete (assq 'lua-mode sp-pairs) sp-pairs))) (setq sp-pairs (delete (assq 'lua-mode sp-pairs) sp-pairs))
(def-menu! +lua/build-menu
"Build/compilation commands for `lua-mode' buffers."
'(("Run Love app" :exec +lua/run-love-game :when +lua-love-mode))
:prompt "Build tasks: ")
(map! :map lua-mode-map
:localleader
"b" #'+lua/build-menu))
(def-package! company-lua (def-package! company-lua
@ -32,9 +41,5 @@
(def-project-mode! +lua-love-mode (def-project-mode! +lua-love-mode
:modes (lua-mode markdown-mode json-mode) :modes (lua-mode markdown-mode json-mode)
:files (and "main.lua" "conf.lua") :files (and "main.lua" "conf.lua"))
:init
(set! :build 'run-love-app '+lua-love-mode
(lambda ()
(async-shell-command (format "open -a love.app '%s'" (doom-project-root))))))

View File

@ -37,7 +37,7 @@
:m "]p" #'markdown-demote :m "]p" #'markdown-demote
:m "[l" #'markdown-next-link :m "[l" #'markdown-next-link
:m "]l" #'markdown-previous-link :m "]l" #'markdown-previous-link
:i "M--" #'markdown-insert-hr) :i "M--" #'markdown-insert-hr
(:localleader (:localleader
:nv "o" #'markdown-open :nv "o" #'markdown-open
@ -45,7 +45,7 @@
(:prefix "i" (:prefix "i"
:nv "t" #'markdown-toc-generate-toc :nv "t" #'markdown-toc-generate-toc
:nv "i" #'markdown-insert-image :nv "i" #'markdown-insert-image
:nv "l" #'markdown-insert-link)))) :nv "l" #'markdown-insert-link)))))
(def-package! markdown-toc (def-package! markdown-toc

View File

@ -1,5 +1,20 @@
;;; lang/python/config.el -*- lexical-binding: t; -*- ;;; lang/python/config.el -*- lexical-binding: t; -*-
(defvar +python-pyenv-root nil
"The path to pyenv's root directory. This is automatically set when `python'
is loaded.")
(defvar +python-pyenv-versions nil
"Available versions of python in pyenv.")
(defvar-local +python-current-version nil
"The currently active pyenv version.")
;;
;; Plugins
;;
(def-package! python (def-package! python
:commands python-mode :commands python-mode
:init :init
@ -24,6 +39,33 @@
python-shell-completion-string-code python-shell-completion-string-code
"';'.join(get_ipython().Completer.all_completions('''%s'''))\n")) "';'.join(get_ipython().Completer.all_completions('''%s'''))\n"))
;; Version management with pyenv
(defun +python|add-version-to-modeline ()
"Add version string to the major mode in the modeline."
(setq mode-name
(if +python-current-version
(format "Python %s" +python-current-version)
"Python")))
(add-hook 'python-mode-hook #'+python|add-version-to-modeline)
(if (not (executable-find "pyenv"))
(setq +python-current-version (string-trim (shell-command-to-string "python --version 2>&1 | cut -d' ' -f2")))
(setq +python-pyenv-root (string-trim (shell-command-to-string "pyenv root"))
+python-pyenv-versions (split-string (shell-command-to-string "pyenv versions --bare") "\n" t))
(defun +python|detect-pyenv-version ()
"Detect the pyenv version for the current project and set the relevant
environment variables."
(when-let (version-str (shell-command-to-string "python --version 2>&1 | cut -d' ' -f2"))
(setq version-str (string-trim version-str)
+python-current-version version-str)
(let ((pyenv-current-path (concat +python-pyenv-root "/versions/" version-str)))
(when (file-directory-p pyenv-current-path)
(setq pythonic-environment pyenv-current-path)))
(when (member version-str +python-pyenv-versions)
(setenv "PYENV_VERSION" version-str))))
(add-hook 'python-mode-hook #'+python|detect-pyenv-version))
(define-key python-mode-map (kbd "DEL") nil) ; interferes with smartparens (define-key python-mode-map (kbd "DEL") nil) ; interferes with smartparens
(sp-with-modes 'python-mode (sp-with-modes 'python-mode
(sp-local-pair "'" nil :unless '(sp-point-before-word-p sp-point-after-word-p sp-point-before-same-p)))) (sp-local-pair "'" nil :unless '(sp-point-before-word-p sp-point-after-word-p sp-point-before-same-p))))

View File

@ -1,5 +1,16 @@
;;; lang/ruby/config.el -*- lexical-binding: t; -*- ;;; lang/ruby/config.el -*- lexical-binding: t; -*-
(defvar +ruby-rbenv-versions nil
"Available versions of ruby in rbenv.")
(defvar-local +ruby-current-version nil
"The currently active ruby version.")
;;
;; Plugins
;;
(def-package! ruby-mode (def-package! ruby-mode
:mode ("\\.rb$" "\\.rake$" "\\.gemspec$" "\\.?pryrc$" :mode ("\\.rb$" "\\.rake$" "\\.gemspec$" "\\.?pryrc$"
"/\\(Gem\\|Cap\\|Vagrant\\|Rake\\|Pod\\|Puppet\\|Berks\\)file$") "/\\(Gem\\|Cap\\|Vagrant\\|Rake\\|Pod\\|Puppet\\|Berks\\)file$")
@ -13,6 +24,29 @@
;; Don't interfere with my custom RET behavior ;; Don't interfere with my custom RET behavior
(define-key ruby-mode-map [?\n] nil) (define-key ruby-mode-map [?\n] nil)
;; Version management with rbenv
(defun +ruby|add-version-to-modeline ()
"Add version string to the major mode in the modeline."
(setq mode-name
(if +python-current-version
(format "Ruby %s" +ruby-current-version)
"Ruby")))
(add-hook 'ruby-mode-hook #'+ruby|add-version-to-modeline)
(if (not (executable-find "rbenv"))
(setq +ruby-current-version (string-trim (shell-command-to-string "ruby --version 2>&1 | cut -d' ' -f2")))
(setq +ruby-rbenv-versions (split-string (shell-command-to-string "rbenv versions --bare") "\n" t))
(defun +ruby|detect-rbenv-version ()
"Detect the rbenv version for the current project and set the relevant
environment variables."
(when-let (version-str (shell-command-to-string "ruby --version 2>&1 | cut -d' ' -f2"))
(setq version-str (string-trim version-str)
+ruby-current-version version-str)
(when (member version-str +ruby-rbenv-versions)
(setenv "RBENV_VERSION" version-str))))
(add-hook 'ruby-mode-hook #'+ruby|detect-rbenv-version))
(map! :map ruby-mode-map (map! :map ruby-mode-map
:localleader :localleader
:prefix "r" :prefix "r"
@ -72,12 +106,7 @@
:config (set! :company-backend 'inf-ruby-mode '(company-inf-ruby))) :config (set! :company-backend 'inf-ruby-mode '(company-inf-ruby)))
;; (def-package! rake
;; TODO Frameworks :commands (rake rake-find-task rake-rerun)
;; :config (setq rake-completion-system 'default))
;; (def-project-mode! +ruby-rake-mode
;; :files "Rakefile"
;; :init
;; (set! :build 'rake '+ruby-rake-mode '+ruby/rake
;; :when (lambda () (doom-project-has! "Rakefile"))))

View File

@ -7,6 +7,7 @@
(package! rspec-mode) (package! rspec-mode)
(package! ruby-refactor) (package! ruby-refactor)
(package! yard-mode) (package! yard-mode)
(package! rake)
(when (featurep! :completion company) (when (featurep! :completion company)
(package! company-inf-ruby)) (package! company-inf-ruby))

View File

@ -2,4 +2,7 @@
;; TODO (defun +rust/run-cargo () (interactive)) ;; TODO (defun +rust/run-cargo () (interactive))
;; TODO (defun +rust-cargo-project-p ()) ;;;###autoload
(defun +rust-cargo-project-p ()
"Return t if this is a cargo project."
(doom-project-has! "Cargo.toml"))

View File

@ -1,15 +1,21 @@
;;; lang/rust/config.el -*- lexical-binding: t; -*- ;;; lang/rust/config.el -*- lexical-binding: t; -*-
(defvar +rust-ext-dir (concat doom-etc-dir "rust/") (defvar +rust-src-dir (concat doom-etc-dir "rust/")
"TODO") "The path to Rust source library. Required by racer.")
;;
;; Plugins
;;
(def-package! rust-mode (def-package! rust-mode
:mode "\\.rs$" :mode "\\.rs$"
:init
(add-hook 'rust-mode-hook #'flycheck-mode)
:config :config
(set! :build 'run-cargo '(rust-mode toml-mode) #'+rust/run-cargo (def-menu! +rust/build-menu
:when #'+rust-cargo-project-p)) "TODO"
'(("run" :exec "cargo run" :cwd t :when (+rust-cargo-project-p))
("build" :exec "cargo build" :cwd t :when (+rust-cargo-project-p)))
:prompt "Cargo: "))
(def-package! racer (def-package! racer
@ -18,14 +24,13 @@
:init :init
(add-hook! 'rust-mode-hook #'(racer-mode eldoc-mode flycheck-rust-setup)) (add-hook! 'rust-mode-hook #'(racer-mode eldoc-mode flycheck-rust-setup))
:config :config
(setq racer-cmd (expand-file-name "racer/target/release/racer" +rust-ext-dir) (setq racer-cmd (expand-file-name "racer/target/release/racer" +rust-src-dir)
racer-rust-src-path (expand-file-name "rust/src/" +rust-ext-dir)) racer-rust-src-path (expand-file-name "rust/src/" +rust-src-dir))
(set! :jump 'rust-mode :definition #'racer-find-definition)
(unless (file-exists-p racer-cmd) (unless (file-exists-p racer-cmd)
(warn "rust-mode: racer binary can't be found; auto-completion is disabled")) (warn "rust-mode: racer binary can't be found; auto-completion is disabled")))
;; TODO Unit test keybinds
(map! :map rust-mode-map :m "gd" #'racer-find-definition))
(def-package! company-racer (def-package! company-racer
@ -36,5 +41,6 @@
(def-package! flycheck-rust (def-package! flycheck-rust
:when (featurep! :feature syntax-checker) :when (featurep! :feature syntax-checker)
:after rust-mode) :after rust-mode
:config (add-hook 'rust-mode-hook #'flycheck-mode))

View File

@ -14,6 +14,7 @@
(def-package! sh-script ; built-in (def-package! sh-script ; built-in
:mode ("\\.zsh$" . sh-mode) :mode ("\\.zsh$" . sh-mode)
:mode ("\\.zunit$" . sh-mode)
:mode ("/bspwmrc$" . sh-mode) :mode ("/bspwmrc$" . sh-mode)
:init :init
(add-hook! sh-mode #'(flycheck-mode highlight-numbers-mode)) (add-hook! sh-mode #'(flycheck-mode highlight-numbers-mode))

View File

@ -16,9 +16,7 @@
;; '(tide-jump-to-definition "jump to definition") ;; '(tide-jump-to-definition "jump to definition")
;; '(tide-documentation-at-point "current type documentation") ;; '(tide-documentation-at-point "current type documentation")
;; '(tide-restart-server "restart tide server")) ;; '(tide-restart-server "restart tide server"))
)
(map! :localleader
:m "fh" #'tide-documentation-at-point))
(def-package! tide (def-package! tide
@ -41,8 +39,7 @@
(equal (file-name-extension buffer-file-name) "tsx"))) (equal (file-name-extension buffer-file-name) "tsx")))
(tide-setup) (tide-setup)
(flycheck-mode +1) (flycheck-mode +1)
(eldoc-mode +1))) (eldoc-mode +1)
(add-hook! (typescript-mode web-mode) #'+typescript|init-tide) (setq tide-project-root (doom-project-root))))
(add-hook! (typescript-mode web-mode) #'+typescript|init-tide))
(advice-add #'tide-project-root :override #'doom*project-root))

View File

@ -13,6 +13,7 @@
(:localleader (:localleader
:n "rb" #'+css/toggle-inline-or-block)) :n "rb" #'+css/toggle-inline-or-block))
;; ;;
;; Packages ;; Packages
;; ;;
@ -36,15 +37,14 @@
:mode ("\\.scss$" . scss-mode) :mode ("\\.scss$" . scss-mode)
:config :config
(set! :company-backend '(css-mode scss-mode) '(company-css company-yasnippet)) (set! :company-backend '(css-mode scss-mode) '(company-css company-yasnippet))
(set! :build 'compile-to-css 'scss-mode #'+css/scss-build)) (map! :map scss-mode-map :localleader "b" #'+css/scss-build))
(def-package! sass-mode (def-package! sass-mode
:mode "\\.sass$" :mode "\\.sass$"
:config :config
(setq sass-command-options '("--style compressed"))
(set! :company-backend 'sass-mode '(company-css company-yasnippet)) (set! :company-backend 'sass-mode '(company-css company-yasnippet))
(set! :build 'compile-to-css 'sass-mode #'+css/sass-build)) (map! :map scss-mode-map :localleader "b" #'+css/sass-build))
(def-package! less-css-mode (def-package! less-css-mode

View File

@ -41,7 +41,4 @@
:mode "\\.jade$" :mode "\\.jade$"
:mode "\\.pug$" :mode "\\.pug$"
:config :config
(set! :company-backend 'pug-mode '(company-yasnippet)) (set! :company-backend 'pug-mode '(company-yasnippet)))
(map! :map pug-mode-map
:i [tab] #'doom/dumb-indent
:i [backtab] #'doom/dumb-dedent))

View File

@ -1,6 +1,5 @@
;;; lang/web/autoload/html.el -*- lexical-binding: t; -*- ;;; lang/web/autoload/html.el -*- lexical-binding: t; -*-
;;;###autoload
(defvar +web-entities-list (defvar +web-entities-list
[["&nbsp;" " "] ["&ensp;" ""] ["&emsp;" ""] ["&thinsp;" ""] [["&nbsp;" " "] ["&ensp;" ""] ["&emsp;" ""] ["&thinsp;" ""]
["&rlm;" ""] ["&lrm;" ""] ["&zwj;" ""] ["&zwnj;" ""] ["&rlm;" ""] ["&lrm;" ""] ["&zwj;" ""] ["&zwnj;" ""]

View File

@ -38,11 +38,9 @@
(def-project-mode! +web-jekyll-mode (def-project-mode! +web-jekyll-mode
:modes (web-mode js-mode coffee-mode css-mode haml-mode pug-mode) :modes (web-mode js-mode coffee-mode css-mode haml-mode pug-mode)
:files (and "config.yml" (or "_layouts/" "_posts/")) :files (and "config.yml" (or "_layouts/" "_posts/"))
:init :on-enter
(defun +web|init-jekyll-mode ()
(when (eq major-mode 'web-mode) (when (eq major-mode 'web-mode)
(web-mode-set-engine "django"))) (web-mode-set-engine "django")))
(add-hook '+web-jekyll-mode-hook #'+web|init-jekyll-mode))
(def-project-mode! +web-wordpress-mode (def-project-mode! +web-wordpress-mode
:modes (php-mode web-mode css-mode haml-mode pug-mode) :modes (php-mode web-mode css-mode haml-mode pug-mode)

View File

@ -9,11 +9,12 @@
(package! counsel-css :recipe (:fetcher github :repo "hlissner/emacs-counsel-css"))) (package! counsel-css :recipe (:fetcher github :repo "hlissner/emacs-counsel-css")))
;; +html.el ;; +html.el
(package! company-web)
(package! emmet-mode) (package! emmet-mode)
(package! haml-mode) (package! haml-mode)
(package! pug-mode) (package! pug-mode)
(package! web-mode) (package! web-mode)
(when (featurep! :completion company)
(package! company-web))
;; +css.el ;; +css.el
(package! less-css-mode) (package! less-css-mode)

View File

@ -91,7 +91,7 @@ the cursor."
(or ext (file-name-extension filename)))) (or ext (file-name-extension filename))))
;;;###autoload ;;;###autoload
(defun +org-attach*insert-link (link filename) (defun +org-attach*insert-link (_link filename)
"TODO" "TODO"
(if (looking-back "^[ \t]+" (line-beginning-position)) (if (looking-back "^[ \t]+" (line-beginning-position))
(delete-region (match-beginning 0) (match-end 0)) (delete-region (match-beginning 0) (match-end 0))

View File

@ -1,10 +1,10 @@
;;; org/org-capture/autoload/evil.el -*- lexical-binding: t; -*- ;;; org/org-capture/autoload/evil.el -*- lexical-binding: t; -*-
;;;###autoload (autoload '+org-capture:dwim "org/org-capture/autoload/evil" nil t) ;;;###autoload (autoload '+org-capture:open "org/org-capture/autoload/evil" nil t)
(evil-define-operator +org-capture:dwim (&optional beg end) (evil-define-operator +org-capture:open (&optional beg end)
"Evil ex interface to `+org-capture/dwim'." "Evil ex interface to `+org-capture/dwim'."
:move-point nil :type inclusive :move-point nil :type inclusive
(interactive "<r>") (interactive "<r>")
(+org-capture/dwim (+org-capture/open
(unless (or (evil-normal-state-p) (evil-insert-state-p)) (unless (or (evil-normal-state-p) (evil-insert-state-p))
(buffer-substring beg end)))) (buffer-substring beg end))))

View File

@ -1,7 +1,7 @@
;;; org/org-capture/autoload/org-capture.el -*- lexical-binding: t; -*- ;;; org/org-capture/autoload/org-capture.el -*- lexical-binding: t; -*-
;;;###autoload ;;;###autoload
(defun +org-capture/dwim (&optional string key) (defun +org-capture/open (&optional string key)
"Sends STRING, the current selection or prompted input to `org-capture'. "Sends STRING, the current selection or prompted input to `org-capture'.
Uses the capture template specified by KEY. Otherwise, prompts you for one." Uses the capture template specified by KEY. Otherwise, prompts you for one."
@ -15,3 +15,57 @@ Uses the capture template specified by KEY. Otherwise, prompts you for one."
(region-end))))) (region-end)))))
(org-capture-string string key) (org-capture-string string key)
(org-capture nil key)))) (org-capture nil key))))
;; --- External frame ---------------------
(defvar +org-capture-window-params
`((name . "org-capture")
(width . 70)
(height . 25)
(window-system . ,(cond (IS-MAC 'ns)
(IS-LINUX 'x)
(t 'w32)))
,(if IS-LINUX '(display . ":0")))
"TODO")
;;;###autoload
(defun +org-capture|cleanup-frame ()
"Closes the org-capture frame once done adding an entry."
(when (+org-capture-frame-p)
(delete-frame nil t)))
;;;###autoload
(defun +org-capture-frame-p (&rest _)
"Return t if the current frame is an org-capture frame opened by
`+org-capture/open-frame'."
(equal "org-capture" (frame-parameter nil 'name)))
;;;###autoload
(defun +org-capture/open-frame (&optional string key)
"Opens the org-capture window in a floating frame that cleans itself up once
you're done. This can be called from an external shell script."
(interactive)
(require 'org)
(let (after-make-frame-functions before-make-frame-hook)
(let ((frame (if (+org-capture-frame-p)
(selected-frame)
(make-frame +org-capture-window-params))))
(with-selected-frame frame
(condition-case ex
(cl-letf (((symbol-function #'pop-to-buffer)
(symbol-function #'switch-to-buffer)))
(if (and (stringp string)
(not (string-empty-p string)))
(org-capture-string string key)
(org-capture nil key))
(when (featurep 'solaire-mode)
(solaire-mode +1)))
('error
(message "org-capture: %s" (error-message-string ex))
(delete-frame frame)))))))
;;;###autoload
(defun +org-capture-available-keys ()
"TODO"
(string-join (mapcar #'car org-capture-templates) ""))

View File

@ -12,56 +12,21 @@
;; anywhere I can call org-capture (whether or not Emacs is open/running), ;; anywhere I can call org-capture (whether or not Emacs is open/running),
;; like, say, from qutebrowser, vimperator, dmenu or a global keybinding. ;; like, say, from qutebrowser, vimperator, dmenu or a global keybinding.
(defun +org-capture|init ()
"Set up a sane `org-capture' workflow."
(setq org-default-notes-file (concat +org-dir "notes.org") (setq org-default-notes-file (concat +org-dir "notes.org")
;; FIXME This is incomplete!
org-capture-templates org-capture-templates
'(;; TODO: New vocabulary word '(("t" "Todo" entry
("t" "Todo" entry
(file+headline (expand-file-name "todo.org" +org-dir) "Inbox") (file+headline (expand-file-name "todo.org" +org-dir) "Inbox")
"* [ ] %?") "* [ ] %?\n%i" :prepend t :kill-buffer t)
("c" "Changelog" entry
(file+headline (expand-file-name "CHANGELOG.org" (doom-project-root)) "Unreleased")
"* %?")
;; ("p" "Project Notes" entry
;; (file+headline org-default-notes-file "Inbox")
;; "* %u %?\n%i" :prepend t)
;; ("m" "Major-mode Notes" entry
;; (file+headline org-default-notes-file "Inbox")
;; "* %u %?\n%i" :prepend t)
("n" "Notes" entry ("n" "Notes" entry
(file+headline (concat +org-dir "notes.org") "Inbox") (file+headline org-default-notes-file "Inbox")
"* %u %?\n%i" :prepend t) "* %u %?\n%i" :prepend t :kill-buffer t)))
;; ("v" "Vocab" entry (defun +org-capture|init ()
;; (file+headline (concat org-directory "topics/vocab.org") "Unsorted") (add-hook 'org-capture-after-finalize-hook #'+org-capture|cleanup-frame)
;; "** %i%?\n")
))
(when (featurep! :feature evil) (when (featurep! :feature evil)
(add-hook 'org-capture-mode-hook #'evil-insert-state)) (add-hook 'org-capture-mode-hook #'evil-insert-state))
;; Allows the Emacs mini-frame (opened from an external shell script to run (when (featurep! :ui doom-dashboard)
;; and clean up properly) if the frame is named "org-capture". (add-hook '+doom-dashboard-inhibit-functions #'+org-capture-frame-p)))
(require 'org-capture)
(require 'org-protocol)
(defun +org-capture*init (&rest _)
"Makes sure the org-capture window is the only window in the frame."
(when (equal "org-capture" (frame-parameter nil 'name))
(setq mode-line-format nil)
(delete-other-windows)))
(advice-add #'org-capture :after #'+org-capture*init)
(defun +org-capture|finalize ()
"Closes the frame once org-capture is done."
(when (equal "org-capture" (frame-parameter nil 'name))
(when (and (featurep 'persp-mode) persp-mode)
(+workspace/delete (+workspace-current-name)))
(delete-frame)))
(add-hook 'org-capture-after-finalize-hook #'+org-capture|finalize))

View File

@ -1,36 +0,0 @@
;;; org/org-notebook/autoload.el -*- lexical-binding: t; -*-
(defun +org-notebook--explore-notes (dir)
(unless (file-directory-p dir)
(error "Directory doesn't exist: %s" dir))
(if (fboundp '+evil/neotree)
(neotree-dir dir)
(let ((default-directory dir))
(call-interactively (command-remapping 'find-file)))))
;;;###autoload
(defun +org-notebook/find-major-mode-notes ()
"Browse org notes in `+org-notebook-code-dir' in neotree, ido, ivy or helm --
whichever is available."
(interactive)
(let ((dir (expand-file-name
(concat (or (cdr (assq major-mode +org-notebook-code-alist))
(replace-regexp-in-string
"+" "p"
(string-remove-suffix "-mode" (symbol-name major-mode))
nil t))
"/")
+org-notebook-code-dir)))
(unless (file-in-directory-p dir +org-notebook-code-dir)
(error "Invalid location for %s notes: %s"
major-mode (abbreviate-file-name dir)))
(unless (file-directory-p dir)
(make-directory dir t))
(+org-notebook--explore-notes dir)))
;;;###autoload
(defun +org-notebook/find-project-notes ()
"Browse org notes in `+org-notebook-project-dir' in neotree, ido, ivy or helm --
whichever is available."
(interactive)
(+org-notebook--explore-notes +org-notebook-project-dir))

View File

@ -1,27 +0,0 @@
;;; org/org-notebook/config.el -*- lexical-binding: t; -*-
;; (add-hook 'org-load-hook '+org|init-notebook t)
;; While I program, write or plan, I want easy access to notes of various kinds,
;; such as major-mode/language specific notes, or project-specific notes. They
;; can be accessed via `+org-notebook/find-major-mode-notes' and
;; `+org-notebook/find-project-notes'.
(defvar +org-notebook-dir (concat +org-dir "notes/")
"The directory where the notes are kept.")
(defvar +org-notebook-code-dir (concat +org-notebook-dir "code/")
"The directory where programming notes and snippets are kept.")
(defvar +org-notebook-project-dir (concat +org-notebook-dir "projects/")
"The directory where project notes are kept.")
(defvar +org-notebook-code-alist
'((js2-mode . "javascript"))
"An alist mapping certain modes (symbols) to their org notes directory name.
If a mode isn't here, it's guessed by stripping out the -mode suffix and
replacing '+' characters with 'p's.")
;; (defun +org|init-notebook ())

View File

@ -8,8 +8,9 @@
:group 'evil-org :group 'evil-org
(setq org-hide-emphasis-markers +org-pretty-mode) (setq org-hide-emphasis-markers +org-pretty-mode)
(org-toggle-pretty-entities) (org-toggle-pretty-entities)
(org-with-silent-modifications
;; In case the above un-align tables ;; In case the above un-align tables
(org-table-map-tables 'org-table-align t)) (org-table-map-tables 'org-table-align t)))
;;;###autoload ;;;###autoload
(defun +org|realign-table-maybe () (defun +org|realign-table-maybe ()
@ -45,6 +46,10 @@ If on a:
(let* ((scroll-pt (window-start)) (let* ((scroll-pt (window-start))
(context (org-element-context)) (context (org-element-context))
(type (org-element-type context))) (type (org-element-type context)))
;; skip over unimportant contexts
(while (and context (memq type '(verbatim code bold italic underline strike-through subscript superscript)))
(setq context (org-element-property :parent context)
type (org-element-type context)))
(pcase type (pcase type
((guard (org-element-property :checkbox (org-element-lineage context '(item) t))) ((guard (org-element-property :checkbox (org-element-lineage context '(item) t)))
(let ((match (and (org-at-item-checkbox-p) (match-string 1)))) (let ((match (and (org-at-item-checkbox-p) (match-string 1))))
@ -180,8 +185,10 @@ wrong places)."
(- (point) (line-beginning-position))))) (- (point) (line-beginning-position)))))
(pcase direction (pcase direction
('below ('below
(goto-char (line-end-position)) (org-end-of-item)
(insert (concat "\n" (make-string pad ? ) marker))) (goto-char (line-beginning-position))
(insert (make-string pad 32) (or marker ""))
(save-excursion (insert "\n")))
('above ('above
(goto-char (line-beginning-position)) (goto-char (line-beginning-position))
(insert (make-string pad 32) (or marker "")) (insert (make-string pad 32) (or marker ""))
@ -217,7 +224,6 @@ wrong places)."
(org-back-to-heading) (org-back-to-heading)
(org-insert-heading) (org-insert-heading)
(when (= level 1) (when (= level 1)
(save-excursion (evil-open-above 1))
(save-excursion (insert "\n"))))) (save-excursion (insert "\n")))))
(when (org-element-property :todo-type context) (when (org-element-property :todo-type context)
(org-todo 'todo)))) (org-todo 'todo))))
@ -276,3 +282,18 @@ with `org-cycle'). Also:
(let ((window-beg (window-start))) (let ((window-beg (window-start)))
(org-cycle) (org-cycle)
(set-window-start nil window-beg)))))) (set-window-start nil window-beg))))))
;;;###autoload
(defun +org/remove-link ()
"Unlink the text at point."
(interactive)
(unless (org-in-regexp org-bracket-link-regexp 1)
(user-error "No link at point"))
(save-excursion
(let ((remove (list (match-beginning 0) (match-end 0)))
(description (if (match-end 3)
(org-match-string-no-properties 3)
(org-match-string-no-properties 1))))
(apply #'delete-region remove)
(insert description))))

View File

@ -2,7 +2,8 @@
;; Ensure ELPA org is prioritized above built-in org. ;; Ensure ELPA org is prioritized above built-in org.
(when-let (path (locate-library "org" nil doom--package-load-path)) (when-let (path (locate-library "org" nil doom--package-load-path))
(cl-pushnew (file-name-directory path) load-path :test #'equal)) (setq load-path (delete path load-path))
(push (file-name-directory path) load-path))
;; Custom variables ;; Custom variables
(defvar +org-dir (expand-file-name "~/work/org/") (defvar +org-dir (expand-file-name "~/work/org/")
@ -55,6 +56,7 @@
;; ;;
(setq line-spacing 1) (setq line-spacing 1)
(visual-line-mode +1) (visual-line-mode +1)
(org-indent-mode +1)
(doom|disable-line-numbers) (doom|disable-line-numbers)
;; show-paren-mode causes problems for org-indent-mode, so disable it ;; show-paren-mode causes problems for org-indent-mode, so disable it
@ -97,7 +99,6 @@
org-agenda-skip-unavailable-files nil org-agenda-skip-unavailable-files nil
org-cycle-include-plain-lists t org-cycle-include-plain-lists t
org-cycle-separator-lines 1 org-cycle-separator-lines 1
;; org-ellipsis "  "
org-entities-user '(("flat" "\\flat" nil "" "" "266D" "") ("sharp" "\\sharp" nil "" "" "266F" "")) org-entities-user '(("flat" "\\flat" nil "" "" "266D" "") ("sharp" "\\sharp" nil "" "" "266F" ""))
org-ellipsis "" org-ellipsis ""
org-fontify-done-headline t org-fontify-done-headline t
@ -113,6 +114,10 @@
org-indent-mode-turns-on-hiding-stars t org-indent-mode-turns-on-hiding-stars t
org-pretty-entities nil org-pretty-entities nil
org-pretty-entities-include-sub-superscripts t org-pretty-entities-include-sub-superscripts t
org-priority-faces
`((?a . ,(face-foreground 'error))
(?b . ,(face-foreground 'warning))
(?c . ,(face-foreground 'success)))
org-startup-folded t org-startup-folded t
org-startup-indented t org-startup-indented t
org-startup-with-inline-images nil org-startup-with-inline-images nil
@ -143,7 +148,10 @@
"Sets up org-mode and evil keybindings. Tries to fix the idiosyncrasies "Sets up org-mode and evil keybindings. Tries to fix the idiosyncrasies
between the two." between the two."
(map! (:map org-mode-map (map! (:map org-mode-map
"RET" #'org-return-indent) "RET" #'org-return-indent
"C-c C-S-l" #'+org/remove-link
:n "j" "gj"
:n "k" "gk")
(:map +org-evil-mode-map (:map +org-evil-mode-map
:n "RET" #'+org/dwim-at-point :n "RET" #'+org/dwim-at-point
@ -213,9 +221,9 @@ between the two."
(defun +org|remove-occur-highlights () (defun +org|remove-occur-highlights ()
"Remove org occur highlights on ESC in normal mode." "Remove org occur highlights on ESC in normal mode."
(when (derived-mode-p 'org-mode) (when (and (derived-mode-p 'org-mode)
(org-remove-occur-highlights) org-occur-highlights)
t)) (org-remove-occur-highlights)))
(add-hook '+evil-esc-hook #'+org|remove-occur-highlights) (add-hook '+evil-esc-hook #'+org|remove-occur-highlights)
(after! recentf (after! recentf

Some files were not shown because too many files have changed in this diff Show More