d0089a23de
When a layer doesn't have a package, its name was added to the candidate list as a symbol instead of a string, breaking actions such as layer-action-open-packages which expect a string.
394 lines
16 KiB
EmacsLisp
394 lines
16 KiB
EmacsLisp
;;; ivy-spacemacs-help.el --- Spacemacs layer exploration with `ivy'.
|
|
|
|
;; Author: Justin Burkett <justin@burkett.cc>
|
|
;; Keywords: ivy, spacemacs
|
|
;; Version: 0.1
|
|
;; Package-Requires: ((ivy "0.7"))
|
|
|
|
;; This file is free software; you can redistribute it and/or modify
|
|
;; it under the terms of the GNU General Public License as published by
|
|
;; the Free Software Foundation; either version 3, or (at your option)
|
|
;; any later version.
|
|
|
|
;; This file is distributed in the hope that it will be useful,
|
|
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
;; GNU General Public License for more details.
|
|
|
|
;; You should have received a copy of the GNU General Public License
|
|
;; along with GNU Emacs; see the file COPYING. If not, write to
|
|
;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
;; Boston, MA 02110-1301, USA.
|
|
|
|
;;; Commentary:
|
|
;; This package adds a convenient way to discover Spacemacs configuration
|
|
;; layers thanks to ivy.
|
|
|
|
;;; Code:
|
|
|
|
(require 'cl)
|
|
(require 'ht)
|
|
(require 'ivy)
|
|
(require 'core-configuration-layer)
|
|
|
|
(defvar ivy-spacemacs--initialized nil
|
|
"Non nil if ivy-spacemacs is initialized.")
|
|
|
|
;; (defvar ivy-spacemacs-help-all-layers nil
|
|
;; "Alist of all configuration layers.")
|
|
|
|
;; (defvar ivy-spacemacs-help-all-packages nil
|
|
;; "Hash table of all packages in all layers.")
|
|
|
|
(defun ivy-spacemacs-help//init (&optional arg)
|
|
(when (or arg (null ivy-spacemacs--initialized))
|
|
(configuration-layer/make-all-packages)
|
|
(setq ivy-spacemacs--initialized t)))
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; Docs
|
|
|
|
(defun ivy-spacemacs-help//documentation-candidates ()
|
|
(let (result file-extension)
|
|
(dolist (filename (directory-files spacemacs-docs-directory))
|
|
(setq file-extension (file-name-extension filename))
|
|
(when (or (equal file-extension "md")
|
|
(equal file-extension "org"))
|
|
(push filename result)))
|
|
|
|
;; CONTRIBUTING.org is a special case as it should be at the root of the
|
|
;; repository to be linked as the contributing guide on Github.
|
|
(push "CONTRIBUTING.org" result)
|
|
|
|
;; delete DOCUMENTATION.org to make it the first guide
|
|
(delete "DOCUMENTATION.org" result)
|
|
(push "DOCUMENTATION.org" result)
|
|
|
|
;; give each document an appropriate title
|
|
(mapcar (lambda (r)
|
|
(cond
|
|
((string-equal r "BEGINNERS_TUTORIAL.org")
|
|
`("Beginners tutorial" . ,r))
|
|
((string-equal r "CONTRIBUTING.org")
|
|
`("How to contribute to Spacemacs" . ,r))
|
|
((string-equal r "CONVENTIONS.org")
|
|
`("Spacemacs conventions" . ,r))
|
|
((string-equal r "DOCUMENTATION.org")
|
|
`("Spacemacs documentation" . ,r))
|
|
((string-equal r "FAQ.org")
|
|
`("Spacemacs FAQ" . ,r))
|
|
((string-equal r "LAYERS.org")
|
|
`("Tips on writing layers for Spacemacs" . ,r))
|
|
((string-equal r "QUICK_START.org")
|
|
`("Quick start guide for Spacemacs" . ,r))
|
|
((string-equal r "VIMUSERS.org")
|
|
`("Vim users migration guide" . ,r))
|
|
(t
|
|
`(r . ,r))))
|
|
result)))
|
|
|
|
(defun ivy-spacemacs-help//documentation-action-open-file (candidate)
|
|
"Open documentation FILE."
|
|
(let* ((candidate (cdr candidate))
|
|
(file (if (string= candidate "CONTRIBUTING.org")
|
|
;; CONTRIBUTING.org is a special case as it should be at the
|
|
;; root of the repository to be linked as the contributing
|
|
;; guide on Github.
|
|
(concat spacemacs-start-directory candidate)
|
|
(concat spacemacs-docs-directory candidate))))
|
|
(cond ((equal (file-name-extension file) "md")
|
|
(condition-case-unless-debug nil
|
|
(with-current-buffer (find-file-noselect file)
|
|
(gh-md-render-buffer)
|
|
(spacemacs/kill-this-buffer))
|
|
;; if anything fails, fall back to simply open file
|
|
(find-file file)))
|
|
((equal (file-name-extension file) "org")
|
|
(spacemacs/view-org-file file "^" 'all))
|
|
(t
|
|
(find-file file)))))
|
|
|
|
;;;###autoload
|
|
(defun ivy-spacemacs-help-docs (arg)
|
|
(interactive "P")
|
|
(ivy-spacemacs-help//init arg)
|
|
(ivy-read "Spacemacs Documentation: "
|
|
(ivy-spacemacs-help//documentation-candidates)
|
|
:action #'ivy-spacemacs-help//documentation-action-open-file))
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; Layers
|
|
|
|
(defun ivy-spacemacs-help//layer-candidates ()
|
|
(sort (mapcar 'symbol-name (configuration-layer/get-layers-list))
|
|
'string<))
|
|
|
|
(defun ivy-spacemacs-help//layer-action-get-directory (candidate)
|
|
"Get directory of layer passed CANDIDATE."
|
|
(configuration-layer/get-layer-path (intern candidate)))
|
|
|
|
(defun ivy-spacemacs-help//layer-action-open-file (file candidate &optional edit)
|
|
"Open FILE of the passed CANDIDATE. If EDIT is false, open in view mode."
|
|
(let ((path (configuration-layer/get-layer-path (intern candidate))))
|
|
(if (equal (file-name-extension file) "org")
|
|
(if edit
|
|
(find-file (concat path file))
|
|
(spacemacs/view-org-file (concat path file) "^" 'all))
|
|
(let ((filepath (concat path file)))
|
|
(if (file-exists-p filepath)
|
|
(find-file filepath)
|
|
(message "%s does not have %s" candidate file))))))
|
|
|
|
(defun ivy-spacemacs-help//layer-action-open-readme (candidate)
|
|
"Open the `README.org' file of the passed CANDIDATE for reading."
|
|
(ivy-spacemacs-help//layer-action-open-file "README.org" candidate))
|
|
|
|
(defun ivy-spacemacs-help//layer-action-add-layer (candidate)
|
|
"Adds layer to dotspacemacs file and reloads configuration"
|
|
(if (configuration-layer/layer-used-p (intern candidate))
|
|
(message "Layer already added.")
|
|
(let ((dotspacemacs (find-file-noselect (dotspacemacs/location))))
|
|
(with-current-buffer dotspacemacs
|
|
(beginning-of-buffer)
|
|
(let ((insert-point (re-search-forward
|
|
"dotspacemacs-configuration-layers *\n?.*\\((\\)")))
|
|
(insert (format "\n%s\n" candidate))
|
|
(indent-region insert-point (+ insert-point (length candidate)))
|
|
(save-current-buffer)))
|
|
(dotspacemacs/sync-configuration-layers))))
|
|
|
|
(defun ivy-spacemacs-help//layer-action-open-dired (candidate)
|
|
"Open dired at the location of the passed layer CANDIDATE."
|
|
(dired
|
|
(ivy-spacemacs-help//layer-action-get-directory candidate)))
|
|
|
|
(defun ivy-spacemacs-help//layer-action-open-readme-edit (candidate)
|
|
"Open the `README.org' file of the passed CANDIDATE for editing."
|
|
(ivy-spacemacs-help//layer-action-open-file "README.org" candidate t))
|
|
|
|
(defun ivy-spacemacs-help//layer-action-open-config (candidate)
|
|
"Open the `config.el' file of the passed CANDIDATE."
|
|
(ivy-spacemacs-help//layer-action-open-file "config.el" candidate))
|
|
|
|
(defun ivy-spacemacs-help//layer-action-open-packages (candidate)
|
|
"Open the `packages.el' file of the passed CANDIDATE."
|
|
(ivy-spacemacs-help//layer-action-open-file "packages.el" candidate))
|
|
|
|
;;;###autoload
|
|
(defun ivy-spacemacs-help-layers ()
|
|
(interactive)
|
|
(ivy-spacemacs-help//init)
|
|
(ivy-read "Spacemacs Layers: "
|
|
(ivy-spacemacs-help//layer-candidates)
|
|
:action 'ivy-spacemacs-help//layer-action-open-readme
|
|
:caller 'ivy-spacemacs-help-layers))
|
|
|
|
(ivy-set-actions
|
|
'ivy-spacemacs-help-layers
|
|
'(("a" ivy-spacemacs-help//layer-action-add-layer "add layer")
|
|
("d" ivy-spacemacs-help//layer-action-open-dired "open dired at layer location")
|
|
("e" ivy-spacemacs-help//layer-action-open-readme-edit "open readme for editing")
|
|
("c" ivy-spacemacs-help//layer-action-open-config "open config.el")
|
|
("p" ivy-spacemacs-help//layer-action-open-packages "open packages.el")
|
|
("r" ivy-spacemacs-help//layer-action-open-readme "open readme")))
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; Layers and packages
|
|
|
|
(defun ivy-spacemacs-help//help-candidates ()
|
|
"Return the sorted candidates for package source."
|
|
(let (result
|
|
(left-column-width
|
|
(number-to-string
|
|
(cl-reduce
|
|
(lambda (a x) (max (length (symbol-name x)) a))
|
|
(configuration-layer/get-layers-list) :initial-value 0)))
|
|
(owners (cl-remove-duplicates
|
|
(mapcar (lambda (pkg)
|
|
(let ((obj (configuration-layer/get-package pkg)))
|
|
(car (oref obj :owners))))
|
|
(configuration-layer/get-packages-list)))))
|
|
(dolist (pkg-name (configuration-layer/get-packages-list))
|
|
(let ((pkg (configuration-layer/get-package pkg-name)))
|
|
(push (list (format (concat "%-" left-column-width "S %s %s")
|
|
(car (oref pkg :owners ))
|
|
(propertize (symbol-name (oref pkg :name))
|
|
'face 'font-lock-type-face)
|
|
(propertize
|
|
(if (package-installed-p (oref pkg :name))
|
|
"[installed]" "")
|
|
'face 'font-lock-comment-face))
|
|
(symbol-name
|
|
(car (oref pkg :owners )))
|
|
(symbol-name (oref pkg :name)))
|
|
result)))
|
|
(dolist (layer (delq nil
|
|
(cl-remove-if
|
|
(lambda (x) (memq x owners))
|
|
(configuration-layer/get-layers-list))))
|
|
(push (list (format (concat "%-" left-column-width "S %s")
|
|
layer
|
|
(propertize "no packages"
|
|
'face 'warning))
|
|
(symbol-name layer)
|
|
nil)
|
|
result))
|
|
(sort result (lambda (a b) (string< (car a) (car b))))))
|
|
|
|
(defun ivy-spacemacs-help//help-action (args)
|
|
"Open the file `packages.el' and go to the init function."
|
|
(if (null (caddr args))
|
|
(message "There are no packages associated with this layer.")
|
|
(let* ((layer-str (cadr args))
|
|
(layer-sym (intern layer-str))
|
|
(package-str (caddr args))
|
|
(path (configuration-layer/get-layer-path layer-sym))
|
|
(filename (concat path "packages.el")))
|
|
(find-file filename)
|
|
(goto-char (point-min))
|
|
(re-search-forward (format "init-%s" package-str))
|
|
(beginning-of-line))))
|
|
|
|
(defun ivy-spacemacs-help//help-action-describe-package (args)
|
|
"Describe the passed package using Spacemacs describe function."
|
|
(if (null (caddr args))
|
|
(message "There are no packages associated with this layer.")
|
|
(let ((package-str (caddr args)))
|
|
(configuration-layer/describe-package (intern package-str)))))
|
|
|
|
(defun ivy-spacemacs-help//help-action-open-dired (args)
|
|
"Open the `packages.el' file of the passed `car' of ARGS."
|
|
(dired
|
|
(ivy-spacemacs-help//layer-action-get-directory (cadr args))))
|
|
|
|
(defun ivy-spacemacs-help//help-action-open-config (args)
|
|
"Open the `packages.el' file of the passed CANDIDATE."
|
|
(ivy-spacemacs-help//layer-action-open-file "config.el" (cadr args)))
|
|
|
|
(defun ivy-spacemacs-help//help-action-open-packages (args)
|
|
"Open the `packages.el' file of the passed CANDIDATE."
|
|
(ivy-spacemacs-help//layer-action-open-file "packages.el" (cadr args)))
|
|
|
|
(defun ivy-spacemacs-help//help-action-open-readme (args)
|
|
"Open the `README.org' file of the passed CANDIDATE for reading."
|
|
(ivy-spacemacs-help//layer-action-open-file "README.org" (cadr args)))
|
|
|
|
(defun ivy-spacemacs-help//help-action-open-readme-edit (args)
|
|
"Open the `README.org' file of the passed CANDIDATE for editing."
|
|
(ivy-spacemacs-help//layer-action-open-file "README.org" (cadr args) t))
|
|
|
|
(defun ivy-spacemacs-help//help-action-add-layer (args)
|
|
"Adds layer to dotspacemacs file and reloads configuration"
|
|
(if (configuration-layer/layer-used-p (intern (cadr args)))
|
|
(message "Layer already added.")
|
|
(let ((dotspacemacs (find-file-noselect (dotspacemacs/location))))
|
|
(with-current-buffer dotspacemacs
|
|
(beginning-of-buffer)
|
|
(let ((insert-point (re-search-forward
|
|
"dotspacemacs-configuration-layers *\n?.*\\((\\)")))
|
|
(insert (format "\n%s\n" (cadr args)))
|
|
(indent-region insert-point (+ insert-point (length (cadr args))))
|
|
(save-current-buffer)))
|
|
(dotspacemacs/sync-configuration-layers))))
|
|
|
|
;;;###autoload
|
|
(defun ivy-spacemacs-help ()
|
|
(interactive)
|
|
(ivy-spacemacs-help//init)
|
|
(ivy-read "Spacemacs Layers and Packages: "
|
|
(ivy-spacemacs-help//help-candidates)
|
|
:action 'ivy-spacemacs-help//help-action
|
|
:caller 'ivy-spacemacs-help))
|
|
|
|
(ivy-set-actions
|
|
'ivy-spacemacs-help
|
|
'(("a" ivy-spacemacs-help//help-action-add-layer "add layer")
|
|
("d" ivy-spacemacs-help//help-action-open-dired "open dired at layer location")
|
|
("D" ivy-spacemacs-help//help-action-describe-package "describe package")
|
|
("e" ivy-spacemacs-help//help-action-open-readme-edit "open readme for editing")
|
|
("c" ivy-spacemacs-help//help-action-open-config "open config.el")
|
|
("p" ivy-spacemacs-help//help-action-open-packages "open packages.el")
|
|
("r" ivy-spacemacs-help//help-action-open-readme "open readme")))
|
|
|
|
;;;###autoload
|
|
(defalias 'ivy-spacemacs-help-packages 'ivy-spacemacs-help)
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; Toggles
|
|
|
|
(defun ivy-spacemacs-help//toggle-candidates ()
|
|
"Return the sorted candidates for toggle source."
|
|
(let (result)
|
|
(dolist (toggle spacemacs-toggles)
|
|
(let* ((toggle-symbol (symbol-name (car toggle)))
|
|
(toggle-status (funcall (plist-get (cdr toggle) :predicate)))
|
|
(toggle-name (capitalize (replace-regexp-in-string "-" " " toggle-symbol)))
|
|
(toggle-doc (format "(%s) %s: %s"
|
|
(if toggle-status "+" "-")
|
|
toggle-name
|
|
(propertize
|
|
(or (plist-get (cdr toggle) :documentation) "")
|
|
'face 'font-lock-doc-face))))
|
|
(when (plist-member (cdr toggle) :evil-leader)
|
|
(let ((key (key-description
|
|
(kbd (concat dotspacemacs-leader-key " "
|
|
(plist-get (cdr toggle) :evil-leader))))))
|
|
(setq toggle-doc
|
|
(format "%s (%s)"
|
|
toggle-doc
|
|
(propertize key 'face 'font-lock-keyword-face)))))
|
|
(if (plist-member (cdr toggle) :documentation)
|
|
(push `(,toggle-doc . ,toggle-symbol) result)
|
|
(push `(,toggle-name . ,toggle-symbol) result))))
|
|
(setq result (cl-sort result 'string< :key 'car))
|
|
result))
|
|
|
|
(defun ivy-spacemacs-help//toggle (candidate)
|
|
"Toggle candidate."
|
|
(let ((toggle (assq (intern candidate) spacemacs-toggles)))
|
|
(when toggle
|
|
(funcall (plist-get (cdr toggle) :function)))))
|
|
|
|
;;;###autoload
|
|
(defun ivy-spacemacs-help-toggles ()
|
|
(interactive)
|
|
(ivy-read "Spacemacs Toggles: "
|
|
(ivy-spacemacs-help//toggle-candidates)
|
|
:action 'ivy-spacemacs-help//toggle))
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; .spacemacs vars
|
|
|
|
(defun ivy-spacemacs-help//dotspacemacs-candidates ()
|
|
"Return the sorted candidates for all the dospacemacs variables."
|
|
(sort (dotspacemacs/get-variable-string-list) 'string<))
|
|
|
|
(defun ivy-spacemacs-help//go-to-dotfile-variable (candidate)
|
|
"Go to candidate in the dotfile."
|
|
(find-file dotspacemacs-filepath)
|
|
(goto-char (point-min))
|
|
;; try to exclude comments
|
|
(re-search-forward (format "^[a-z\s\\(\\-]*%s" candidate))
|
|
(beginning-of-line))
|
|
|
|
;;;###autoload
|
|
(defun ivy-spacemacs-help-dotspacemacs ()
|
|
(interactive)
|
|
(ivy-read ".spacemacs variables: "
|
|
(ivy-spacemacs-help//dotspacemacs-candidates)
|
|
:action 'ivy-spacemacs-help//go-to-dotfile-variable))
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; FAQ
|
|
|
|
;;;###autoload
|
|
(defun ivy-spacemacs-help-faq ()
|
|
"Show FAQ and launch swiper session."
|
|
(interactive)
|
|
(find-file-read-only
|
|
(expand-file-name "FAQ.org" spacemacs-docs-directory))
|
|
(swiper "\\*\\* "))
|
|
|
|
(provide 'ivy-spacemacs-help)
|
|
|
|
;;; ivy-spacemacs-help.el ends here
|