Add and expand doom/help* commands

Adds the following commands:

- doom/help (opens the Doom manual)
- doom/help-search (for searching through org headlines in Doom's
  documentation)
- doom/help-faq (for searching the FAQ)
- doom/help-news (for browsing the Doom newsletters)
- doom/help-autodefs (renamed from doom/describe-autodef -- for looking
  up documentation on autodef function/macros, like
  `set-lookup-handler!`)
- doom/help-modules (renamed from doom/describe-module, for jumping to a
  Doom module's documentation)
- doom/help-packages (renamed from doom/describe-package and recently
  fixed -- looks up information about installed packages, including what
  Doom module(s) install it and where it is configured)
- doom/help-package-config (for searching and jumping to any block where
  a package is configured in Doom Emacs)

Also adds the SPC h d (or C-h d) prefix for Doom-specific help commands.
SPC h D will invoke doom/help.

However, the documentation itself hasn't been committed yet, so some of
these commands may be useless atm. Sorry!
This commit is contained in:
Henrik Lissner 2019-04-23 20:46:58 -04:00
parent 5df4a2f18e
commit fa7f7042b0
No known key found for this signature in database
GPG key ID: 5F6C0EA160557395
3 changed files with 227 additions and 112 deletions

View file

@ -1,7 +1,8 @@
;;; core/autoload/help.el -*- lexical-binding: t; -*-
(defvar doom--module-mode-alist
(defvar doom--help-major-mode-module-alist
'((dockerfile-mode :tools docker)
(agda2-mode :lang agda)
(haxor-mode :lang assembly)
(mips-mode :lang assembly)
(nasm-mode :lang assembly)
@ -27,12 +28,14 @@
(go-mode :lang go)
(haskell-mode :lang haskell)
(hy-mode :lang hy)
(idris-mode :lang idris)
(java-mode :lang java)
(js2-mode :lang javascript)
(rjsx-mode :lang javascript)
(typescript-mode :lang javascript)
(coffee-mode :lang javascript)
(julia-mode :lang julia)
(kotlin-mode :lang kotlin)
(latex-mode :lang latex)
(LaTeX-mode :lang latex)
(ledger-mode :lang ledger)
@ -61,12 +64,14 @@
(scss-mode :lang web)
(sass-mode :lang web)
(less-css-mode :lang web)
(stylus-mode :lang web))
(stylus-mode :lang web)
(terra-mode :lang terra)
(vala-mode :lang vala))
"TODO")
;;
;; Helpers
;;; Helpers
;;;###autoload
(defun doom-active-minor-modes ()
@ -77,16 +82,131 @@
;;
;; Commands
;;; Custom describe commands
;;;###autoload (defalias 'doom/describe-autodefs #'doom/help-autodefs)
;;;###autoload (defalias 'doom/describe-module #'doom/help-modules)
;;;###autoload (defalias 'doom/describe-package #'doom/help-packages)
;;;###autoload
(defun doom/describe-autodefs (autodef)
"Open the documentation of Doom autodefs.
(defun doom/describe-active-minor-mode (mode)
"Get information on an active minor mode. Use `describe-minor-mode' for a
selection of all minor-modes, active or not."
(interactive
(list (completing-read "Minor mode: " (doom-active-minor-modes))))
(describe-minor-mode-from-symbol
(cond ((stringp mode) (intern mode))
((symbolp mode) mode)
((error "Expected a symbol/string, got a %s" (type-of mode))))))
What is an autodef? It's a function or macro that is always defined, even if its
containing module is disabled (in which case it will safely no-op). This
syntactic sugar lets you use them without needing to check if they are
available."
;;;###autoload
(defun doom/describe-symbol (symbol)
"Show help for SYMBOL, a variable, function or macro."
(interactive
(list (helpful--read-symbol "Symbol: " #'helpful--bound-p)))
(let* ((sym (intern-soft symbol))
(bound (boundp sym))
(fbound (fboundp sym)))
(cond ((and sym bound (not fbound))
(helpful-variable sym))
((and sym fbound (not bound))
(helpful-callable sym))
((apropos (format "^%s\$" symbol)))
((apropos (format "%s" symbol))))))
;;
;;; Documentation commands
(defun doom--org-headings (files &optional depth prompt include-files)
(let ((org-agenda-files (doom-enlist files))
(default-directory doom-docs-dir))
(unwind-protect
(delq nil
(org-map-entries
(lambda ()
(let* ((components (org-heading-components))
(path (org-get-outline-path))
(level (nth 0 components))
(text (nth 4 components))
(tags (nth 5 components)))
(when (and (or (not depth)
(and (integerp depth)
(<= level depth)))
(or (not tags)
(not (string-match-p ":TOC" tags))))
(list
(mapconcat
'identity
(list (mapconcat 'identity
(append (when include-files
(list (or (+org-get-property "TITLE")
(file-relative-name buffer-file-name))))
path
(list text))
" > ")
tags)
" ")
buffer-file-name
(point)))))
nil
'agenda))
(mapc #'kill-buffer org-agenda-new-buffers)
(setq org-agenda-new-buffers nil))))
;;;###autoload
(defun doom/help ()
"Open Doom's user manual."
(interactive)
(find-file (expand-file-name "index.org" doom-docs-dir)))
;;;###autoload
(defun doom/help-search ()
"Search Doom's documentation and jump to a headline."
(interactive)
(let (ivy-sort-functions-alist)
(completing-read "Find in Doom help: "
(doom--org-headings (list "getting_started.org"
"contributing.org"
"troubleshooting.org"
"tutorials.org"
"faq.org")
2 nil t))))
;;;###autoload
(defun doom/help-faq ()
"Search Doom's FAQ and jump to a question."
(interactive)
(completing-read "Find in FAQ: "
(doom--org-headings (list "faq.org"))))
;;;###autoload
(defun doom/help-news ()
"Open a Doom newsletter.
The latest newsletter will be selected by default."
(interactive)
(let* ((default-directory (expand-file-name "news/" doom-docs-dir))
(news-files (doom-files-in default-directory)))
(find-file
(read-file-name (format "Open Doom newsletter (current: v%s): "
doom-version)
default-directory
(if (member doom-version news-files)
doom-version
(concat (mapconcat #'number-to-string
(nbutlast (version-to-list doom-version) 1)
".")
".x"))
t doom-version))))
;;;###autoload
(defun doom/help-autodefs (autodef)
"Open the documentation of an autodef.
An autodef is a Doom concept. It is a function or macro that is always defined,
whether or not its containing module is disabled (in which case it will safely
no-op). This syntactic sugar lets you use them without needing to check if they
are available."
(interactive
(let* ((settings
(cl-loop with case-fold-search = nil
@ -133,26 +253,15 @@ available."
(describe-function fn))))
;;;###autoload
(defun doom/describe-active-minor-mode (mode)
"Get information on an active minor mode. Use `describe-minor-mode' for a
selection of all minor-modes, active or not."
(interactive
(list (completing-read "Minor mode: " (doom-active-minor-modes))))
(describe-minor-mode-from-symbol
(cond ((stringp mode) (intern mode))
((symbolp mode) mode)
((error "Expected a symbol/string, got a %s" (type-of mode))))))
;;;###autoload
(defun doom/describe-module (category module)
"Open the documentation of CATEGORY MODULE.
(defun doom/help-modules (category module)
"Open the documentation for a Doom module.
CATEGORY is a keyword and MODULE is a symbol. e.g. :editor and 'evil.
Automatically selects a) the module at point (in private init files), b) the
module derived from a `featurep!' or `require!' call, c) the module that the
current file is in, or d) the module associated with the current major mode (see
`doom--module-mode-alist')."
`doom--help-major-mode-module-alist')."
(interactive
(let* ((module
(cond ((and buffer-file-name
@ -173,7 +282,7 @@ current file is in, or d) the module associated with the current major mode (see
((and buffer-file-name
(when-let* ((mod (doom-module-from-path buffer-file-name)))
(format "%s %s" (car mod) (cdr mod)))))
((when-let* ((mod (cdr (assq major-mode doom--module-mode-alist))))
((when-let* ((mod (cdr (assq major-mode doom--help-major-mode-module-alist))))
(format "%s %s"
(symbol-name (car mod))
(symbol-name (cadr mod)))))))
@ -185,7 +294,7 @@ current file is in, or d) the module associated with the current major mode (see
for format = (format "%s %s" cat mod)
if (doom-module-p cat mod)
collect format
else
else if (and cat mod)
collect (propertize format 'face 'font-lock-comment-face))
nil t nil nil module))
(key (split-string module-string " ")))
@ -203,6 +312,10 @@ current file is in, or d) the module associated with the current major mode (see
(doom-project-browse path)
(user-error "Aborted module lookup")))))
;;
;;; `doom/help-packages'
(defun doom--describe-package-insert-button (label path &optional regexp)
(declare (indent defun))
(insert-text-button
@ -223,49 +336,61 @@ current file is in, or d) the module associated with the current major mode (see
(recenter)
(message "Couldn't find the config block"))))))))
;;;###autoload
(global-set-key [remap describe-package] #'doom/describe-package)
(defun doom--completing-package (&optional prompt refresh)
(let* (guess
(sym (symbol-at-point))
(package-list (or (unless refresh (doom-cache-get 'help-packages))
(doom-cache-set 'help-packages (doom-package-list 'all))))
(pkg-alist (seq-group-by #'car package-list))
(packages
;; TODO Refactor me
(cl-loop for (pkg . descs) in (cl-sort pkg-alist #'string-lessp :key #'car)
for str =
(format "%-40s %s"
pkg
(cl-loop for (desc . plist) in descs
for module = (car (plist-get plist :modules))
collect
(format "%s%s"
(car module)
(if (cdr module) (format " %s" (cdr module)) ""))
into modules
finally return
(propertize (string-join modules ", ")
'face 'font-lock-comment-face)))
collect str
and do
(when (eq pkg sym)
(setq guess str)))))
(intern
(car (split-string
(completing-read
(or prompt
(if guess
(format "Describe package (default %s): "
(car (split-string guess " ")))
"Describe package: "))
packages nil t nil nil
(if guess guess))
" ")))))
(defvar doom--describe-package-list-cache nil)
;;;###autoload
(defun doom/describe-package (package)
"Like `describe-packages', but is Doom aware.
(defun doom/help-packages (package)
"Like `describe-package', but for packages installed by Doom modules.
Only shows installed packages. Includes information about where packages are
defined and configured.
If prefix arg is prsent, refresh the cache."
If prefix arg is present, refresh the cache."
(interactive
(list
(let* ((guess (or (function-called-at-point)
(symbol-at-point))))
(require 'finder-inf nil t)
(require 'core-packages)
(doom-initialize-packages)
(let ((packages
(or (unless current-prefix-arg doom--describe-package-list-cache)
(cl-loop for pkg
in (cl-delete-duplicates
(sort (append (mapcar #'car package-alist)
(mapcar #'car package-archive-contents)
(mapcar #'car package--builtins))
#'string-greaterp))
if (assq pkg package-alist)
collect (symbol-name pkg)
else
collect (propertize (symbol-name pkg) 'face 'font-lock-comment-face)))))
(unless (memq guess packages)
(setq guess nil))
(setq doom--describe-package-list-cache packages)
(intern
(completing-read
(if guess
(format "Describe package (default %s): "
guess)
"Describe package: ")
packages nil t nil nil
(if guess (symbol-name guess))))))))
(list (doom--completing-package nil current-prefix-arg)))
(if (or (package-desc-p package)
(and (symbolp package)
(or (assq package package-alist)
(assq package package-archive-contents)
(assq package package--builtins))))
(describe-package package)
(doom--describe-package package))
(save-excursion
(with-current-buffer (help-buffer)
(let ((inhibit-read-only t))
@ -275,7 +400,7 @@ If prefix arg is prsent, refresh the cache."
(end-of-line)
(let ((indent (make-string (length (match-string 0)) ? )))
(insert "\n" indent "Installed by the following Doom modules:\n")
(dolist (m (get package 'doom-module))
(dolist (m (doom-package-prop package :modules))
(insert indent)
(doom--describe-package-insert-button
(format " %s %s" (car m) (or (cdr m) ""))
@ -293,45 +418,25 @@ If prefix arg is prsent, refresh the cache."
(insert "\n")
(package--print-help-section "Configs")
(dolist (file (get package 'doom-files))
(doom--describe-package-insert-button
(abbreviate-file-name file)
file
(format "\\((\\(:?after!\\|def-package!\\)[ \t\n]*%s\\|^[ \t]*;; `%s'$\\)"
package package))
(insert "\n" indent))
(delete-char -1)))))))
(insert-text-button
"See where this package is configured"
'face 'link
'follow-link t
'action
`(lambda (_) (doom/help-package-config ',package)))))))))
;;;###autoload
(defun doom/describe-symbol (symbol)
"Show help for SYMBOL, a variable, function or macro."
(defun doom/help-package-config (package)
"Jump to a configuration block for PACKAGE."
(interactive
(list (helpful--read-symbol "Symbol: " #'helpful--bound-p)))
(let* ((sym (intern-soft symbol))
(bound (boundp sym))
(fbound (fboundp sym)))
(cond ((and sym bound (not fbound))
(helpful-variable sym))
((and sym fbound (not bound))
(helpful-callable sym))
((apropos (format "^%s\$" symbol)))
((apropos (format "%s" symbol))))))
;;;###autoload
(defalias 'doom/help 'doom/open-manual)
;;;###autoload
(defun doom/open-manual ()
"TODO"
(interactive)
(user-error "This command isn't implemented yet")
;; (find-file (expand-file-name "index.org" doom-docs-dir))
)
;;;###autoload
(defun doom/open-news ()
"TODO"
(interactive)
(user-error "This command isn't implemented yet")
;; (find-file (expand-file-name (concat "news/" doom-version) doom-docs-dir))
)
(list (doom--completing-package "Select package to search for: " current-prefix-arg)))
(let* ((default-directory doom-emacs-dir)
(results (shell-command-to-string
(format "git grep --no-break --no-heading --line-number '%s %s\\($\\| \\)'"
"\\(^;;;###package\\|(after!\\|(def-package!\\)"
package)))
(select (completing-read "Jump to config: " (split-string results "\n" t))))
(cl-destructuring-bind (file line _match)
(split-string select ":")
(pop-to-buffer file)
(goto-line line))))

View file

@ -219,9 +219,9 @@ files."
(push (cons name
(plist-put plist :modules
(cond ((file-in-directory-p file doom-private-dir)
(list :private))
'((:private)))
((file-in-directory-p file doom-core-dir)
(list :core))
'((:core)))
((doom-module-from-path file)))))
doom-packages))))))
((debug error)

View file

@ -183,9 +183,8 @@
(define-key! help-map
;; new keybinds
"'" #'describe-char
"A" #'doom/describe-autodefs
"B" #'doom/open-bug-report
"D" #'doom/open-manual
"D" #'doom/help
"E" #'doom/open-vanilla-sandbox
"M" #'doom/describe-active-minor-mode
"O" #'+lookup/online
@ -195,7 +194,6 @@
"C-k" #'describe-key-briefly
"C-l" #'describe-language-environment
"C-m" #'info-emacs-manual
"C-v" #'doom/version
;; Unbind `help-for-help'. Conflicts with which-key's help command for the
;; <leader> h prefix. It's already on ? and F1 anyway.
@ -210,22 +208,34 @@
"rf" #'doom/reload-font
"re" #'doom/reload-env
;; replaces `apropos-documentation' b/c `apropos' covers this
"d" nil
"d/" #'doom/help-search
"da" #'doom/help-autodefs
"dd" #'doom/toggle-debug-mode
"df" #'doom/help-faq
"dh" #'doom/help
"dm" #'doom/help-modules
"dn" #'doom/help-news
"dp" #'doom/help-packages
"dc" #'doom/help-package-config
"dt" #'doom/toggle-profiler
"dv" #'doom/version
;; replaces `apropos-command'
"a" #'apropos
;; replaces `describe-copying' b/c not useful
"C-c" #'describe-coding-system
;; replaces `apropos-documentation' b/c `apropos' covers this
"d" #'doom/describe-module
;; replaces `Info-got-emacs-command-node' b/c redundant w/ `Info-goto-node'
"F" #'describe-face
;; replaces `view-hello-file' b/c annoying
"h" #'doom/describe-symbol
"h" #'doom/help
;; replaces `describe-language-environment' b/c remapped to C-l
"L" #'global-command-log-mode
;; replaces `view-emacs-news' b/c it's on C-n too
"n" #'doom/open-news
"n" #'doom/help-news
;; replaces `finder-by-keyword'
;; "p" #'doom/describe-package
"p" #'doom/describe-package
;; replaces `describe-package' b/c redundant w/ `doom/describe-package'
"P" #'find-library)