💥 Remove :feature category
:feature was a "catch-all" category. Many of its modules fit better in other categories, so they've been moved: - feature/debugger -> tools/debugger - feature/evil -> editor/evil - feature/eval -> tools/eval - feature/lookup -> tools/lookup - feature/snippets -> editor/snippets - feature/file-templates -> editor/file-templates - feature/workspaces -> ui/workspaces More potential changes in the future: - A new :term category for terminal emulation modules (eshell, term and vterm). - A new :os category for modules dedicated to os-specific functionality. The :tools macos module would fit here, but so would modules for nixos and arch. - A new :services category for web-service integration, like wakatime, twitter, elfeed, gist and pastebin services.
This commit is contained in:
parent
52eed893fe
commit
77e4cc4d58
193 changed files with 304 additions and 303 deletions
11
modules/tools/debugger/autoload/debug.el
Normal file
11
modules/tools/debugger/autoload/debug.el
Normal file
|
@ -0,0 +1,11 @@
|
|||
;;; tools/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)))
|
||||
|
34
modules/tools/debugger/autoload/evil.el
Normal file
34
modules/tools/debugger/autoload/evil.el
Normal file
|
@ -0,0 +1,34 @@
|
|||
;; tools/debugger/autoload/evil.el -*- lexical-binding: t; -*-
|
||||
;;;###if (featurep! :editor evil)
|
||||
|
||||
;;;###autoload (autoload '+debugger:start "tools/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 "tools/debugger/autoload/evil" nil t)
|
||||
(evil-define-command +debugger:toggle-breakpoint (&optional bang)
|
||||
(interactive "<!>")
|
||||
(call-interactively (if bang #'realgud:cmd-clear #'realgud:cmd-break)))
|
44
modules/tools/debugger/config.el
Normal file
44
modules/tools/debugger/config.el
Normal file
|
@ -0,0 +1,44 @@
|
|||
;;; tools/debugger/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
(def-package! realgud
|
||||
:commands (realgud:gdb realgud:trepanjs realgud:bashdb realgud:zshdb)
|
||||
:config
|
||||
(set-popup-rule! "^\\*\\(?:trepanjs:\\(?:g\\|zsh\\|bash\\)db\\)" :size 20)
|
||||
|
||||
;; TODO Temporary Ex commands for the debugger
|
||||
;; (def-tmp-excmd! doom:def-debug-on doom:def-debug-off
|
||||
;; ("n[ext]" . realgud:cmd-next)
|
||||
;; ("s[tep]" . realgud:cmd-step)
|
||||
;; ("b[reak]" . +debug:toggle-breakpoint)
|
||||
;; ("c[ontinue]" . realgud:cmd-continue))
|
||||
;; (advice-add #'realgud-cmdbuf-init :after #'doom:def-debug-on)
|
||||
;; (advice-add #'realgud:cmd-quit :after #'doom:def-debug-off)
|
||||
|
||||
;; Monkey-patch `realgud:run-process' to run in a popup.
|
||||
;; TODO Find a more elegant solution
|
||||
;; FIXME Causes realgud:cmd-* to focus popup on every invocation
|
||||
(defun +debugger*realgud-run-process
|
||||
(debugger-name script-filename cmd-args minibuffer-history-var &optional no-reset)
|
||||
(let* ((cmd-buf (apply #'realgud-exec-shell debugger-name script-filename
|
||||
(car cmd-args) no-reset (cdr cmd-args)))
|
||||
(process (get-buffer-process cmd-buf)))
|
||||
(cond ((and process (eq 'run (process-status process)))
|
||||
(pop-to-buffer cmd-buf)
|
||||
(define-key evil-emacs-state-local-map (kbd "ESC ESC") #'+debug/quit)
|
||||
(realgud:track-set-debugger debugger-name)
|
||||
(realgud-cmdbuf-info-in-debugger?= 't)
|
||||
(realgud-cmdbuf-info-cmd-args= cmd-args)
|
||||
(when cmd-buf
|
||||
(switch-to-buffer cmd-buf)
|
||||
(when realgud-cmdbuf-info
|
||||
(let* ((info realgud-cmdbuf-info)
|
||||
(cmd-args (realgud-cmdbuf-info-cmd-args info))
|
||||
(cmd-str (mapconcat #'identity cmd-args " ")))
|
||||
(set minibuffer-history-var
|
||||
(list-utils-uniq (cons cmd-str (eval minibuffer-history-var))))))))
|
||||
(t
|
||||
(if cmd-buf (switch-to-buffer cmd-buf))
|
||||
(message "Error running command: %s" (mapconcat #'identity cmd-args " "))))
|
||||
cmd-buf))
|
||||
(advice-add #'realgud:run-process :override #'+debugger*realgud-run-process))
|
||||
|
4
modules/tools/debugger/packages.el
Normal file
4
modules/tools/debugger/packages.el
Normal file
|
@ -0,0 +1,4 @@
|
|||
;; -*- no-byte-compile: t; -*-
|
||||
;;; tools/debugger/packages.el
|
||||
|
||||
(package! realgud)
|
7
modules/tools/editorconfig/autoload.el
Normal file
7
modules/tools/editorconfig/autoload.el
Normal file
|
@ -0,0 +1,7 @@
|
|||
;;; tools/editorconfig/autoload.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autodef
|
||||
(defun set-editorconfig-indent-var! (mode &rest vars)
|
||||
"Add (MODE VARS...) to `editorconfig-indentation-alist'."
|
||||
(after! editorconfig
|
||||
(nconc editorconfig-indentation-alist (cons mode vars))))
|
117
modules/tools/eval/README.org
Normal file
117
modules/tools/eval/README.org
Normal file
|
@ -0,0 +1,117 @@
|
|||
#+TITLE: tools/eval
|
||||
#+DATE: February 13, 2017
|
||||
#+SINCE: v2.0
|
||||
#+STARTUP: inlineimages
|
||||
|
||||
* Table of Contents :TOC_3:noexport:
|
||||
- [[Description][Description]]
|
||||
- [[Module Flags][Module Flags]]
|
||||
- [[Plugins][Plugins]]
|
||||
- [[Hacks][Hacks]]
|
||||
- [[Prerequisites][Prerequisites]]
|
||||
- [[Features][Features]]
|
||||
- [[Inline Code Evaluation][Inline Code Evaluation]]
|
||||
- [[REPLs][REPLs]]
|
||||
- [[Configuration][Configuration]]
|
||||
- [[Register a REPL for a major-mode][Register a REPL for a major-mode]]
|
||||
- [[Change how code is evaluated in a major mode][Change how code is evaluated in a major mode]]
|
||||
- [[Troubleshooting][Troubleshooting]]
|
||||
|
||||
* Description
|
||||
This modules adds inline code evaluation support to Emacs, and supplies a
|
||||
universal interface for opening and interacting with REPLs.
|
||||
|
||||
** Module Flags
|
||||
This module has no flags.
|
||||
|
||||
** Plugins
|
||||
+ [[https://github.com/syohex/emacs-quickrun][quickrun]]
|
||||
|
||||
** Hacks
|
||||
+ Quickrun has been modified to:
|
||||
+ Use only one output window, in case of consecutive execution of code.
|
||||
+ The quickrun window will resize itself to fit its output, once the
|
||||
underlying process is finished executing the code.
|
||||
|
||||
* Prerequisites
|
||||
This module has no direct prerequisites.
|
||||
|
||||
However, specific languages may require additional setup. Check the
|
||||
documentation of that language's module for details.
|
||||
|
||||
* Features
|
||||
** Inline Code Evaluation
|
||||
Quickrun can be invoked via:
|
||||
+ ~M-x +eval/buffer~ (or ~gR~, or ~M-r~)
|
||||
+ ~M-x +eval/region~
|
||||
+ ~M-x +eval/region-and-replace~
|
||||
+ Evil users can use the ~gr~ operator to select and run a region.
|
||||
|
||||
** REPLs
|
||||
Invoked via:
|
||||
+ =SPC o r= or ~:repl~ will open a REPL in a popup window. =C-u SPC o r= or
|
||||
~:repl!~ will open a REPL in the current window. If a REPL is already open and
|
||||
a selection is active, it will be sent to the REPL.
|
||||
+ ~M-x +eval/open-repl-other-window~
|
||||
+ ~M-x +eval/open-repl-same-window~
|
||||
+ ~M-x +eval/send-region-to-repl~ while a selection (and REPL) is active
|
||||
|
||||
* Configuration
|
||||
** Register a REPL for a major-mode
|
||||
REPLs are defined for most languages Doom supports. Check that language module's
|
||||
README.org to see if it does (and if it requires additional setup).
|
||||
|
||||
To use them, you may use ~M-x +eval/open-repl-other-window~, ~M-x
|
||||
+eval/open-repl-same-window~, ~:repl~ (for evil users) or the default binding:
|
||||
=SPC o r=. These will open a REPL in a popup window.
|
||||
|
||||
#+begin_quote
|
||||
You can simply call that mode's REPL command manually. e.g. ~M-x ielm~, but
|
||||
#+end_quote
|
||||
|
||||
Otherwise, you can define your own for a specified major mode:
|
||||
|
||||
~(set-repl-handler! MAJOR-MODE FUNCTION)~
|
||||
|
||||
FUNCTION should return a repl buffer. Any window changes in this function are
|
||||
ignored, then the REPL is opened in a popup window.
|
||||
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
(defun +lua/open-repl ()
|
||||
(interactive)
|
||||
(lua-start-process "lua" "lua")
|
||||
(pop-to-buffer lua-process-buffer))
|
||||
|
||||
(set-repl-handler! 'lua-mode #'+lua/open-repl)
|
||||
#+END_SRC
|
||||
|
||||
** Change how code is evaluated in a major mode
|
||||
Run regions or entire buffers with [[https://github.com/syohex/emacs-quickrun][Quickrun]]. Output is show in a popup window.
|
||||
|
||||
Quickrun includes support for many languages, usually by sending text directly
|
||||
to interpreters or compilers. However, occasionally, you'll find a language
|
||||
without support (like [[https://crystal-lang.org/][Crystal]]), or a language with better Emacs integration
|
||||
(like elisp).
|
||||
|
||||
Here's how you define a "runner":
|
||||
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
(set-eval-handler! 'crystal-mode
|
||||
'((:command . "crystal")
|
||||
(:exec . "%c %s")
|
||||
(:description . "Run Crystal script")))
|
||||
#+END_SRC
|
||||
|
||||
A simpler version is simply to use the path to the binary:
|
||||
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
(set-eval-handler! 'groovy-mode "groovy")
|
||||
#+END_SRC
|
||||
|
||||
Or if you'd rather run an elisp command:
|
||||
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
(set-eval-handler! 'emacs-lisp-mode #'+emacs-lisp-eval)
|
||||
#+END_SRC
|
||||
|
||||
* Troubleshooting
|
49
modules/tools/eval/autoload/eval.el
Normal file
49
modules/tools/eval/autoload/eval.el
Normal file
|
@ -0,0 +1,49 @@
|
|||
;;; tools/eval/autoload/eval.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autoload
|
||||
(defun +eval/buffer ()
|
||||
"Evaluate the whole buffer."
|
||||
(interactive)
|
||||
(cond ((assq major-mode +eval-runners)
|
||||
(+eval/region (point-min) (point-max)))
|
||||
(t (quickrun))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +eval/region (beg end)
|
||||
"Evaluate a region between BEG and END and display the output."
|
||||
(interactive "r")
|
||||
(let ((load-file-name buffer-file-name))
|
||||
(if-let* ((runner (cdr (assq major-mode +eval-runners))))
|
||||
(funcall runner beg end)
|
||||
(quickrun-region beg end))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +eval/line-or-region ()
|
||||
"Evaluate the current line or selected region."
|
||||
(interactive)
|
||||
(if (use-region-p)
|
||||
(call-interactively #'+eval/region)
|
||||
(+eval/region (line-beginning-position) (line-end-position))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +eval/buffer-or-region ()
|
||||
"Evaluate the whole buffer."
|
||||
(interactive)
|
||||
(call-interactively
|
||||
(if (use-region-p)
|
||||
#'+eval/region
|
||||
#'+eval/buffer)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +eval/region-and-replace (beg end)
|
||||
"Evaluation a region between BEG and END, and replace it with the result."
|
||||
(interactive "r")
|
||||
(cond ((eq major-mode 'emacs-lisp-mode)
|
||||
(kill-region beg end)
|
||||
(condition-case nil
|
||||
(prin1 (eval (read (current-kill 0)))
|
||||
(current-buffer))
|
||||
(error (message "Invalid expression")
|
||||
(insert (current-kill 0)))))
|
||||
(t (quickrun-replace-region beg end))))
|
||||
|
23
modules/tools/eval/autoload/evil.el
Normal file
23
modules/tools/eval/autoload/evil.el
Normal file
|
@ -0,0 +1,23 @@
|
|||
;; tools/eval/autoload/evil.el -*- lexical-binding: t; -*-
|
||||
;;;###if (featurep! :editor evil)
|
||||
|
||||
;;;###autoload (autoload '+eval:region "tools/eval/autoload/evil" nil t)
|
||||
(evil-define-operator +eval:region (beg end)
|
||||
"Send region to the currently open repl, if available."
|
||||
:move-point nil
|
||||
(interactive "<r>")
|
||||
(+eval/region beg end))
|
||||
|
||||
;;;###autoload (autoload '+eval:replace-region "tools/eval/autoload/evil" nil t)
|
||||
(evil-define-operator +eval:replace-region (beg end)
|
||||
:move-point nil
|
||||
(interactive "<r>")
|
||||
(+eval/region-and-replace beg end))
|
||||
|
||||
;;;###autoload (autoload '+eval:repl "tools/eval/autoload/evil" nil t)
|
||||
(evil-define-operator +eval:repl (beg end &optional bang)
|
||||
:move-point nil
|
||||
(interactive "<r><!>")
|
||||
(if (evil-normal-state-p)
|
||||
(+eval/open-repl-other-window bang)
|
||||
(+eval/send-region-to-repl beg end bang)))
|
102
modules/tools/eval/autoload/repl.el
Normal file
102
modules/tools/eval/autoload/repl.el
Normal file
|
@ -0,0 +1,102 @@
|
|||
;;; tools/eval/autoload/repl.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defvar +eval-repl-buffers (make-hash-table :test 'equal)
|
||||
"The buffer of the last open repl.")
|
||||
|
||||
(define-minor-mode +eval-repl-mode
|
||||
"A minor mode for REPL buffers.")
|
||||
|
||||
(defun +eval--ensure-in-repl-buffer (&optional command other-window-p)
|
||||
(maphash (lambda (key buffer)
|
||||
(unless (buffer-live-p buffer)
|
||||
(remhash key +eval-repl-buffers)))
|
||||
+eval-repl-buffers)
|
||||
(let* ((project-root (doom-project-root))
|
||||
(key (cons major-mode project-root))
|
||||
(buffer (gethash key +eval-repl-buffers)))
|
||||
(cl-check-type buffer (or buffer null))
|
||||
(unless (eq buffer (current-buffer))
|
||||
(funcall (if other-window-p #'pop-to-buffer #'switch-to-buffer)
|
||||
(if (buffer-live-p buffer)
|
||||
buffer
|
||||
(setq buffer
|
||||
(save-window-excursion
|
||||
(if (commandp command)
|
||||
(call-interactively command)
|
||||
(funcall command))))
|
||||
(cond ((null buffer)
|
||||
(error "REPL handler %S couldn't open the REPL buffer" command))
|
||||
((not (bufferp buffer))
|
||||
(error "REPL handler %S failed to return a buffer" command)))
|
||||
(with-current-buffer buffer
|
||||
(+eval-repl-mode +1))
|
||||
(puthash key buffer +eval-repl-buffers)
|
||||
buffer)))
|
||||
(with-current-buffer buffer
|
||||
(goto-char (if (and (derived-mode-p 'comint-mode)
|
||||
(cdr comint-last-prompt))
|
||||
(cdr comint-last-prompt)
|
||||
(point-max)))
|
||||
buffer)))
|
||||
|
||||
(defun +eval-open-repl (prompt-p &optional other-window-p)
|
||||
(let ((command (cdr (assq major-mode +eval-repls))))
|
||||
(when (or (not command) prompt-p)
|
||||
(let* ((choices (or (cl-loop for sym being the symbols
|
||||
for sym-name = (symbol-name sym)
|
||||
if (string-match "^\\(?:\\+\\)?\\([^/]+\\)/open-\\(?:\\(.+\\)-\\)?repl$" sym-name)
|
||||
collect
|
||||
(format "%s (%s)"
|
||||
(match-string-no-properties 1 sym-name)
|
||||
(or (match-string-no-properties 2 sym-name) "default")))
|
||||
(user-error "There are no known available REPLs")))
|
||||
(choice (or (completing-read "Open a REPL for: " choices)
|
||||
(user-error "Aborting")))
|
||||
(choice-split (split-string choice " " t))
|
||||
(module (car choice-split))
|
||||
(repl (substring (cadr choice-split) 1 -1)))
|
||||
(setq command
|
||||
(intern-soft
|
||||
(format "+%s/open-%srepl" module
|
||||
(if (string= repl "default")
|
||||
""
|
||||
repl))))))
|
||||
(unless (commandp command)
|
||||
(error "Couldn't find a valid REPL for %s" major-mode))
|
||||
(when (+eval--ensure-in-repl-buffer command other-window-p)
|
||||
(when (bound-and-true-p evil-mode)
|
||||
(call-interactively #'evil-append-line))
|
||||
t)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +eval/open-repl-same-window (&optional arg)
|
||||
"Opens (or reopens) the REPL associated with the current major-mode and place
|
||||
the cursor at the prompt.
|
||||
|
||||
If ARG (universal argument), prompt for a specific REPL to open."
|
||||
(interactive "P")
|
||||
(+eval-open-repl arg))
|
||||
|
||||
;;;###autoload
|
||||
(defun +eval/open-repl-other-window (&optional arg)
|
||||
"Does `+eval/open-repl', but in a popup window.
|
||||
|
||||
If ARG (universal argument), prompt for a specific REPL to open."
|
||||
(interactive "P")
|
||||
(+eval-open-repl arg t))
|
||||
|
||||
;;;###autoload
|
||||
(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
|
||||
execute it immediately after."
|
||||
(interactive "r")
|
||||
(let ((selection (buffer-substring-no-properties beg end)))
|
||||
(unless (+eval--ensure-in-repl-buffer)
|
||||
(error "No REPL open"))
|
||||
(when (bound-and-true-p evil-mode)
|
||||
(call-interactively #'evil-append-line))
|
||||
(insert (string-trim selection))
|
||||
(when auto-execute-p
|
||||
;; I don't use `comint-send-input' because different REPLs may have their
|
||||
;; own. So I just emulate the keypress.
|
||||
(execute-kbd-macro (kbd "RET")))))
|
58
modules/tools/eval/autoload/settings.el
Normal file
58
modules/tools/eval/autoload/settings.el
Normal file
|
@ -0,0 +1,58 @@
|
|||
;;; tools/eval/autoload/settings.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;
|
||||
;; REPLs
|
||||
|
||||
;;;###autoload
|
||||
(defvar +eval-repls nil
|
||||
"An alist mapping major modes to plists that describe REPLs. Used by
|
||||
`+eval/open-repl-other-window' and filled with the `:repl' setting.")
|
||||
|
||||
;;;###autodef
|
||||
(defun set-repl-handler! (modes command)
|
||||
"Defines a REPL for MODES.
|
||||
|
||||
MODES is either a single major mode symbol or a list of them. COMMAND is a
|
||||
function that creates and returns the REPL buffer.
|
||||
|
||||
COMMAND can either be a function that takes no arguments, or an interactive
|
||||
command that will be called interactively."
|
||||
(dolist (mode (doom-enlist modes))
|
||||
(setf (alist-get mode +eval-repls) command)))
|
||||
|
||||
|
||||
;;
|
||||
;; Evaluation
|
||||
|
||||
;;;###autoload
|
||||
(defvar +eval-runners nil
|
||||
"Alist mapping major modes to interactive runner functions.")
|
||||
|
||||
;;;###autodef
|
||||
(defun set-eval-handler! (mode command)
|
||||
"Define a code evaluator for major mode MODE with `quickrun'.
|
||||
|
||||
1. If MODE is a string and COMMAND is the string, MODE is a file regexp and
|
||||
COMMAND is a string key for an entry in `quickrun-file-alist'.
|
||||
2. If MODE is not a string and COMMAND is a string, MODE is a major-mode symbol
|
||||
and COMMAND is a key (for `quickrun--language-alist'), and will be registered
|
||||
in `quickrun--major-mode-alist'.
|
||||
3. If MODE is not a string and COMMAND is an alist, see `quickrun-add-command':
|
||||
(quickrun-add-command MODE COMMAND :mode MODE).
|
||||
4. If MODE is not a string and COMMANd is a symbol, add it to
|
||||
`+eval-runners', which is used by `+eval/region'."
|
||||
(declare (indent defun))
|
||||
(cond ((symbolp command)
|
||||
(push (cons mode command) +eval-runners))
|
||||
((stringp command)
|
||||
(after! quickrun
|
||||
(push (cons mode command)
|
||||
(if (stringp mode)
|
||||
quickrun-file-alist
|
||||
quickrun--major-mode-alist))))
|
||||
((listp command)
|
||||
(after! quickrun
|
||||
(quickrun-add-command
|
||||
(or (cdr (assq mode quickrun--major-mode-alist))
|
||||
(string-remove-suffix "-mode" (symbol-name mode)))
|
||||
command :mode mode)))))
|
38
modules/tools/eval/config.el
Normal file
38
modules/tools/eval/config.el
Normal file
|
@ -0,0 +1,38 @@
|
|||
;;; tools/eval/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
;; remove ellipsis when printing sexps in message buffer
|
||||
(setq eval-expression-print-length nil
|
||||
eval-expression-print-level nil)
|
||||
|
||||
|
||||
;;
|
||||
;; Packages
|
||||
|
||||
(after! quickrun
|
||||
(setq quickrun-focus-p nil)
|
||||
|
||||
(set-popup-rule! "^\\*quickrun" :size 0.3 :ttl 0)
|
||||
|
||||
(defun +eval*quickrun-auto-close (&rest _)
|
||||
"Allows us to silently re-run quickrun from within the quickrun buffer."
|
||||
(when-let* ((win (get-buffer-window quickrun--buffer-name)))
|
||||
(let ((inhibit-message t))
|
||||
(quickrun--kill-running-process)
|
||||
(message ""))
|
||||
(delete-window win)))
|
||||
(advice-add #'quickrun :before #'+eval*quickrun-auto-close)
|
||||
(advice-add #'quickrun-region :before #'+eval*quickrun-auto-close)
|
||||
|
||||
(defun +eval|quickrun-shrink-window ()
|
||||
"Shrink the quickrun output window once code evaluation is complete."
|
||||
(with-selected-window (get-buffer-window quickrun--buffer-name)
|
||||
(let ((ignore-window-parameters t))
|
||||
(shrink-window-if-larger-than-buffer))))
|
||||
(add-hook 'quickrun-after-run-hook #'+eval|quickrun-shrink-window)
|
||||
|
||||
(defun +eval|quickrun-scroll-to-bof ()
|
||||
"Ensures window is scrolled to BOF on invocation."
|
||||
(with-selected-window (get-buffer-window quickrun--buffer-name)
|
||||
(goto-char (point-min))))
|
||||
(add-hook 'quickrun-after-run-hook #'+eval|quickrun-scroll-to-bof))
|
||||
|
5
modules/tools/eval/packages.el
Normal file
5
modules/tools/eval/packages.el
Normal file
|
@ -0,0 +1,5 @@
|
|||
;; -*- no-byte-compile: t; -*-
|
||||
;;; tools/eval/packages.el
|
||||
|
||||
(package! quickrun)
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
;;; tools/gist/autoload/evil.el -*- lexical-binding: t; -*-
|
||||
;;;###if (featurep! :feature evil)
|
||||
;;;###if (featurep! :editor evil)
|
||||
|
||||
;;;###autoload (autoload '+gist:send "tools/gist/autoload/evil" nil t)
|
||||
(evil-define-operator +gist:send (bang)
|
||||
|
|
189
modules/tools/lookup/README.org
Normal file
189
modules/tools/lookup/README.org
Normal file
|
@ -0,0 +1,189 @@
|
|||
#+TITLE: tools/lookup
|
||||
#+DATE: January 4, 2018
|
||||
#+SINCE: v2.0.9
|
||||
#+STARTUP: inlineimages
|
||||
|
||||
* Table of Contents :TOC:
|
||||
- [[Description][Description]]
|
||||
- [[Module Flags][Module Flags]]
|
||||
- [[Plugins][Plugins]]
|
||||
- [[Install][Install]]
|
||||
- [[Module flags][Module flags]]
|
||||
- [[Dependencies][Dependencies]]
|
||||
- [[Features][Features]]
|
||||
- [[Jump to definition][Jump to definition]]
|
||||
- [[Find references][Find references]]
|
||||
- [[Look up documentation][Look up documentation]]
|
||||
- [[Search a specific documentation backend][Search a specific documentation backend]]
|
||||
- [[Configuration][Configuration]]
|
||||
- [[Settings][Settings]]
|
||||
- [[Open in eww instead of browser][Open in eww instead of browser]]
|
||||
- [[Appendix][Appendix]]
|
||||
- [[Commands][Commands]]
|
||||
|
||||
* Description
|
||||
Integrates with code navigation and documentation tools to help you quickly look
|
||||
up definitions, references and documentation.
|
||||
|
||||
+ Jump-to-definition and find-references implementations that just work.
|
||||
+ Powerful xref integration for languages that support it.
|
||||
+ Documentation lookup for a variety of online sources (like devdocs.io,
|
||||
stackoverflow or youtube).
|
||||
+ Integration with Dash.app docsets.
|
||||
|
||||
** Module Flags
|
||||
+ ~+docsets~ Enable integration with Dash.app docsets.
|
||||
|
||||
** Plugins
|
||||
+ [[https://github.com/jacktasia/dumb-jump][dumb-jump]]
|
||||
+ [[https://github.com/alexmurray/ivy-xref][ivy-xref]] or [[https://github.com/brotzeit/helm-xref][helm-xref]]
|
||||
+ [[https://github.com/nathankot/counsel-dash][counsel-dash]] or [[https://github.com/areina/helm-dash][helm-dash]]
|
||||
|
||||
* Install
|
||||
To enable the module add =:tools lookup= to your ~doom!~ block in
|
||||
=~/.emacs.d/init.el=.
|
||||
|
||||
** Module flags
|
||||
This module provides two flags:
|
||||
|
||||
+ ~+docsets~ Enables integration with Dash docsets.
|
||||
|
||||
** Dependencies
|
||||
This module has several soft dependencies:
|
||||
|
||||
+ ~the_silver_searcher~ and/or ~ripgrep~ as a last-resort fallback for
|
||||
jump-to-definition/find-references.
|
||||
+ Optionally, ~sqlite3~ for Dash docset support.
|
||||
|
||||
*** MacOS
|
||||
#+BEGIN_SRC sh
|
||||
brew install the_silver_searcher ripgrep
|
||||
|
||||
# An older version of sqlite is included in MacOS. If it causes you problems (and
|
||||
# it has been reported that it will), install it through homebrew:
|
||||
brew install sqlite
|
||||
# Note that it's keg-only, meaning it isn't symlinked to /usr/local/bin. You'll
|
||||
# have to add it to PATH yourself (or symlink it into your PATH somewhere). e.g.
|
||||
export PATH="/usr/local/opt/sqlite/bin:$PATH"
|
||||
#+END_SRC
|
||||
|
||||
*** Arch Linux
|
||||
#+BEGIN_SRC sh
|
||||
sudo pacman -S sqlite the_silver_searcher ripgrep
|
||||
#+END_SRC
|
||||
|
||||
* Features
|
||||
** Jump to definition
|
||||
Use ~+lookup/definition~ (bound to =gd= in normal mode) to jump to the
|
||||
definition of the symbol at point
|
||||
|
||||
This module provides a goto-definition implementation that will try the
|
||||
following sources before giving up:
|
||||
|
||||
1. Whatever ~:definition~ function is registered for the current buffer with the
|
||||
~:lookup~ setting (see "Configuration" section).
|
||||
2. Any available xref backends.
|
||||
3. ~dumb-jump~ (a text search with aides to reduce false positives).
|
||||
3. An ordinary project-wide text search with ripgrep or the_silver_searcher.
|
||||
5. If ~evil-mode~ is active, use ~evil-goto-definition~, which preforms a simple
|
||||
text search within the current buffer.
|
||||
|
||||
If there are multiple results, you will be prompted to select one.
|
||||
|
||||
** Find references
|
||||
Use ~+lookup/references~ (bound to =gD= in normal mode) to see a list of
|
||||
references for the symbol at point from throughout your project.
|
||||
|
||||
Like ~+lookup/definition~, this tries a number of sources before giving up. It
|
||||
will try:
|
||||
|
||||
1. Whatever ~:references~ function is registered for the current buffer with the
|
||||
~:lookup~ setting (see "Configuration" section).
|
||||
2. Any available xref backends.
|
||||
3. An ordinary project-wide text search with ripgrep or the_silver_searcher.
|
||||
|
||||
If there are multiple results, you will be prompted to select one.
|
||||
|
||||
** Look up documentation
|
||||
~+lookup/documentation~ (bound to =K= in normal mode) will open documentation
|
||||
for the symbol at point.
|
||||
|
||||
Depending on your configuration, this will try a list of sources:
|
||||
|
||||
1. Whatever ~:documentation~ function is registered for the current buffer with
|
||||
the ~:lookup~ setting (see "Configuration" section).
|
||||
2. Any Dash.app docsets, if any are installed for the current major mode.
|
||||
3. devdocs.io, if it has a docset for the current mode.
|
||||
4. An online search; using the last engine used (it will prompt you the first
|
||||
time, or if ~current-prefix-arg~ is non-nil).
|
||||
|
||||
** Search a specific documentation backend
|
||||
You can perform a documentation lookup on any backends directly:
|
||||
|
||||
+ Dash Docsets: ~+lookup/in-docsets~, or ~:dash QUERY~ for evil users.
|
||||
+ devdocs.io: ~+lookup/in-devdocs~, or ~:dd QUERY~ for evil users.
|
||||
+ Online (generic): ~+lookup/online~ or ~+lookup/online-select~ (bound to =SPC /
|
||||
o=), or ~:lo[okup] QUERY~ for evil users.
|
||||
|
||||
* Configuration
|
||||
** Settings
|
||||
This module provides two setters:
|
||||
|
||||
*** ~set-lookup-handlers! MODES &rest PLIST~
|
||||
Defines a lookup target for major MODES (one major-mode symbol or a list
|
||||
thereof). PLIST accepts the following optional properties:
|
||||
|
||||
+ ~:definition FN~ :: Run when jumping to a symbol's definition. Used by
|
||||
~+lookup/definition~.
|
||||
+ ~:references FN~ :: Run when looking for usage references of a symbol in the
|
||||
current project. Used by ~+lookup/references~.
|
||||
+ ~:documentation FN~ :: Run when looking up documentation for a symbol. Used by
|
||||
~+lookup/documentation~.
|
||||
+ ~:file FN~ :: Run when looking up the file for a symbol/string. Typically a
|
||||
file path. Used by ~+lookup/file~.
|
||||
+ ~:xref-backend FN~ :: Defines an xref backend for a major-mode. With this,
|
||||
:definition and :references are unnecessary.
|
||||
|
||||
**** Example
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
;; For python-mode, anaconda-mode offers a backend for all three lookup
|
||||
;; functions. We can register them like so:
|
||||
(set-lookup-handlers! 'python-mode
|
||||
:definition #'anaconda-mode-find-definitions
|
||||
:references #'anaconda-mode-find-references
|
||||
:documentation #'anaconda-mode-show-doc)
|
||||
|
||||
;; If a language or plugin provides a custom xref backend available for it, use
|
||||
;; that instead. It will provide the best jump-to-definition and find-references
|
||||
;; experience. You can specify custom xref backends with:
|
||||
(set-lookup-handlers! 'js2-mode :xref-backend #'xref-js2-xref-backend)
|
||||
;; NOTE: xref doesn't provide a :documentation backend.
|
||||
#+END_SRC
|
||||
|
||||
*** ~set-docsets! MODES &rest DOCSETS~
|
||||
Registers DOCSETS (one string or list of strings) for MODES (one major mode
|
||||
symbol or a list of them). It is used by ~+lookup/in-docsets~ and
|
||||
~+lookup/documentation~.
|
||||
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
(set-docsets! 'js2-mode "JavaScript" "JQuery")
|
||||
;; Add docsets to minor modes by starting DOCSETS with :add
|
||||
(set-docsets! 'rjsx-mode :add "React")
|
||||
;; Or remove docsets from minor modes
|
||||
(set-docsets! 'nodejs-mode :remove "JQuery")
|
||||
#+END_SRC
|
||||
|
||||
** Open in eww instead of browser
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
(setq +lookup-open-url-fn 'eww)
|
||||
#+END_SRC
|
||||
|
||||
* Appendix
|
||||
** Commands
|
||||
+ ~+lookup/definition~
|
||||
+ ~+lookup/references~
|
||||
+ ~+lookup/documentation~
|
||||
+ ~+lookup/online~
|
||||
+ ~+lookup/online-select~
|
||||
+ ~+lookup/in-devdocs~
|
||||
+ ~+lookup/in-docsets~
|
100
modules/tools/lookup/autoload/docsets.el
Normal file
100
modules/tools/lookup/autoload/docsets.el
Normal file
|
@ -0,0 +1,100 @@
|
|||
;;; tools/lookup/autoload/docsets.el -*- lexical-binding: t; -*-
|
||||
;;;###if (featurep! +docsets)
|
||||
|
||||
(defvar +lookup-docset-alist nil
|
||||
"An alist mapping major and minor modes to lists of Dash docsets.
|
||||
|
||||
Entries are added by `set-docsets!' and used by `+lookup-docsets-for-buffer' to
|
||||
assemble a list of installed & active docsets.")
|
||||
|
||||
;;;###autodef
|
||||
(defun set-docsets! (modes &rest docsets)
|
||||
"Registers a list of DOCSETS for MODES.
|
||||
|
||||
MODES can be one major mode, or a list thereof.
|
||||
|
||||
DOCSETS can be strings, each representing a dash docset, or a vector with the
|
||||
structure [DOCSET FORM]. If FORM evaluates to nil, the DOCSET is omitted. If it
|
||||
is non-nil, (format DOCSET FORM) is used as the docset.
|
||||
|
||||
The first element in DOCSETS can be :add or :remove, making it easy for users to
|
||||
add to or remove default docsets from modes.
|
||||
|
||||
DOCSETS can also contain sublists, which will be flattened.
|
||||
|
||||
Example:
|
||||
|
||||
(set-docsets! '(js2-mode rjsx-mode) \"JavaScript\"
|
||||
[\"React\" (eq major-mode 'rjsx-mode)]
|
||||
[\"TypeScript\" (bound-and-true-p tide-mode)])
|
||||
|
||||
Used by `+lookup/in-docsets' and `+lookup/documentation'."
|
||||
(declare (indent defun))
|
||||
(dolist (mode (doom-enlist modes))
|
||||
(if (null docsets)
|
||||
(setq +lookup-docset-alist
|
||||
(delq (assq mode +lookup-docset-alist)
|
||||
+lookup-docset-alist))
|
||||
(let ((action (if (keywordp (car docsets)) (pop docsets)))
|
||||
(docsets (mapcan #'doom-enlist docsets))) ; flatten list
|
||||
(setf (alist-get mode +lookup-docset-alist)
|
||||
(pcase action
|
||||
(:add (append docsets (alist-get mode +lookup-docset-alist)))
|
||||
(:remove (cl-set-difference (alist-get mode +lookup-docset-alist) docsets))
|
||||
(_ docsets)))))))
|
||||
|
||||
|
||||
;;
|
||||
;; Library
|
||||
|
||||
;;;###autoload
|
||||
(defun +lookup-docsets-for-buffer ()
|
||||
"Return list of installed & selected docsets for the current major mode.
|
||||
|
||||
This list is built from `+lookup-docset-alist'."
|
||||
(cl-loop for docset in (cdr (assq major-mode +lookup-docset-alist))
|
||||
when (or (stringp docset)
|
||||
(and (vectorp docset)
|
||||
(eval (aref docset 1) t)))
|
||||
collect docset))
|
||||
|
||||
;;;###autoload
|
||||
(defun +lookup-docset-installed-p (docset)
|
||||
"Return t if DOCSET is installed."
|
||||
(let ((path (helm-dash-docsets-path)))
|
||||
(file-directory-p
|
||||
(expand-file-name (format "%s.docset" docset)
|
||||
path))))
|
||||
|
||||
;;;###autoload
|
||||
(autoload 'helm-dash-installed-docsets "helm-dash")
|
||||
|
||||
;;;###autoload
|
||||
(autoload 'helm-dash-docset-installed-p "helm-dash")
|
||||
|
||||
|
||||
;;
|
||||
;; Commands
|
||||
|
||||
;;;###autoload
|
||||
(defalias '+lookup/install-docset #'helm-dash-install-docset)
|
||||
|
||||
(defvar counsel-dash-docsets)
|
||||
(defvar helm-dash-docsets)
|
||||
;;;###autoload
|
||||
(defun +lookup/in-docsets (&optional query docsets)
|
||||
"Lookup QUERY in dash DOCSETS.
|
||||
|
||||
QUERY is a string and docsets in an array of strings, each a name of a Dash
|
||||
docset. Requires either helm or ivy.
|
||||
|
||||
Use `+lookup/install-docset' to install docsets."
|
||||
(interactive)
|
||||
(let* ((counsel-dash-docsets (or docsets (+lookup-docsets-for-buffer)))
|
||||
(helm-dash-docsets counsel-dash-docsets)
|
||||
(query (or query (+lookup--symbol-or-region) "")))
|
||||
(cond ((featurep! :completion helm)
|
||||
(helm-dash query))
|
||||
((featurep! :completion ivy)
|
||||
(counsel-dash query))
|
||||
((user-error "No dash backend is installed, enable ivy or helm.")))))
|
22
modules/tools/lookup/autoload/evil.el
Normal file
22
modules/tools/lookup/autoload/evil.el
Normal file
|
@ -0,0 +1,22 @@
|
|||
;;; tools/lookup/autoload/evil.el -*- lexical-binding: t; -*-
|
||||
;;;###if (featurep! :editor evil)
|
||||
|
||||
;;;###autoload (autoload '+lookup:online "tools/lookup/autoload/evil" nil t)
|
||||
(evil-define-command +lookup:online (query &optional bang)
|
||||
"Look up QUERY online. Will prompt for search engine the first time, then
|
||||
reuse it on consecutive uses of this command. If BANG, always prompt for search
|
||||
engine."
|
||||
(interactive "<a><!>")
|
||||
(+lookup/online query (+lookup--online-provider bang 'evil-ex)))
|
||||
|
||||
;;;###autoload (autoload '+lookup:dash "tools/lookup/autoload/evil" nil t)
|
||||
(evil-define-command +lookup:dash (query &optional bang)
|
||||
"Look up QUERY in your dash docsets. If BANG, prompt to select a docset (and
|
||||
install it if necessary)."
|
||||
(interactive "<a><!>")
|
||||
(let (selected)
|
||||
(when bang
|
||||
(setq selected (helm-dash-read-docset "Select docset" (helm-dash-official-docsets)))
|
||||
(unless (+lookup-docset-installed-p selected)
|
||||
(+lookup/install-docset selected)))
|
||||
(+lookup/in-docsets query (or selected (+lookup-docsets-for-buffer)))))
|
320
modules/tools/lookup/autoload/lookup.el
Normal file
320
modules/tools/lookup/autoload/lookup.el
Normal file
|
@ -0,0 +1,320 @@
|
|||
;;; tools/lookup/autoload/lookup.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defvar +lookup--handler-alist nil)
|
||||
|
||||
;;;###autodef
|
||||
(cl-defun set-lookup-handlers!
|
||||
(modes &rest plist &key definition references documentation file xref-backend async)
|
||||
"Define a jump target for major MODES.
|
||||
|
||||
This overwrites previously defined handlers for MODES. If used on minor modes,
|
||||
they are combined with handlers defined for other minor modes or the major mode
|
||||
it's activated in.
|
||||
|
||||
This can be passed nil as its second argument to unset handlers for MODES. e.g.
|
||||
|
||||
(set-lookup-handlers! 'python-mode nil)
|
||||
|
||||
Otherwise, these properties are available to be set:
|
||||
|
||||
:definition FN
|
||||
Run when jumping to a symbol's definition.
|
||||
Used by `+lookup/definition'.
|
||||
:references FN
|
||||
Run when looking for usage references of a symbol in the current project.
|
||||
Used by `+lookup/references'.
|
||||
:documentation FN
|
||||
Run when looking up documentation for a symbol.
|
||||
Used by `+lookup/documentation'.
|
||||
:file FN
|
||||
Run when looking up the file for a symbol/string. Typically a file path.
|
||||
Used by `+lookup/file'.
|
||||
:xref-backend FN
|
||||
Defines an xref backend for a major-mode. If you define :definition and
|
||||
:references along with :xref-backend, those will have higher precedence.
|
||||
:async BOOL
|
||||
Indicates that the supplied handlers *after* this property are asynchronous.
|
||||
Note: async handlers do not fall back to the default handlers, due to their
|
||||
nature. To get around this, you must write specialized wrappers to wait for
|
||||
the async response and return 'fallback."
|
||||
(declare (indent defun))
|
||||
(dolist (mode (doom-enlist modes))
|
||||
(let ((hook (intern (format "%s-hook" mode)))
|
||||
(fn (intern (format "+lookup|init-%s" mode))))
|
||||
(cond ((null (car plist))
|
||||
(remove-hook hook fn)
|
||||
(delq! mode +lookup--handler-alist 'assq)
|
||||
(unintern fn nil))
|
||||
((fset fn
|
||||
(lambda ()
|
||||
(when (or (eq major-mode mode)
|
||||
(and (boundp mode)
|
||||
(symbol-value mode)))
|
||||
(cl-mapc #'+lookup--set-handler
|
||||
(list definition
|
||||
references
|
||||
documentation
|
||||
file
|
||||
xref-backend)
|
||||
(list '+lookup-definition-functions
|
||||
'+lookup-references-functions
|
||||
'+lookup-documentation-functions
|
||||
'+lookup-file-functions
|
||||
'xref-backend-functions)))))
|
||||
(add-hook hook fn))))))
|
||||
|
||||
|
||||
;;
|
||||
;;; Helpers
|
||||
|
||||
(defun +lookup--set-handler (spec functions-var &optional async)
|
||||
(when spec
|
||||
(cl-destructuring-bind (fn . plist)
|
||||
(doom-enlist spec)
|
||||
(put fn '+lookup-plist (plist-put plist :async async))
|
||||
(add-hook functions-var fn nil t))))
|
||||
|
||||
(defun +lookup--symbol-or-region (&optional initial)
|
||||
(cond ((stringp initial)
|
||||
initial)
|
||||
((use-region-p)
|
||||
(buffer-substring-no-properties (region-beginning)
|
||||
(region-end)))
|
||||
((require 'xref nil t)
|
||||
(xref-backend-identifier-at-point (xref-find-backend)))))
|
||||
|
||||
(defun +lookup--run-handler (handler identifier)
|
||||
(if (commandp handler)
|
||||
(call-interactively handler)
|
||||
(funcall handler identifier)))
|
||||
|
||||
(defun +lookup--run-handlers (handler identifier origin &optional other-window)
|
||||
(doom-log "Looking up '%s' with '%s'" identifier handler)
|
||||
(condition-case e
|
||||
(let ((plist (get handler '+lookup-plist)))
|
||||
(cond ((plist-get plist :async)
|
||||
(when other-window
|
||||
;; If async, we can't catch the window change or destination
|
||||
;; buffer reliably, so we set up the new window ahead of time.
|
||||
(switch-to-buffer-other-window (current-buffer))
|
||||
(goto-char (marker-position origin)))
|
||||
(+lookup--run-handler handler identifier)
|
||||
t)
|
||||
((save-window-excursion
|
||||
(and (or (+lookup--run-handler handler identifier)
|
||||
(null origin)
|
||||
(/= (point-marker) origin))
|
||||
(point-marker))))))
|
||||
((error user-error debug)
|
||||
(message "Lookup handler %S: %s" handler e)
|
||||
nil)))
|
||||
|
||||
(defun +lookup--jump-to (prop identifier &optional other-window)
|
||||
(let ((result
|
||||
(run-hook-wrapped
|
||||
(plist-get (list :definition '+lookup-definition-functions
|
||||
:references '+lookup-references-functions
|
||||
:documentation '+lookup-documentation-functions
|
||||
:file '+lookup-file-functions)
|
||||
prop)
|
||||
#'+lookup--run-handlers
|
||||
identifier
|
||||
(point-marker)
|
||||
other-window)))
|
||||
(if (not (markerp result))
|
||||
(ignore (message "No lookup handler could find %S" identifier))
|
||||
(funcall (if other-window
|
||||
#'switch-to-buffer-other-window
|
||||
#'switch-to-buffer)
|
||||
(marker-buffer result))
|
||||
(goto-char result)
|
||||
(recenter)
|
||||
result)))
|
||||
|
||||
|
||||
;;
|
||||
;;; Lookup backends
|
||||
|
||||
(defun +lookup-xref-definitions-backend (identifier)
|
||||
"Non-interactive wrapper for `xref-find-definitions'"
|
||||
(xref-find-definitions identifier))
|
||||
|
||||
(defun +lookup-xref-references-backend (identifier)
|
||||
"Non-interactive wrapper for `xref-find-references'"
|
||||
(xref-find-references identifier))
|
||||
|
||||
(defun +lookup-dumb-jump-backend (_identifier)
|
||||
"Look up the symbol at point (or selection) with `dumb-jump', which conducts a
|
||||
project search with ag, rg, pt, or git-grep, combined with extra heuristics to
|
||||
reduce false positives.
|
||||
|
||||
This backend prefers \"just working\" over accuracy."
|
||||
(when (require 'dumb-jump nil t)
|
||||
;; dumb-jump doesn't tell us if it succeeded or not
|
||||
(plist-get (dumb-jump-go) :results)))
|
||||
|
||||
(defun +lookup-project-search-backend (identifier)
|
||||
"Conducts a simple project text search for IDENTIFIER.
|
||||
|
||||
Uses and requires `+ivy-file-search' or `+helm-file-search'. Will return nil if
|
||||
neither is available. These search backends will use ag, rg, or pt (in an order
|
||||
dictated by `+ivy-project-search-engines' or `+helm-project-search-engines',
|
||||
falling back to git-grep)."
|
||||
(unless identifier
|
||||
(let ((query (rxt-quote-pcre identifier)))
|
||||
(ignore-errors
|
||||
(cond ((featurep! :completion ivy)
|
||||
(+ivy-file-search nil :query query)
|
||||
t)
|
||||
((featurep! :completion helm)
|
||||
(+helm-file-search nil :query query)
|
||||
t))))))
|
||||
|
||||
(defun +lookup-evil-goto-definition-backend (_identifier)
|
||||
"Uses `evil-goto-definition' to conduct a text search for IDENTIFIER in the
|
||||
current buffer."
|
||||
(and (fboundp 'evil-goto-definition)
|
||||
(ignore-errors
|
||||
(cl-destructuring-bind (beg . end)
|
||||
(bounds-of-thing-at-point 'symbol)
|
||||
(evil-goto-definition)
|
||||
(let ((pt (point)))
|
||||
(not (and (>= pt beg)
|
||||
(< pt end))))))))
|
||||
|
||||
(defun +lookup-dash-docsets-backend (identifier)
|
||||
"Looks up IDENTIFIER in available Dash docsets, if any are installed.
|
||||
|
||||
Docsets must be installed with `+lookup/install-docset'. These can also be
|
||||
accessed via `+lookup/in-docsets'."
|
||||
(and (featurep! +docsets)
|
||||
(or (require 'counsel-dash nil t)
|
||||
(require 'helm-dash nil t))
|
||||
(let ((docsets (+lookup-docsets-for-buffer)))
|
||||
(when (cl-some #'+lookup-docset-installed-p docsets)
|
||||
(+lookup/in-docsets identifier docsets)
|
||||
t))))
|
||||
|
||||
|
||||
;;
|
||||
;;; Main commands
|
||||
|
||||
;;;###autoload
|
||||
(defun +lookup/definition (identifier &optional other-window)
|
||||
"Jump to the definition of IDENTIFIER (defaults to the symbol at point).
|
||||
|
||||
If OTHER-WINDOW (universal argument), open the result in another window.
|
||||
|
||||
Each function in `+lookup-definition-functions' is tried until one changes the
|
||||
point or current buffer. Falls back to dumb-jump, naive
|
||||
ripgrep/the_silver_searcher text search, then `evil-goto-definition' if
|
||||
evil-mode is active."
|
||||
(interactive
|
||||
(list (+lookup--symbol-or-region)
|
||||
current-prefix-arg))
|
||||
(cond ((null identifier) (user-error "Nothing under point"))
|
||||
|
||||
((+lookup--jump-to :definition identifier other-window))
|
||||
|
||||
((error "Couldn't find the definition of '%s'" identifier))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +lookup/references (identifier &optional other-window)
|
||||
"Show a list of usages of IDENTIFIER (defaults to the symbol at point)
|
||||
|
||||
Tries each function in `+lookup-references-functions' until one changes the
|
||||
point and/or current buffer. Falls back to a naive ripgrep/the_silver_searcher
|
||||
search otherwise."
|
||||
(interactive
|
||||
(list (+lookup--symbol-or-region)
|
||||
current-prefix-arg))
|
||||
(cond ((null identifier) (user-error "Nothing under point"))
|
||||
|
||||
((+lookup--jump-to :references identifier other-window))
|
||||
|
||||
((error "Couldn't find references of '%s'" identifier))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +lookup/documentation (identifier &optional _arg)
|
||||
"Show documentation for IDENTIFIER (defaults to symbol at point or selection.
|
||||
|
||||
First attempts the :documentation handler specified with `set-lookup-handlers!'
|
||||
for the current mode/buffer (if any), then falls back to the backends in
|
||||
`+lookup-documentation-functions'."
|
||||
(interactive
|
||||
(list (+lookup--symbol-or-region)
|
||||
current-prefix-arg))
|
||||
(cond ((+lookup--jump-to :documentation identifier t))
|
||||
|
||||
((user-error "Couldn't find documentation for '%s'" identifier))))
|
||||
|
||||
(defvar ffap-file-finder)
|
||||
;;;###autoload
|
||||
(defun +lookup/file (path)
|
||||
"Figure out PATH from whatever is at point and open it.
|
||||
|
||||
Each function in `+lookup-file-functions' is tried until one changes the point
|
||||
or the current buffer.
|
||||
|
||||
Otherwise, falls back on `find-file-at-point'."
|
||||
(interactive
|
||||
(progn
|
||||
(require 'ffap)
|
||||
(list
|
||||
(or (ffap-guesser)
|
||||
(ffap-read-file-or-url
|
||||
(if ffap-url-regexp "Find file or URL: " "Find file: ")
|
||||
(+lookup--symbol-or-region))))))
|
||||
(require 'ffap)
|
||||
(cond ((not path)
|
||||
(call-interactively #'find-file-at-point))
|
||||
|
||||
((ffap-url-p path)
|
||||
(find-file-at-point path))
|
||||
|
||||
((not (+lookup--jump-to :file path))
|
||||
(let ((fullpath (expand-file-name path)))
|
||||
(when (and buffer-file-name (file-equal-p fullpath buffer-file-name))
|
||||
(user-error "Already here"))
|
||||
(let* ((insert-default-directory t)
|
||||
(project-root (doom-project-root))
|
||||
(ffap-file-finder
|
||||
(cond ((not (file-directory-p fullpath))
|
||||
#'find-file)
|
||||
((file-in-directory-p fullpath project-root)
|
||||
(lambda (dir)
|
||||
(let ((default-directory dir))
|
||||
(without-project-cache!
|
||||
(let ((file (projectile-completing-read "Find file: "
|
||||
(projectile-current-project-files)
|
||||
:initial-input path)))
|
||||
(find-file (expand-file-name file (doom-project-root)))
|
||||
(run-hooks 'projectile-find-file-hook))))))
|
||||
(#'doom-project-browse))))
|
||||
(find-file-at-point path))))))
|
||||
|
||||
|
||||
;;
|
||||
;;; Source-specific commands
|
||||
|
||||
(defvar counsel-dash-docsets)
|
||||
(defvar helm-dash-docsets)
|
||||
;;;###autoload
|
||||
(defun +lookup/in-docsets (&optional query docsets)
|
||||
"Looks up QUERY (a string) in available Dash docsets for the current buffer.
|
||||
|
||||
DOCSETS is a list of docset strings. Docsets can be installed with
|
||||
`+lookup/install-docset'."
|
||||
(interactive)
|
||||
(let* ((counsel-dash-docsets
|
||||
(unless (eq docsets 'blank)
|
||||
(or docsets
|
||||
(or (bound-and-true-p counsel-dash-docsets)
|
||||
(bound-and-true-p helm-dash-docsets)))))
|
||||
(helm-dash-docsets counsel-dash-docsets)
|
||||
(query (or query (+lookup--symbol-or-region) "")))
|
||||
(cond ((featurep! :completion helm)
|
||||
(helm-dash query))
|
||||
((featurep! :completion ivy)
|
||||
(counsel-dash query))
|
||||
((user-error "No dash backend is installed, enable ivy or helm.")))))
|
65
modules/tools/lookup/autoload/online.el
Normal file
65
modules/tools/lookup/autoload/online.el
Normal file
|
@ -0,0 +1,65 @@
|
|||
;;; tools/lookup/autoload/online.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defvar +lookup--last-provider nil)
|
||||
|
||||
(defun +lookup--online-provider (&optional force-p namespace)
|
||||
(let ((key (or namespace major-mode)))
|
||||
(or (and (not force-p)
|
||||
(cdr (assq key +lookup--last-provider)))
|
||||
(when-let* ((provider
|
||||
(completing-read
|
||||
"Search on: "
|
||||
(mapcar #'car +lookup-provider-url-alist)
|
||||
nil t)))
|
||||
(setf (alist-get key +lookup--last-provider) provider)
|
||||
provider))))
|
||||
|
||||
(defun +lookup-online-backend (identifier)
|
||||
"Opens the browser and searches for IDENTIFIER online.
|
||||
|
||||
Will prompt for which search engine to use the first time (or if the universal
|
||||
argument is non-nil)."
|
||||
(+lookup/online
|
||||
identifier
|
||||
(+lookup--online-provider (not current-prefix-arg))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +lookup/online (search &optional provider)
|
||||
"Looks up SEARCH (a string) in you browser using PROVIDER.
|
||||
|
||||
PROVIDER should be a key of `+lookup-provider-url-alist'.
|
||||
|
||||
When used interactively, it will prompt for a query and, for the first time, the
|
||||
provider from `+lookup-provider-url-alist'. On consecutive uses, the last
|
||||
provider will be reused. If the universal argument is supplied, always prompt
|
||||
for the provider."
|
||||
(interactive
|
||||
(let ((provider (+lookup--online-provider current-prefix-arg)))
|
||||
(list (or (and (use-region-p)
|
||||
(buffer-substring-no-properties (region-beginning)
|
||||
(region-end)))
|
||||
(read-string (format "Search for (on %s): " provider)
|
||||
(thing-at-point 'symbol t)))
|
||||
provider)))
|
||||
(condition-case-unless-debug e
|
||||
(let ((url (cdr (assoc provider +lookup-provider-url-alist))))
|
||||
(unless url
|
||||
(user-error "'%s' is an invalid search engine" provider))
|
||||
(when (or (functionp url) (symbolp url))
|
||||
(setq url (funcall url)))
|
||||
(cl-assert (stringp url))
|
||||
(when (string-empty-p search)
|
||||
(user-error "The search query is empty"))
|
||||
(funcall +lookup-open-url-fn (format url (url-encode-url search))))
|
||||
(error
|
||||
(setq +lookup--last-provider
|
||||
(delq (assq major-mode +lookup--last-provider)
|
||||
+lookup--last-provider))
|
||||
(signal (car e) (cdr e)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +lookup/online-select ()
|
||||
"Runs `+lookup/online', but always prompts for the provider to use."
|
||||
(interactive)
|
||||
(let ((current-prefix-arg t))
|
||||
(call-interactively #'+lookup/online)))
|
144
modules/tools/lookup/config.el
Normal file
144
modules/tools/lookup/config.el
Normal file
|
@ -0,0 +1,144 @@
|
|||
;;; tools/lookup/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
;; "What am I looking at?" This module helps you answer this question.
|
||||
;;
|
||||
;; + `+lookup/definition': a jump-to-definition that should 'just work'
|
||||
;; + `+lookup/references': find a symbol's references in the current project
|
||||
;; + `+lookup/file': open the file referenced at point
|
||||
;; + `+lookup/online'; look up a symbol on online resources
|
||||
;; + `+lookup/in-docsets': look up in Dash docsets
|
||||
;;
|
||||
;; This module uses `xref', an experimental new library in Emacs. It may change
|
||||
;; in the future. When xref can't be depended on it will fall back to
|
||||
;; `dumb-jump' to find what you want.
|
||||
|
||||
(defvar +lookup-provider-url-alist
|
||||
'(("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")
|
||||
("DevDocs.io" . "https://devdocs.io/#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
|
||||
produces an url. Used by `+lookup/online'.")
|
||||
|
||||
(defvar +lookup-open-url-fn #'browse-url
|
||||
"Function to use to open search urls.")
|
||||
|
||||
(defvar +lookup-definition-functions
|
||||
'(+lookup-xref-definitions-backend
|
||||
+lookup-dumb-jump-backend
|
||||
+lookup-project-search-backend
|
||||
+lookup-evil-goto-definition-backend)
|
||||
"Functions for `+lookup/definition' to try, before resorting to `dumb-jump'.
|
||||
Stops at the first function to return non-nil or change the current
|
||||
window/point.
|
||||
|
||||
If the argument is interactive (satisfies `commandp'), it is called with
|
||||
`call-interactively' (with no arguments). Otherwise, it is called with one
|
||||
argument: the identifier at point.")
|
||||
|
||||
(defvar +lookup-references-functions
|
||||
'(+lookup-xref-references-backend
|
||||
+lookup-project-search-backend)
|
||||
"Functions for `+lookup/references' to try, before resorting to `dumb-jump'.
|
||||
Stops at the first function to return non-nil or change the current
|
||||
window/point.
|
||||
|
||||
If the argument is interactive (satisfies `commandp'), it is called with
|
||||
`call-interactively' (with no arguments). Otherwise, it is called with one
|
||||
argument: the identifier at point.")
|
||||
|
||||
(defvar +lookup-documentation-functions
|
||||
'(+lookup-dash-docsets-backend
|
||||
+lookup-online-backend)
|
||||
"Functions for `+lookup/documentation' to try, before resorting to
|
||||
`dumb-jump'. Stops at the first function to return non-nil or change the current
|
||||
window/point.
|
||||
|
||||
If the argument is interactive (satisfies `commandp'), it is called with
|
||||
`call-interactively' (with no arguments). Otherwise, it is called with one
|
||||
argument: the identifier at point.")
|
||||
|
||||
(defvar +lookup-file-functions ()
|
||||
"Function for `+lookup/file' to try, before restoring to `find-file-at-point'.
|
||||
Stops at the first function to return non-nil or change the current
|
||||
window/point.
|
||||
|
||||
If the argument is interactive (satisfies `commandp'), it is called with
|
||||
`call-interactively' (with no arguments). Otherwise, it is called with one
|
||||
argument: the identifier at point.")
|
||||
|
||||
;; Recenter buffer after certain jumps
|
||||
(add-hook!
|
||||
'(imenu-after-jump-hook evil-jumps-post-jump-hook
|
||||
counsel-grep-post-action-hook dumb-jump-after-jump-hook)
|
||||
#'recenter)
|
||||
|
||||
|
||||
;;
|
||||
;;; dumb-jump
|
||||
|
||||
(def-package! dumb-jump
|
||||
:commands dumb-jump-result-follow
|
||||
:config
|
||||
(setq dumb-jump-default-project doom-emacs-dir
|
||||
dumb-jump-aggressive nil
|
||||
dumb-jump-selector
|
||||
(cond ((featurep! :completion ivy) 'ivy)
|
||||
((featurep! :completion helm) 'helm)
|
||||
('popup))))
|
||||
|
||||
|
||||
;;
|
||||
;;; xref
|
||||
|
||||
;; By default, `etags--xref-backend' is the default xref backend. No need. We'll
|
||||
;; set these up ourselves in other modules.
|
||||
(setq-default xref-backend-functions '(t))
|
||||
|
||||
;; ...however, it breaks `projectile-find-tag', unless we put it back.
|
||||
(defun +lookup*projectile-find-tag (orig-fn)
|
||||
(let ((xref-backend-functions '(etags--xref-backend t)))
|
||||
(funcall orig-fn)))
|
||||
(advice-add #'projectile-find-tag :around #'+lookup*projectile-find-tag)
|
||||
|
||||
|
||||
(def-package! ivy-xref
|
||||
:when (featurep! :completion ivy)
|
||||
:after xref
|
||||
:config (setq xref-show-xrefs-function #'ivy-xref-show-xrefs))
|
||||
|
||||
|
||||
(def-package! helm-xref
|
||||
:when (featurep! :completion helm)
|
||||
:after xref
|
||||
:config (setq xref-show-xrefs-function #'helm-xref-show-xrefs))
|
||||
|
||||
|
||||
;;
|
||||
;;; Dash docset integration
|
||||
|
||||
;; Both packages depend on helm-dash, for now
|
||||
(def-package! helm-dash
|
||||
:when (featurep! +docsets)
|
||||
:defer t
|
||||
:init
|
||||
(setq helm-dash-enable-debugging doom-debug-mode
|
||||
helm-dash-browser-func #'eww)
|
||||
:config
|
||||
(unless (file-directory-p helm-dash-docsets-path)
|
||||
(setq helm-dash-docsets-path (concat doom-etc-dir "docsets/")))
|
||||
(unless (file-directory-p helm-dash-docsets-path)
|
||||
(make-directory helm-dash-docsets-path t)))
|
||||
|
||||
(def-package! counsel-dash
|
||||
:when (and (featurep! +docsets)
|
||||
(featurep! :completion ivy))
|
||||
:commands counsel-dash-install-docset
|
||||
:config (setq counsel-dash-min-length 2))
|
21
modules/tools/lookup/packages.el
Normal file
21
modules/tools/lookup/packages.el
Normal file
|
@ -0,0 +1,21 @@
|
|||
;; -*- no-byte-compile: t; -*-
|
||||
;;; tools/lookup/packages.el
|
||||
|
||||
;; `dumb-jump' uses the `helm-build-sync-source' macro, but this requires helm
|
||||
;; be loaded before it is byte-compiled during installation. To ensure this, we
|
||||
;; declare helm before dumb-jump.
|
||||
(when (featurep! :completion helm)
|
||||
(package! helm))
|
||||
|
||||
;;
|
||||
(package! dumb-jump)
|
||||
(when (featurep! :completion ivy)
|
||||
(package! ivy-xref))
|
||||
(when (featurep! :completion helm)
|
||||
(package! helm-xref))
|
||||
|
||||
(when (featurep! +docsets)
|
||||
(when (featurep! :completion helm)
|
||||
(package! helm-dash))
|
||||
(when (featurep! :completion ivy)
|
||||
(package! counsel-dash)))
|
|
@ -68,7 +68,7 @@ It is passed a user and repository name.")
|
|||
|
||||
|
||||
(def-package! evil-magit
|
||||
:when (featurep! :feature evil +everywhere)
|
||||
:when (featurep! :editor evil +everywhere)
|
||||
:after magit
|
||||
:init
|
||||
(setq evil-magit-state 'normal
|
||||
|
|
|
@ -5,5 +5,5 @@
|
|||
(package! forge)
|
||||
(package! magit-gitflow)
|
||||
(package! magit-todos)
|
||||
(when (featurep! :feature evil +everywhere)
|
||||
(when (featurep! :editor evil +everywhere)
|
||||
(package! evil-magit)))
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
;;; tools/tmux/autoload/evil.el -*- lexical-binding: t; -*-
|
||||
;;;###if (featurep! :feature evil)
|
||||
;;;###if (featurep! :editor evil)
|
||||
|
||||
;;;###autoload (autoload '+tmux:run "tools/tmux/autoload/evil" nil t)
|
||||
(evil-define-command +tmux:run (bang &optional command)
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
;; Automatically kill buffer when vterm exits.
|
||||
(add-to-list 'vterm-exit-functions (lambda (buffer) (if buffer (kill-buffer buffer))))
|
||||
|
||||
(when (featurep! :feature evil)
|
||||
(when (featurep! :editor evil)
|
||||
(evil-set-initial-state 'vterm-mode 'insert)
|
||||
;; Go back to normal state but don't move cursor backwards. Moving cursor
|
||||
;; backwards is the default Vim behavior but it is not appropriate in some
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue